DHT to EEPROM

Ok, not really scientific and kind of trivial in fact, but might help someone.

The aircon timer at the office is out of whack, so it only powers up at 12h48 on Mondays after being off the weekend. Nobody seems to know where the timer is located, to fix that. Nor do they care much about the office conditions and it's summer here. Pretty uncomfortable by the time it powers up.

So, I put together a data logger to provide some evidence to the H&S folks, not that they give a rat's. I start at 0600 so I'll have 7hr of readings by the time it kicks in.

  • Uno with prototype shield and tiny breadboard
  • DHT11
  • Tiny adafruit oled I2C display
  • DS1307 RTC module with battery backup

Screen shows current humidity and temp on top line, min and max since power up under that. I arbitrarily chose to log at XXh15, XXh35 and XXh55, capturing the time, humidity and temperature to the on-board EEPROM.

Here's the code, and Circuit schematic below.

UPDATED sketch in Reply #2, with thanks to Rob Tilliart.

/*
read h and t from dht
 catch min and Max
 Display on oled
 Log temp and humidity to eeprom at specified minutes past hour.
 reminder: sda a4, scl a5 on uno
 
 
 */

// libraries
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <dht11.h>
#include <EEPROM.h>


// screen
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

//dht
dht11 DHT;
#define DHT11_PIN 2
int dht_read;
int minTemp=50; //first actual reading will replace this
int maxTemp=0;  //first actual reading will replace this
int curTemp;
int humid;

//logging
int logLed=A0;
int hourAddress=91;  //just some random start point
boolean tempLogged = false;

//time
tmElements_t tm;

void setup()
{
  Serial.begin(9600);
  Serial.println("dht v2 14 dec");

  pinMode(logLed, OUTPUT);
  digitalWrite(logLed, LOW);

  //oled screen initialise

  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x32)
  // init done

  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.display();
  delay(2500);

  // Clear the buffer.
  display.clearDisplay();


}

void loop() {



  // read the dht
  dht_read = DHT.read(DHT11_PIN); 
  humid=DHT.humidity;
  curTemp=DHT.temperature;
  if (curTemp<minTemp) minTemp=curTemp;
  if (curTemp>maxTemp) maxTemp=curTemp;


  // text display
  display.clearDisplay();
  display.setCursor(0,0);
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.print("H");
  display.print(humid);
  display.print("%  T");
  display.print(curTemp);
  display.print("C");

  
  display.setCursor(0,16); // text line 2 = 16 pixels
   display.print("m");
   display.print(minTemp);
   display.print("C  M");
   display.print(maxTemp);
   display.print("C");
 
  display.display();

  //check if it's time to write to EEPROM
  if (RTC.read(tm)) {
    //Serial.println(tm.Minute);
    if (tm.Minute==15 || tm.Minute==35 || tm.Minute==55)
    {
      if  (tempLogged == false)
      {
        //Serial.println("led on");
        digitalWrite(logLed, HIGH);
        delay(100);
        digitalWrite(logLed, LOW);
        //Serial.println("led off");
        doEEPROM();
        tempLogged = true;
      } //end of false if
    } // end of right time if
  }//end of if read time


    if (tm.Minute!=15 && tm.Minute!=35 && tm.Minute!=55)
  {
    tempLogged = false;
  } //end of wrong time if


  delay(3000);  //only read dht >2secs apart
}


void print2digits(int number) {   //ala jim
  if (number >= 0 && number < 10) {
    display.print('0');
  }
  display.print(number);
}

void doEEPROM()
{
  EEPROM.write(hourAddress, tm.Hour);
  delay(5);
  EEPROM.write(hourAddress+1, tm.Minute);
  delay(5);
  EEPROM.write(hourAddress+2, curTemp);
  delay(5);
  EEPROM.write(hourAddress+3, humid);
  delay(5);
  hourAddress=hourAddress+4; //for next time
}

Big challenge is to remember to take it to work tomorrow.....

Good application JimboZA!

small change proposal, do the ledON/OFF around the EEPROM, if it got stuck there you would notice...

  //check if it's time to write to EEPROM
  if (RTC.read(tm)) {
    //Serial.println(tm.Minute);
    if (tm.Minute==15 || tm.Minute==35 || tm.Minute==55)
    {
      if  (tempLogged == false)
      {
        //Serial.println("led on");
        digitalWrite(logLed, HIGH);


        doEEPROM();

        delay(100);
        //Serial.println("led off");
        digitalWrite(logLed, LOW);

        tempLogged = true;
      } 
    }
    else 
    {
      tempLogged = false;   //  MERGED THIS ONE IN
    }
  }//end of if read time

Good ideas, Rob (as always).

Complete updated sketch below:

/*
read h and t from dht
 catch min and Max
 Display on oled
 Log temp and humidity to eeprom at specified minutes past hour.
 reminder: sda a4, scl a5 on uno
 
 v3: slight changes ala Rob Tilliart
 
 
 */

// libraries
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <dht11.h>
#include <EEPROM.h>


// screen
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

//dht
dht11 DHT;
#define DHT11_PIN 2
int dht_read;
int minTemp=50; //first actual reading will replace this
int maxTemp=0;  //first actual reading will replace this
int curTemp;
int humid;

//logging
int logLed=A0;
int hourAddress=91;  //just some random start point
boolean tempLogged = false;

//time
tmElements_t tm;

void setup()
{
  Serial.begin(9600);
  Serial.println("dht v2 14 dec");

  pinMode(logLed, OUTPUT);
  digitalWrite(logLed, LOW);

  //oled screen initialise

  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x32)
  // init done

  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  display.display();
  delay(2500);

  // Clear the buffer.
  display.clearDisplay();


}

void loop() {



  // read the dht
  dht_read = DHT.read(DHT11_PIN); 
  humid=DHT.humidity;
  curTemp=DHT.temperature;
  if (curTemp<minTemp) minTemp=curTemp;
  if (curTemp>maxTemp) maxTemp=curTemp;


  // text display
  display.clearDisplay();
  display.setCursor(0,0);
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.print("H");
  display.print(humid);
  display.print("%  T");
  display.print(curTemp);
  display.print("C");

  
  display.setCursor(0,16); // text line 2 = 16 pixels
   display.print("m");
   display.print(minTemp);
   display.print("C  M");
   display.print(maxTemp);
   display.print("C");
 
  display.display();

 //check if it's time to write to EEPROM
  if (RTC.read(tm)) {
    //Serial.println(tm.Minute);
    if (tm.Minute==15 || tm.Minute==35 || tm.Minute==55)
    {
      if  (tempLogged == false)
      {
        //Serial.println("led on");
        digitalWrite(logLed, HIGH);


        doEEPROM();

        delay(100);
        //Serial.println("led off");
        digitalWrite(logLed, LOW);

        tempLogged = true;
      } 
    }
    else 
    {
      tempLogged = false;   //  MERGED THIS ONE IN
    }
  }//end of if read time


  delay(3000);  //only read dht >2secs apart
}


void print2digits(int number) {   //ala jim
  if (number >= 0 && number < 10) {
    display.print('0');
  }
  display.print(number);
}

void doEEPROM()
{
  EEPROM.write(hourAddress, tm.Hour);
  delay(5);
  EEPROM.write(hourAddress+1, tm.Minute);
  delay(5);
  EEPROM.write(hourAddress+2, curTemp);
  delay(5);
  EEPROM.write(hourAddress+3, humid);
  delay(5);
  hourAddress=hourAddress+4; //for next time
}

I will confirm later that the changes passed regression testing, ie an hour to check all three writes read back correctly.

Ok so that all works and the hour's 3 readings read back correctly with another sketch I use for that.

TODO: Keep a counter of how many doEEPROMs it did, and then add a section to the sketch (under push button control) to read back only that many when I'm done logging.

Right now I read them back with this which is a bit messy...

/*
 * EEPROM Read
 *
 * Reads the value of each byte of the EEPROM and prints it 
 * to the computer.
 * This example code is in the public domain.
 */

#include <EEPROM.h>

// start reading from the required byte of the EEPROM
int address = 91; //to match the logger code
byte value;

void setup()
{
  // initialize serial and wait for port to open:
  Serial.begin(9600);
  /*while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }*/
}

void loop()
{

  
  // read 4 bytes for hour, minute, temp and humuid
  Serial.print(EEPROM.read(address), DEC);
  Serial.print(":");
    Serial.print(EEPROM.read(address+1), DEC);
  Serial.print("\t");
    Serial.print(EEPROM.read(address+2), DEC);
  Serial.print("\t");
    Serial.print(EEPROM.read(address+3), DEC);
  Serial.println();
  
  // advance to the next address of the EEPROM
  address = address + 4;
  
  // there are only 512 bytes of EEPROM, from 0 to 511, so if we're
  // on address 512, wrap around to address 0
  if (address == 512)
    address = 0;
    
  delay(500);
}

TODO: Keep a counter of how many doEEPROMs it did, and then add a section to the sketch (under push button control) to read back only that many when I'm done logging.

fill the EEPROM with 0xFF and the reading sketch can find out.

Yep I thought about that too Rob, but worried about running out of cycles. ( :wink: Not really... )

you need the wrap around in the writer too, I expect it will wrap automatically but still better make it explicit.

void doEEPROM()
{
  EEPROM.write(hourAddress++, tm.Hour);
  delay(5);
  EEPROM.write(hourAddress++, tm.Minute);
  delay(5);
  EEPROM.write(hourAddress++, curTemp);
  delay(5);
  EEPROM.write(hourAddress++, humid);
  delay(5);
  if (houradress >= 512) houraddress = 0;
}

BTW is the delay(5) really needed for the internal EEPROM?
for some external it does, but ok it doesn't harm either.


As the hour and minute and temp and hum all are in a subrange of 1 byte I propose packing the stuff so EEPROM can hold more.

Hour = 0..23 == 5 bits
Minute = 15, 35, 55 = 2 bits
temp = 20..60 = 6 bits
hum = 20..80 = 6 bits

that would increase # samples fitting in mem. (40 hours -> 55 hrs)

skipping non office hours would even double your mem

Well for this specific test, it's only Monday up to 12h48 which is when the aircon kicks in, late. Then it takes an hour to cool down, and then I leave at 14h30 seeing as I start at 6.

But you're right: for general purpose robustness it should know about the end of memory and / or compress the data.

robtillaart:
BTW is the delay(5) really needed for the internal EEPROM?
for some external it does, but ok it doesn't harm either.

No, not needed. A previous version of this code used I2C EEPROM, but I'm low on physical space here so I went with the internal memory. Those delays are a relic of that earlier code.

Deployed for two hours gathering H&S evidence.....

looks good, when is your kickstart launch :wink:

Will you post the Excel graph here ?

robtillaart:
Will you post the Excel graph here ?

Good idea. Public Holiday here today, so I'll do that. I cut and paste from the monitor to Excel so yep a graph of T, H and the Humidex is a good idea.