Advertisement
metimmee

Untitled

Oct 28th, 2018
138
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 20.54 KB | None | 0 0
  1. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  2. //    ESP 8266 Temperature and Humidity Station - V0.2
  3. //
  4. //    This sketch does the following:
  5. //
  6. //                                    1.  Reads temperature and humidity from DHT sensors every 10 seconds.
  7. //                                    2.  If either the temperature or humidity differs from the previous value, it saves the values to an InfluxDB database.
  8. //                                    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
  9. //                                    with the current temperature and humidity.
  10. //                                    4.  Flashes an LED on D0 every time the database is updated,
  11. //
  12. //                              TODO:
  13. //                                    1.  Improve the presentation and beautification of the web page - ongoing.
  14. //                                    2.  Save web page related data in flash memory.
  15. //                                    4.  Clean up redundant code and improve structure, check best practices.
  16. //                                    7.  Restructure the code to improve reusability to include .h in future projects e.g. DHT function / InfluxDB.
  17. //                                    8.  Remove magic numbers.
  18. //                                    9.  Present simple graph on web page, this'll require historical data to be stored locally and presented to the webpage
  19. //                                    to process, dependent on user input.
  20. //
  21. //    Notes on external sources:
  22. //
  23. //          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.  
  24. //          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!
  25. //  
  26. //    Sources:
  27. //          Vaduva Ionut Lucian – GeeksTips.com owner and author - https://www.geekstips.com/arduino-time-sync-ntp-server-esp8266-udp/
  28. //          Volmer Mihai-Cristian - AAC - https://elf.cs.pub.ro/wsn/wiki/proiecte/influxdb
  29. //          Rui Santos / Sara Santos - Random Nerd Tutorials - https://randomnerdtutorials.com/esp8266-dht11dht22-temperature-and-humidity-web-server-with-arduino-ide/
  30. //          GitHub hwwong - https://github.com/hwwong/ESP_influxdb
  31. //          lady ada / Tony DiCola - https://learn.adafruit.com/dht
  32. //          Ray Hammond           - https://geeksretreat.wordpress.com/2012/04/20/making-a-thermometer-using-html5s-canvas/
  33. //          Ray Hammond           - https://github.com/rheh/HTML5-canvas-projects/tree/master/thermometer
  34. //
  35. //          "Based on" = Where I have edited to suit my application.
  36. //          "Source" = If external, mostly a copy and paste, or very minor edit from the original source.
  37. //
  38. //
  39. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  40.  
  41. // Including the ESP8266 WiFi library
  42. #include <ESP8266WiFi.h> //
  43. #include <WiFiUdp.h>
  44. #include "DHT.h"          //https://learn.adafruit.com/dht
  45. #include "ESPinfluxdb.h"  //https://github.com/hwwong/ESP_influxdb
  46. #include <Ticker.h>
  47. #include "PinOut_Notes.h"//https://github.com/esp8266/Arduino/blob/master/variants/nodemcu/pins_arduino.h#L37-L59
  48.  
  49. #include "TimeLib.h"//https://github.com/PaulStoffregen/Time
  50. #include "lastSunday.h" //Based on a now corrected version of http://rosettacode.org/wiki/Find_the_last_Sunday_of_each_month
  51. #include "webpage.h"
  52.  
  53.  
  54. // Uncomment one of the lines below for whatever DHT sensor type you're using!
  55. //#define DHTTYPE DHT11   // DHT 11
  56. //#define DHTTYPE DHT21   // DHT 21 (AM2301)
  57. #define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
  58.  
  59. // Replace with your network details
  60. const char* ssid = "ssidofyournetwork";
  61. const char* password = "passwordofyournetwork";
  62.  
  63. //influxdb details
  64. const char *INFLUXDB_HOST = "192.168.1.201";
  65. const uint16_t INFLUXDB_PORT = 8086;
  66. const char *DATABASE = "dbofyourdata";
  67. const char *DB_USER = "dbuserofyourdata";
  68. const char *DB_PASSWORD = "dbpasswordofyourdata";
  69. Influxdb influxdb(INFLUXDB_HOST, INFLUXDB_PORT);
  70.  
  71. // Web Server on port 80
  72. WiFiServer server(80);
  73. WiFiClient client;
  74.  
  75. //NTP Variables
  76. unsigned int localUDPPort = 2390;      // local port to listen for UDP packets
  77. IPAddress timeServer(192, 168, 1, 201); // time server IP
  78. const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
  79. byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
  80. WiFiUDP udp;// A UDP instance to let us send and receive packets over UDP
  81.  
  82. // DHT Sensor
  83. const int DHTPin = D1;
  84. // Initialize DHT sensor.
  85. DHT dht(DHTPin, DHTTYPE);
  86.  
  87. //LED
  88. #define LED D0
  89.  
  90. //Ticker Callback
  91. Ticker timerTicker;
  92. static bool ReadyToRead = false;
  93. static bool SystemReady = false;
  94.  
  95.  
  96. uint32_t chipid=ESP.getChipId();
  97. static String ChipIDStr=String(chipid,HEX);
  98. char *HexChipID = (char*)malloc(9);
  99.  
  100.  
  101. float tc,tf,h=0;
  102. float old_tc,old_tf,old_h=0;
  103.  
  104. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  105. //  Setup - Sets up the project objects and variables
  106. //
  107. //  Source - Based on various including my own, details included at the opening comment.
  108. //
  109. //  1.  Initialises the DHT device
  110. //  2.  Sets the call-back for timer triggered measurement
  111. //  3.  Connects to Wifi
  112. //  4.  Creates and starts the web server which will serve the web page with the device ID, temp and humidity
  113. //  5.  Opens the InfluxDB database
  114. //  6.  Opens UDP port for NTP queries later
  115. //
  116. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  117.  
  118. void setup() {
  119.   // Initializing serial port for debugging purposes
  120.   Serial.begin(115200);
  121.   delay(10);
  122.   //initialise the dht device
  123.   dht.begin();
  124.  
  125.  //set callback for periodic environment checks
  126.  timerTicker.attach(10,TimerEvent);
  127.  
  128.   // Connect to WiFi network
  129.   Serial.println();
  130.   Serial.print("Connecting to ");
  131.   Serial.println(ssid);  
  132.   WiFi.begin(ssid, password);  
  133.   while (WiFi.status() != WL_CONNECTED) {
  134.     delay(500);
  135.     Serial.print(".");
  136.   }
  137.   Serial.println("");
  138.   Serial.println("WiFi connected");
  139.  
  140.   // Starting the web server
  141.   server.begin();
  142.   Serial.println("Web server running. Waiting for the ESP IP...");
  143.   while(!WiFi.localIP()){
  144.     delay(1000);  //delay before recheck local ip
  145.   }
  146.   // Printing the ESP IP address
  147.   Serial.println(WiFi.localIP());
  148.  
  149.   //TODO need to ensure we can pop out of this loop if the DB fails
  150.   while (influxdb.opendb(DATABASE,DB_USER,DB_PASSWORD)!=DB_SUCCESS) {          
  151.           Serial.println("Open influxdb database failed");
  152.           delay(10000);
  153.   }
  154.        
  155.   Serial.printf(" ESP8266 Chip id = %08X\n", ESP.getChipId());
  156.   sprintf(HexChipID,"%08X",ESP.getChipId());
  157.  
  158.   //UDP open port for NTP Query
  159.   Serial.println("Starting UDP");
  160.   udp.begin(localUDPPort);
  161.   Serial.print("Local port: ");
  162.   Serial.println(udp.localPort());
  163.  
  164.   //this opens the gate for the environment measurements to begin, triggered by the timer
  165.   SystemReady = true;  
  166.  
  167.   //LED
  168.   pinMode(LED,OUTPUT);
  169.  
  170. }
  171.  
  172. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  173. //  GetLocalTime - gets UTC time from an NTP server & calculates the local time
  174. //
  175. //  Based on:  https://www.geekstips.com/arduino-time-sync-ntp-server-esp8266-udp/
  176. //
  177. //  1.  Queries NTP server
  178. //  2.  When a UDP NTP packet is received:
  179. //  3.  Convert to hours, minutes and seconds.
  180. //
  181. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  182.  
  183. bool GetLocalTime(time_t &LocalTime)
  184. {
  185.  
  186.    
  187.    sendNTPpacket(timeServer);
  188.  
  189.   // wait to see if a reply arrives
  190.   delay(1000);
  191.  
  192.   int cb = udp.parsePacket();
  193.   if (!cb) {
  194.     Serial.println("No UDP packets received");
  195.     digitalWrite(LED,LOW);
  196.     return false;
  197.   }
  198.   else {
  199.     Serial.print("packet received, length =");
  200.     Serial.println(cb);
  201.     // We've received a packet, read the data from it
  202.     udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
  203.  
  204.     //the timestamp starts at byte 40 of the received packet and is four bytes,
  205.     // or two words, long. First, esxtract the two words:
  206.  
  207.     unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
  208.     unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
  209.     // combine the four bytes (two words) into a long integer
  210.     // this is NTP time (seconds since Jan 1 1900):
  211.     unsigned long secsSince1900 = highWord << 16 | lowWord;
  212.  
  213.     // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
  214.     const unsigned long seventyYears = 2208988800UL;
  215.     // subtract seventy years:
  216.     unsigned long epoch = secsSince1900 - seventyYears;
  217.    
  218.     //the date
  219.     time_t t = epoch; // store the current time in time variable t
  220.     LocalTime = epoch;
  221.  
  222.  
  223.     //bst starts the last Sunday of March at 0100 UTC
  224.     tmElements_t bst_start;
  225.     bst_start.Day = lastSunday(3,year(t));//last Sunday of
  226.     bst_start.Month = 3;//March      
  227.     bst_start.Year = year(t)-1970;//this year
  228.     bst_start.Hour = 1;
  229.     bst_start.Minute = 0;
  230.     bst_start.Second = 0;
  231.     time_t bst_Start_time_t(makeTime(bst_start));
  232.        
  233.     //bst ends the last Sunday of October at 0100 UTC
  234.     tmElements_t bst_end;
  235.     bst_end.Day = lastSunday(10,year(t));// last Sunday of
  236.     bst_end.Month = 10;// of October
  237.     bst_end.Year = year(t)-1970;// this year
  238.     bst_end.Hour = 1;
  239.     bst_end.Minute = 0;
  240.     bst_end.Second = 0;
  241.     time_t bst_End_time_t(makeTime(bst_end));
  242.  
  243.     //test if the local time is within the daylight saving window
  244.     if (t > bst_Start_time_t && t < bst_End_time_t){
  245.       //adjust the hour +1
  246.       LocalTime = epoch + 3600;
  247.     }
  248.  
  249.   }
  250.  
  251.   return true;
  252. }
  253.  
  254. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  255. //  sendNTPpacket - sends an NTP packet to the specified time server
  256. //
  257. //  Source:  https://www.geekstips.com/arduino-time-sync-ntp-server-esp8266-udp/
  258. //
  259. //  1.  Forms an NTP packet
  260. //  2.  Send an NTP request to the time server at the given address
  261. //
  262. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  263. unsigned long sendNTPpacket(IPAddress& address)
  264. {
  265.   Serial.println("sending NTP packet...");
  266.   // set all bytes in the buffer to 0
  267.   memset(packetBuffer, 0, NTP_PACKET_SIZE);
  268.   // Initialize values needed to form NTP request
  269.   // (see URL above for details on the packets)
  270.   packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  271.   packetBuffer[1] = 0;     // Stratum, or type of clock
  272.   packetBuffer[2] = 6;     // Polling Interval
  273.   packetBuffer[3] = 0xEC;  // Peer Clock Precision
  274.   // 8 bytes of zero for Root Delay & Root Dispersion
  275.   packetBuffer[12]  = 49;
  276.   packetBuffer[13]  = 0x4E;
  277.   packetBuffer[14]  = 49;
  278.   packetBuffer[15]  = 52;
  279.  
  280.   // all NTP fields have been given values, now
  281.   // you can send a packet requesting a timestamp:
  282.   udp.beginPacket(address, 123); //NTP requests are to port 123
  283.   udp.write(packetBuffer, NTP_PACKET_SIZE);
  284.   udp.endPacket();
  285. }
  286.  
  287.  
  288. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  289. //  GetMeasurement - Gets a measurement from the DH sensor
  290. //
  291. // Based on:  https://randomnerdtutorials.com/esp8266-dht11dht22-temperature-and-humidity-web-server-with-arduino-ide/
  292. //
  293. //  1.  When the sensor is ready read the sensor
  294. //  2.  If the sensor output is valid, compute the values and return a flag that denotes success.
  295. //
  296. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  297. bool GetMeasurement(float &temp_C,float &temp_F,float &humidity,bool SerialDebugOutput){
  298.   //status of the sensor read. false returned = fail
  299.  
  300.   bool status = false;
  301.    
  302.   while(!status){
  303.     // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  304.     float h = dht.readHumidity();
  305.     // Read temperature as Celsius (the default)
  306.     float t = dht.readTemperature();
  307.     // Read temperature as Fahrenheit (isFahrenheit = true)
  308.     float f = dht.readTemperature(true);
  309.     // Check if any reads failed and exit early (to try again).
  310.       if ((isnan(h) || isnan(t) || isnan(f)) && SerialDebugOutput) {
  311.         Serial.println("Failed to read from DHT sensor!");
  312.       }
  313.     else{
  314.       // Computes temperature values in Celsius + Fahrenheit and Humidity
  315.       temp_C = dht.computeHeatIndex(t, h, false);      
  316.       temp_F = dht.computeHeatIndex(f, h);
  317.       humidity = h;      
  318.       status = true;
  319.       }
  320.    
  321.     }//while status  
  322.    
  323.     return status;
  324. }
  325. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  326.  
  327. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  328. //  ServiceHttpRequest
  329. //
  330. //  Source:  https://randomnerdtutorials.com/esp8266-dht11dht22-temperature-and-humidity-web-server-with-arduino-ide/
  331. //
  332. //  1.  Gets the UTC time.
  333. //  2.  Outputs the ID of the ESP8266 sensor along with the last measured temperaure and humidity.
  334. //  3.  Closes the connection.
  335. //
  336. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  337. void ServiceHttpRequest(WiFiClient client){
  338.  
  339.   time_t myLocalTime;
  340.  
  341.   Serial.println("New client");
  342.   // boolean to locate when the http request ends
  343.   boolean blank_line = true;
  344.   while (client.connected()) {
  345.       if (client.available()) {
  346.         char c = client.read();
  347.        
  348.         if (c == '\n' && blank_line) {
  349.            
  350.               char celsiusTemp[9];
  351.               char farenheitTemp[9];
  352.               char humidityTemp[9];
  353.              
  354.               //Output the current environment values
  355.               dtostrf(tc, 6, 2, celsiusTemp);            
  356.               dtostrf(tf, 6, 2, farenheitTemp);        
  357.               dtostrf(h, 6, 2, humidityTemp);
  358.              
  359.               client.println("HTTP/1.1 200 OK");
  360.               client.println("Content-Type: text/html");
  361.               client.println("Connection: close");
  362.               client.println();
  363.               // your actual web page that displays temperature and humidity
  364.               client.println("<!DOCTYPE HTML>");
  365.               client.println("<html>");
  366.               client.println("<!--Variables -->");
  367.               client.println("<script>");
  368.               client.print("var unitNo = \"");
  369.               client.print(HexChipID);
  370.               client.println("\";");
  371.               //time
  372.               if(GetLocalTime(myLocalTime)){
  373.                 Serial.printf("The local time received is: %02d:%02d:%02d\n",hour(myLocalTime),minute(myLocalTime),second(myLocalTime));
  374.                 client.print("var hours = ");
  375.                 client.print(hour(myLocalTime));
  376.                 client.println(";");
  377.                 client.println("if(hours < 10){hours = \"0\"+hours;}");
  378.  
  379.                 client.print("var minutes = ");
  380.                 client.print(minute(myLocalTime));
  381.                 client.println(";");
  382.                 client.println("if(minutes < 10){minutes = \"0\"+minutes;}");
  383.  
  384.                 client.print("var seconds = ");
  385.                 client.print(second(myLocalTime));
  386.                 client.println(";");
  387.                 client.println("if(seconds < 10){seconds = \"0\"+seconds;}");
  388.               }
  389.               else
  390.               {
  391.                 client.println("No time received from time server");
  392.               }
  393.              
  394.               client.print("var tempC = ");
  395.               client.print(celsiusTemp);
  396.               client.println(";");
  397.               client.println("var tempC = tempC.toFixed(2);");
  398.               client.println("var tempF = tempC * 9/5 + 32;");
  399.               client.println("var tempF = tempF.toFixed(2);");
  400.               client.print("var humidity = ");
  401.               client.print(humidityTemp);
  402.               client.println(";");
  403.               client.println("humidity = humidity.toFixed(2);");
  404.               client.println("</script>");
  405.               client.print(html);
  406.               break;
  407.         }//(c == '\n' && blank_line)
  408.        
  409.         if (c == '\n') {
  410.           // when starts reading a new line
  411.           blank_line = true;
  412.         }
  413.         else if (c != '\r') {
  414.           // when finds a character on the current line
  415.           blank_line = false;
  416.         }
  417.       } //client.available
  418.   }  //client.connected
  419.   // closing the client connection
  420.   delay(1);
  421.   client.stop();
  422.   Serial.println("Client disconnected.");
  423. }
  424. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  425.  
  426. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  427. //  SendDataToDatabase - Sends data to existing InfluxDB database
  428. //
  429. //  Based on:  https://elf.cs.pub.ro/wsn/wiki/proiecte/influxdb
  430. //
  431. //  1.  Connect to the pre-existing InfluxDB
  432. //  2.  Output the UID of the ESP8266, tempC, tempF and humidity to the database.
  433. //  3.  Keep a track of how many rows have been stored.
  434. //  4.  ???Should I disconnect from the database????
  435. //
  436. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  437.  
  438. void SendDataToDatabase(WiFiClient client,const int t,const int h){
  439.   if(!client.connect(INFLUXDB_HOST,INFLUXDB_PORT)) {
  440.     Serial.println("Cant connect to InfluxDB");
  441.     }
  442.     else {
  443.         //Create data object with measurment name=analog_read
  444.         dbMeasurement row("temp_humidity");
  445.         row.addTag("UID_HEX",String(HexChipID));
  446.         row.addField("temp_C", t); //
  447.         row.addField("humidity",h); //                        
  448.         Serial.println(influxdb.write(row) == DB_SUCCESS ? "Object write success":"Writing failed");                
  449.         // Empty field object.
  450.         row.empty();
  451.     }
  452. }
  453. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  454.  
  455. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  456. //  TimerEvent - Main program loop.
  457. //
  458. //  1. When the timer fires, if the setup() is complete the ReadyToRead gate is opened so that the sensor can
  459. //  be read on the next loop.
  460. //
  461. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  462. void TimerEvent(){  
  463.  
  464.   if(SystemReady){
  465.     ReadyToRead=true;    
  466.   }//systemready
  467.  
  468. }//TimerEvent()
  469. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  470.  
  471. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  472. //  loop - Main program loop.
  473. //
  474. //  1.  Listens for http clients
  475. //  2.  If a client connects, send out the web page.
  476. //  3.  If the program is ready (the timer event opens the gate for new measurements every 10 seconds), the sensor is read.
  477. //  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.
  478. //  5.  Gate is closed until next timer event
  479. //
  480. //////////////////////////////////////////////////////////////////////////////////////////////////////////////
  481. void loop() {
  482.  
  483.   // Listenning for new clients
  484.   client = server.available();
  485.  
  486.   if (client) {
  487.     ServiceHttpRequest(client);    
  488.   }//client
  489.  
  490.     //if the sensor is ready and the timer event has fired, then take store the latest measurement
  491.     //keeping hold of the current values.  This allows us to only update the database when values
  492.     //have changed.
  493.     if(GetMeasurement(tc,tf,h,true) && ReadyToRead){
  494.        
  495.       if(tc!=old_tc | tf!=old_tf | h != old_h){//only send new data
  496.         digitalWrite(LED,HIGH);//so we know the unit is still alive
  497.         Serial.println("Sending new data to database");
  498.         SendDataToDatabase(client,tc,h);  
  499.         digitalWrite(LED,LOW);        
  500.        
  501.         //these are now our latest measurements
  502.         old_tc = tc;
  503.         old_tf = tf;
  504.         old_h = h;
  505.         ReadyToRead=false;//closes the gate until the timer fires again
  506.       }//send new data to the database
  507.      }//getmeasurement  
  508. }//loop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement