WiFi Temperature & Humidity Data Logger

The WiFi Temperature & Humidity Data Logger design samples data from a DHT11 temperature and humidity temperature sensor and sends this temperature to two fields of a channel set up on the thingspeak.com site. An ESP8266 Serial WIFI Wireless Transceiver Module connects to a local WiFi network, creates a TCP connection to the site and periodically uploads current  temperature and humidity data.

Here are two windows onto the data being collected remotely by the WiFi Temperature & Humidity Data Logger.

The ESP8266 is controlled by an Arduino Nano and powered by a separate and stable 3.3v power supply.

In the first version of this design, the Nano sampled the analogue inputs from both a stable reference voltage and the TMP36 temperature sensor.  This low power sensor has a linear temperature to voltage characteristic of 10mV / ºC and outputs a voltage of 750mV at 25ºC. Using the reference voltage, the temperature sensor input is scaled and converted to a floating point temperature in degrees Celcius. For more flexibility, the TMP36 was replaced with a DHT-type temperature and humidity sensor. The data from this sensor is read through a digital pin of the Nano.

Periodically, the Nano commands the ESP8266 to connect securely to the local WiFi network and establish a TCP connection to a unique data channel on the thingspeak site. The temperature and humidity data (in the form “xx.x”) are uploaded to unique fields in this data channel on the site and the connection dropped. The data are then displayed graphically, as above.

Overall, while the ESP8266 is a neat little device and a very cheap solution for a WiFi transceiver, I have found it to be somewhat unstable and somewhat unpredictable. I shall try to narrow down what’s happening (rail stability is one issue that I already reported on) but on occasion it enters a “reset loop” where it spits out a “System Ready” message repeatedly and fails to respond to AT commands. A hard reset (power cycle) or a chip reset (RST low for 50ms) seems to get everything back on track. I shall build this into the software to force a reset if multiple failed transmission errors are detected (around the response to message 5 in the software below).

While interesting in its own right, this temperature and humidity logger is a proof-of-concept test vehicle for the development of a remote water level and temperature sensor for use on the lake at the cottage. Another data field in the same channel of the data logger will be used to display data from an ultrasonic water level sensor. As this is to be located remotely, it will be powered with a solar cell and rechargeable batteries. To conserve power, it is intended that the unit will awake a couple of times an hour to transmit its data and then return to sleep in a low quiescent power state.

Here’s the Arduino code that now includes status LEDs and error recovery:

[codesyntax lang=”php” title=”WoodUino Data Logger” blockstate=”collapsed”]

//*****************************************************************************************//
//                             TEMPERATURE & HUMIDITY DATA LOGGER 
//                                  Adrian Jones, Dec. 2014
// Based on ideas from http://www.instructables.com/id/ESP8266-Wifi-Temperature-Logger/
// Using the DHT11 temperature and humidity sensor
//*****************************************************************************************//
 
// Build 1
//   r1 141205 - initial build with TMP36 analogue temperature sensor
//   r2 141206 - use of DHT11 sensor for temp and humidity
//   r3 141209 - addition of status LEDs and error counter to force reset
//*****************************************************************************************//
#define build 1
#define revision 3
//*****************************************************************************************//

// ARDUINO PIN USAGE
// D2 -> ESP8266 TX
// D3 -> 1k/2k (to GND) -> ESP8266 RX
// D4 -> diode, 4k7 (to 3.3v) -> ESP8266 RST
// D5 -> DHT11/21 Data
// D6 -> DI of LED strip

#include <avr/pgmspace.h>                                 // for memory storage in program space
#include <stdlib.h> 
#include <SoftwareSerial.h> 

#define SSID "YOUR_SSID" 
#define PASS "YOUR_SSID_PASSWORD" 
#define IP "184.106.153.149"                               // IP address of thingspeak.com 
#define ESP8266rst  4                                      // ESP8266 reset pin
String KEYstring = "GET /update?key=QOFEG48EO9Q9EKJL";     // unique key for hingspeak.com channel
SoftwareSerial WiFiSerial(2, 3);                           // RX, TX (connected to ESP8266 TX/RX resp.)

// temperature and humidity sensor
#include <dht.h>
dht DHT;
#define DHT11_PIN 5                                        // sensor data pin
String tempC,humid;                                        // temperature and humidity strings "xx.x"
char buffer[10];                                           // buffer to create strings
float tmp,hum;                                             // actual readings from DHT

// LEDs
#include "FastLED.h"
#define NUM_LEDS 3                                         // #LEDs in strip
#define DATA_PIN 6                                         // Data transfer pin
CRGB leds[NUM_LEDS];                                       // Define the array of leds

#define power      0                                       // power LED
#define conn       1                                       // WIFI connection LED
#define data       2                                       // data sent LED
#define blue     170                                       // blue hue
#define green     85                                       // green hue
#define yellow    42                                       // yellow hue
#define purple   212                                       // purple hue
#define red        0                                       // red hue
#define off       -1                                       // turn off
#define val       80                                       // LED brightness (0-255)
#define sat      255                                       // LED saturation (0-255)

// operation
int loopcount;                                             // loop counter (seconds)
int errorCnt=0;                                            // error counter

//*****************************************************************************************//
//                                      Initial Setup
//*****************************************************************************************//
void setup() {
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);      // LED Strip

  Serial.begin(57600); 
  Serial.println(F("TEMPERATURE & HUMIDITY DATA LOGGER: Adrian Jones, December 2014"));
  Serial.print(F("Build ")); 
  Serial.print(build); 
  Serial.print(F(".")); 
  Serial.println(revision);
  Serial.print(F("Free RAM: "));  
  Serial.print(freeRam()); 
  Serial.println(F("B\r\n"));

  resetWiFiModule();                            // reset WiFi module
  startWiFiModule();                            // start it up
  connectWiFiModule();                          // connect to local wifi
} 

//*****************************************************************************************//
//                                      MAIN LOOP
//*****************************************************************************************//
void loop() {
  if(errorCnt >= 10) {                           // if too many simultaneous errors
    errorCnt=0;
    resetWiFiModule();                           // reset WiFi module
    startWiFiModule();                           // restart serial connection to module
    connectWiFiModule();                         // connect to local wifi
  }
  
  if(loopcount == 0) {
    updateTempHum();                             // sample, connect and upload data   
  }
  if(loopcount == 5) {                           // after 5 seconds, turn off LEDs
     doLED(power,yellow);
     doLED(conn,off);
     doLED(data,off);
  }
  if(loopcount > 5) doLED(data,loopcount%2? purple : off);  
  delay(1000);                                   // pulse data LED as seconds counter
  loopcount = (loopcount+1)%27;
} 


// ********************************************************************************** //
//                                      SUBROUTINES
// ********************************************************************************** //

// ********************************************************************************** //
// updateTempHum(): starts TCP connection and uploads data...
// ********************************************************************************** //
boolean updateTempHum(){ 
  doLED(power,green);                           // connection made OK 
  doLED(conn,blue);                           // connection made OK
  Serial.println(F("3. Set up TCP connection "));
  String cmd = "AT+CIPSTART=\"TCP\",\"";       // set up TCP connection to...
  cmd += IP;                                   // this IP address and...     
  cmd += "\",80";                              // this port
  WiFiSerial.println(cmd);                     // send command
  Serial.print(F("   SEND: "));
  Serial.println(cmd);                         // send command
  delay(1000);                                 // wait for it.
  if(!WiFiSerial.find("OK")){                  // if failed to make connection, return with error
    Serial.println(F("   TCP connection error"));
    doLED(conn,red); errorCnt++;
    return false; 
  } 
  Serial.println(F("   TCP connection OK"));
  doLED(conn,green);                           // connection made OK
 
  doReadSensor( DHT.read11(DHT11_PIN) );       // read sensor data (tmp and hum)
  doLED(data,blue); 

// SEND DATA
  cmd = KEYstring;                             // unique key for thingspeak.com channel
  cmd +="&field1=";                
  tempC = dtostrf(tmp, 4, 1, buffer);
  cmd += tempC;                                // add string containing temperature (xx.x)
  cmd +="&field2=";
  humid = dtostrf(hum, 4, 1, buffer);
  cmd += humid;                                // add string containing humidity (xx.x)
  cmd += "\r\n";
  Serial.println(F("4. Report number of bytes to send"));
  WiFiSerial.print("AT+CIPSEND=");              
  WiFiSerial.println(cmd.length());            // tell TCP connection how many bytes to send
  Serial.print(F("   SEND: AT+CIPSEND="));
  Serial.println(cmd.length(),DEC);
  delay(1000);                                 // wait for ">" ready response
  if(WiFiSerial.find(">")){                    // if ready to accept data, look for ">"
    Serial.println(F("5. Ready to send")); 
    WiFiSerial.println(cmd);                   // send data string
    Serial.print(F("   SEND: ")); 
    Serial.print(cmd);    
  } else { 
    WiFiSerial.println("AT+CIPCLOSE");         // otherwise, close connection
  } 
  
  if(WiFiSerial.find("OK")) {                  // response id "SEND OK"
    Serial.println(F("   Data sent OK\r\n"));   
    doLED(data,green); errorCnt=0;
    return true; 
  } else { 
    Serial.println(F("   Data send Error\r\n"));
    doLED(data,red); errorCnt++;
    return false; 
  } 
}

// ********************************************************************************** //
// resetWiFiModule: reset ESP8266 module by pulling RST line low..
// ********************************************************************************** //
void resetWiFiModule() {
  doLED(power,red); 
  pinMode(ESP8266rst, OUTPUT);
  digitalWrite(ESP8266rst,LOW);                  // hold reset line low for 100ms
  delay(100);
  digitalWrite(ESP8266rst,HIGH);                 // release
  delay(2000);                                   // wait for module to recover
  doLED(power,yellow);                           // Ok for now
}


// ********************************************************************************** //
// startWiFiModule: open serial connection and check for AT command response
// ********************************************************************************** //
boolean startWiFiModule(){
  WiFiSerial.begin(9600); 
  delay(100);                                   // wait for it...
  Serial.println(F("1. Resetting ESP8266 module"));
  Serial.println(F("   SEND: AT"));   
  WiFiSerial.println("AT");                     // send AT reset command, the response should include "System Ready"
  delay(1000);                                  // wait for it...
  if(WiFiSerial.find("OK")){                    // if "Ready" received then we're all good to go   
    Serial.println(F("   RESET complete"));
    doLED(power,green);
    return true; 
  } else {
    Serial.println(F("   RESET error"));
    doLED(power,red);
    errorCnt++;
    return false; 
  }
}

// ********************************************************************************** //
// connectWiFi: attempts to set up WiFi connection to the ESP8266 module..
// ********************************************************************************** //
boolean connectWiFiModule() { 
  Serial.println(F("2. Connecting to WiFi"));
  WiFiSerial.println("AT+CWMODE=3");            // set the WiFi to STA (station) & AP (acceee point) mode
  Serial.println(F("   SEND: AT+CWMODE=3"));    // set the WiFi to STA (station) & AP (acceee point) mode
  delay(1000);                                  // wait for response
  String cmd="AT+CWJAP=\"";                     // create join access point message
  cmd+=SSID;                                    // SSID of WiFi connection
  cmd+="\",\""; 
  cmd+=PASS;                                    // Password
  cmd+="\""; 
  WiFiSerial.println(cmd);                      // send command
  Serial.println(F("   SEND: ****"));           // wait
  delay(2000);                                  // wait for connection to be completed
  if(WiFiSerial.find("OK")) {                   // if OK then return true
    Serial.println(F("   Connection OK"));
    doLED(conn,yellow); 
    return true; 
  } else { 
    Serial.println(F("   Connection Error"));
    doLED(conn,red); errorCnt++;
    return false; 
  } 
}

// ********************************************************************************** //
// doReadSensor: read temperature and humidity data
// ********************************************************************************** //
boolean doReadSensor(int sen) {
  boolean senseOK = false;
  switch (sen)  {
  case DHTLIB_OK:  
    hum = DHT.humidity;
    tmp = DHT.temperature;
    senseOK = true;
    break;
  case DHTLIB_ERROR_CHECKSUM: 
    Serial.println("Checksum error,\t"); 
    break;
  case DHTLIB_ERROR_TIMEOUT: 
    Serial.println("Time out error,\t"); 
    break;
  default: 
    Serial.println("Unknown error,\t"); 
    break;
  }
  return senseOK;
}

// ********************************************************************************** //
// doLED: set num LED to hue
// ********************************************************************************** //
void doLED(byte num, int hue) {
  leds[num] =  (hue != off)? CHSV( hue, sat, val ) : CHSV( 0,0,0 );
  FastLED.show();
}


// ********************************************************************************** //
//                              OPERATION ROUTINES
// ********************************************************************************** //
// FREERAM: Returns the number of bytes currently free in RAM  
int freeRam(void) {
  extern int  __bss_end, *__brkval; 
  int free_memory; 
  if((int)__brkval == 0) {
    free_memory = ((int)&free_memory) - ((int)&__bss_end); 
  } 
  else {
    free_memory = ((int)&free_memory) - ((int)__brkval); 
  }
  return free_memory; 
}

[/codesyntax]

Neat huh?

 

Come on... leave a comment...