/* * connectingStuff, Oregon Scientific v2.1 Emitter * http://connectingstuff.net/blog/encodage-protocoles-oregon-scientific-sur-arduino * and * http://blog.idleman.fr/raspberry-pi-18-construire-une-sonde-de-temperature-radio-pour-7e/ * * This program is free software; you can redistribute it and/or * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // ------------------------------ // Radio // ------------------------------ #include const byte ALIM_TRANSMITTER_PIN = 8; const byte TRANSMITTER_PIN = 9; const byte LED_PIN = 13; #define DEBUG TRUE #define SEND_MESSAGE_DELAY 22500 // Ne pas dépasser 32000 !! Delay in ms between each value's extraction #define SEND_433_PAUSE 160 // 16 multiple // On crée une instance de la classe oneWire pour communiquer avec le materiel on wire (dont le capteur ds18b20) OneWire oneWire(TRANSMITTER_PIN); // Buffer for Oregon message #ifdef THN132N byte OregonMessageBuffer[8]; #else byte OregonMessageBuffer[9]; #endif // ------------------------------ // Energie // ------------------------------ #include "EmonLib.h" // Include Emon Library EnergyMonitor emon1; // Create an instance EnergyMonitor emon2; const unsigned long TIME = 512; const unsigned long TWOTIME = TIME*2; #define SEND_HIGH() digitalWrite(TRANSMITTER_PIN, HIGH) #define SEND_LOW() digitalWrite(TRANSMITTER_PIN, LOW) // ---------------------------- // Smooth // ---------------------------- const int numReadings = 10; const int numIndex = 2; float last_values[numIndex][numReadings]; int readIndex[numIndex]; long total[numIndex]; int boucle = 0; #include SoftwareSerial ESPserial(3, 2); // RX | TX // ---------------------------- // Dimmer // ---------------------------- //#include //#define outputPin 6 //#define zerocross D2 // for boards with CHANGEBLE input pins //#define pas 5 //dimmerLamp dimmer(outputPin); //initialase port for dimmer for ESP8266, ESP32, Arduino due boards //// To set dimmer off ==> dimmer.setPower(128); //// value 0 = On // ---------------------------- // Interruption // ---------------------------- const byte interruptPin = 3; volatile byte backlight_status = LOW; // ---------------------------- // Screen LCD // ---------------------------- #include #include "LowPower.h" #define I2C_SLAVE_ADDRESS 0x0B // 12 pour l'esclave 2 et ainsi de suite #define PAYLOAD_SIZE 2 LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display // ---------------------------- // Timer // ---------------------------- //#include "PinChangeInterrupt.h" // //#include // inclusion de la librairie Timer2 //#define pinBlink 7 /** * \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_HIGH(); delayMicroseconds(TIME); SEND_LOW(); delayMicroseconds(TWOTIME); 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_LOW(); delayMicroseconds(TIME); SEND_HIGH(); delayMicroseconds(TWOTIME); SEND_LOW(); delayMicroseconds(TIME); } /** * Send a bits quarter (4 bits = MSB from 8 bits value) over RF * * @param data Source data to process and sent */ /** * \brief Send a bits quarter (4 bits = MSB from 8 bits value) over RF * \param data Data to send */ inline void sendQuarterMSB(const byte data) { (bitRead(data, 4)) ? sendOne() : sendZero(); (bitRead(data, 5)) ? sendOne() : sendZero(); (bitRead(data, 6)) ? sendOne() : sendZero(); (bitRead(data, 7)) ? sendOne() : sendZero(); } /** * \brief Send a bits quarter (4 bits = LSB from 8 bits value) over RF * \param data Data to send */ inline void sendQuarterLSB(const byte data) { (bitRead(data, 0)) ? sendOne() : sendZero(); (bitRead(data, 1)) ? sendOne() : sendZero(); (bitRead(data, 2)) ? sendOne() : sendZero(); (bitRead(data, 3)) ? sendOne() : sendZero(); } /******************************************************************/ /******************************************************************/ /******************************************************************/ /** * \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) { sendQuarterLSB(data[i]); sendQuarterMSB(data[i]); } } /** * \brief Send an Oregon message * \param data The Oregon message */ void sendOregon(byte *data, byte size) { sendPreamble(); //sendSync(); sendData(data, size); sendPostamble(); } /** * \brief Send preamble * \details The preamble consists of 16 "1" bits */ inline void sendPreamble(void) { byte PREAMBLE[]={0xFF,0xFF}; sendData(PREAMBLE, 2); } /** * \brief Send postamble * \details The postamble consists of 8 "0" bits */ inline void sendPostamble(void) { #ifdef THN132N sendQuarterLSB(0x00); #else byte POSTAMBLE[]={0x00}; sendData(POSTAMBLE, 1); #endif } /** * \brief Send sync nibble * \details The sync is 0xA. It is not use in this version since the sync nibble * \ is include in the Oregon message to send. */ inline void sendSync(void) { sendQuarterLSB(0xA); } /******************************************************************/ /******************************************************************/ /******************************************************************/ /** * \brief Set the sensor type * \param data Oregon message * \param type Sensor type */ inline void setType(byte *data, byte* type) { data[0] = type[0]; data[1] = type[1]; } /** * \brief Set the sensor channel * \param data Oregon message * \param channel Sensor channel (0x10, 0x20, 0x30) */ inline void setChannel(byte *data, byte channel) { data[2] = channel; } /** * \brief Set the sensor ID * \param data Oregon message * \param ID Sensor unique ID */ inline void setId(byte *data, byte ID) { data[3] = ID; } /** * \brief Set the sensor battery level * \param data Oregon message * \param level Battery level (0 = low, 1 = high) */ void setBatteryLevel(byte *data, byte level) { if(!level) data[4] = 0x0C; else data[4] = 0x00; } /** * \brief Set the sensor temperature * \param data Oregon message * \param temp the temperature */ void setTemperature(byte *data, float temp) { // Set temperature sign if(temp < 0) { data[6] = 0x08; temp *= -1; } else { data[6] = 0x00; } // Determine decimal and float part int tempInt = (int)temp; int td = (int)(tempInt / 10); int tf = (int)round((float)((float)tempInt/10 - (float)td) * 10); int tempFloat = (int)round((float)(temp - (float)tempInt) * 10); // Set temperature decimal part data[5] = (td << 4); data[5] |= tf; // Set temperature float part data[4] |= (tempFloat << 4); } /** * \brief Set the sensor humidity * \param data Oregon message * \param hum the humidity */ void setHumidity(byte* data, byte hum) { data[7] = (hum/10); data[6] |= (hum - data[7]*10) << 4; //Serial.print("Hum=" + hum); } /** * \brief Sum data for checksum * \param count number of bit to sum * \param data Oregon message */ int Sum(byte count, const byte* data) { int s = 0; for(byte i = 0; i> 4; s += (data[i]&0xF); } if(int(count) != count) s += (data[count]&0xF0) >> 4; return s; } /** * \brief Calculate checksum * \param data Oregon message */ void calculateAndSetChecksum(byte* data) { #ifdef THN132N int s = ((Sum(6, data) + (data[6]&0xF) - 0xa) & 0xff); data[6] |= (s&0x0F) << 4; data[7] = (s&0xF0) >> 4; #else data[8] = ((Sum(8, data) - 0xa) & 0xFF); #endif } /******************************************************************/ /******************************************************************/ /******************************************************************/ float puissance_reelle1; void setup() { lcd.init(); // initialize the lcd delay(200); lcd.noBacklight(); lcd.setCursor(0,0); lcd.print("Screen Ok"); Serial.begin(9600); // Screen Serial.println("Screen init..."); delay(200); pinMode(TRANSMITTER_PIN, OUTPUT); pinMode(ALIM_TRANSMITTER_PIN, OUTPUT); pinMode(LED_PIN, OUTPUT); lcd.clear(); lcd.setCursor(0,0); lcd.print("[Oregon V2.1 encoder]"); //analogReference(EXTERNAL); // float vcc = readVcc() / 1000.0; // Serial.print(vcc); emon1.voltage(A2, 320 , 2.6); // Voltage: input pin, calibration, phase_shift emon1.current(A1, 30); // Current: input pin, calibration. //emon2.voltage(A2, 320, 7); // Voltage: input pin, calibration, phase_shift emon2.current(A0, 5); SEND_LOW(); // Interruption // pinMode(interruptPin, INPUT_PULLUP); // attachInterrupt(digitalPinToInterrupt(interruptPin), onEvent, CHANGE); // pinMode(pinBlink, INPUT_PULLUP); // attachPCINT(digitalPinToPCINT(pinBlink), onEvent, CHANGE); // // Serial.println(F("Interruption attachée")); // delay(200); // //// // // Timer // MsTimer2::set(12000, InterruptTimer2); // période 1000ms // Serial.println(F("Timer démarré")); // delay(200); // Dimmer // Serial.println("Dimmer Program is starting..."); // delay(1000); // dimmer.begin(NORMAL_MODE, ON); //dimmer initialisation: name.begin(MODE, STATE) // Serial.println("Set value"); // dimmer.setPower(50); // setPower(0-100%); // lcd.clear(); // lcd.setCursor(0,0); // lcd.print("Dimmer set to 0"); // delay(200); } void loop() { // if (backlight_status) { // lcd.backlight(); // } // else { // lcd.noBacklight(); // } double Irms[2]; boucle ++; for (int bcl = 1; bcl <=2; bcl++) { digitalWrite(13, HIGH); digitalWrite(8, HIGH); if (bcl == 1) { emon1.calcVI(20,200); // 1 Demande a Emonlib de tout calculer, (puissance relle, volts moyen, ampère moyen et facteur de puissance) //Irms[0] = emon1.calcIrms(5440) * 230; //emon1.apparentPower); Irms[0] = emon1.apparentPower; puissance_reelle1 = emon1.realPower; // 1 creation de la variable flottante "puissance reelle" qui existe dans la librairie sous "emon1.realPower" float verif_voltage = emon1.Vrms; // 1 creation de la variable "volts moyen" (mesurable avec un voltmètre pour l'etalonnage) float verif_ampere = emon1.Irms; // 1 creation de la variable "Ampères Moyen" (mesurable avec une pince ampèremétrique pour l'etalonnage)) float Cos_phi = emon1.powerFactor; Serial.print(verif_voltage); // 1 envoyer vers l'ordinateur la valeur "verif_voltage (Vrms)" Serial.print(" V "); // 1 envoyer vers l'ordinateur le caractère "V" Serial.print(verif_ampere); // 1 envoyer vers l'ordinateur la valeur "verif_voltage (Vrms)" Serial.print(" A "); // 1 envoyer vers l'ordinateur le caractère "A" Serial.print(emon1.realPower); Serial.print(" Wr "); Serial.print(emon1.apparentPower); // Calculate Irms only Serial.print(" Wcap "); Serial.print(Irms[0]); // Calculate Irms only Serial.print(" Wc "); // Serial.print(emon1.apparentPower); // Serial.print(" Wa "); // Serial.print(Cos_phi); // 1 envoyer vers l'ordinateur la valeur "verif_voltage (Vrms)" // Serial.print(" cos "); } else { //emon2.calcVI(20,200); Irms[1] = emon2.calcIrms(5440) * 230; //smooth(1,emon2.realPower); //apparentPower; //Irms = emon2.apparentPower; // Serial.print(1,emon2.realPower); // Serial.print(" Wsr "); // Serial.print(emon2.apparentPower); // Serial.print(" Wsap "); Serial.print(Irms[1]); // Calculate Irms only Serial.println(" Ws "); //emon2.serialprint(); String json = "/json.htm?type=command¶m=udevice&idx=892&nvalue=0&svalue=0;0;0;0;" + String(Irms[1]) + ";0"; ESPserial.print(json); } if (boucle > 5) { if (bcl == 1) { sendMessage(0xBD, Irms[0]); delay(500); sendMessage(0xBF, puissance_reelle1); } else { sendMessage(0xBE, Irms[1]); } delay (300); } digitalWrite(8, LOW); digitalWrite(13, LOW); if (boucle < 10) { delay (200); lcd.clear(); lcd.setCursor(0,0); lcd.print("Calibration :" + String(boucle)); } else { boucle = 50; //max(50, boucle); lcd.clear(); // dimmer.setPower(50 + (boucle * 10)% 50); lcd.setCursor(0,0); lcd.print("Cso:" + String(Irms[0], 0) + " R " + String(emon1.realPower, 0)); lcd.setCursor(0,1); lcd.print("Sol:" + String(Irms[1],0) + " V " + String(emon1.Vrms, 1)); delay (300); } } } void sendMessage(byte ID, double Irms) { // LA LECTURE DE LA TENSION FAIT PLANTER LES PORTS ANALOGIQUES ???? long currentVcc = 5000; //readVcc(); // Create the Oregon message for a temperature/humidity sensor (THGR2228N) byte TYPE[] = {0x1A,0x2D}; setType(OregonMessageBuffer, TYPE); setChannel(OregonMessageBuffer, 0x20); setId(OregonMessageBuffer, ID); if (currentVcc > 5000) { currentVcc = 5000; } if (currentVcc < 3800) { setBatteryLevel(OregonMessageBuffer, 0); // 0 : low, 1 : high } else { setBatteryLevel(OregonMessageBuffer, 1); // 0 : low, 1 : high } if (int(Irms / 100) == 0 && Irms < 0) { setTemperature(OregonMessageBuffer, -0.1); } else { setTemperature(OregonMessageBuffer,(int)(Irms / 100)); } // Set // Serial.print(Irms); // Serial.print(" "); // // Serial.print((int) Irms / 100); // Serial.print(" " ); // Serial.println((int) Irms % 100); setHumidity(OregonMessageBuffer, abs(int(Irms) % 100)); // Calculate the checksum calculateAndSetChecksum(OregonMessageBuffer); // Show the Oregon Message // for (byte i = 0; i < sizeof(OregonMessageBuffer); ++i) { // Serial.print(OregonMessageBuffer[i] >> 4, HEX); // Serial.print(OregonMessageBuffer[i] & 0x0F, HEX); // // } // Serial.println(""); // Send the Message over RF sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer)); // Send a "pause" SEND_LOW(); delayMicroseconds(TWOTIME*8); // Send a copie of the first message. The v2.1 protocol send the // message two time sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer)); SEND_LOW(); } // //void onEvent() { // backlight_status = !backlight_status; // Serial.print(F("Switch LED 13 : ")); // if(backlight_status){ // Serial.println(F("ON")); // }else{ // Serial.println(F("OFF")); // } // MsTimer2::start(); // active Timer 2 // //} // //void InterruptTimer2() { // debut de la fonction d'interruption Timer2 // // digitalWrite(LED_PIN, HIGH); // Serial.println(F("Timer interruption")); // backlight_status = LOW; // // delayMicroseconds(300); // la fonction delayMicroseconds ne bloque pas les interruptions // digitalWrite(LED_PIN, LOW); // // MsTimer2::stop(); // active Timer 2 // // //output = !output; // // //} float smooth(int index, float last_value) { /* function smooth */ ////Perform average on sensor last_values float average; // subtract the last reading: total[index] = total[index] - last_values[index][readIndex[index]]; // read the sensor: last_values[index][readIndex[index]] = last_value; // add value to total: total[index] = total[index] + last_values[index][readIndex[index]]; // handle index readIndex[index] = readIndex[index] + 1; if (readIndex[index] >= numReadings) { readIndex[index] = 0; } float tot = 0; for (int j = 0; j< numReadings; j++) { tot +=last_values[index][j]; } //Serial.println(""); // calculate the average: average = tot / numReadings; //al[index] / numReadings; return average; }