Building a weather station

Hi

I have used OEM hardware in the past and when mominlaw complained that these cheap weather stations dont last long it got me thinking that i could build something better

i got one of these big graphic lcd’s used for 3d printers and from some very short research that is what was used in the EmonGLCD if not much mistaken… so i could use that for the display in the kitchen, with the eagle files i will be able to mod things to my needs and 3dprint a case

all she needs is temp and rain gauge and time so i took the eagle files for the emonth and removed much of the battery related stuff and sensors so i only have the DHT and ds18b20 back.

it will be mounted inside a sheed where mains are availble so consumption is not a problem

i plan to feed a wire out where wall meets roof and mount the rain gauge in the gutter and with the sensors inside the rain gauge housing. then a ds18b20 in the soil to get surface temp

i might also build some TINY boards that can monitor her plants water needs…

as a central “hub” i planned on using a nanoderf but as they are out of production i figured i could build my own cut down version and looked at some schematics for the nanoderf and found that an emonth with a real time clock is close to an nanoderf but it will miss the internet connection… i’m working on how i plan to connect to internet but not sure yet since i might only use that to get time from an NTP server

so far i have come up with an emonth clone that has the realtime clock also… for the board in the sheed i will skip soldering in the real time clock… when i figure how to connect to internet that will also be skipped

goal is to have one board that can be used as hub and as node depending on what i mount on the board

now for the firmware… i’m looking at NanodeRF/NanodeRF_multinode.ino at master · openenergymonitor/NanodeRF · GitHub

one of the bullet points in the start says:

Relay’s time data to emonglcd - and any other listening nodes.

where does it send the time to the emonglcd? that escapes my mind, the rest i can figure
also how is power data sent to the display? i planned to use that to send the temp and rain data

hope everything above makes sense, english is not my native language

am i correct that the the nanoderf did not send data to the display as such but the glcd just listened for the data?

code for the node in the sheed: ////////////const byte version = 27; // firmware version divided by - Pastebin.com

i skipped the intro text to keep it small in the pastebin

if people can spare a few time i would like an extra set of eye’s on it

i have commented out quite a bit since i will be powering this off the mains and hence powersave is not needed. it will be in a sealed box with a few small bag of moisture absorbers, but a little heat from the RFM will not hurt when its gets freezing

code does compile so no major faults, but have i removed to much or are there “dead” code i could remove?

The NanodeRF was a pure “base” station. You are right, all it did was pass data to and from emoncms.org. Energy data was broadcast by the emonTx to both the Nanode and the GLCD (so yes, you are right there - the GLCD got the energy data direct) and onwards from the Nanode to emoncms.org, and the time came from emoncms.org to the Nanode, which broadcast it to the GLCD. Time was (still is?) 4 digits - hhmm, not full Internet time.

Bo, can you attach your code here, please? I’ve changed your Trust level so you should be able to do so now.

sure:



////////////const byte version = 27;         // 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 <JeeLib.h>                                                   // https://github.com/jcw/jeelib - Tested with JeeLib 3/11/14

const 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 = 10;                                                      // 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=12;                                   // 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 750                                               // 9bit requres 95ms, 10bit 187ms, 11bit 375ms and 12bit resolution takes 750ms
// See block comment above for library info
////////#include <avr/power.h>
////////#include <avr/sleep.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#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

// 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       19
#define DHTPIN             18

// 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);

  //################################################################################################################################
  // 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();

  //################################################################################################################################
  // 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 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("Detected "); Serial.print(numSensors); Serial.println(" DS18B20");
       if (DHT22_status==1) Serial.println("DS18B20 & DHT22 found, assume DS18B20 is external");
    }
  }
  if (debug==1) delay(200);

  //################################################################################################################################
  // 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);
} // 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<numSensors;j++) sensors.setResolution(allAddress[j], TEMPERATURE_PRECISION);      // and set the a to d conversion resolution of each.
      sensors.requestTemperatures();                                        // Send the command to get temperatures
      dodelay(ASYNC_DELAY); //Must wait for conversion, since we use ASYNC mode
      float temp=(sensors.getTempC(allAddress[0]));
////////      digitalWrite(DS18B20_PWR, LOW);
      if ((temp<125.0) && (temp>-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);

    if (debug==1)
    {
      if (DS18B20)
      {
        Serial.print("DS18B20 Temperature: ");
        if (DHT22_status) Serial.print(emonth.temp_external/10.0);
        if (!DHT22_status) Serial.print(emonth.temp/10.0);
        Serial.print("C, ");
      }

      if (DHT22_status)
      {
        Serial.print("DHT22 Temperature: ");
        Serial.print(emonth.temp/10.0);
        Serial.print("C, DHT22 Humidity: ");
        Serial.print(emonth.humidity/10.0);
        Serial.print("%, ");
      }

////////////      Serial.print("Battery voltage: ");
////////////      Serial.print(emonth.battery/10.0);
      Serial.print("V, Pulse count: ");
      Serial.print(emonth.pulsecount);
      Serial.println("n");

      unsigned long last = now;
      now = millis();

      delay(100);
    }


    ////////////power_spi_enable();

    ////////////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);

    WDT_number=0;
  }

} // 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
}

hmm… i tried to use the block quote, did not go well…

how is it done?

but thanks for the info on how data is sent arround, since this is for an old lady (and to keep it stupid simple for me) i think i will place an esp8266 at the display and get the time directly.

that is the only thing i wanted internet access for, and i could drop the real time clock

The best way for something as big as this is to upload the file.

use 3 back ticks before and after the block of code

    ```
    code goes here
    ```

Personally speaking I find it really annoying when logs and code are attached rather than inline in code blocks as I cannot just view the code or the log files on my mobile, that means I cannot offer support when out of the house as I do not want to download everyone’s logs and sketches to my mobile.

will use back ticks next time…

and it’s no rush… i take this as a over winter project

Nothing wrong with the emonGLCD, but if you fancy a change I built/wrote the emonNOTIFY device

Code is all on github

Hi Stu

I dont plan to do any upload as mominlaw just want a simple “weather station” with a display in the kitchen

The more simple it can get the better, a pi would just complicate things.

sine the lcd just listen for data i can get away with a display in the kitchen and a node in the sheed with sensors on the outside

only thing i have not though of is getting time from internet… i could do a minimal nanoderf clone thing, but if that could be avoided i will take that

my plan if its possible is to fit the wifi module to the display and pull time directly

but a 3d printed case is mostly welcome… will have a look at that, but might need to mod it as i dont plan on having switches