Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // ESP 8266 Temperature and Humidity Station - V0.2
- //
- // This sketch does the following:
- //
- // 1. Reads temperature and humidity from DHT sensors every 10 seconds.
- // 2. If either the temperature or humidity differs from the previous value, it saves the values to an InfluxDB database.
- // 3. If a http request is received at port 80, the time will be retrieved at a specified time server and presented on a web page
- // with the current temperature and humidity.
- // 4. Flashes an LED on D0 every time the database is updated,
- //
- // TODO:
- // 1. Improve the presentation and beautification of the web page - ongoing.
- // 2. Save web page related data in flash memory.
- // 4. Clean up redundant code and improve structure, check best practices.
- // 7. Restructure the code to improve reusability to include .h in future projects e.g. DHT function / InfluxDB.
- // 8. Remove magic numbers.
- // 9. Present simple graph on web page, this'll require historical data to be stored locally and presented to the webpage
- // to process, dependent on user input.
- //
- // Notes on external sources:
- //
- // Thanks go to those that have provided the examples that were the basis for this project. Where I have remembered the source of the source code, I have linked to it.
- // Please take the time to visit their pages. This is my first full project and intend to copy and paste much less as I develop my skills!
- //
- // Sources:
- // Vaduva Ionut Lucian – GeeksTips.com owner and author - https://www.geekstips.com/arduino-time-sync-ntp-server-esp8266-udp/
- // Volmer Mihai-Cristian - AAC - https://elf.cs.pub.ro/wsn/wiki/proiecte/influxdb
- // Rui Santos / Sara Santos - Random Nerd Tutorials - https://randomnerdtutorials.com/esp8266-dht11dht22-temperature-and-humidity-web-server-with-arduino-ide/
- // GitHub hwwong - https://github.com/hwwong/ESP_influxdb
- // lady ada / Tony DiCola - https://learn.adafruit.com/dht
- // Ray Hammond - https://geeksretreat.wordpress.com/2012/04/20/making-a-thermometer-using-html5s-canvas/
- // Ray Hammond - https://github.com/rheh/HTML5-canvas-projects/tree/master/thermometer
- //
- // "Based on" = Where I have edited to suit my application.
- // "Source" = If external, mostly a copy and paste, or very minor edit from the original source.
- //
- //
- /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // Including the ESP8266 WiFi library
- #include <ESP8266WiFi.h> //
- #include <WiFiUdp.h>
- #include "DHT.h" //https://learn.adafruit.com/dht
- #include "ESPinfluxdb.h" //https://github.com/hwwong/ESP_influxdb
- #include <Ticker.h>
- #include "PinOut_Notes.h"//https://github.com/esp8266/Arduino/blob/master/variants/nodemcu/pins_arduino.h#L37-L59
- #include "TimeLib.h"//https://github.com/PaulStoffregen/Time
- #include "lastSunday.h" //Based on a now corrected version of http://rosettacode.org/wiki/Find_the_last_Sunday_of_each_month
- #include "webpage.h"
- // Uncomment one of the lines below for whatever DHT sensor type you're using!
- //#define DHTTYPE DHT11 // DHT 11
- //#define DHTTYPE DHT21 // DHT 21 (AM2301)
- #define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
- // Replace with your network details
- const char* ssid = "ssidofyournetwork";
- const char* password = "passwordofyournetwork";
- //influxdb details
- const char *INFLUXDB_HOST = "192.168.1.201";
- const uint16_t INFLUXDB_PORT = 8086;
- const char *DATABASE = "dbofyourdata";
- const char *DB_USER = "dbuserofyourdata";
- const char *DB_PASSWORD = "dbpasswordofyourdata";
- Influxdb influxdb(INFLUXDB_HOST, INFLUXDB_PORT);
- // Web Server on port 80
- WiFiServer server(80);
- WiFiClient client;
- //NTP Variables
- unsigned int localUDPPort = 2390; // local port to listen for UDP packets
- IPAddress timeServer(192, 168, 1, 201); // time server IP
- const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
- byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
- WiFiUDP udp;// A UDP instance to let us send and receive packets over UDP
- // DHT Sensor
- const int DHTPin = D1;
- // Initialize DHT sensor.
- DHT dht(DHTPin, DHTTYPE);
- //LED
- #define LED D0
- //Ticker Callback
- Ticker timerTicker;
- static bool ReadyToRead = false;
- static bool SystemReady = false;
- uint32_t chipid=ESP.getChipId();
- static String ChipIDStr=String(chipid,HEX);
- char *HexChipID = (char*)malloc(9);
- float tc,tf,h=0;
- float old_tc,old_tf,old_h=0;
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // Setup - Sets up the project objects and variables
- //
- // Source - Based on various including my own, details included at the opening comment.
- //
- // 1. Initialises the DHT device
- // 2. Sets the call-back for timer triggered measurement
- // 3. Connects to Wifi
- // 4. Creates and starts the web server which will serve the web page with the device ID, temp and humidity
- // 5. Opens the InfluxDB database
- // 6. Opens UDP port for NTP queries later
- //
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- void setup() {
- // Initializing serial port for debugging purposes
- Serial.begin(115200);
- delay(10);
- //initialise the dht device
- dht.begin();
- //set callback for periodic environment checks
- timerTicker.attach(10,TimerEvent);
- // Connect to WiFi network
- Serial.println();
- Serial.print("Connecting to ");
- Serial.println(ssid);
- WiFi.begin(ssid, password);
- while (WiFi.status() != WL_CONNECTED) {
- delay(500);
- Serial.print(".");
- }
- Serial.println("");
- Serial.println("WiFi connected");
- // Starting the web server
- server.begin();
- Serial.println("Web server running. Waiting for the ESP IP...");
- while(!WiFi.localIP()){
- delay(1000); //delay before recheck local ip
- }
- // Printing the ESP IP address
- Serial.println(WiFi.localIP());
- //TODO need to ensure we can pop out of this loop if the DB fails
- while (influxdb.opendb(DATABASE,DB_USER,DB_PASSWORD)!=DB_SUCCESS) {
- Serial.println("Open influxdb database failed");
- delay(10000);
- }
- Serial.printf(" ESP8266 Chip id = %08X\n", ESP.getChipId());
- sprintf(HexChipID,"%08X",ESP.getChipId());
- //UDP open port for NTP Query
- Serial.println("Starting UDP");
- udp.begin(localUDPPort);
- Serial.print("Local port: ");
- Serial.println(udp.localPort());
- //this opens the gate for the environment measurements to begin, triggered by the timer
- SystemReady = true;
- //LED
- pinMode(LED,OUTPUT);
- }
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // GetLocalTime - gets UTC time from an NTP server & calculates the local time
- //
- // Based on: https://www.geekstips.com/arduino-time-sync-ntp-server-esp8266-udp/
- //
- // 1. Queries NTP server
- // 2. When a UDP NTP packet is received:
- // 3. Convert to hours, minutes and seconds.
- //
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- bool GetLocalTime(time_t &LocalTime)
- {
- sendNTPpacket(timeServer);
- // wait to see if a reply arrives
- delay(1000);
- int cb = udp.parsePacket();
- if (!cb) {
- Serial.println("No UDP packets received");
- digitalWrite(LED,LOW);
- return false;
- }
- else {
- Serial.print("packet received, length =");
- Serial.println(cb);
- // We've received a packet, read the data from it
- udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
- //the timestamp starts at byte 40 of the received packet and is four bytes,
- // or two words, long. First, esxtract the two words:
- unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
- unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
- // combine the four bytes (two words) into a long integer
- // this is NTP time (seconds since Jan 1 1900):
- unsigned long secsSince1900 = highWord << 16 | lowWord;
- // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
- const unsigned long seventyYears = 2208988800UL;
- // subtract seventy years:
- unsigned long epoch = secsSince1900 - seventyYears;
- //the date
- time_t t = epoch; // store the current time in time variable t
- LocalTime = epoch;
- //bst starts the last Sunday of March at 0100 UTC
- tmElements_t bst_start;
- bst_start.Day = lastSunday(3,year(t));//last Sunday of
- bst_start.Month = 3;//March
- bst_start.Year = year(t)-1970;//this year
- bst_start.Hour = 1;
- bst_start.Minute = 0;
- bst_start.Second = 0;
- time_t bst_Start_time_t(makeTime(bst_start));
- //bst ends the last Sunday of October at 0100 UTC
- tmElements_t bst_end;
- bst_end.Day = lastSunday(10,year(t));// last Sunday of
- bst_end.Month = 10;// of October
- bst_end.Year = year(t)-1970;// this year
- bst_end.Hour = 1;
- bst_end.Minute = 0;
- bst_end.Second = 0;
- time_t bst_End_time_t(makeTime(bst_end));
- //test if the local time is within the daylight saving window
- if (t > bst_Start_time_t && t < bst_End_time_t){
- //adjust the hour +1
- LocalTime = epoch + 3600;
- }
- }
- return true;
- }
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // sendNTPpacket - sends an NTP packet to the specified time server
- //
- // Source: https://www.geekstips.com/arduino-time-sync-ntp-server-esp8266-udp/
- //
- // 1. Forms an NTP packet
- // 2. Send an NTP request to the time server at the given address
- //
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- unsigned long sendNTPpacket(IPAddress& address)
- {
- Serial.println("sending NTP packet...");
- // set all bytes in the buffer to 0
- memset(packetBuffer, 0, NTP_PACKET_SIZE);
- // Initialize values needed to form NTP request
- // (see URL above for details on the packets)
- packetBuffer[0] = 0b11100011; // LI, Version, Mode
- packetBuffer[1] = 0; // Stratum, or type of clock
- packetBuffer[2] = 6; // Polling Interval
- packetBuffer[3] = 0xEC; // Peer Clock Precision
- // 8 bytes of zero for Root Delay & Root Dispersion
- packetBuffer[12] = 49;
- packetBuffer[13] = 0x4E;
- packetBuffer[14] = 49;
- packetBuffer[15] = 52;
- // all NTP fields have been given values, now
- // you can send a packet requesting a timestamp:
- udp.beginPacket(address, 123); //NTP requests are to port 123
- udp.write(packetBuffer, NTP_PACKET_SIZE);
- udp.endPacket();
- }
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // GetMeasurement - Gets a measurement from the DH sensor
- //
- // Based on: https://randomnerdtutorials.com/esp8266-dht11dht22-temperature-and-humidity-web-server-with-arduino-ide/
- //
- // 1. When the sensor is ready read the sensor
- // 2. If the sensor output is valid, compute the values and return a flag that denotes success.
- //
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- bool GetMeasurement(float &temp_C,float &temp_F,float &humidity,bool SerialDebugOutput){
- //status of the sensor read. false returned = fail
- bool status = false;
- while(!status){
- // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
- float h = dht.readHumidity();
- // Read temperature as Celsius (the default)
- float t = dht.readTemperature();
- // Read temperature as Fahrenheit (isFahrenheit = true)
- float f = dht.readTemperature(true);
- // Check if any reads failed and exit early (to try again).
- if ((isnan(h) || isnan(t) || isnan(f)) && SerialDebugOutput) {
- Serial.println("Failed to read from DHT sensor!");
- }
- else{
- // Computes temperature values in Celsius + Fahrenheit and Humidity
- temp_C = dht.computeHeatIndex(t, h, false);
- temp_F = dht.computeHeatIndex(f, h);
- humidity = h;
- status = true;
- }
- }//while status
- return status;
- }
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // ServiceHttpRequest
- //
- // Source: https://randomnerdtutorials.com/esp8266-dht11dht22-temperature-and-humidity-web-server-with-arduino-ide/
- //
- // 1. Gets the UTC time.
- // 2. Outputs the ID of the ESP8266 sensor along with the last measured temperaure and humidity.
- // 3. Closes the connection.
- //
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- void ServiceHttpRequest(WiFiClient client){
- time_t myLocalTime;
- Serial.println("New client");
- // boolean to locate when the http request ends
- boolean blank_line = true;
- while (client.connected()) {
- if (client.available()) {
- char c = client.read();
- if (c == '\n' && blank_line) {
- char celsiusTemp[9];
- char farenheitTemp[9];
- char humidityTemp[9];
- //Output the current environment values
- dtostrf(tc, 6, 2, celsiusTemp);
- dtostrf(tf, 6, 2, farenheitTemp);
- dtostrf(h, 6, 2, humidityTemp);
- client.println("HTTP/1.1 200 OK");
- client.println("Content-Type: text/html");
- client.println("Connection: close");
- client.println();
- // your actual web page that displays temperature and humidity
- client.println("<!DOCTYPE HTML>");
- client.println("<html>");
- client.println("<!--Variables -->");
- client.println("<script>");
- client.print("var unitNo = \"");
- client.print(HexChipID);
- client.println("\";");
- //time
- if(GetLocalTime(myLocalTime)){
- Serial.printf("The local time received is: %02d:%02d:%02d\n",hour(myLocalTime),minute(myLocalTime),second(myLocalTime));
- client.print("var hours = ");
- client.print(hour(myLocalTime));
- client.println(";");
- client.println("if(hours < 10){hours = \"0\"+hours;}");
- client.print("var minutes = ");
- client.print(minute(myLocalTime));
- client.println(";");
- client.println("if(minutes < 10){minutes = \"0\"+minutes;}");
- client.print("var seconds = ");
- client.print(second(myLocalTime));
- client.println(";");
- client.println("if(seconds < 10){seconds = \"0\"+seconds;}");
- }
- else
- {
- client.println("No time received from time server");
- }
- client.print("var tempC = ");
- client.print(celsiusTemp);
- client.println(";");
- client.println("var tempC = tempC.toFixed(2);");
- client.println("var tempF = tempC * 9/5 + 32;");
- client.println("var tempF = tempF.toFixed(2);");
- client.print("var humidity = ");
- client.print(humidityTemp);
- client.println(";");
- client.println("humidity = humidity.toFixed(2);");
- client.println("</script>");
- client.print(html);
- break;
- }//(c == '\n' && blank_line)
- if (c == '\n') {
- // when starts reading a new line
- blank_line = true;
- }
- else if (c != '\r') {
- // when finds a character on the current line
- blank_line = false;
- }
- } //client.available
- } //client.connected
- // closing the client connection
- delay(1);
- client.stop();
- Serial.println("Client disconnected.");
- }
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // SendDataToDatabase - Sends data to existing InfluxDB database
- //
- // Based on: https://elf.cs.pub.ro/wsn/wiki/proiecte/influxdb
- //
- // 1. Connect to the pre-existing InfluxDB
- // 2. Output the UID of the ESP8266, tempC, tempF and humidity to the database.
- // 3. Keep a track of how many rows have been stored.
- // 4. ???Should I disconnect from the database????
- //
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- void SendDataToDatabase(WiFiClient client,const int t,const int h){
- if(!client.connect(INFLUXDB_HOST,INFLUXDB_PORT)) {
- Serial.println("Cant connect to InfluxDB");
- }
- else {
- //Create data object with measurment name=analog_read
- dbMeasurement row("temp_humidity");
- row.addTag("UID_HEX",String(HexChipID));
- row.addField("temp_C", t); //
- row.addField("humidity",h); //
- Serial.println(influxdb.write(row) == DB_SUCCESS ? "Object write success":"Writing failed");
- // Empty field object.
- row.empty();
- }
- }
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // TimerEvent - Main program loop.
- //
- // 1. When the timer fires, if the setup() is complete the ReadyToRead gate is opened so that the sensor can
- // be read on the next loop.
- //
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- void TimerEvent(){
- if(SystemReady){
- ReadyToRead=true;
- }//systemready
- }//TimerEvent()
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // loop - Main program loop.
- //
- // 1. Listens for http clients
- // 2. If a client connects, send out the web page.
- // 3. If the program is ready (the timer event opens the gate for new measurements every 10 seconds), the sensor is read.
- // 4. New values are compared with last values read to see if there is a change, if there is, these are stored in the InfluxDB database.
- // 5. Gate is closed until next timer event
- //
- //////////////////////////////////////////////////////////////////////////////////////////////////////////////
- void loop() {
- // Listenning for new clients
- client = server.available();
- if (client) {
- ServiceHttpRequest(client);
- }//client
- //if the sensor is ready and the timer event has fired, then take store the latest measurement
- //keeping hold of the current values. This allows us to only update the database when values
- //have changed.
- if(GetMeasurement(tc,tf,h,true) && ReadyToRead){
- if(tc!=old_tc | tf!=old_tf | h != old_h){//only send new data
- digitalWrite(LED,HIGH);//so we know the unit is still alive
- Serial.println("Sending new data to database");
- SendDataToDatabase(client,tc,h);
- digitalWrite(LED,LOW);
- //these are now our latest measurements
- old_tc = tc;
- old_tf = tf;
- old_h = h;
- ReadyToRead=false;//closes the gate until the timer fires again
- }//send new data to the database
- }//getmeasurement
- }//loop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement