commit fae003af5d8b9f5d11ac23e1c76fc893de63dbf1 Author: Glyn Hudson Date: Tue Sep 20 21:51:26 2016 +0100 first commit fimware for emonTH V2.0 firmware diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..432b9c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.pioenvs +*~ diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..18bcd0a --- /dev/null +++ b/platformio.ini @@ -0,0 +1,32 @@ +# +# Project Configuration File +# +# A detailed documentation with the EXAMPLES is located here: +# http://docs.platformio.org/en/latest/projectconf.html +# + +# A sign `#` at the beginning of the line indicates a comment +# Comment lines are ignored. + +# Simple and base environment +# [env:mybaseenv] +# platform = %INSTALLED_PLATFORM_NAME_HERE% +# framework = +# board = +# +# Automatic targets - enable auto-uploading +# targets = upload + +[platformio] +src_dir = src + +[env:uno] +platform = atmelavr +framework = arduino +board = uno +lib_install = 19, 54, 252, 536 + +# [ 19 ] Adafruit-DHT +# [ 54 ] DallasTemperature +# [ 252 ] JeeLib +# [ 536 ] https://github.com/LowPowerLab/SI7021 \ No newline at end of file diff --git a/src/src.ino b/src/src.ino new file mode 100644 index 0000000..86b5172 --- /dev/null +++ b/src/src.ino @@ -0,0 +1,440 @@ +/* + emonTH Low Power DHT22 Humidity & Temperature, DS18B20 Temperature & Pulse counting Node Example + + Checkes at startup for presence of a DS18B20 temp sensor , DHT22 (temp + humidity) or both + If it finds both sensors the temperature value will be taken from the DS18B20 (external) and DHT22 (internal) and humidity from DHT22 + If it finds only DS18B20 then no humidity value will be reported + If it finds only a DHT22 then both temperature and humidity values will be obtained from this sesor + + Technical hardware documentation wiki: http://wiki.openenergymonitor.org/index.php?title=EmonTH + + Part of the openenergymonitor.org project + Licence: GNU GPL V3 + + Authors: Glyn Hudson + Builds upon JCW JeeLabs RF12 library, Arduino and Martin Harizanov's work + + THIS SKETCH REQUIRES: + + Libraries in the standard arduino libraries folder: + - JeeLib https://github.com/jcw/jeelib + - DHT22 Sensor Library https://github.com/adafruit/DHT-sensor-library - be sure to rename the sketch folder to remove the '-' + - OneWire library http://www.pjrc.com/teensy/td_libs_OneWire.html + - DallasTemperature http://download.milesburton.com/Arduino/MaximTemperature/DallasTemperature_LATEST.zip + + Recommended node ID allocation + ----------------------------------------------------------------------------------------------------------- + -ID- -Node Type- + 0 - Special allocation in JeeLib RFM12 driver - reserved for OOK use + 1-4 - Control nodes + 5-10 - Energy monitoring nodes + 11-14 --Un-assigned -- + 15-16 - Base Station & logging nodes + 17-30 - Environmental sensing nodes (temperature humidity etc.) + 31 - Special allocation in JeeLib RFM12 driver - Node31 can communicate with nodes on any network group + ------------------------------------------------------------------------------------------------------------- + Change log: + v2.1 - Branched from emonTH_DHT22_DS18B20 example, first version of pulse counting version + v2.2 - 60s RF transmit period now uses timer1, pulse events are decoupled from RF transmit + v2.3 - rebuilt based on low power pulse counting code by Eric Amann: http://openenergymonitor.org/emon/node/10834 + v2.4 - 5 min default transmisson time = 300 ms + v2.3 - (12/10/14) don't flash LED on RF transmission to save power + V2.4 - (15/10/15) activate pulse count pin input pullup to stop spurious pulses when no sensor connected + V2.5 - (23/10/15) default nodeID 23 to enable new emonHub.conf decoder for pulseCount packet structure + V2.6 - (24/10/15) Tweek RF transmission timmng to help reduce RF packet loss + V2.7 - (15/09/16) Serial print serial pairs for emonesp compatiable e.g. temp:210,humidity:56 + V3.0 - (15/09/16) Add support for SI7021 sensor (emonTH V2.0 hardware) + ------------------------------------------------------------------------------------------------------------- + emonhub.conf node decoder: + See: https://github.com/openenergymonitor/emonhub/blob/emon-pi/configuration.md + + [[23]] + nodename = emonTH_5 + firmware = V2.x_emonTH_DHT22_DS18B20_RFM69CW_Pulse + hardware = emonTH_(Node_ID_Switch_DIP1:OFF_DIP2:OFF) + [[[rx]]] + names = temperature, external temperature, humidity, battery, pulseCount + datacodes = h,h,h,h,L + scales = 0.1,0.1,0.1,0.1,1 + units = C,C,%,V,p + */ + +const byte version = 30; // firmware version divided by 10 e,g 16 = V1.6 + // These variables control the transmit timing of the emonTH +const unsigned long WDT_PERIOD = 80; // mseconds. +const unsigned long WDT_MAX_NUMBER = 690; // Data sent after WDT_MAX_NUMBER periods of WDT_PERIOD ms without pulses: + // 690x 80 = 55.2 seconds (it needs to be about 5s less than the record interval in emoncms) + +const unsigned long PULSE_MAX_NUMBER = 100; // Data sent after PULSE_MAX_NUMBER pulses +const unsigned long PULSE_MAX_DURATION = 50; + + +#define RF69_COMPAT 1 // Set to 1 if using RFM69CW or 0 is using RFM12B +#include // https://github.com/jcw/jeelib - Tested with JeeLib 3/11/14 + +boolean debug=1; // Set to 1 to few debug serial output, turning debug off increases battery life + +#define RF_freq RF12_433MHZ // Frequency of RF12B module can be RF12_433MHZ, RF12_868MHZ or RF12_915MHZ. You should use the one matching the module you have. +int nodeID = 23; // EmonTH temperature RFM12B node ID - should be unique on network +const int networkGroup = 210; // EmonTH RFM12B wireless network group - needs to be same as emonBase and emonGLCD + // DS18B20 resolution 9,10,11 or 12bit corresponding to (0.5, 0.25, 0.125, 0.0625 degrees C LSB), + // lower resolution means lower power + +const int TEMPERATURE_PRECISION=11; // 9 (93.8ms),10 (187.5ms) ,11 (375ms) or 12 (750ms) bits equal to resplution of 0.5C, 0.25C, 0.125C and 0.0625C +#define ASYNC_DELAY 375 // 9bit requres 95ms, 10bit 187ms, 11bit 375ms and 12bit resolution takes 750ms +// See block comment above for library info +#include +#include +#include +#include +#include "DHT.h" +ISR(WDT_vect) { Sleepy::watchdogEvent(); } // Attached JeeLib sleep function to Atmega328 watchdog -enables MCU to be put into sleep mode inbetween readings to reduce power consumption + +// SI7021_status SPI temperature & humidity sensor +#include +#include +SI7021 SI7021_sensor; +boolean SI7021_status; + +// Hardwired emonTH pin allocations +const byte DS18B20_PWR= 5; +const byte DHT22_PWR= 6; +const byte LED= 9; +const byte BATT_ADC= 1; +const byte DIP_switch1= 7; +const byte DIP_switch2= 8; +const byte pulse_countINT= 1; // INT 1 / Dig 3 Screw Terminal Block Number 4 on emonTH V1.5 - Change to INT0 DIG2 on emonTH V1.4 +const byte pulse_count_pin=3; // INT 1 / Dig 3 Screw Terminal Block Number 4 on emonTH V1.5 - Change to INT0 DIG2 on emonTH V1.4 +#define ONE_WIRE_BUS 16 // D19 emonTH V1.5 +#define DHTPIN 17 // D18 emonTH V1.5 + +// Humidity code adapted from ladyada' example // emonTh DHT22 data pin +// Uncomment whatever type you're using! +// #define DHTTYPE DHT11 // DHT 11 +#define DHTTYPE DHT22 // DHT 22 (AM2302) +DHT dht(DHTPIN, DHTTYPE); +boolean DHT22_status; // create flag variable to store presence of DS18B20 + +OneWire oneWire(ONE_WIRE_BUS); +DallasTemperature sensors(&oneWire); +boolean DS18B20; // create flag variable to store presence of DS18B20 + +// Note: Please update emonhub configuration guide on OEM wide packet structure change: +// https://github.com/openenergymonitor/emonhub/blob/emon-pi/configuration.md +typedef struct { // RFM12B RF payload datastructure + int temp; + int temp_external; + int humidity; + int battery; + unsigned long pulsecount; +} Payload; +Payload emonth; + +int numSensors; +//addresses of sensors, MAX 4!! +byte allAddress [4][8]; // 8 bytes per address + +volatile unsigned long pulseCount; +unsigned long WDT_number; +boolean p; + +unsigned long now = 0; + +//################################################################################################################################ +//################################################################################################################################ +void setup() { +//################################################################################################################################ + + pinMode(LED,OUTPUT); digitalWrite(LED,HIGH); // Status LED on + + //READ DIP SWITCH POSITIONS - LOW when switched on (default off - pulled up high) + pinMode(DIP_switch1, INPUT_PULLUP); + pinMode(DIP_switch2, INPUT_PULLUP); + boolean DIP1 = digitalRead(DIP_switch1); + boolean DIP2 = digitalRead(DIP_switch2); + + if ((DIP1 == HIGH) && (DIP2 == HIGH)) nodeID=nodeID; + if ((DIP1 == LOW) && (DIP2 == HIGH)) nodeID=nodeID+1; + if ((DIP1 == HIGH) && (DIP2 == LOW)) nodeID=nodeID+2; + if ((DIP1 == LOW) && (DIP2 == LOW)) nodeID=nodeID+3; + + rf12_initialize(nodeID, RF_freq, networkGroup); // Initialize RFM12B + + // Send RFM69CW test sequence (for factory testing) + for (int i=10; i>-1; i--) + { + emonth.temp=i; + rf12_sendNow(0, &emonth, sizeof emonth); + delay(100); + } + rf12_sendWait(2); + emonth.temp=0; + // end of factory test sequence + + rf12_sleep(RF12_SLEEP); + if (debug==1) + { + Serial.begin(9600); + Serial.print(DIP1); Serial.println(DIP2); + Serial.println("OpenEnergyMonitor.org"); + Serial.print("emonTH - Firmware V"); Serial.println(version*0.1); + #if (RF69_COMPAT) + Serial.println("RFM69CW Init> "); + #else + Serial.println("RFM12B Init> "); + #endif + Serial.print("Node: "); + Serial.print(nodeID); + Serial.print(" Freq: "); + if (RF_freq == RF12_433MHZ) Serial.print("433Mhz"); + if (RF_freq == RF12_868MHZ) Serial.print("868Mhz"); + if (RF_freq == RF12_915MHZ) Serial.print("915Mhz"); + Serial.print(" Network: "); + Serial.println(networkGroup); + delay(100); + } + + pinMode(DHT22_PWR,OUTPUT); + pinMode(DS18B20_PWR,OUTPUT); + pinMode(BATT_ADC, INPUT); + digitalWrite(DHT22_PWR,LOW); + pinMode(pulse_count_pin, INPUT_PULLUP); + + //################################################################################################################################ + // Test for presence of DHT22 + //################################################################################################################################ + digitalWrite(DHT22_PWR,HIGH); + dodelay(2000); // wait 2s for DH22 to warm up + dht.begin(); + float h = dht.readHumidity(); // Read Humidity + float t = dht.readTemperature(); // Read Temperature + digitalWrite(DHT22_PWR,LOW); // Power down + + if (isnan(t) || isnan(h)) // check if returns are valid, if they are NaN (not a number) then something went wrong! + { + Sleepy::loseSomeTime(1500); + float h = dht.readHumidity(); float t = dht.readTemperature(); + if (isnan(t) || isnan(h)) + { + if (debug==1) Serial.println("No DHT22"); + DHT22_status=0; + } + } + else + { + DHT22_status=1; + if (debug==1) Serial.println("Detected DHT22"); + } + + //################################################################################################################################ + // Setup and for presence of si7201 + //################################################################################################################################ + SI7021_sensor.begin(); + int deviceid = SI7021_sensor.getDeviceId(); + if (deviceid!=0) { //need to test this!!! + Serial.print("Found SI7021: "); Serial.println(deviceid); + SI7021_status=1; + if (DHT22_status==1){ + Serial.println("Disabling DHT22"); + DHT22_status = 0; //disable DHT22 if SI7021_status is present + } + si7021_env data = SI7021_sensor.getHumidityAndTemperature(); + Serial.print("SI7021 t: "); Serial.println(data.celsiusHundredths/100.0); + Serial.print("SI7021 h: "); Serial.println(data.humidityBasisPoints/100.0); + // might need some delay here + } + else { + SI7021_status=0; + Serial.println("No SI7021_status"); + } + //################################################################################################################################ + // Setup and for presence of DS18B20 + //################################################################################################################################ + digitalWrite(DS18B20_PWR, HIGH); delay(50); + sensors.begin(); + sensors.setWaitForConversion(false); //disable automatic temperature conversion to reduce time spent awake, conversion will be implemented manually in sleeping http://harizanov.com/2013/07/optimizing-ds18b20-code-for-low-power-applications/ + numSensors=(sensors.getDeviceCount()); + + byte j=0; // search for one wire devices and + // copy to device address arrays. + while ((j < numSensors) && (oneWire.search(allAddress[j]))) j++; + digitalWrite(DS18B20_PWR, LOW); + + if (numSensors==0) + { + if (debug==1) Serial.println("No DS18B20"); + DS18B20=0; + } + else + { + DS18B20=1; + if (debug==1) { + Serial.print("Found "); Serial.print(numSensors); Serial.println(" DS18B20"); + if ((DHT22_status==1) || (SI7021_status==1)) Serial.println("DS18B20 & DHT22/SI7021 found, assume DS18B20 is ext"); + } + } + if (debug==1) delay(200); + + + //################################################################################################################################ + // Interrupt pulse counting setup + //################################################################################################################################ + // Serial.print(DS18B20); Serial.print(DHT22_status); + // if (debug==1) delay(200); + + digitalWrite(LED,LOW); + + emonth.pulsecount = 0; + pulseCount = 0; + WDT_number=720; + p = 0; + + attachInterrupt(pulse_countINT, onPulse, RISING); + + //################################################################################################################################ + // Power Save - turn off what we don't need - http://www.nongnu.org/avr-libc/user-manual/group__avr__power.html + //################################################################################################################################ + ACSR |= (1 << ACD); // disable Analog comparator + if (debug==0) power_usart0_disable(); //disable serial UART + power_twi_disable(); //Disable the Two Wire Interface module. + // power_timer0_disable(); //don't disable necessary for the DS18B20 library + power_timer1_disable(); + power_spi_disable(); + +} // end of setup + + +//################################################################################################################################ +//################################################################################################################################ +void loop() +//################################################################################################################################ +{ + + if (p) { + Sleepy::loseSomeTime(PULSE_MAX_DURATION); + p=0; + } + + if (Sleepy::loseSomeTime(WDT_PERIOD)==1) { + WDT_number++; + } + + if (WDT_number>=WDT_MAX_NUMBER || pulseCount>=PULSE_MAX_NUMBER) + { + cli(); + emonth.pulsecount += (unsigned int) pulseCount; + pulseCount = 0; + sei(); + + if (DS18B20==1) + { + digitalWrite(DS18B20_PWR, HIGH); dodelay(50); + for(int j=0;j-40.0)) + { + if (DHT22_status==0) emonth.temp=(temp*10); // if DHT22 is not present assume DS18B20 is primary sensor (internal) + if (DHT22_status==1) emonth.temp_external=(temp*10); // if DHT22 is present assume DS18B20 is external sensor wired into terminal block + } + } + + if (DHT22_status==1) + { + digitalWrite(DHT22_PWR,HIGH); // Send the command to get temperatures + dodelay(2000); //sleep for 1.5 - 2's to allow sensor to warm up + // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) + emonth.humidity = ((dht.readHumidity())*10); + + float temp=(dht.readTemperature()); + if ((temp<85.0) && (temp>-40.0)) emonth.temp = (temp*10); + + digitalWrite(DHT22_PWR,LOW); + } + + emonth.battery=int(analogRead(BATT_ADC)*0.03225806); //read battery voltage, convert ADC to volts x10 + + //Enhanced battery monitoring mode. In this mode battery values + //sent in x*1000 mode instead of x*10. This allows to have more accurate + //values on emonCMS x.xx instead of x.x + // NOTE if you are going to enable this mode you need to + // 1. Disable x*10 mode. By commenting line above. + // 2. Change multiplier in line 353 Serial.print(emonth.battery/10.0); + // 3. Change scales factor in the emonhub node decoder entry for the emonTH + // See more https://community.openenergymonitor.org/t/emonth-battery-measurement-accuracy/1317 + //emonth.battery=int(analogRead(BATT_ADC)*3.222); + + + power_spi_enable(); + + // Read from SI7021 SPI temp & humidity sensor + if (SI7021_status==1){ + si7021_env data = SI7021_sensor.getHumidityAndTemperature(); + emonth.temp = data.celsiusHundredths; + emonth.humidity = data.humidityBasisPoints; + // might nee dome delay here + } + + + // Send data via RF + rf12_sleep(RF12_WAKEUP); + dodelay(100); + rf12_sendNow(0, &emonth, sizeof emonth); + // set the sync mode to 2 if the fuses are still the Arduino default + // mode 3 (full powerdown) can only be used with 258 CK startup fuses + rf12_sendWait(2); + rf12_sleep(RF12_SLEEP); + dodelay(100); + power_spi_disable(); + //digitalWrite(LED,HIGH); + //dodelay(100); + //digitalWrite(LED,LOW); + + if (debug==1) + { + Serial.print("temp:");Serial.print(emonth.temp); Serial.print(","); + + if (((DHT22_status) || (SI7021_status)) && (DS18B20)){ // DS18b230 + other sensor = assume ds18b20 is external + Serial.print("temp_ex:");Serial.print(emonth.temp_external); Serial.print(","); + } + + if ((DHT22_status) || (SI7021_status)) //humidy sensor present + { + Serial.print("humidity:");Serial.print(emonth.humidity); Serial.print(","); + } + Serial.print("battery: "); Serial.print(emonth.battery); Serial.print(","); + if (emonth.pulsecount > 0) { + Serial.print("pulse:"); Serial.print(emonth.pulsecount); + } + delay(50); + } // end serial print debug + + + unsigned long last = now; + now = millis(); + WDT_number=0; + } // end WDT + +} // end loop + +void dodelay(unsigned int ms) +{ + byte oldADCSRA=ADCSRA; + byte oldADCSRB=ADCSRB; + byte oldADMUX=ADMUX; + + Sleepy::loseSomeTime(ms); // JeeLabs power save function: enter low power mode for x seconds (valid range 16-65000 ms) + + ADCSRA=oldADCSRA; // restore ADC state + ADCSRB=oldADCSRB; + ADMUX=oldADMUX; +} + +// The interrupt routine - runs each time a rising edge of a pulse is detected +void onPulse() +{ + p=1; // flag for new pulse set to true + pulseCount++; // number of pulses since the last RF sent +}