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:

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!
-
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.
-
Here are the Fotos
First page, containing most informations

Just the local DHT-Sensor

Like first page, but less information and bigger font:

Local temperature-Sensor plus first MySensor:

All MySensors-informations on one page: temperature-reading, battery level and last update:

This one is tricky: it reads the last 128 values from SD-Card and displays a diagram. Actually very slow and nearly unsuable :(

This page is for setting the RTC

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.

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.
-
Here are the Fotos
First page, containing most informations

Just the local DHT-Sensor

Like first page, but less information and bigger font:

Local temperature-Sensor plus first MySensor:

All MySensors-informations on one page: temperature-reading, battery level and last update:

This one is tricky: it reads the last 128 values from SD-Card and displays a diagram. Actually very slow and nearly unsuable :(

This page is for setting the RTC

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.

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.
-
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 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.