first commit
This commit is contained in:
492
ATMEGA_OWL180/ATMEGA_OWL180.ino
Normal file
492
ATMEGA_OWL180/ATMEGA_OWL180.ino
Normal file
@@ -0,0 +1,492 @@
|
||||
|
||||
/* Ce programme a été élaboré à partir du code de M. Olivier LEBRUN réutilisé en grande partie pour réaliser un encodeur OWL CM180 (Micro+)
|
||||
/* ainsi qu'à partir du code (+ carte électronique à relier au compteur) de M. Pascal CARDON pour la partie téléinfo
|
||||
/* Onlinux a fourni des trames du OWL CM180 me permettant de faire les algo d'encodage (il a développer un code de décodage des trames)
|
||||
/* Je remercie les auteurs. Ci-dessous les liens vers leur site internet.
|
||||
/*=======================================================================================================================
|
||||
ONLINUX : Decode and parse the Oregon Scientific V3 radio data transmitted by OWL CM180 Energy sensor (433.92MHz)
|
||||
|
||||
References :
|
||||
http://blog.onlinux.fr
|
||||
https://github.com/onlinux/OWL-CMR180
|
||||
http://domotique.web2diz.net/?p=11
|
||||
http://www.domotique-info.fr/2014/05/recuperer-teleinformation-arduino/
|
||||
http://connectingstuff.net/blog/encodage-protocoles-oregon-scientific-sur-arduino/
|
||||
/*=======================================================================================================================
|
||||
/*
|
||||
* connectingStuff, Oregon Scientific v2.1 Emitter
|
||||
* http://connectingstuff.net/blog/encodage-protocoles-oregon-scientific-sur-arduino/
|
||||
*
|
||||
* Copyright (C) 2013 olivier.lebrun@gmail.com
|
||||
*
|
||||
/*=======================================================================================================================
|
||||
my_teleinfo
|
||||
(c) 2012-2013 by
|
||||
Script name : my_teleinfo
|
||||
http://www.domotique-info.fr/2014/05/recuperer-teleinformation-arduino/
|
||||
|
||||
VERSIONS HISTORY
|
||||
Version 1.00 30/11/2013 + Original version
|
||||
Version 1.10 03/05/2015 + Manu : Small ajustment to variabilise the PIN numbers for Transmiter and Teleinfo
|
||||
See ref here : http://domotique.web2diz.net/?p=11#
|
||||
|
||||
montage électronique conforme à http://www.domotique-info.fr/2014/05/recuperer-teleinformation-arduino/
|
||||
======================================================================================================================*/
|
||||
|
||||
|
||||
|
||||
#include <SoftwareSerial.h>
|
||||
// PIN SIETTINGS //
|
||||
const byte TELEINFO_PIN = 8; //Connexion TELEINFO
|
||||
const byte TX_PIN = 3; //emetteur 433 MHZ
|
||||
// PIN SIETTINGS //
|
||||
|
||||
const unsigned long TIME = 488;
|
||||
const unsigned long TWOTIME = TIME*2;
|
||||
|
||||
#define SEND_HIGH() digitalWrite(TX_PIN, HIGH)
|
||||
#define SEND_LOW() digitalWrite(TX_PIN, LOW)
|
||||
byte OregonMessageBuffer[13]; // OWL180
|
||||
//*********************************************************
|
||||
SoftwareSerial* mySerial;
|
||||
char HHPHC;
|
||||
int ISOUSC; // intensité souscrite
|
||||
int IINST; // intensité instantanée en A
|
||||
int IMAX; // intensité maxi en A
|
||||
int PAPP; // puissance apparente en VA
|
||||
unsigned long HCHC; // compteur Heures Creuses en W
|
||||
unsigned long HCHP; // compteur Heures Pleines en W
|
||||
String PTEC; // Régime actuel : HPJB, HCJB, HPJW, HCJW, HPJR, HCJR
|
||||
String ADCO; // adresse compteur
|
||||
String OPTARIF; // option tarifaire
|
||||
String MOTDETAT; // status word
|
||||
String pgmVersion; // TeleInfo program version
|
||||
boolean ethernetIsOK;
|
||||
boolean teleInfoReceived;
|
||||
|
||||
|
||||
char chksum(char *buff, uint8_t len);
|
||||
boolean handleBuffer(char *bufferTeleinfo, int sequenceNumnber);
|
||||
char version[17] = "TeleInfo V 1.00";
|
||||
|
||||
unsigned long PAPP_arrondi; // PAPP*497/500/16 arrondi
|
||||
unsigned long chksum_CM180;
|
||||
unsigned long long HCP;
|
||||
|
||||
//********** debug
|
||||
// char buffer[100];// à virer ***************
|
||||
//**********************************************************************
|
||||
|
||||
/**
|
||||
* \brief Send logical "0" over RF
|
||||
* \details azero bit be represented by an off-to-on transition
|
||||
* \ of the RF signal at the middle of a clock period.
|
||||
* \ Remenber, the Oregon v2.1 protocol add an inverted bit first
|
||||
*/
|
||||
inline void sendZero(void)
|
||||
{
|
||||
SEND_LOW();
|
||||
delayMicroseconds(TIME);
|
||||
SEND_HIGH();
|
||||
delayMicroseconds(TIME);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Send logical "1" over RF
|
||||
* \details a one bit be represented by an on-to-off transition
|
||||
* \ of the RF signal at the middle of a clock period.
|
||||
* \ Remenber, the Oregon v2.1 protocol add an inverted bit first
|
||||
*/
|
||||
inline void sendOne(void)
|
||||
{
|
||||
SEND_HIGH();
|
||||
delayMicroseconds(TIME);
|
||||
SEND_LOW();
|
||||
delayMicroseconds(TIME);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Send a buffer over RF
|
||||
* \param data Data to send
|
||||
* \param size size of data to send
|
||||
*/
|
||||
void sendData(byte *data, byte size)
|
||||
{
|
||||
for(byte i = 0; i < size; ++i)
|
||||
{
|
||||
(bitRead(data[i], 0)) ? sendOne() : sendZero();
|
||||
(bitRead(data[i], 1)) ? sendOne() : sendZero();
|
||||
(bitRead(data[i], 2)) ? sendOne() : sendZero();
|
||||
(bitRead(data[i], 3)) ? sendOne() : sendZero();
|
||||
(bitRead(data[i], 4)) ? sendOne() : sendZero();
|
||||
(bitRead(data[i], 5)) ? sendOne() : sendZero();
|
||||
(bitRead(data[i], 6)) ? sendOne() : sendZero();
|
||||
(bitRead(data[i], 7)) ? sendOne() : sendZero();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Send an Oregon message
|
||||
* \param data The Oregon message
|
||||
*/
|
||||
void sendOregon(byte *data, byte size)
|
||||
{
|
||||
sendPreamble();
|
||||
sendData(data,size);
|
||||
sendPostamble();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Send preamble
|
||||
* \details The preamble consists of 10 X "1" bits (minimum)
|
||||
*/
|
||||
inline void sendPreamble(void)
|
||||
{
|
||||
for(byte i = 0; i < 10; ++i) //OWL CM180
|
||||
{
|
||||
sendOne();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Send postamble
|
||||
*/
|
||||
inline void sendPostamble(void)
|
||||
{
|
||||
|
||||
for(byte i = 0; i <4 ; ++i) //OWL CM180
|
||||
{
|
||||
sendZero() ;
|
||||
}
|
||||
SEND_LOW();
|
||||
delayMicroseconds(TIME);
|
||||
}
|
||||
|
||||
//=================================================================================================================
|
||||
// Basic constructor
|
||||
//=================================================================================================================
|
||||
void TeleInfo(String version)
|
||||
{
|
||||
// Serial.begin(1200,SERIAL_7E1);
|
||||
mySerial = new SoftwareSerial(TELEINFO_PIN, 9); // RX, TX
|
||||
mySerial->begin(1200);
|
||||
pgmVersion = version;
|
||||
|
||||
// variables initializations
|
||||
ADCO = "270622224349";
|
||||
OPTARIF = "----";
|
||||
ISOUSC = 0;
|
||||
HCHC = 0L; // compteur Heures Creuses en W
|
||||
HCHP = 0L; // compteur Heures Pleines en W
|
||||
PTEC = "----"; // Régime actuel : HPJB, HCJB, HPJW, HCJW, HPJR, HCJR
|
||||
HHPHC = '-';
|
||||
IINST = 0; // intensité instantanée en A
|
||||
IMAX = 0; // intensité maxi en A
|
||||
PAPP = 0; // puissance apparente en VA
|
||||
MOTDETAT = "------";
|
||||
}
|
||||
|
||||
//=================================================================================================================
|
||||
// Capture des trames de Teleinfo
|
||||
//=================================================================================================================
|
||||
boolean readTeleInfo(boolean ethernetIsConnected)
|
||||
{
|
||||
#define startFrame 0x02
|
||||
#define endFrame 0x03
|
||||
#define startLine 0x0A
|
||||
#define endLine 0x0D
|
||||
#define maxFrameLen 280
|
||||
|
||||
|
||||
int comptChar=0; // variable de comptage des caractères reçus
|
||||
char charIn=0; // variable de mémorisation du caractère courant en réception
|
||||
|
||||
char bufferTeleinfo[21] = "";
|
||||
int bufferLen = 0;
|
||||
int checkSum;
|
||||
|
||||
ethernetIsOK = ethernetIsConnected;
|
||||
|
||||
int sequenceNumnber= 0; // number of information group
|
||||
|
||||
//--- wait for starting frame character
|
||||
while (charIn != startFrame)
|
||||
{ // "Start Text" STX (002 h) is the beginning of the frame
|
||||
if (mySerial->available())
|
||||
charIn = mySerial->read()& 0x7F; // Serial.read() vide buffer au fur et à mesure
|
||||
} // fin while (tant que) pas caractère 0x02
|
||||
// while (charIn != endFrame and comptChar<=maxFrameLen)
|
||||
while (charIn != endFrame)
|
||||
{ // tant que des octets sont disponibles en lecture : on lit les caractères
|
||||
// if (Serial.available())
|
||||
if (mySerial->available())
|
||||
{
|
||||
charIn = mySerial->read()& 0x7F;
|
||||
// incrémente le compteur de caractère reçus
|
||||
comptChar++;
|
||||
if (charIn == startLine)
|
||||
bufferLen = 0;
|
||||
bufferTeleinfo[bufferLen] = charIn;
|
||||
// on utilise une limite max pour éviter String trop long en cas erreur réception
|
||||
// ajoute le caractère reçu au String pour les N premiers caractères
|
||||
if (charIn == endLine)
|
||||
{
|
||||
checkSum = bufferTeleinfo[bufferLen -1];
|
||||
if (chksum(bufferTeleinfo, bufferLen) == checkSum)
|
||||
{// we clear the 1st character
|
||||
strncpy(&bufferTeleinfo[0], &bufferTeleinfo[1], bufferLen -3);
|
||||
bufferTeleinfo[bufferLen -3] = 0x00;
|
||||
sequenceNumnber++;
|
||||
if (! handleBuffer(bufferTeleinfo, sequenceNumnber))
|
||||
{
|
||||
Serial.println(F("Sequence error ..."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println(F("Checksum error ..."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
bufferLen++;
|
||||
}
|
||||
if (comptChar > maxFrameLen)
|
||||
{
|
||||
Serial.println(F("Overflow error ..."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//=================================================================================================================
|
||||
// Frame parsing
|
||||
//=================================================================================================================
|
||||
//void handleBuffer(char *bufferTeleinfo, uint8_t len)
|
||||
boolean handleBuffer(char *bufferTeleinfo, int sequenceNumnber)
|
||||
{
|
||||
// create a pointer to the first char after the space
|
||||
char* resultString = strchr(bufferTeleinfo,' ') + 1;
|
||||
boolean sequenceIsOK;
|
||||
|
||||
switch(sequenceNumnber)
|
||||
{
|
||||
case 1:
|
||||
if (sequenceIsOK = bufferTeleinfo[0]=='A')
|
||||
ADCO = String(resultString);
|
||||
break;
|
||||
case 2:
|
||||
if (sequenceIsOK = bufferTeleinfo[0]=='O')
|
||||
OPTARIF = String(resultString);
|
||||
break;
|
||||
case 3:
|
||||
if (sequenceIsOK = bufferTeleinfo[1]=='S')
|
||||
ISOUSC = atol(resultString);
|
||||
break;
|
||||
case 4:
|
||||
if (sequenceIsOK = bufferTeleinfo[3]=='C')
|
||||
HCHC = atol(resultString);
|
||||
break;
|
||||
case 5:
|
||||
if (sequenceIsOK = bufferTeleinfo[3]=='P')
|
||||
HCHP = atol(resultString);
|
||||
break;
|
||||
case 6:
|
||||
if (sequenceIsOK = bufferTeleinfo[1]=='T')
|
||||
PTEC = String(resultString);
|
||||
break;
|
||||
case 7:
|
||||
if (sequenceIsOK = bufferTeleinfo[1]=='I')
|
||||
IINST =atol(resultString);
|
||||
break;
|
||||
case 8:
|
||||
if (sequenceIsOK = bufferTeleinfo[1]=='M')
|
||||
IMAX =atol(resultString);
|
||||
break;
|
||||
case 9:
|
||||
if (sequenceIsOK = bufferTeleinfo[1]=='A')
|
||||
PAPP =atol(resultString);
|
||||
break;
|
||||
case 10:
|
||||
if (sequenceIsOK = bufferTeleinfo[1]=='H')
|
||||
HHPHC = resultString[0];
|
||||
break;
|
||||
case 11:
|
||||
if (sequenceIsOK = bufferTeleinfo[1]=='O')
|
||||
MOTDETAT = String(resultString);
|
||||
break;
|
||||
}
|
||||
#ifdef debug
|
||||
if(!sequenceIsOK)
|
||||
{
|
||||
Serial.print(F("Out of sequence ..."));
|
||||
Serial.println(bufferTeleinfo);
|
||||
}
|
||||
#endif
|
||||
return sequenceIsOK;
|
||||
}
|
||||
|
||||
//=================================================================================================================
|
||||
// Calculates teleinfo Checksum
|
||||
//=================================================================================================================
|
||||
char chksum(char *buff, uint8_t len)
|
||||
{
|
||||
int i;
|
||||
char sum = 0;
|
||||
for (i=1; i<(len-2); i++)
|
||||
sum = sum + buff[i];
|
||||
sum = (sum & 0x3F) + 0x20;
|
||||
return(sum);
|
||||
}
|
||||
|
||||
|
||||
//=================================================================================================================
|
||||
// This function displays the TeleInfo Internal counters
|
||||
// It's usefull for debug purpose
|
||||
//=================================================================================================================
|
||||
void displayTeleInfo()
|
||||
{
|
||||
/*
|
||||
ADCO 270622224349 B
|
||||
OPTARIF HC.. <
|
||||
ISOUSC 30 9
|
||||
HCHC 014460852 $
|
||||
HCHP 012506372 -
|
||||
PTEC HP..
|
||||
IINST 002 Y
|
||||
IMAX 035 G
|
||||
PAPP 00520 (
|
||||
HHPHC C .
|
||||
MOTDETAT 000000 B
|
||||
*/
|
||||
|
||||
Serial.print(F(" "));
|
||||
Serial.println();
|
||||
Serial.print(F("ADCO "));
|
||||
Serial.println(ADCO);
|
||||
Serial.print(F("OPTARIF "));
|
||||
Serial.println(OPTARIF);
|
||||
Serial.print(F("ISOUSC "));
|
||||
Serial.println(ISOUSC);
|
||||
Serial.print(F("HCHC "));
|
||||
Serial.println(HCHC);
|
||||
Serial.print(F("HCHP "));
|
||||
Serial.println(HCHP);
|
||||
Serial.print(F("PTEC "));
|
||||
Serial.println(PTEC);
|
||||
Serial.print(F("IINST "));
|
||||
Serial.println(IINST);
|
||||
Serial.print(F("IMAX "));
|
||||
Serial.println(IMAX);
|
||||
Serial.print(F("PAPP "));
|
||||
Serial.println(PAPP);
|
||||
Serial.print(F("HHPHC "));
|
||||
Serial.println(HHPHC);
|
||||
Serial.print(F("MOTDETAT "));
|
||||
Serial.println(MOTDETAT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void encodeur_OWL_CM180()
|
||||
{
|
||||
if (PTEC.substring(1,2)=="C")
|
||||
{
|
||||
HCP=(HCHC*223666LL)/1000LL;
|
||||
}
|
||||
else
|
||||
{
|
||||
HCP=(HCHP*223666LL)/1000LL;
|
||||
}
|
||||
|
||||
OregonMessageBuffer[0] =0x62; // imposé
|
||||
OregonMessageBuffer[1] =0x80; // GH G= non décodé par RFXCOLM, H = Count
|
||||
|
||||
|
||||
//OregonMessageBuffer[2] =0x3C; // IJ ID compteur : "L IJ 2" soit (L & 1110 )*16*16*16+I*16*16+J*16+2
|
||||
// si heure creuse compteur 3D, si HP compteur 3C
|
||||
if (PTEC.substring(1,2)=="C")
|
||||
{
|
||||
OregonMessageBuffer[2] =0x3D;
|
||||
// Serial.print(F("HEURE CREUSE 0x3D")); //débug *******************************
|
||||
}
|
||||
else
|
||||
{
|
||||
OregonMessageBuffer[2] =0x3C;
|
||||
}
|
||||
|
||||
//OregonMessageBuffer[3] =0xE1; // KL K sert pour puissance instantanée, L sert pour identifiant compteur
|
||||
PAPP_arrondi=long(long(PAPP)*497/500/16);
|
||||
|
||||
// améliore un peu la précision de la puissance apparente encodée (le CM180 transmet la PAPP * 497/500/16)
|
||||
if ((float(PAPP)*497/500/16-PAPP_arrondi)>0.5)
|
||||
{
|
||||
++PAPP_arrondi;
|
||||
}
|
||||
|
||||
OregonMessageBuffer[3]=(PAPP_arrondi&0x0F)<<4;
|
||||
|
||||
//OregonMessageBuffer[4] =0x00; // MN puissance instantée = (P MN K)*16 soit : (P*16*16*16 + M*16*16 +N*16+K)*16*500/497. attention RFXCOM corrige cette valeur en multipliant par 16 puis 500/497.
|
||||
OregonMessageBuffer[4]=(PAPP_arrondi>>4)&0xFF;
|
||||
|
||||
//OregonMessageBuffer[5] =0xCD; // OP Total conso : YZ WX UV ST QR O : Y*16^10 + Z*16^9..R*16 + O
|
||||
OregonMessageBuffer[5] =((PAPP_arrondi>>12)&0X0F)+((HCP&0x0F)<<4);
|
||||
|
||||
//OregonMessageBuffer[6] =0x97; // QR sert total conso
|
||||
OregonMessageBuffer[6] =(HCP>>4)&0xFF;
|
||||
|
||||
//OregonMessageBuffer[7] =0xCE; // ST sert total conso
|
||||
OregonMessageBuffer[7] =(HCP>>12)&0xFF; // ST sert total conso
|
||||
|
||||
//OregonMessageBuffer[8] =0x12; // UV sert total conso
|
||||
OregonMessageBuffer[8] =(HCP>>20)&0xFF; // UV sert total conso
|
||||
|
||||
//OregonMessageBuffer[9] =0x00; // WX sert total conso
|
||||
OregonMessageBuffer[9] =(HCP>>28)&0xFF;
|
||||
|
||||
//OregonMessageBuffer[10] =0x00; //YZ sert total conso
|
||||
OregonMessageBuffer[10] =(HCP>>36)&0xFF;
|
||||
|
||||
chksum_CM180= 0;
|
||||
for (byte i=0; i<11; i++)
|
||||
{
|
||||
chksum_CM180 += long(OregonMessageBuffer[i]&0x0F) + long(OregonMessageBuffer[i]>>4) ;
|
||||
}
|
||||
|
||||
chksum_CM180 -=2; // = =b*16^2 + d*16+ a ou [b d a]
|
||||
|
||||
//OregonMessageBuffer[11] =0xD0; //ab sert CHECKSUM somme(nibbles ci-dessuus)=b*16^2 + d*16+ a + 2
|
||||
OregonMessageBuffer[11] =((chksum_CM180&0x0F)<<4) + ((chksum_CM180>>8)&0x0F);
|
||||
|
||||
//OregonMessageBuffer[12] =0xF6; //cd d sert checksum, a non décodé par RFXCOM
|
||||
OregonMessageBuffer[12] =(int(chksum_CM180>>4)&0x0F); //C = 0 mais inutilisé
|
||||
}
|
||||
|
||||
//************************************************************************************
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200); // pour la console, enlever les barres de commentaires ci dessous pour displayTeleInfo()
|
||||
TeleInfo(version);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
teleInfoReceived=readTeleInfo(true);
|
||||
if (teleInfoReceived)
|
||||
{
|
||||
encodeur_OWL_CM180();
|
||||
mySerial->end(); //NECESSAIRE !! arrête les interruptions de softwareserial (lecture du port téléinfo) pour émission des trames OWL
|
||||
sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer)); // Send the Message over RF
|
||||
mySerial->begin(1200); //NECESSAIRE !! relance les interuptions pour la lecture du port téléinfo
|
||||
displayTeleInfo(); // console pour voir les trames téléinfo
|
||||
|
||||
// ajout d'un delais de 12s apres chaque trame envoyés pour éviter d'envoyer
|
||||
// en permanence des informations à domoticz et de créer des interférances
|
||||
delay(12000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user