My controller-less mysensors temperature logger



  • Hi!

    I built a mysensors-datalogger that does not need a controller. The reason for this is that I wanted to log some temperature-data at my office which has a strict IT-security policy and would not allow any network based controller like rasperry pi or vera.

    This is what it looks like:
    DSC_0945 Kopie.jpg

    I used the following hardware:

    • Arduino Mega 2560 clone (atmega328 has not enough flash for this project)
    • oled-display based on sh1106 chipset
    • rtc based on DS1302 (would not recommend, but had this one available)
    • micro-SD breakout board
    • NRF24L01 module
    • DHT22 temperature and humidity senosr
    • DS18B20 temperature sensor (waterproof)
    • some buttons

    It is possible to deactivate most of the hardware-pieces in the software. If the RTC has an error or is deactivated by hand it falls back to software RTC.
    For powering the NRF24L01 I used the 3.3V regulator of the micro-SD breakout as the mega2560 has a very weak regulator.

    Basically the software continuously fetches the onboard sensors and waits for radio signals. Every 5 minutes it writes all available values to the SD-Card.

    The mysensors-support is very basic. Currently it only supports up to 6 temperature sensors with one temperature and battery reading each.

    Here is the code:

    /*
     Weatherstation-"Gateway" (Stand-alone)
    
     Created by Florian Heinze
    
     This program is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License
     version 2 as published by the Free Software Foundation.
    */
    
    
    #include <U8glib.h>
    #include <SPI.h>
    #include "SD.h"
    #include <MySensor.h>
    #include <MyGateway.h>
    #include <stdarg.h>
    #include <DHT.h>
    #include <DallasTemperature.h>
    #include <OneWire.h>
    #include <Time.h>
    #include <DS1302RTC.h>
    #include <EEPROM.h>
    
    const uint8_t ARROW[] U8G_PROGMEM = {
      B00000000,
      B00001100,
      B00000110,
      B11111111,
      B11111111,
      B00000110,
      B00001100,
      B00000000
    };
    
    /*
     * PIN-definitions
     */
    const int DHT_BUS = A9;
    
    const int ONE_WIRE_BUS = 25;
    const int ONE_WIRE_VCC = 23;
    const int ONE_WIRE_GND = 27;
    
    const int NRF_CE_PIN = 30;
    const int NRF_CS_PIN = 36;
    
    const int SD_CS_PIN = 12;
    const int SD_MISO_PIN = 9;
    const int SD_MOSI_PIN = 10;
    const int SD_SCK_PIN = 11;
    
    const int DISP_CS_PIN = A4;
    const int DISP_DC_PIN = A3;
    const int DISP_RES_PIN = A2;
    const int DISP_SCK_PIN = A0;
    const int DISP_MOSI_PIN = A1;
    
    const int RTC_SCK_PIN = 16;
    const int RTC_DAT_PIN = 15;
    const int RTC_RST_PIN = 14;
    
    const int BUTTON_RIGHT_PIN = 4;
    const int BUTTON_LEFT_PIN = 5;
    const int BUTTON_UP_PIN = 6;
    const int BUTTON_DOWN_PIN = 3;
    const int BUTTON_ENTER_PIN = 7;
    
    //Variables for buttons
    boolean right = false;
    boolean left = false;
    boolean up = false;
    boolean down = false;
    boolean enter = false;
    boolean lastright = false;
    boolean lastleft = false;
    boolean lastup = false;
    boolean lastdown = false;
    boolean lastenter = false;
    
    const unsigned long DEBOUNCE_DELAY = 10;
    unsigned long debouncetime = 0;
    unsigned long lastSDWrite = 0;
    
    //Channel for RF
    const int RF24_CUST_CHANNEL = 50;
    
    //EEPROM-addreses. start at 512 so it doesnt collide with mysensors.
    const int START_EEPROM_ADR = 512;
    const int DHT_EEPROM_ADR = START_EEPROM_ADR;
    const int ONEWIRE_EEPROM_ADR = START_EEPROM_ADR + 1;
    const int RTC_EEPROM_ADR = START_EEPROM_ADR + 2;
    const int NRF_EEPROM_ADR = START_EEPROM_ADR + 3;
    const int SD_EEPROM_ADR = START_EEPROM_ADR + 4;
    
    //Filenames
    #define MYS_LOG "myslog.txt"
    #define DATA_LOG "datalog.txt"
    
    const unsigned int WRITE_MINUTE = 5;
    
    //status of hardware
    typedef enum {OFF = 0, ERR, ON} hw_stat_t;
    
    hw_stat_t dht_stat;
    hw_stat_t onewire_stat;
    hw_stat_t rtc_stat;
    hw_stat_t nrf_stat; //no ERR state
    hw_stat_t sd_stat;
    
    //used for rtc
    time_t t = 0;
    boolean clock_set = false;
    enum {DAY, MONTH, YEAR, HOUR, MINUTE, SECOND} set_digit = DAY;
    #define LEAP_YEAR(Y)     ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
    static  const uint8_t monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // API starts months from 1, this array starts from 0
    
    //state of HW-page
    enum {DHT_22 = 0, DS18B22, RTC, NRF, SD_CARD} hw_page_stat = DHT_22;
    
    //state of display
    enum {PAGE1, PAGE2, PAGE3, PAGE4, PAGE5, DIAGRAM, CLOCK, HW_SET} dispstat = PAGE1;
    
    //max number of mysensors
    const int MYS_MAX_SENSORS = 6;
    
    //variables for sensors
    long lastDHTTime;
    long lastOnewireTime;
    float tempDHT = 0;
    float humDHT = 0;
    float tempOnewire = 0;
    float tempMyS[MYS_MAX_SENSORS] = {0};//temp of mysensors
    time_t lastReadMyS[MYS_MAX_SENSORS] = {0};//last time the mysensors are read
    uint8_t battMyS[MYS_MAX_SENSORS] = {0};//battery of mysensors
    
    //used for the diagram
    float diagram[128];
    boolean diagramRead = false;
    boolean diagramScroll = false;
    boolean linesUpdate = false;
    unsigned long diagramStart = 1;
    unsigned long lines = 0;
    uint8_t numDiagram = 1;
    const int BOT_PXL = 64;
    const int TOP_PXL = 0;
    boolean dispOnewire = false;
    
    //objects for hardware: temperature-sensors
    DHT dht;
    OneWire oneWire(ONE_WIRE_BUS);
    DallasTemperature sensors(&oneWire);
    
    //rtc
    DS1302RTC rtc(RTC_RST_PIN, RTC_DAT_PIN, RTC_SCK_PIN);
    
    //display
    U8GLIB_SH1106_128X64 u8g(DISP_SCK_PIN, DISP_MOSI_PIN, DISP_CS_PIN, DISP_DC_PIN, DISP_RES_PIN);
    
    //mysensors
    MyGateway gw(NRF_CE_PIN, NRF_CS_PIN);
    
    //setup-routine of dht
    void setup_dht() {
      dht.setup(DHT_BUS);
      lastDHTTime = millis();
      if (dht.getStatus() == DHT::ERROR_NONE) dht_stat = ON; //check for error
      else dht_stat = ERR;
    }
    
    //setup routine of onewire
    void setup_onewire() {
      sensors.begin();
      sensors.setWaitForConversion(false);
      sensors.requestTemperatures();
    
      if (sensors.getDeviceCount() == 1) onewire_stat = ON; //check for error
      else onewire_stat = ERR;
    
      lastOnewireTime = millis();
    }
    
    //setup rtc
    void setup_rtc() {
      if (rtc.get() == 0) //check for error
      {
        rtc_stat = ERR;
      }
      else
      {
        rtc_stat = ON;
        setSyncProvider(rtc.get); //rtc is set up as sync provider. normal time comes from internal clock.
      }
    }
    
    //setup for sd-card
    void setup_sd() {
      if (SD.begin(SD_CS_PIN, SD_MOSI_PIN, SD_MISO_PIN, SD_SCK_PIN)) {//start and check for error
        sd_stat = ON;
        if (!SD.exists(DATA_LOG)) {//if the file does not exist, write header
          File dataFile = SD.open(DATA_LOG, FILE_WRITE);
          if (dataFile) {
            dataFile.print("time,temp-dht,humidity-dht,temp-onewire");
            for (int i = 1; i <= MYS_MAX_SENSORS; i++) {
              dataFile.print(",temp");
              dataFile.print(i);
            }
            for (int i = 1; i <= MYS_MAX_SENSORS; i++) {
              dataFile.print(",batt");
              dataFile.print(i);
            }
            dataFile.print("\n");
            dataFile.close();
          }
        }
      }
      else
      {
        sd_stat = ERR;
      }
    }
    
    //reads the last line of the datalog-file to initialize the mysensor-values with the last values after reset.
    void readLastMyS() {
      File dataFile = SD.open(DATA_LOG, FILE_READ);
      char buf[106];
      char *str, *p;
    
      if (dataFile) { //11 date 6 time 6 dhttemp 6 dhthum 6 onewire 6*6 onewire 6*4 onewire = 101 max 56 min
        unsigned long filesize = dataFile.size();
        dataFile.seek(filesize - 106);
        dataFile.find("\n");
        dataFile.readBytesUntil('\n', buf, 105); //read a line
        uint8_t j = 0;
        for (str = strtok_r(buf, ",", &p);                // goto last entry before MyS-Data
             str && j < 3;
             str = strtok_r(NULL, ",", &p)
            ) {
          j++;
        }
        for (int i = 0; i < MYS_MAX_SENSORS; i++) { //read MyS-temps
          str = strtok_r(NULL, ",", &p);
          if (str) {
            tempMyS[i] = atof(str);
          }
          else break;
        }
        for (int i = 0; i < MYS_MAX_SENSORS; i++) { //read MyS batts
          str = strtok_r(NULL, ",", &p);
          if (str) {
            battMyS[i] = atoi(str);
          }
          else break;
        }
      }
    }
    
    void(* reset_func) (void) = 0; //declare reset function @ address 0
    
    void setup() {
      pinMode(BUTTON_RIGHT_PIN, INPUT_PULLUP);
      pinMode(BUTTON_LEFT_PIN, INPUT_PULLUP);
      pinMode(BUTTON_UP_PIN, INPUT_PULLUP);
      pinMode(BUTTON_DOWN_PIN, INPUT_PULLUP);
      pinMode(BUTTON_ENTER_PIN, INPUT_PULLUP);
    
      LoadParameters(); //load hw-state from eeprom
    
      if (digitalRead(BUTTON_ENTER_PIN) == LOW) nrf_stat = OFF; //to power up without NRF24, hold down enter button on startup (no error state)
    
      //startup hardware
      pinMode(ONE_WIRE_GND, OUTPUT);
      pinMode(ONE_WIRE_VCC, OUTPUT);
      digitalWrite(ONE_WIRE_GND, LOW);
      digitalWrite(ONE_WIRE_VCC, HIGH);
      delay(10);//let the sensor power up.
    
      if (onewire_stat != OFF)
      {
        setup_onewire();
      }
    
      if (dht_stat != OFF)
      {
        setup_dht();
      }
    
      if (rtc_stat != OFF)
      {
        setup_rtc();
      }
    
      if (nrf_stat != OFF)//no ERR
      {
        gw.begin(RF24_PA_MAX, RF24_CUST_CHANNEL, RF24_DATARATE, incomingRadio);
      }
      else
      {
        Serial.begin(115200);
      }
    
      if (sd_stat != OFF) {
        setup_sd();
      }
    
      if (sd_stat == ON) { //read last MyS-Data from SD-card
        readLastMyS();
      }
    }
    
    
    void loop() {
      if ( (millis() - lastDHTTime > dht.getMinimumSamplingPeriod()) && (dht_stat == ON) ) //read DHT
      {
        tempDHT = dht.getTemperature();
        humDHT = dht.getHumidity();
        lastDHTTime = millis();
      }
    
      if ( (millis() - lastOnewireTime >= 750) && (onewire_stat == ON) ) //read onewire-sensor
      {
        tempOnewire = sensors.getTempCByIndex(1);
        sensors.requestTemperatures();
        lastOnewireTime = millis();
      }
    
      t = now();//read time
    
      if (nrf_stat != OFF) {//process radio
        gw.processRadioMessage();
      }
    
      updateDisplay();
    
      readButtons();
    
      updateState();//change display states depending on buttons
    
      //write SD
      if (sd_stat == ON && (minute(t) % WRITE_MINUTE == 0) && (millis() - lastSDWrite >= 120000)) {
        writeLogSD();
      }
    }
    
    //read and debounce the buttons
    void readButtons() {
      //inverted for internal pullup
      boolean tempright = !digitalRead(BUTTON_RIGHT_PIN);
      boolean templeft = !digitalRead(BUTTON_LEFT_PIN);
      boolean tempup = !digitalRead(BUTTON_UP_PIN);
      boolean tempdown = !digitalRead(BUTTON_DOWN_PIN);
      boolean tempenter = !digitalRead(BUTTON_ENTER_PIN);
    
      if (right || left || up || down || enter) //any button last pressed
      {
        debouncetime = millis();
      }
      if ( (millis() - debouncetime) > DEBOUNCE_DELAY )
      {
        if (tempright && !lastright) right = tempright;
        if (templeft && !lastleft) left = templeft;
        if (tempup && !lastup) up = tempup;
        if (tempdown && !lastdown) down = tempdown;
        if (tempenter && !lastenter) enter = tempenter;
      }
      else
      {
        right = 0; //right stays high only for one loop
        left = 0;
        up = 0;
        down = 0;
        enter = 0;
      }
      lastright = tempright;
      lastleft = templeft;
      lastup = tempup;
      lastdown = tempdown;
      lastenter = tempenter;
    }
    
    //write line to log-file
    void writeLogSD() {
      File dataFile = SD.open(DATA_LOG, FILE_WRITE);
      if (dataFile) {//"time,temp-dht,humidity-dht,temp-onewire,temp1,temp2,temp3,temp4,temp5,temp6"
        //dataFile.print((float)(t) / 86400 + 25569,6); //excel timestamp
        dataFile.print(day(t) > 9 ? "" : "0");
        dataFile.print(day(t));
        dataFile.print(".");
        dataFile.print(month(t) > 9 ? "" : "0");
        dataFile.print(month(t));
        dataFile.print(".");
        dataFile.print(year(t));
        dataFile.print(" ");
        dataFile.print(hour(t) > 9 ? "" : "0");
        dataFile.print(hour(t));
        dataFile.print(":");
        dataFile.print(minute(t) > 9 ? "" : "0");
        dataFile.print(minute(t));
        dataFile.print(",");
        dataFile.print(tempDHT, 1);
        dataFile.print(",");
        dataFile.print(humDHT, 1);
        dataFile.print(",");
        dataFile.print(tempOnewire, 1);
        for (int i = 0; i < MYS_MAX_SENSORS; i++) {
          dataFile.print(",");
          dataFile.print(tempMyS[i], 1);
        }
        for (int i = 0; i < MYS_MAX_SENSORS; i++) {
          dataFile.print(",");
          dataFile.print(battMyS[i]);
        }
        dataFile.print("\n");
        dataFile.close();
        lastSDWrite = millis();
      }
    }
    
    //returns the number of lines of the logfile
    unsigned long readSDFileLines() {
      unsigned long numLines = 0;
      if (sd_stat != ON) return 0;
      File dataFile = SD.open(DATA_LOG, FILE_READ);
    
      while (dataFile.find("\n")) { //find number of lines
        numLines++;
      }
      dataFile.close();
      return numLines;
    }
    
    //read values from sd-card for the diagramm
    void readTempFromSD(uint8_t numToRead, unsigned long startRecord) {
      char buf[128];
      char *str, *p;
    
      if (sd_stat != ON) return;
    
      File dataFile = SD.open(DATA_LOG, FILE_READ);
    
      for (int i = 0; i < startRecord; i++) {
        dataFile.find("\n");
      }
    
      for (uint8_t i = 0; i < 128; i++) {
        dataFile.readBytesUntil('\n', buf, 127); //read a line
        uint8_t j = 0;
        for (str = strtok_r(buf, ",", &p);        // split using comma
             str && j < numToRead;                               // loop while str is not null an max 5 times
             str = strtok_r(NULL, ",", &p)               // get subsequent tokens
            ) {
          j++;
        }
        if (str) {
          diagram[i] = atof(str);
        }
        else {
          for (; i < 128; i++) {
            diagram[i] = -127;
          }
          break;
        }
      }
      dataFile.close();
    }
    
    //print the diagram
    void printTempDiagram() {
      int maxtemp = (int) ceil(diagram[0]);//start with valid value
      int mintemp = (int) floor(diagram[0]);//start with valid value
      for (int i = 126; i >= 0; i--)
      {
        if (diagram[i] > maxtemp)
        {
          maxtemp = (int) ceil(diagram[i]);
        }
        if (diagram[i] < mintemp && diagram[i] > -127)
        {
          mintemp = (int) floor(diagram[i]);
        }
      }
    
      u8g.setFont(u8g_font_helvB08);
      u8g.setPrintPos(0, TOP_PXL + 8);
      u8g.print(maxtemp); //scale
      u8g.setPrintPos(0, BOT_PXL);
      u8g.print(mintemp); //scale
    
      if (diagramScroll) {
        u8g.setPrintPos(30, BOT_PXL);
        u8g.print("scroll "); //scale
        u8g.print(diagramStart);
      }
    
      u8g.setPrintPos(20, 8);
      if (numDiagram == 1) {
        u8g.print("Temp DHT");
      }
      else if (numDiagram == 2) {
        u8g.print("Hum DHT");
      }
      else if (numDiagram == 3) {
        u8g.print("Temp Onewire");
      }
      else if (numDiagram > 3 && numDiagram <= (3 + MYS_MAX_SENSORS)) {
        u8g.print("Temp MyS ");
        u8g.print(numDiagram - 3);
      }
      else if (numDiagram <= (3 + MYS_MAX_SENSORS * 2)) {
        u8g.print("Batt MyS ");
        u8g.print(numDiagram - 3 - MYS_MAX_SENSORS);
      }
    
      for (int i = 0; i < 128; i++) //print array
      {
        float pxl = (diagram[i] - mintemp) * (BOT_PXL - TOP_PXL) / (-maxtemp + mintemp) + BOT_PXL;
        if (diagram[i] >= mintemp && diagram[i] <= maxtemp) //dont print if out of boundery
        {
          u8g.drawPixel(i, (int)pxl);
        }
      }
    }
    
    //dump the logfile to the serial port
    void dumpFile() {
      if (sd_stat == ON) {
        File dataFile = SD.open(DATA_LOG, FILE_READ);
        if (dataFile) {
          while (dataFile.available()) {
            Serial.write(dataFile.read());
          }
          dataFile.close();
        }
      }
    }
    
    //updates the state-variables depending on pressed buttons
    void updateState() {
      switch (dispstat)
      {
        case PAGE1:
          if (right == true) dispstat = PAGE2;
          if (left == true) dispstat = HW_SET;
          if (enter == true) dumpFile();
          break;
        case PAGE2:
          if (right == true) dispstat = PAGE3;
          if (left == true) dispstat = PAGE1;
          if (up == true || down == true) dispOnewire = !dispOnewire;
          break;
        case PAGE3:
          if (right == true) dispstat = PAGE4;
          if (left == true) dispstat = PAGE2;
          if (up == true || down == true) dispOnewire = !dispOnewire;
          break;
        case PAGE4:
          if (right == true) dispstat = PAGE5;
          if (left == true) dispstat = PAGE3;
          if (up == true || down == true) dispOnewire = !dispOnewire;
          break;
        case PAGE5:
          if (right == true) dispstat = DIAGRAM;
          if (left == true) dispstat = PAGE4;
          break;
        case DIAGRAM:
          if (right == true) {
            if (diagramScroll == false) {
              dispstat = CLOCK;
              linesUpdate = false;
            }
            else {
              if (diagramStart < lines - 256 && lines > 128) {
                diagramStart += 128;
              }
              else {
                diagramStart = lines > 128 ? lines - 128 : 1;
                //Serial.print(diagramStart);
              }
              diagramRead = false;
            }
          }
          else if (left == true) {
            if (diagramScroll == false) {
              dispstat = PAGE5;
              linesUpdate = false;
            }
            else
            {
              if (diagramStart > 128) {
                diagramStart -= 128;
              }
              else {
                diagramStart = 1;
              }
              diagramRead = false;
            }
          }
          else if (enter == true) {
            diagramScroll = !diagramScroll;
          }
    
          //unsigned long diagramStart = 0;
          else if (up == true && numDiagram > 1) {
            numDiagram--;
            diagramRead = false;
          }
          else if (down == true && numDiagram < (3 + 2 * MYS_MAX_SENSORS)) {
            numDiagram++;
            diagramRead = false;
          }
          else if (!linesUpdate) {
            lines = readSDFileLines();
            diagramStart = lines - 128;
            linesUpdate = true;
          }
          else if (!diagramRead) {
            //readTempFromSD(numDiagram, diagramStart);
            readTempFromSD(numDiagram, diagramStart);
            diagramRead = true;
          }
          break;
        case CLOCK:
          if (clock_set == false && right == true) dispstat = HW_SET;
          if (clock_set == false && left == true) dispstat = DIAGRAM;
          if (enter == true) clock_set = !clock_set;
          if (clock_set == true) {
            if (left == true) {
              switch (set_digit) {
                case SECOND:
                  set_digit = MINUTE;
                  break;
                case MINUTE:
                  set_digit = HOUR;
                  break;
                case HOUR:
                  set_digit = YEAR;
                  break;
                case YEAR:
                  set_digit = MONTH;
                  break;
                case MONTH:
                  set_digit = DAY;
                  break;
                case DAY:
                  set_digit = SECOND;
                  break;
              }
            }
            else if (right == true) {
              switch (set_digit) {
                case SECOND:
                  set_digit = DAY;
                  break;
                case MINUTE:
                  set_digit = SECOND;
                  break;
                case HOUR:
                  set_digit = MINUTE;
                  break;
                case YEAR:
                  set_digit = HOUR;
                  break;
                case MONTH:
                  set_digit = YEAR;
                  break;
                case DAY:
                  set_digit = MONTH;
                  break;
              }
            }
            if (up == true || down == true) {
              int inc = up - down;
              int d = day(t);
              int m = month(t);
              int y = year(t);
              int h = hour(t);
              int mi = minute(t);
              int s = second(t);
              switch (set_digit) {
                case SECOND:
                  s += inc;
                  if (s < 0) s = 59;
                  if (s > 59) s = 0;
                  break;
                case MINUTE:
                  mi += inc;
                  if (mi < 0) mi = 59;
                  if (mi > 59) mi = 0;
                  break;
                case HOUR:
                  h += inc;
                  if (h < 0) h = 23;
                  if (h > 23) h = 0;
                  break;
                case YEAR:
                  y += inc;
                  break;
                case MONTH:
                  m += inc;
                  if (m < 1) m = 12;
                  if (m > 12) m = 1;
                  break;
                case DAY:
                  d += inc;
                  uint8_t maxday = monthDays[m - 1];
                  if (LEAP_YEAR(y - 1970) && m == 2) maxday = 29;
                  if (d < 1) d = maxday;
                  if (d > maxday) d = 1;
                  break;
              }
    
              //set time
              tmElements_t tm = {s, mi, h, 0, d, m, y - 1970};
              t = makeTime(tm);
              if (rtc_stat == ON) {
                rtc.set(t);   // set the RTC and the system time to the received value
              }
              setTime(t);
            }
          }
          break;
        case HW_SET:
          if (right == true) dispstat = PAGE1;
          if (left == true) dispstat = CLOCK;
          if (up == true) {
            switch (hw_page_stat) {
              case DHT_22:
                hw_page_stat = SD_CARD;
                break;
              case DS18B22:
                hw_page_stat = DHT_22;
                break;
              case RTC:
                hw_page_stat = DS18B22;
                break;
              case NRF:
                hw_page_stat = RTC;
                break;
              case SD_CARD:
                hw_page_stat = NRF;
                break;
            }
          }
          if (down == true) {
            switch (hw_page_stat) {
              case DHT_22:
                hw_page_stat = DS18B22;
                break;
              case DS18B22:
                hw_page_stat = RTC;
                break;
              case RTC:
                hw_page_stat = NRF;
                break;
              case NRF:
                hw_page_stat = SD_CARD;
                break;
              case SD_CARD:
                hw_page_stat = DHT_22;
                break;
            }
          }
          if (enter == true) {
            switch (hw_page_stat) {
              case DHT_22:
                if (dht_stat == ON) {
                  dht_stat = OFF;
                  tempDHT = 0;
                  humDHT = 0;
                }
                else {
                  dht_stat = ON;
                  setup_dht();
                }
                break;
              case DS18B22:
                if (onewire_stat == ON) {
                  onewire_stat = OFF;
                  tempOnewire = 0;
                }
                else {
                  onewire_stat = ON;
                  setup_onewire();
                }
                break;
              case RTC://TBD
                if (rtc_stat == ON) {
                  rtc_stat = OFF;
                  setSyncProvider(NULL);
                }
                else {
                  rtc_stat = ON;
                  setup_rtc();
                }
                break;
              case NRF:
                if (nrf_stat == ON) {
                  nrf_stat = OFF;
                  SaveParameters();
                  reset_func();
                }
                else {
                  nrf_stat = ON;
                  SaveParameters();
                  reset_func();
                }
                break;
              case SD_CARD:
                if (sd_stat == ON) {
                  sd_stat = OFF;
                  SD.end();
                }
                else {
                  sd_stat = ON;
                  setup_sd();
                }
                break;
            }
            SaveParameters();
          }
          break;
        default:
          break;
      }
    }
    
    //updates the display depending on state-variables
    void updateDisplay() {
      u8g.firstPage();
      do {
        switch (dispstat)
        {
          case PAGE1:
            u8g.setFont(u8g_font_helvB08);
            u8g.setPrintPos(40, 10);
            u8g.print(tempDHT, 1);
            u8g.print("\xb0 C ");
            u8g.print(humDHT, 1);
            u8g.print("%");
            u8g.setPrintPos(40, 20);
            u8g.print(tempOnewire, 1);
            u8g.print("\xb0 C");
            u8g.setPrintPos(40, 30);
            u8g.print(tempMyS[0], 1);
            u8g.print("\xb0 C ");
            u8g.print(tempMyS[1], 1);
            u8g.print("\xb0 C");
            u8g.setPrintPos(40, 40);
            u8g.print(tempMyS[2], 1);
            u8g.print("\xb0 C ");
            u8g.print(tempMyS[3], 1);
            u8g.print("\xb0 C");
            u8g.setPrintPos(0, 60);
            u8g.print(day(t) > 9 ? "" : "0");
            u8g.print(day(t));
            u8g.print(".");
            u8g.print(month(t) > 9 ? "" : "0");
            u8g.print(month(t));
            u8g.print(".");
            u8g.print(year(t));
            u8g.print(" ");
            u8g.print(hour(t) > 9 ? "" : "0");
            u8g.print(hour(t));
            u8g.print(":");
            u8g.print(minute(t) > 9 ? "" : "0");
            u8g.print(minute(t));
            u8g.print(":");
            u8g.print(second(t) > 9 ? "" : "0");
            u8g.print(second(t));
            u8g.setFont(u8g_font_5x7);
            u8g.setPrintPos(0, 10);
            u8g.print("DHT:");
            u8g.setPrintPos(0, 20);
            u8g.print("Dallas:");
            u8g.setPrintPos(0, 30);
            u8g.print("MyS:");
            break;
          case PAGE2:
            u8g.setFont(u8g_font_helvB18);
            u8g.setPrintPos(20, 30);
            u8g.print(dispOnewire ? tempOnewire : tempDHT, 1);
            u8g.print("\xb0 C");
            u8g.setPrintPos(20, 60);
            u8g.print(humDHT, 1);
            u8g.print("%");
            break;
          case PAGE3:
            u8g.setFont(u8g_font_helvB10);
            u8g.setPrintPos(10, 14);
            u8g.print(dispOnewire ? tempOnewire : tempDHT, 1);
            u8g.print("\xb0 C");
            u8g.setPrintPos(74, 14);
            u8g.print(humDHT, 1);
            u8g.print("%");
            u8g.setPrintPos(0, 38);
            u8g.setFont(u8g_font_5x7);
            u8g.print("1 ");
            u8g.setFont(u8g_font_helvB10);
            u8g.print(tempMyS[0], 1);
            u8g.print("\xb0 C ");
            u8g.setPrintPos(64, 38);
            u8g.setFont(u8g_font_5x7);
            u8g.print("2 ");
            u8g.setFont(u8g_font_helvB10);
            u8g.print(tempMyS[1], 1);
            u8g.print("\xb0 C ");
            u8g.setPrintPos(0, 60);
            u8g.setFont(u8g_font_5x7);
            u8g.print("3 ");
            u8g.setFont(u8g_font_helvB10);
            u8g.print(tempMyS[2], 1);
            u8g.print("\xb0 C ");
            u8g.setPrintPos(64, 60);
            u8g.setFont(u8g_font_5x7);
            u8g.print("4 ");
            u8g.setFont(u8g_font_helvB10);
            u8g.print(tempMyS[3], 1);
            u8g.print("\xb0 C ");
            break;
          case PAGE4:
            u8g.setFont(u8g_font_helvB18);
            u8g.setPrintPos(20, 30);
            u8g.print(dispOnewire ? tempOnewire : tempDHT, 1);
            u8g.print("\xb0 C");
            u8g.setPrintPos(20, 60);
            u8g.print(tempMyS[0], 1);
            u8g.print("\xb0 C");
            u8g.setFont(u8g_font_5x7);
            u8g.setPrintPos(0, 30);
            u8g.print("in");
            u8g.setPrintPos(0, 60);
            u8g.print("out");
            break;
          case PAGE5:
            for (uint8_t i = 0; i < MYS_MAX_SENSORS; i++)
            {
              u8g.setFont(u8g_font_helvB08);
              u8g.setPrintPos(0, 10 * (i + 1));
              u8g.print(i + 1);
              u8g.print(":");
              u8g.setPrintPos(10, 10 * (i + 1));
              u8g.print(tempMyS[i], 1);
              u8g.print("\xb0 C");
              u8g.setPrintPos(55, 10 * (i + 1));
              u8g.print(battMyS[i]);
              u8g.print("%");
              u8g.setPrintPos(90, 10 * (i + 1));
              if (lastReadMyS[i] == 0) u8g.print("nie");
              else if (t - lastReadMyS[i] < 60) {
                u8g.print(t - lastReadMyS[i]);
                u8g.print("s");
              }
              else if (t - lastReadMyS[i] < 3600) {
                u8g.print((t - lastReadMyS[i]) / 60, 1);
                u8g.print("min");
              }
              else {
                u8g.print((t - lastReadMyS[i]) / 3600, 1);
                u8g.print("h");
              }
            }
            break;
          case DIAGRAM:
            printTempDiagram();
            break;
          case CLOCK:
            u8g.setFont(u8g_font_helvB08);
            u8g.setPrintPos(14, 40);
            u8g.print(day(t) > 9 ? "" : "0");
            u8g.print(day(t));
            u8g.print(".");
            u8g.print(month(t) > 9 ? "" : "0");
            u8g.print(month(t));
            u8g.print(".");
            u8g.print(year(t));
            u8g.print(" ");
            u8g.print(hour(t) > 9 ? "" : "0");
            u8g.print(hour(t));
            u8g.print(":");
            u8g.print(minute(t) > 9 ? "" : "0");
            u8g.print(minute(t));
            u8g.print(":");
            u8g.print(second(t) > 9 ? "" : "0");
            u8g.print(second(t));
            if (clock_set == true) {
              //u8g.setPrintPos(0,50);
              //u8g.print("set");
              switch (set_digit) {
                case DAY:
                  u8g.drawFrame(12, 30, 15, 12);
                  break;
                case MONTH:
                  u8g.drawFrame(27, 30, 15, 12);
                  break;
                case YEAR:
                  u8g.drawFrame(42, 30, 27, 12);
                  break;
                case HOUR:
                  u8g.drawFrame(69, 30, 15, 12);
                  break;
                case MINUTE:
                  u8g.drawFrame(84, 30, 15, 12);
                  break;
                case SECOND:
                  u8g.drawFrame(99, 30, 15, 12);
                  break;
              }
    
            }
            break;
          case HW_SET:
            u8g.setFont(u8g_font_helvB08);
            u8g.setPrintPos(10, 10);
            u8g.print("DHT");
            u8g.setPrintPos(70, 10);
            if (dht_stat == OFF) u8g.print("OFF");
            if (dht_stat == ON) u8g.print("ON");
            if (dht_stat == ERR) u8g.print("ERR");
            u8g.setPrintPos(10, 20);
            u8g.print("DS18B20");
            u8g.setPrintPos(70, 20);
            if (onewire_stat == OFF) u8g.print("OFF");
            if (onewire_stat == ON) u8g.print("ON");
            if (onewire_stat == ERR) u8g.print("ERR");
            u8g.setPrintPos(10, 30);
            u8g.print("RTC");
            u8g.setPrintPos(70, 30);
            if (rtc_stat == OFF) u8g.print("OFF");
            if (rtc_stat == ON) u8g.print("ON");
            if (rtc_stat == ERR) u8g.print("ERR");
            u8g.setPrintPos(10, 40);
            u8g.print("NRF");
            u8g.setPrintPos(70, 40);
            if (nrf_stat == OFF) u8g.print("OFF");
            if (nrf_stat == ON) u8g.print("ON");
            u8g.setPrintPos(10, 50);
            u8g.print("SD");
            u8g.setPrintPos(70, 50);
            if (sd_stat == OFF) u8g.print("OFF");
            if (sd_stat == ON) u8g.print("ON");
            if (sd_stat == ERR) u8g.print("ERR");
            u8g.drawBitmapP(0, 2 + (hw_page_stat) * 10, 1, 8, ARROW);
            break;
          default:
            break;
        }
      } while ( u8g.nextPage() );
    }
    
    //callback-method called every time something arrives on the radio.
    void incomingRadio(char *message) {
      char *str, *p, *variable;
      uint8_t node = 0;
      uint8_t sensor = 0;
      uint8_t command = 0;
      uint8_t ack = 0;
      uint8_t type = 0;
      uint8_t i = 0;
    
      //log incoming message on SD-card
      if (sd_stat == ON) {
        File dataFile = SD.open(MYS_LOG, FILE_WRITE);
    
        if (dataFile) {
          dataFile.print(day(t) > 9 ? "" : "0");
          dataFile.print(day(t));
          dataFile.print(".");
          dataFile.print(month(t) > 9 ? "" : "0");
          dataFile.print(month(t));
          dataFile.print(".");
          dataFile.print(year(t));
          dataFile.print(" ");
          dataFile.print(hour(t) > 9 ? "" : "0");
          dataFile.print(hour(t));
          dataFile.print(":");
          dataFile.print(minute(t) > 9 ? "" : "0");
          dataFile.print(minute(t));
          dataFile.print(":");
          dataFile.print(second(t) > 9 ? "" : "0");
          dataFile.print(second(t));
          dataFile.print(" ");
          dataFile.print(message);
          dataFile.close();
        }
      }
    
      //process string
      for (str = strtok_r(message, ";", &p);        // split using semicolon
           str && i < 6;                               // loop while str is not null an max 5 times
           str = strtok_r(NULL, ";", &p)               // get subsequent tokens
          ) {
        switch (i) {
          case 0: // Radioid (destination)
            node = atoi(str);
            break;
          case 1: // Childid
            sensor = atoi(str);
            break;
          case 2: // Message type
            command = atoi(str);
            break;
          case 3: // Should we request ack from destination?
            ack = atoi(str);
            break;
          case 4: // Data type
            type = atoi(str);
            break;
          case 5: // Variable value
            variable = str;
            break;
        }
        i++;
      }
      if ( (node <= MYS_MAX_SENSORS) && (command == 1) && (type == 0) ) { //if temp-message: write to array
        tempMyS[node - 1] = atof(variable);
        lastReadMyS[node - 1] = t;
      }
      if ( (node <= MYS_MAX_SENSORS) && (command == 3) && (type == 0) ) { //battery
        battMyS[node - 1] = atoi(variable);
      }
    }
    
    //load parameters from eeprom (hardware-states)
    void LoadParameters() {
      dht_stat = (hw_stat_t) EEPROM.read(DHT_EEPROM_ADR);
      onewire_stat = (hw_stat_t) EEPROM.read(ONEWIRE_EEPROM_ADR);
      rtc_stat = (hw_stat_t) EEPROM.read(RTC_EEPROM_ADR);
      nrf_stat = (hw_stat_t) EEPROM.read(NRF_EEPROM_ADR);
      sd_stat = (hw_stat_t) EEPROM.read(SD_EEPROM_ADR);
      if (dht_stat == 0xff) dht_stat = ON; //after first boot, set state to one
      if (onewire_stat == 0xff) onewire_stat = ON;
      if (rtc_stat == 0xff) rtc_stat = ON;
      if (nrf_stat == 0xff) nrf_stat = ON;
      if (sd_stat == 0xff) sd_stat = ON;
    }
    
    //save parameters to eeprom (hardware-states)
    void SaveParameters() {
      if (dht_stat != (hw_stat_t) EEPROM.read(DHT_EEPROM_ADR)) EEPROM.write(DHT_EEPROM_ADR, (uint8_t) dht_stat);
      if (onewire_stat != (hw_stat_t) EEPROM.read(ONEWIRE_EEPROM_ADR)) EEPROM.write(ONEWIRE_EEPROM_ADR, (uint8_t) onewire_stat);
      if (rtc_stat != (hw_stat_t) EEPROM.read(RTC_EEPROM_ADR)) EEPROM.write(RTC_EEPROM_ADR, (uint8_t) rtc_stat);
      if (nrf_stat != (hw_stat_t) EEPROM.read(NRF_EEPROM_ADR)) EEPROM.write(NRF_EEPROM_ADR, (uint8_t) nrf_stat);
      if (sd_stat != (hw_stat_t) EEPROM.read(SD_EEPROM_ADR)) EEPROM.write(SD_EEPROM_ADR, (uint8_t) sd_stat);
    }
    

    Hope you like it!



  • PS: The program was written for library-version 1.4. For 1.5 there are some minor changes necessary in the MySensors Constructor and the begin-statement.


  • Hero Member

    Thanks for sharing. I like your coding,, clean and easy to read. Can you post some pictures of the representation on the OLED display? I am experimenting with the U8g lib which turns out to be a memory hog if combined with a few fonts. I was thinking of using an SD card for font storage but have not found good examples yet.



  • I will post some pictures soon (hope tomorrow).



  • Here are the Fotos
    First page, containing most informations
    DSC_1005.jpg
    Just the local DHT-Sensor
    DSC_1006.jpg
    Like first page, but less information and bigger font:
    DSC_1007.jpg
    Local temperature-Sensor plus first MySensor:
    DSC_1008.jpg
    All MySensors-informations on one page: temperature-reading, battery level and last update:
    DSC_1009.jpg
    This one is tricky: it reads the last 128 values from SD-Card and displays a diagram. Actually very slow and nearly unsuable 😞
    DSC_1010.jpg
    This page is for setting the RTC
    DSC_1011.jpg
    Here is the hardware-configuration. Each hardware-entry can be deactivated and restarted after errors. Deactivating the nrf actually causes a reboot with deactivated MySensors.
    DSC_1012.jpg

    It is true that the fonts use a lot of progmem, especially the big types. But I didn't like the look of the adafruit-library-font.


  • Hero Member

    @fleinze Compliments! I have been browsing your code and noticed that you are using the same SPI bus for both the SD and nRF radio. Did you make any changes to the MySensors libraries?



  • @AWI: No, I use hardware SPI. I just changed the CE and CSN-pins.



  • Hi Fleinze. Our project is really closed to what I am planning to do: a node that receives data from others and make something with them (no gateway or controller,...).
    You said your code is 1.4 compatible or easily portable to 1.5 but I do not find the MyGateway.h file, the Mygateway class, the right prototype of the begin function or, most important for me, the function gw.processRadioMessage in the 1.4 API or later.
    Have I missed something in our description?



  • Hi @sbackaer !
    The version 1.5 does not have the gateway class. I currently work with the development branch and I didn't use the 1.5 for quite some time; but I think it is enough to

    • remove the mygateway.h include
    • replace the mygateway contructor with a mysensor constructor
    • replace gw.processRadioMessage with gw.process()

    Take a look at the serial gateway example.


Log in to reply
 

Suggested Topics

  • 8
  • 3
  • 90
  • 7
  • 1
  • 1

21
Online

11.4k
Users

11.1k
Topics

112.7k
Posts