├── CSS.h ├── ESP32_DHT_Sensor_Client_Sleeping_Uploader.ino ├── ESP32_DHT_Sensor_Client_Uploader.ino ├── ESP_BME280_Sensor_Client_Sleeping_Uploader.ino ├── ESP_BMP180_Sensor_Client_Sleeping_Uploader.ino ├── ESP_BMP180_Sensor_Client_Uploader.ino ├── ESP_DS18B20_Sensor_Client_Sleeping_Uploader.ino ├── ESP_DS18B20_Sensor_Client_Uploader.ino ├── ESP_Multiple_DHT_Sensors_One_Client.ino ├── ESP_SHT30_Sensor_Client_Sleeping_Uploader.ino ├── ESP_SHT30_Sensor_Client_Uploader.ino ├── ESP_Sensor_Server_01.ino ├── ESP_Sensor_Server_Advanced_4.ino ├── Licence.txt ├── Network.h ├── README.md ├── Sys_Variables.h └── icons ├── List ├── bedroom.png ├── building.png ├── conserve.png ├── dining.png ├── factory.png ├── garage.png ├── outdoors.png └── study.png /CSS.h: -------------------------------------------------------------------------------- 1 | void append_page_header(bool refresh_on) { 2 | webpage = F(""); 3 | webpage += F(""); 4 | webpage += F("Sensor Server"); // NOTE: 1em = 16px 5 | if (AUpdate && refresh_on) webpage += F(""); // 30-sec refresh time, test needed to prevent auto updates repeating some commands 6 | webpage += F(""); 7 | webpage += F("

Sensor Server "); webpage += String(ServerVersion) + "

"; 32 | } 33 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 34 | void append_page_footer(bool graph_on){ // Saves repeating many lines of code for HTML page footers 35 | if (graph_on) { 36 | webpage += F(""; 37 | webpage += F("

"; 38 | if (readingCnt > display_records) { 39 | webpage += ""; 40 | } 41 | else 42 | { 43 | webpage += ""; 44 | } 45 | } 46 | webpage += F(""); 62 | webpage += ""; 65 | webpage += F(""); 66 | } 67 | -------------------------------------------------------------------------------- /ESP32_DHT_Sensor_Client_Sleeping_Uploader.ino: -------------------------------------------------------------------------------- 1 | /* ESP8266 provides a sensor client that uploads sensor data to a sensor server 2 | The 'MIT License (MIT) Copyright (c) 2018 by David Bird'. 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, 5 | distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 6 | following conditions: 7 | The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the 8 | software use is visible to an end-user. 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | See more at http://www.dsbird.org.uk 13 | */ 14 | #ifdef ESP8266 15 | #include // Built-in 16 | #include // Built-in 17 | #else 18 | #include // Built-in 19 | #include // Built-in 20 | #endif 21 | #include // Built-in 22 | #include // Built-in 23 | #include "DHT.h" // https://github.com/adafruit/DHT-sensor-library 24 | 25 | // Uncomment whatever type you're using! 26 | #define DHTTYPE DHT11 // DHT 11 27 | //#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 28 | //#define DHTTYPE DHT21 // DHT 21 (AM2301) 29 | 30 | // Connect pin 1 (on the left) of the sensor to +5V 31 | // NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1 32 | // to 3.3V instead of 5V! 33 | // Connect pin 2 of the sensor to whatever your DHTPIN is 34 | // Connect pin 4 (on the right) of the sensor to GROUND 35 | // Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor 36 | 37 | // Local ESP web-server address 38 | String UploadData; 39 | float sensor_temperature, sensor_humidity, sensor_pressure, sensor_spare; 40 | 41 | /* Instructions: 42 | * 1. Check which pin your sensor is conencted to, if not a WEMOS D1 Mini, then change D4 to the pin number 43 | * 2. Change 'SensorNumber' to your requirements 44 | * 3. Change 'SensorType' to match your sensor e.g. DHT11 or DHT22 45 | * 4. Adjust the DHT sensor type on line 8-10 above 46 | * 5. Adjust sleep time as required. Maximum of 1-hour on an ESP8266 47 | * 6. Change the sensorserver host address to match your server's setting 48 | */ 49 | // This is device 'sensor-3' adjust these definitions to make each sensor unique 50 | #define DHTPIN 16 // Device is connected to D4 on a WEMOS D1 Mini 16 on an \esp32 MHT-Live 51 | #define SensorNumber 1 52 | #define SensorType "DHT11-1" // You can make these sensor names unique e.g. SHT30-1 and SHT30-2, or your own choice 53 | // ************************************************************************************************************* 54 | // **** NOTE: The ESP8266 only supports deep sleep mode by connecting GPIO 16 to Reset 55 | // Wiring: ESP8266 16 -> ESP8266 RST (or D0 for GPIO16 on most ESP8266 Dev Boards 56 | // 57 | // !!!!! REMEMBER TO REMOVE THE GPIO16 to RST link for (re) programming, otherwise it remains in a RESET state.. 58 | // 59 | // ************************************************************************************************************* 60 | int SleepTime = 30 * 60 * 1000000; //SleepTime of 30mins 61 | //This is the central servers address, all sensors need to know this 62 | const char* ServerHost = "192.168.0.99"; // this is the IP address of the central sensor server 63 | 64 | WiFiClient client; 65 | 66 | #ifdef ESP8266 67 | ESP8266WiFiMulti wifiMulti; 68 | #else 69 | WiFiMulti wifiMulti; 70 | #endif 71 | 72 | DHT dht(DHTPIN, DHTTYPE); 73 | 74 | void setup() { 75 | Serial.begin(115200); 76 | Serial.println("...Connecting to Wi-Fi"); 77 | WiFi.mode(WIFI_STA); 78 | // AP Wi-Fi credentials 79 | wifiMulti.addAP("ssid_for_AP_1", "your_password_for_AP_1"); 80 | wifiMulti.addAP("ssid_for_AP_2", "your_password_for_AP_2"); 81 | wifiMulti.addAP("ssid_for_AP_3", "your_password_for_AP_3"); 82 | // Etc 83 | while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above 84 | delay(250); 85 | Serial.print('.'); 86 | if (millis() > 40000) ESP.restart(); // If more than 40-secs since start-up, then Restart 87 | } 88 | Serial.print("\n\rWi-Fi connected\n\rIP address: "); 89 | Serial.println(WiFi.localIP()); 90 | Serial.println("\n\r...Wi-Fi connected"); 91 | dht.begin(); 92 | Serial.println("Found a sensor continuing"); 93 | // Reading temperature or humidity takes about 250 milliseconds! 94 | // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) 95 | sensor_temperature = dht.readTemperature(); 96 | sensor_humidity = dht.readHumidity(); 97 | while (isnan(sensor_temperature) && isnan(sensor_humidity)) { 98 | Serial.println(sensor_temperature); 99 | sensor_temperature = dht.readTemperature(); 100 | sensor_humidity = dht.readHumidity(); 101 | } 102 | CollectUploadData(); 103 | SendHttpPOST(); 104 | Serial.println(UploadData); 105 | #ifdef ESP8266 106 | ESP.deepSleep(SleepTime, WAKE_RF_DEFAULT); // Sleep for the time set by 'UpdateInterval' 107 | #else 108 | ESP.deepSleep(SleepTime); // Sleep for the time set by 'UpdateInterval' 109 | #endif 110 | } 111 | 112 | void loop() { 113 | } 114 | 115 | void SendHttpPOST() { 116 | // Loop until we're reconnected 117 | while (!client.connected()) { 118 | Serial.print("Attempting connection..."); 119 | if (client.connect(ServerHost,80)) { 120 | Serial.println("connected"); 121 | } else { 122 | Serial.println(" try again in 500 ms"); 123 | delay(500);// Wait 0.5 seconds before retrying 124 | } 125 | } 126 | client.println("GET /"+UploadData+" HTTP/1.1\n\r"); 127 | client.println("Host: "+String(ServerHost)); 128 | delay(500); 129 | client.println("Connection: close"); 130 | client.stop(); 131 | } 132 | 133 | void CollectUploadData() { 134 | Serial.println("...Submitting Upload request"); 135 | UploadData = "sensor?Sensor="+String(SensorNumber); 136 | UploadData += "&temperature="+String(sensor_temperature); 137 | UploadData += "&humidity="+String(sensor_humidity); 138 | UploadData += "&pressure="+String(sensor_pressure); 139 | UploadData += "&spare="+String(sensor_spare); 140 | UploadData += "&sensortype="+String(SensorType); 141 | Serial.println("...Information to be uploaded: "+UploadData); 142 | } 143 | 144 | -------------------------------------------------------------------------------- /ESP32_DHT_Sensor_Client_Uploader.ino: -------------------------------------------------------------------------------- 1 | /* ESP8266 provides a sensor client that uploads sensor data to a sensor server 2 | The 'MIT License (MIT) Copyright (c) 2018 by David Bird'. 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, 5 | distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 6 | following conditions: 7 | The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the 8 | software use is visible to an end-user. 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | See more at http://www.dsbird.org.uk 13 | */ 14 | #ifdef ESP8266 15 | #include // Built-in 16 | #include // Built-in 17 | #else 18 | #include // Built-in 19 | #include // Built-in 20 | #endif 21 | #include // Built-in 22 | #include // Built-in 23 | #include "DHT.h" // https://github.com/adafruit/DHT-sensor-library 24 | 25 | // Uncomment whatever type you're using! 26 | #define DHTTYPE DHT11 // DHT 11 27 | //#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 28 | //#define DHTTYPE DHT21 // DHT 21 (AM2301) 29 | 30 | // Connect pin 1 (on the left) of the sensor to +5V 31 | // NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1 32 | // to 3.3V instead of 5V! 33 | // Connect pin 2 of the sensor to whatever your DHTPIN is 34 | // Connect pin 4 (on the right) of the sensor to GROUND 35 | // Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor 36 | 37 | // Local ESP web-server address 38 | String UploadData; 39 | float sensor_temperature, sensor_humidity, sensor_pressure, sensor_spare; 40 | 41 | /* Instructions: 42 | * 1. Check which pin your sensor is conencted to, if not a WEMOS D1 Mini, then change D4 to the pin number 43 | * 2. Change 'SensorNumber' to your requirements 44 | * 3. Change 'SensorType' to match your sensor e.g. DHT11 or DHT22 45 | * 4. Adjust the DHT sensor type on line 8-10 above 46 | * 5. Adjust sleep time as required. Maximum of 1-hour on an ESP8266 47 | * 6. Change the sensorserver host address to match your server's setting 48 | */ 49 | // This is device 'sensor-3' adjust these definitions to make each sensor unique 50 | #define DHTPIN 16 // Device is connected to D4 on a WEMOS D1 Mini 16 on an \esp32 MHT-Live 51 | #define SensorNumber 1 52 | #define SensorType "DHT11-1" // You can make these sensor names unique e.g. SHT30-1 and SHT30-2, or your own choice 53 | #define SleepTime 60 // 60-secs 54 | //This is the central servers address, all sensors need to know this 55 | const char* ServerHost = "192.168.0.99"; // this is the IP address of the central sensor server 56 | 57 | WiFiClient client; 58 | 59 | #ifdef ESP8266 60 | ESP8266WiFiMulti wifiMulti; 61 | #else 62 | WiFiMulti wifiMulti; 63 | #endif 64 | 65 | DHT dht(DHTPIN, DHTTYPE); 66 | 67 | void setup() { 68 | Serial.begin(115200); 69 | Serial.println("...Connecting to Wi-Fi"); 70 | WiFi.mode(WIFI_STA); 71 | // AP Wi-Fi credentials 72 | wifiMulti.addAP("ssid_for_AP_1", "your_password_for_AP_1"); 73 | wifiMulti.addAP("ssid_for_AP_2", "your_password_for_AP_2"); 74 | wifiMulti.addAP("ssid_for_AP_3", "your_password_for_AP_3"); 75 | // Etc 76 | while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above 77 | delay(250); 78 | Serial.print('.'); 79 | if (millis() > 40000) ESP.restart(); // If more than 40-secs since start-up, then Restart 80 | } 81 | Serial.print("\n\rWi-Fi connected\n\rIP address: "); 82 | Serial.println(WiFi.localIP()); 83 | Serial.println("\n\r...Wi-Fi connected"); 84 | dht.begin(); 85 | Serial.println("Found a sensor continuing"); 86 | } 87 | 88 | void loop() { 89 | // Reading temperature or humidity takes about 250 milliseconds! 90 | // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) 91 | sensor_temperature = dht.readTemperature(); 92 | sensor_humidity = dht.readHumidity(); 93 | while (isnan(sensor_temperature) && isnan(sensor_humidity)) { 94 | Serial.println(sensor_temperature); 95 | sensor_temperature = dht.readTemperature(); 96 | sensor_humidity = dht.readHumidity(); 97 | } 98 | CollectUploadData(); 99 | SendHttpPOST(); 100 | Serial.println(UploadData); 101 | delay(SleepTime*1000); //Wait for next upload 102 | } 103 | 104 | void SendHttpPOST() { 105 | // Loop until we're reconnected 106 | while (!client.connected()) { 107 | Serial.print("Attempting connection..."); 108 | if (client.connect(ServerHost,80)) { 109 | Serial.println("connected"); 110 | } else { 111 | Serial.println(" try again in 500 ms"); 112 | delay(500);// Wait 0.5 seconds before retrying 113 | } 114 | } 115 | client.println("GET /"+UploadData+" HTTP/1.1\n\r"); 116 | client.println("Host: "+String(ServerHost)); 117 | delay(500); // Delay needed for ESP32 118 | client.println("Connection: close"); 119 | client.stop(); 120 | } 121 | 122 | void CollectUploadData() { 123 | Serial.println("...Submitting Upload request"); 124 | UploadData = "sensor?Sensor="+String(SensorNumber); 125 | UploadData += "&temperature="+String(sensor_temperature); 126 | UploadData += "&humidity="+String(sensor_humidity); 127 | UploadData += "&pressure="+String(sensor_pressure); 128 | UploadData += "&spare="+String(sensor_spare); 129 | UploadData += "&sensortype="+String(SensorType); 130 | Serial.println("...Information to be uploaded: "+UploadData); 131 | } 132 | 133 | -------------------------------------------------------------------------------- /ESP_BME280_Sensor_Client_Sleeping_Uploader.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Version 1 ESP8266/32 provides a sensor client that uploads sensor data to a sensor server 3 | * 4 | This software, the ideas and concepts is Copyright (c) David Bird 2018. All rights to this software are reserved. 5 | 6 | Any redistribution or reproduction of any part or all of the contents in any form is prohibited other than the following: 7 | 1. You may print or download to a local hard disk extracts for your personal and non-commercial use only. 8 | 2. You may copy the content to individual third parties for their personal use, but only if you acknowledge the author David Bird as the source of the material. 9 | 3. You may not, except with my express written permission, distribute or commercially exploit the content. 10 | 4. You may not transmit it or store it in any other website or other form of electronic retrieval system for commercial purposes. 11 | 12 | The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the 13 | software use is visible to an end-user. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT. FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY 16 | OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | See more at http://www.dsbird.org.uk 20 | * 21 | */ 22 | #ifdef ESP8266 23 | #include // Built-in 24 | #include // Built-in 25 | #else 26 | #include // Built-in 27 | #include // Built-in 28 | #endif 29 | #include // Built-in 30 | #include // https://github.com/adafruit/Adafruit-BMP280-Library 31 | #include // Built-in 32 | 33 | // Local ESP web-server address 34 | String UploadData; 35 | float sensor_temperature, sensor_humidity, sensor_pressure, sensor_spare; 36 | 37 | /* Instructions: 38 | * 1. Check which pin your sensor is conencted to, if not a WEMOS D1 Mini, then change D4 to the pin number 39 | * 2. Change 'SensorNumber' to your requirements 40 | * 3. Change 'SensorType' to match your sensor e.g. BMP085 or BMP180 or BMP280 41 | * 4. Adjust the BMP sensor type on line 8-10 above 42 | * 5. Adjust sleep time as required. Maximum of 1-hour on an ESP8266 43 | * 6. Change the sensorserver host address to match your server's setting 44 | */ 45 | 46 | // This is device 'sensor-2' adjust these definitions to make each sensor unique 47 | #define SensorNumber 2 48 | #define SensorType "BME280" // You can make these sensor names unique e.g. SHT30-1 and SHT30-2, or your own choice 49 | #define SleepTime 5*60*1000000 // 5-min = 5*60*1000000uS 50 | //This is the central servers address, all sensors need to know this 51 | const char* ServerHost = "192.168.0.99"; // this is the IP address of the central sensor server 52 | 53 | WiFiClient client; 54 | 55 | #ifdef ESP8266 56 | ESP8266WiFiMulti wifiMulti; 57 | #else 58 | WiFiMulti wifiMulti; 59 | #endif 60 | 61 | #define pressure_offset 3.04 // Adjust for your altitude 62 | Adafruit_BME280 bme; 63 | 64 | void setup() { 65 | Serial.begin(115200); 66 | // Wire.begin(4,5); // For most ESP32 boards but check your board with a Serial.println(SDA) and Serial.println(SCL) first 67 | Wire.begin(); // D1 and D2 on a WEMOS D1 Mini(sda,scl) or use like this WEMOS D1 Mini example another pair of pins 68 | Serial.println("...Connecting to Wi-Fi"); 69 | WiFi.mode(WIFI_STA); 70 | // AP Wi-Fi credentials 71 | wifiMulti.addAP("ssid_for_AP_1","your_password_for_AP_1"); 72 | wifiMulti.addAP("ssid_for_AP_2","your_password_for_AP_2"); 73 | wifiMulti.addAP("ssid_for_AP_3","your_password_for_AP_3"); 74 | while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above 75 | delay(250); 76 | Serial.print('.'); 77 | if (millis() > 40000) ESP.restart(); // If more than 40-secs since start-up, then Restart 78 | } 79 | Serial.print("\n\rWi-Fi connected\n\rIP address: "); 80 | Serial.println(WiFi.localIP()); 81 | Serial.println("\n\r...Wi-Fi connected"); 82 | if (!bme.begin()) { 83 | Serial.println("Could not find a sensor, check wiring!"); 84 | } 85 | else 86 | { 87 | Serial.println("Found a sensor continuing"); 88 | sensor_pressure = bme.readPressure(); 89 | while (isnan(sensor_pressure)) { 90 | Serial.println(sensor_pressure); 91 | sensor_pressure = bme.readPressure(); 92 | } 93 | } 94 | } 95 | 96 | void loop() { 97 | sensor_temperature = bme.readTemperature(); 98 | sensor_humidity = bme.readHumidity(); 99 | sensor_pressure = bme.readPressure()/100.0F + pressure_offset; 100 | CollectUploadData(); 101 | SendHttpPOST(); 102 | Serial.println("Going to sleep"); 103 | #ifdef ESP8266 104 | ESP.deepSleep(SleepTime, WAKE_RF_DEFAULT); // Sleep for the time set by 'UpdateInterval' 105 | #else 106 | ESP.deepSleep(SleepTime); // Sleep for the time set by 'UpdateInterval' 107 | #endif 108 | } 109 | 110 | void SendHttpPOST() { 111 | // Loop until we're reconnected 112 | while (!client.connected()) { 113 | Serial.print("Attempting connection..."); 114 | if (client.connect(ServerHost,80)) { 115 | Serial.println("connected"); 116 | } else { 117 | Serial.println(" try again in 500 ms"); 118 | delay(500); // Wait 0.5 seconds before retrying 119 | } 120 | } 121 | client.println("GET /"+UploadData+" HTTP/1.1\n\r"); 122 | client.println("Host: "+String(ServerHost)); 123 | delay(500); // Essential delay for ESP32 124 | client.println("Connection: close"); 125 | client.stop(); 126 | } 127 | 128 | void CollectUploadData() { 129 | Serial.println("...Submitting Upload request"); 130 | UploadData = "sensor?Sensor="+String(SensorNumber); 131 | UploadData += "&temperature="+String(sensor_temperature); 132 | UploadData += "&humidity="+String(sensor_humidity); 133 | UploadData += "&pressure="+String(sensor_pressure); 134 | UploadData += "&spare="+String(sensor_spare); 135 | UploadData += "&sensortype="+String(SensorType); 136 | Serial.println("...Information to be uploaded: "+UploadData); 137 | } 138 | 139 | -------------------------------------------------------------------------------- /ESP_BMP180_Sensor_Client_Sleeping_Uploader.ino: -------------------------------------------------------------------------------- 1 | /* ESP8266 provides a sensor client that uploads sensor data to a sensor server 2 | The 'MIT License (MIT) Copyright (c) 2018 by David Bird'. 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, 5 | distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 6 | following conditions: 7 | The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the 8 | software use is visible to an end-user. 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | See more at http://www.dsbird.org.uk 13 | */ 14 | #ifdef ESP8266 15 | #include // Built-in 16 | #include // Built-in 17 | #else 18 | #include // Built-in 19 | #include // Built-in 20 | #endif 21 | #include // Built-in 22 | #include // https://github.com/adafruit/Adafruit-BMP085-Library 23 | #include // Built-in 24 | 25 | // Local ESP web-server address 26 | String UploadData; 27 | float sensor_temperature, sensor_humidity, sensor_pressure, sensor_spare; 28 | 29 | /* Instructions: 30 | * 1. Check which pin your sensor is conencted to, if not a WEMOS D1 Mini, then change D4 to the pin number 31 | * 2. Change 'SensorNumber' to your requirements 32 | * 3. Change 'SensorType' to match your sensor e.g. BMP085 or BMP180 or BMP280 33 | * 4. Adjust the BMP sensor type on line 8-10 above 34 | * 5. Adjust sleep time as required. Maximum of 1-hour on an ESP8266 35 | * 6. Change the sensorserver host address to match your server's setting 36 | */ 37 | 38 | // This is device 'sensor-2' adjust these definitions to make each sensor unique 39 | #define SensorNumber 2 40 | #define SensorType "BMP180-1" // You can make these sensor names unique e.g. SHT30-1 and SHT30-2, or your own choice 41 | // ************************************************************************************************************* 42 | // **** NOTE: The ESP8266 only supports deep sleep mode by connecting GPIO 16 to Reset 43 | // Wiring: ESP8266 16 -> ESP8266 RST (or D0 for GPIO16 on most ESP8266 Dev Boards 44 | // 45 | // !!!!! REMEMBER TO REMOVE THE GPIO16 to RST link for (re) programming, otherwise it remains in a RESET state.. 46 | // 47 | // ************************************************************************************************************* 48 | int SleepTime = 30 * 60 * 1000000; //SleepTime of 30mins 49 | //This is the central servers address, all sensors need to know this 50 | const char* ServerHost = "192.168.0.99"; // this is the IP address of the central sensor server 51 | 52 | WiFiClient client; 53 | 54 | #ifdef ESP8266 55 | ESP8266WiFiMulti wifiMulti; 56 | #else 57 | WiFiMulti wifiMulti; 58 | #endif 59 | 60 | #define pressure_offset 5.5 61 | Adafruit_BMP085 bmp; 62 | 63 | void setup() { 64 | Serial.begin(115200); 65 | Wire.begin(); // D1 and D2 on a WEMOS D1 Mini(sda,scl) 66 | Serial.println("...Connecting to Wi-Fi"); 67 | WiFi.mode(WIFI_STA); 68 | // AP Wi-Fi credentials 69 | wifiMulti.addAP("ssid_for_AP_1", "your_password_for_AP_1"); 70 | wifiMulti.addAP("ssid_for_AP_2", "your_password_for_AP_2"); 71 | wifiMulti.addAP("ssid_for_AP_3", "your_password_for_AP_3"); 72 | while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above 73 | delay(250); 74 | Serial.print('.'); 75 | if (millis() > 40000) ESP.restart(); // If more than 40-secs since start-up, then Restart 76 | } 77 | Serial.print("\n\rWi-Fi connected\n\rIP address: "); 78 | Serial.println(WiFi.localIP()); 79 | Serial.println("\n\r...Wi-Fi connected"); 80 | if (!bmp.begin()) { 81 | Serial.println("Could not find a sensor, check wiring!"); 82 | } 83 | else 84 | { 85 | Serial.println("Found a sensor continuing"); 86 | sensor_pressure = bmp.readPressure(); 87 | while (isnan(sensor_pressure)) { 88 | Serial.println(sensor_pressure); 89 | sensor_pressure = bmp.readPressure(); 90 | } 91 | } 92 | sensor_temperature = bmp.readTemperature(); 93 | sensor_pressure = bmp.readPressure()/100.0F + pressure_offset; 94 | CollectUploadData(); 95 | SendHttpPOST(); 96 | Serial.println(); 97 | #ifdef ESP8266 98 | ESP.deepSleep(SleepTime, WAKE_RF_DEFAULT); // Sleep for the time set by 'UpdateInterval' 99 | #else 100 | ESP.deepSleep(SleepTime); // Sleep for the time set by 'UpdateInterval' 101 | #endif 102 | } 103 | 104 | void loop() { 105 | } 106 | 107 | void SendHttpPOST() { 108 | // Loop until we're reconnected 109 | while (!client.connected()) { 110 | Serial.print("Attempting connection..."); 111 | if (client.connect(ServerHost,80)) { 112 | Serial.println("connected"); 113 | } else { 114 | Serial.println(" try again in 500 ms"); 115 | delay(500); // Wait 0.5 seconds before retrying 116 | } 117 | } 118 | client.println("GET /"+UploadData+" HTTP/1.1\n\r"); 119 | client.println("Host: "+String(ServerHost)); 120 | delay(500); // Delay needed for ESP32 121 | client.println("Connection: close"); 122 | client.stop(); 123 | } 124 | 125 | void CollectUploadData() { 126 | Serial.println("...Submitting Upload request"); 127 | UploadData = "sensor?Sensor="+String(SensorNumber); 128 | UploadData += "&temperature="+String(sensor_temperature); 129 | UploadData += "&humidity="+String(sensor_humidity); 130 | UploadData += "&pressure="+String(sensor_pressure); 131 | UploadData += "&spare="+String(sensor_spare); 132 | UploadData += "&sensortype="+String(SensorType); 133 | Serial.println("...Information to be uploaded: "+UploadData); 134 | } 135 | 136 | -------------------------------------------------------------------------------- /ESP_BMP180_Sensor_Client_Uploader.ino: -------------------------------------------------------------------------------- 1 | /* ESP8266 provides a sensor client that uploads sensor data to a sensor server 2 | The 'MIT License (MIT) Copyright (c) 2018 by David Bird'. 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, 5 | distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 6 | following conditions: 7 | The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the 8 | software use is visible to an end-user. 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | See more at http://www.dsbird.org.uk 13 | */ 14 | #ifdef ESP8266 15 | #include // Built-in 16 | #include // Built-in 17 | #else 18 | #include // Built-in 19 | #include // Built-in 20 | #endif 21 | #include // Built-in 22 | #include // https://github.com/adafruit/Adafruit-BMP085-Library 23 | #include // Built-in 24 | 25 | // Local ESP web-server address 26 | String UploadData; 27 | float sensor_temperature, sensor_humidity, sensor_pressure, sensor_spare; 28 | 29 | /* Instructions: 30 | * 1. Check which pin your sensor is conencted to, if not a WEMOS D1 Mini, then change D4 to the pin number 31 | * 2. Change 'SensorNumber' to your requirements 32 | * 3. Change 'SensorType' to match your sensor e.g. BMP085 or BMP180 or BMP280 33 | * 4. Adjust the BMP sensor type on line 8-10 above 34 | * 5. Adjust sleep time as required. Maximum of 1-hour on an ESP8266 35 | * 6. Change the sensorserver host address to match your server's setting 36 | */ 37 | 38 | // This is device 'sensor-2' adjust these definitions to make each sensor unique 39 | #define SensorNumber 2 40 | #define SensorType "BMP180-1" // You can make these sensor names unique e.g. SHT30-1 and SHT30-2, or your own choice 41 | #define SleepTime 60 // 60-secs 42 | //This is the central servers address, all sensors need to know this 43 | const char* ServerHost = "192.168.0.99"; // this is the IP address of the central sensor server 44 | 45 | WiFiClient client; 46 | 47 | #ifdef ESP8266 48 | ESP8266WiFiMulti wifiMulti; 49 | #else 50 | WiFiMulti wifiMulti; 51 | #endif 52 | 53 | #define pressure_offset 5.5 54 | Adafruit_BMP085 bmp; 55 | 56 | void setup() { 57 | Serial.begin(115200); 58 | Wire.begin(); // D1 and D2 on a WEMOS D1 Mini(sda,scl) 59 | Serial.println("...Connecting to Wi-Fi"); 60 | WiFi.mode(WIFI_STA); 61 | // AP Wi-Fi credentials 62 | wifiMulti.addAP("ssid_for_AP_1", "your_password_for_AP_1"); 63 | wifiMulti.addAP("ssid_for_AP_2", "your_password_for_AP_2"); 64 | wifiMulti.addAP("ssid_for_AP_3", "your_password_for_AP_3"); 65 | while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above 66 | delay(250); 67 | Serial.print('.'); 68 | if (millis() > 40000) ESP.restart(); // If more than 40-secs since start-up, then Restart 69 | } 70 | Serial.print("\n\rWi-Fi connected\n\rIP address: "); 71 | Serial.println(WiFi.localIP()); 72 | Serial.println("\n\r...Wi-Fi connected"); 73 | if (!bmp.begin()) { 74 | Serial.println("Could not find a sensor, check wiring!"); 75 | } 76 | else 77 | { 78 | Serial.println("Found a sensor continuing"); 79 | sensor_pressure = bmp.readPressure(); 80 | while (isnan(sensor_pressure)) { 81 | Serial.println(sensor_pressure); 82 | sensor_pressure = bmp.readPressure(); 83 | } 84 | } 85 | } 86 | 87 | void loop() { 88 | sensor_temperature = bmp.readTemperature(); 89 | sensor_pressure = bmp.readPressure()/100.0F + pressure_offset; 90 | CollectUploadData(); 91 | SendHttpPOST(); 92 | Serial.println(); 93 | delay(SleepTime*1000); //Wait for next upload 94 | } 95 | 96 | void SendHttpPOST() { 97 | // Loop until we're reconnected 98 | while (!client.connected()) { 99 | Serial.print("Attempting connection..."); 100 | if (client.connect(ServerHost,80)) { 101 | Serial.println("connected"); 102 | } else { 103 | Serial.println(" try again in 500 ms"); 104 | delay(500); // Wait 0.5 seconds before retrying 105 | } 106 | } 107 | client.println("GET /"+UploadData+" HTTP/1.1\n\r"); 108 | client.println("Host: "+String(ServerHost)); 109 | client.println("Connection: close"); 110 | client.stop(); 111 | } 112 | 113 | void CollectUploadData() { 114 | Serial.println("...Submitting Upload request"); 115 | UploadData = "sensor?Sensor="+String(SensorNumber); 116 | UploadData += "&temperature="+String(sensor_temperature); 117 | UploadData += "&humidity="+String(sensor_humidity); 118 | UploadData += "&pressure="+String(sensor_pressure); 119 | UploadData += "&spare="+String(sensor_spare); 120 | UploadData += "&sensortype="+String(SensorType); 121 | Serial.println("...Information to be uploaded: "+UploadData); 122 | } 123 | 124 | -------------------------------------------------------------------------------- /ESP_DS18B20_Sensor_Client_Sleeping_Uploader.ino: -------------------------------------------------------------------------------- 1 | /* ESP8266 provides a sensor client that uploads sensor data to a sensor server 2 | The 'MIT License (MIT) Copyright (c) 2018 by David Bird'. 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, 5 | distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 6 | following conditions: 7 | The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the 8 | software use is visible to an end-user. 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | See more at http://www.dsbird.org.uk 13 | */ 14 | #ifdef ESP8266 15 | #include // Built-in 16 | #include // Built-in 17 | #else 18 | #include // Built-in 19 | #include // Built-in 20 | #endif 21 | #include // Built-in 22 | 23 | //include //Removed because now included in latest DallTemperature library, created a conflict 24 | #include 25 | 26 | #define sensor_pin 21 27 | 28 | #define ONE_WIRE_BUS sensor_pin // DS18B20 connected to pin D2 on a Wemos D1 or 21 on MHT-LIVE 29 | #define TEMPERATURE_PRECISION 12 // Lower resolution 30 | 31 | // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) 32 | OneWire oneWire(ONE_WIRE_BUS); 33 | 34 | // Pass our oneWire reference to Dallas Temperature. 35 | DallasTemperature sensors(&oneWire); 36 | 37 | int numberOfDevices; // Number of temperature devices found 38 | 39 | DeviceAddress tempDeviceAddress, deviceAddress; // We'll use this variable to store a found device address 40 | 41 | String UploadData; 42 | float sensor_temperature = 0, sensor_humidity = 0, sensor_pressure =0, sensor_spare =0; 43 | 44 | // This is device 'sensor-1' adjust this definition to make each sensor unique 45 | #define SensorNumber 4 46 | #define SensorType "DS18B20" 47 | 48 | // ************************************************************************************************************* 49 | // **** NOTE: The ESP8266 only supports deep sleep mode by connecting GPIO 16 to Reset 50 | // Wiring: ESP8266 16 -> ESP8266 RST (or D0 for GPIO16 on most ESP8266 Dev Boards 51 | // 52 | // !!!!! REMEMBER TO REMOVE THE GPIO16 to RST link for (re) programming, otherwise it remains in a RESET state.. 53 | // 54 | // ************************************************************************************************************* 55 | int SleepTime = 30 * 60 * 1000000; //SleepTime of 30mins 56 | // Local ESP web-server address 57 | const char* ServerHost = "192.168.0.99"; // this is the IP address of the sensor server 58 | 59 | WiFiClient client; 60 | 61 | #ifdef ESP8266 62 | ESP8266WiFiMulti wifiMulti; 63 | #else 64 | WiFiMulti wifiMulti; 65 | #endif 66 | 67 | OneWire ds(21); // pin D2 on Wemos D1 Mini or 21 on MHT-LIVE 68 | 69 | // function to print a device address 70 | void printAddress(DeviceAddress deviceAddress) 71 | { 72 | for (uint8_t i = 0; i < 8; i++) 73 | { 74 | if (deviceAddress[i] < 16) Serial.print("0"); 75 | Serial.print(deviceAddress[i], HEX); 76 | } 77 | } 78 | 79 | // function to print the temperature for a device 80 | void printTemperature(DeviceAddress deviceAddress) 81 | { 82 | float tempC = sensors.getTempC(deviceAddress); 83 | sensor_temperature = tempC; 84 | Serial.print("Temp C: "); 85 | Serial.println(tempC); 86 | Serial.print("Temp F: "); 87 | Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit 88 | } 89 | 90 | void setup() { 91 | Serial.begin(115200); 92 | Serial.println("...Connecting to Wi-Fi"); 93 | WiFi.mode(WIFI_STA); 94 | // AP Wi-Fi credentials 95 | wifiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1"); 96 | wifiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2"); 97 | wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3"); 98 | // Etc 99 | while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above 100 | delay(250); 101 | Serial.print('.'); 102 | if (millis() > 40000) ESP.restart(); // If more than 40-secs since start-up, then Restart 103 | } 104 | Serial.print("\n\rWi-Fi connected\n\rIP address: "); 105 | Serial.println(WiFi.localIP()); 106 | Serial.println("\n\r...Wi-Fi connected"); 107 | ////// 108 | sensors.begin(); // Start the DS18B20 reading process 109 | // Get a count of devices on the wire 110 | numberOfDevices = sensors.getDeviceCount(); 111 | // locate device on the bus 112 | Serial.print("Locating devices... Found "); 113 | Serial.print(numberOfDevices, DEC); 114 | Serial.println(" devices."); 115 | 116 | // report parasite power requirements 117 | Serial.print("Parasite power is: "); 118 | if (sensors.isParasitePowerMode()) Serial.println("ON"); 119 | else Serial.println("OFF"); 120 | 121 | // Loop through each device, print out address 122 | for(int i=0; i < numberOfDevices; i++) 123 | { 124 | // Search the wire for address 125 | if(sensors.getAddress(tempDeviceAddress, i)) 126 | { 127 | Serial.print("Found device "); 128 | Serial.print(i, DEC); 129 | Serial.print(" with address: "); 130 | printAddress(tempDeviceAddress); 131 | Serial.println(); 132 | Serial.print("Setting resolution to "); 133 | Serial.println(TEMPERATURE_PRECISION, DEC); 134 | // set the resolution to TEMPERATURE_PRECISION bit (Each Dallas/Maxim device is capable of several different resolutions) 135 | sensors.setResolution(tempDeviceAddress, TEMPERATURE_PRECISION); 136 | Serial.print("Resolution set to: "); 137 | Serial.print(sensors.getResolution(tempDeviceAddress), DEC); 138 | Serial.println(); 139 | } 140 | else 141 | { 142 | Serial.print("Found ghost device at "); 143 | Serial.print(i, DEC); 144 | Serial.print(" but could not detect address. Check power and cabling"); 145 | } 146 | } 147 | sensors.requestTemperatures(); // Send the command to get temperatures 148 | Serial.println("DONE"); 149 | for(int i=0;i // Built-in 16 | #include // Built-in 17 | #else 18 | #include // Built-in 19 | #include // Built-in 20 | #endif 21 | #include // Built-in 22 | 23 | //include //Removed because now included in latest DallTemperature library, created a conflict 24 | #include 25 | 26 | #define sensor_pin 21 27 | 28 | #define ONE_WIRE_BUS sensor_pin // DS18B20 connected to pin D2 on a Wemos D1 or 21 on MHT-LIVE 29 | #define TEMPERATURE_PRECISION 12 // Lower resolution 30 | 31 | // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs) 32 | OneWire oneWire(ONE_WIRE_BUS); 33 | 34 | // Pass our oneWire reference to Dallas Temperature. 35 | DallasTemperature sensors(&oneWire); 36 | 37 | int numberOfDevices; // Number of temperature devices found 38 | 39 | DeviceAddress tempDeviceAddress, deviceAddress; // We'll use this variable to store a found device address 40 | 41 | String UploadData; 42 | float sensor_temperature = 0, sensor_humidity = 0, sensor_pressure =0, sensor_spare =0; 43 | 44 | // This is device 'sensor-1' adjust this definition to make each sensor unique 45 | #define SensorNumber 4 46 | #define SensorType "DS18B20" 47 | 48 | #define SleepTime 60 // 60-secs 49 | // Local ESP web-server address 50 | const char* ServerHost = "192.168.0.99"; // this is the IP address of the sensor server 51 | 52 | WiFiClient client; 53 | 54 | #ifdef ESP8266 55 | ESP8266WiFiMulti wifiMulti; 56 | #else 57 | WiFiMulti wifiMulti; 58 | #endif 59 | 60 | OneWire ds(21); // pin D2 on Wemos D1 Mini or 21 on MHT-LIVE 61 | 62 | // function to print a device address 63 | void printAddress(DeviceAddress deviceAddress) 64 | { 65 | for (uint8_t i = 0; i < 8; i++) 66 | { 67 | if (deviceAddress[i] < 16) Serial.print("0"); 68 | Serial.print(deviceAddress[i], HEX); 69 | } 70 | } 71 | 72 | // function to print the temperature for a device 73 | void printTemperature(DeviceAddress deviceAddress) 74 | { 75 | float tempC = sensors.getTempC(deviceAddress); 76 | sensor_temperature = tempC; 77 | Serial.print("Temp C: "); 78 | Serial.println(tempC); 79 | Serial.print("Temp F: "); 80 | Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit 81 | } 82 | 83 | void setup() { 84 | Serial.begin(115200); 85 | Serial.println("...Connecting to Wi-Fi"); 86 | WiFi.mode(WIFI_STA); 87 | // AP Wi-Fi credentials 88 | wifiMulti.addAP("ssid_from_AP_1" "your_password_for_AP_1"); 89 | wifiMulti.addAP("ssid_from_AP_2" "your_password_for_AP_2"); 90 | wifiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3"); 91 | // Etc 92 | while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above 93 | delay(250); 94 | Serial.print('.'); 95 | if (millis() > 40000) ESP.restart(); // If more than 40-secs since start-up, then Restart 96 | } 97 | Serial.print("\n\rWi-Fi connected\n\rIP address: "); 98 | Serial.println(WiFi.localIP()); 99 | Serial.println("\n\r...Wi-Fi connected"); 100 | ////// 101 | sensors.begin(); // Start the DS18B20 reading process 102 | // Get a count of devices on the wire 103 | numberOfDevices = sensors.getDeviceCount(); 104 | // locate device on the bus 105 | Serial.print("Locating devices... Found "); 106 | Serial.print(numberOfDevices, DEC); 107 | Serial.println(" devices."); 108 | 109 | // report parasite power requirements 110 | Serial.print("Parasite power is: "); 111 | if (sensors.isParasitePowerMode()) Serial.println("ON"); 112 | else Serial.println("OFF"); 113 | 114 | // Loop through each device, print out address 115 | for(int i=0; i < numberOfDevices; i++) 116 | { 117 | // Search the wire for address 118 | if(sensors.getAddress(tempDeviceAddress, i)) 119 | { 120 | Serial.print("Found device "); 121 | Serial.print(i, DEC); 122 | Serial.print(" with address: "); 123 | printAddress(tempDeviceAddress); 124 | Serial.println(); 125 | Serial.print("Setting resolution to "); 126 | Serial.println(TEMPERATURE_PRECISION, DEC); 127 | // set the resolution to TEMPERATURE_PRECISION bit (Each Dallas/Maxim device is capable of several different resolutions) 128 | sensors.setResolution(tempDeviceAddress, TEMPERATURE_PRECISION); 129 | Serial.print("Resolution set to: "); 130 | Serial.print(sensors.getResolution(tempDeviceAddress), DEC); 131 | Serial.println(); 132 | } 133 | else 134 | { 135 | Serial.print("Found ghost device at "); 136 | Serial.print(i, DEC); 137 | Serial.print(" but could not detect address. Check power and cabling"); 138 | } 139 | } 140 | } 141 | 142 | void loop() { 143 | sensors.requestTemperatures(); // Send the command to get temperatures 144 | Serial.println("DONE"); 145 | for(int i=0;i // Built-in 24 | #include // Built-in 25 | #else 26 | #include // Built-in 27 | #include // Built-in 28 | #endif 29 | #include // Built-in 30 | #include // Built-in 31 | #include "DHT.h" // https://github.com/adafruit/DHT-sensor-library 32 | 33 | // Uncomment whatever type you're using! 34 | #define DHTTYPE DHT11 // DHT 11 35 | //#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 36 | //#define DHTTYPE DHT21 // DHT 21 (AM2301) 37 | 38 | // Connect pin 1 (on the left) of the sensor to +5V 39 | // NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1 40 | // to 3.3V instead of 5V! 41 | // Connect pin 2 of the sensor to whatever your DHTPIN is 42 | // Connect pin 4 (on the right) of the sensor to GROUND 43 | // Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor 44 | 45 | // Local ESP web-server address 46 | String UploadData; 47 | 48 | /* Instructions: 49 | * 1. Check which pin your sensor is conencted to, if not a WEMOS D1 Mini, then change D4 to the pin number 50 | * 2. Change 'SensorNumber' to your requirements 51 | * 3. Change 'SensorType' to match your sensor e.g. DHT11 or DHT22 52 | * 4. Adjust the DHT sensor type on line 8-10 above 53 | * 5. Adjust sleep time as required. Maximum of 1-hour on an ESP8266 54 | * 6. Change the sensorserver host address to match your server's setting 55 | */ 56 | // ************************************************************************************************************* 57 | // **** NOTE: The ESP8266 only supports deep sleep mode by connecting GPIO 16 to Reset 58 | // Wiring: ESP8266 16 -> ESP8266 RST (or D0 for GPIO16 on most ESP8266 Dev Boards 59 | // 60 | // !!!!! REMEMBER TO REMOVE THE GPIO16 to RST link for (re) programming, otherwise it remains in a RESET state.. 61 | // 62 | // ************************************************************************************************************* 63 | int SleepTime = 30 * 60 * 1000000; //SleepTime of 30mins 64 | //This is the central servers address, all sensors need to know this 65 | const char* ServerHost = "192.168.0.99"; // this is the IP address of the central sensor server 66 | 67 | WiFiClient client; 68 | 69 | #ifdef ESP8266 70 | ESP8266WiFiMulti wifiMulti; 71 | #else 72 | WiFiMulti wifiMulti; 73 | #endif 74 | 75 | DHT dht1(16, DHT11); // On pin-16 76 | DHT dht2(17, DHT11); // On pin-17 77 | DHT dht3(18, DHT11); // On pin-18 78 | 79 | void setup() { 80 | Serial.begin(115200); 81 | Serial.println("...Connecting to Wi-Fi"); 82 | WiFi.mode(WIFI_STA); 83 | // AP Wi-Fi credentials 84 | wifiMulti.addAP("ssid_for_AP_1", "your_password_for_AP_1"); 85 | wifiMulti.addAP("ssid_for_AP_2", "your_password_for_AP_2"); 86 | wifiMulti.addAP("ssid_for_AP_3", "your_password_for_AP_3"); 87 | // Etc 88 | while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above 89 | delay(250); 90 | Serial.print('.'); 91 | if (millis() > 40000) ESP.restart(); // If more than 40-secs since start-up, then Restart 92 | } 93 | Serial.print("\n\rWi-Fi connected\n\rIP address: "); 94 | Serial.println(WiFi.localIP()); 95 | Serial.println("\n\r...Wi-Fi connected"); 96 | dht1.begin(); 97 | dht2.begin(); 98 | dht3.begin(); 99 | Serial.println("Found a sensor continuing"); 100 | // Reading temperature or humidity takes about 250 milliseconds! 101 | // Sensor readings may also be up to 2 seconds it's an 'old' and slow sensor 102 | float sensor_temperature = dht1.readTemperature(); 103 | float sensor_humidity = dht1.readHumidity(); 104 | while (isnan(sensor_temperature) && isnan(sensor_humidity)) { 105 | Serial.println(sensor_temperature); 106 | sensor_temperature = dht1.readTemperature(); 107 | sensor_humidity = dht1.readHumidity(); 108 | } 109 | // At this point assume the other sensors are providing valid readings as only Sensor1 was tested 110 | // Collate and send Sensor-1 data 111 | Collect_and_UploadData(1,dht1.readTemperature(),dht1.readHumidity(),0,0,"DHT11"); 112 | delay(1000); // To give the server time to receive and process the data 113 | // Collate and send Sensor-2 data 114 | Collect_and_UploadData(2,dht2.readTemperature(),dht2.readHumidity(),0,0,"DHT11"); 115 | delay(1000); // To give the server time to receive and process the data 116 | // Collate and send Sensor-3 data 117 | Collect_and_UploadData(3,dht3.readTemperature(),dht3.readHumidity(),0,0,"DHT11"); 118 | delay(1000); // To give the server time to receive and process the data 119 | 120 | #ifdef ESP8266 121 | ESP.deepSleep(SleepTime, WAKE_RF_DEFAULT); // Sleep for the time set by 'UpdateInterval' 122 | #else 123 | ESP.deepSleep(SleepTime); // Sleep for the time set by 'UpdateInterval' 124 | #endif 125 | } 126 | 127 | void loop() { 128 | } 129 | 130 | void SendHttpPOST() { 131 | // Loop until we're reconnected 132 | while (!client.connected()) { 133 | Serial.print("Attempting connection..."); 134 | if (client.connect(ServerHost,80)) { 135 | Serial.println("connected"); 136 | } else { 137 | Serial.println(" try again in 500 ms"); 138 | delay(500);// Wait 0.5 seconds before retrying 139 | } 140 | } 141 | client.println("GET /"+UploadData+" HTTP/1.1\n\r"); 142 | client.println("Host: "+String(ServerHost)); 143 | delay(500); // Essential delay to enable the webserver to receive the HTTP and process it 144 | client.println("Connection: close"); 145 | client.stop(); 146 | } 147 | 148 | void Collect_and_UploadData(int SensorNumber, float sensor_temperature, float sensor_humidity, float sensor_pressure, float sensor_spare, String SensorType) { 149 | Serial.println("...Submitting Upload request"); 150 | UploadData = "sensor?Sensor="+String(SensorNumber); 151 | UploadData += "&temperature="+String(sensor_temperature); 152 | UploadData += "&humidity="+String(sensor_humidity); 153 | UploadData += "&pressure="+String(sensor_pressure); 154 | UploadData += "&spare="+String(sensor_spare); 155 | UploadData += "&sensortype="+String(SensorType); 156 | Serial.println("...Information uploaded: "+UploadData); 157 | SendHttpPOST(); 158 | } 159 | 160 | 161 | -------------------------------------------------------------------------------- /ESP_SHT30_Sensor_Client_Sleeping_Uploader.ino: -------------------------------------------------------------------------------- 1 | /* An ESP32 or ESP8266 provides a sensor client that uploads sensor data to a sensor server 2 | The 'MIT License (MIT) Copyright (c) 2018 by David Bird'. 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, 5 | distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 6 | following conditions: 7 | The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the 8 | software use is visible to an end-user. 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | See more at http://www.dsbird.org.uk 13 | */ 14 | #ifdef ESP8266 15 | #include // Built-in 16 | #include // Built-in 17 | #else 18 | #include // Built-in 19 | #include // Built-in 20 | #endif 21 | #include // Built-in 22 | #include // https://github.com/wemos/WEMOS_SHT3x_Arduino_Library 23 | #include // Built-in 24 | 25 | // Local ESP web-server address 26 | String UploadData; 27 | float sensor_temperature, sensor_humidity, sensor_pressure, sensor_spare; 28 | 29 | /* Instructions: 30 | * 1. Check which pin your sensor is conencted to, if not a WEMOS D1 Mini, then change D4 to the pin number 31 | * 2. Change 'SensorNumber' to your requirements 32 | * 3. Change 'SensorType' to match your sensor e.g. SHT30, SHT31 or SHT35 33 | * 4. Adjust the DHT sensor type on line 8-10 above 34 | * 5. Adjust sleep time as required. Maximum of 1-hour on an ESP8266 35 | * 6. Change the sensorserver host address to match your server's setting 36 | */ 37 | 38 | // This is device 'sensor-1' adjust these definitions to make each sensor unique 39 | #define SensorNumber 3 40 | #define SensorType "SHT30-1" // You can make these sensor names unique e.g. SHT30-1 and SHT30-2, or your own choice 41 | // ************************************************************************************************************* 42 | // **** NOTE: The ESP8266 only supports deep sleep mode by connecting GPIO 16 to Reset 43 | // Wiring: ESP8266 16 -> ESP8266 RST (or D0 for GPIO16 on most ESP8266 Dev Boards 44 | // 45 | // !!!!! REMEMBER TO REMOVE THE GPIO16 to RST link for (re) programming, otherwise it remains in a RESET state.. 46 | // 47 | // ************************************************************************************************************* 48 | int SleepTime = 30 * 60 * 1000000; //SleepTime of 30mins 49 | //This is the central servers address, all sensors need to know this 50 | const char* ServerHost = "192.168.0.99"; // this is the IP address of the central sensor server 51 | 52 | WiFiClient client; 53 | 54 | #ifdef ESP8266 55 | ESP8266WiFiMulti wifiMulti; 56 | #else 57 | WiFiMulti wifiMulti; 58 | #endif 59 | 60 | SHT3X sht30(0x45); // SHT30 object to enable readings (I2C address = 0x45) 61 | 62 | void setup() { 63 | Serial.begin(115200); 64 | Serial.println("...Connecting to Wi-Fi"); 65 | WiFi.mode(WIFI_STA); 66 | // AP Wi-Fi credentials 67 | wifiMulti.addAP("ssid_for_AP_1", "your_password_for_AP_1"); 68 | wifiMulti.addAP("ssid_for_AP_2", "your_password_for_AP_2"); 69 | wifiMulti.addAP("ssid_for_AP_3", "your_password_for_AP_3"); 70 | // Etc 71 | while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above 72 | delay(250); 73 | Serial.print('.'); 74 | if (millis() > 40000) ESP.restart(); // If more than 40-secs since start-up, then Restart 75 | } 76 | Serial.print("\n\rWi-Fi connected\n\rIP address: "); 77 | Serial.println(WiFi.localIP()); 78 | Serial.println("\n\r...Wi-Fi connected"); 79 | sht30.get(); // Update temp and humi 80 | sensor_temperature = sht30.cTemp; 81 | sensor_humidity = sht30.humidity; 82 | while (isnan(sensor_temperature) && isnan(sensor_humidity)) { 83 | Serial.println(sensor_temperature); 84 | sensor_temperature = sht30.cTemp; 85 | sensor_humidity = sht30.humidity; 86 | } 87 | CollectUploadData(); 88 | SendHttpPOST(); 89 | Serial.println(UploadData); 90 | #ifdef ESP8266 91 | ESP.deepSleep(SleepTime, WAKE_RF_DEFAULT); // Sleep for the time set by 'UpdateInterval' 92 | #else 93 | ESP.deepSleep(SleepTime); // Sleep for the time set by 'UpdateInterval' 94 | #endif 95 | } 96 | 97 | void loop() { 98 | } 99 | 100 | void SendHttpPOST() { 101 | // Loop until we're reconnected 102 | while (!client.connected()) { 103 | Serial.print("Attempting connection..."); 104 | if (client.connect(ServerHost,80)) { 105 | Serial.println("connected"); 106 | } else { 107 | Serial.println(" try again in 500 ms"); 108 | delay(500); // Wait 0.5 seconds before retrying 109 | } 110 | } 111 | client.println("GET /"+UploadData+" HTTP/1.1\n\r"); 112 | client.println("Host: "+String(ServerHost)); 113 | delay(500); // Delay needed for ESP32 114 | client.println("Connection: close"); 115 | client.stop(); 116 | } 117 | 118 | void CollectUploadData() { 119 | Serial.println("...Submitting Upload request"); 120 | UploadData = "sensor?Sensor="+String(SensorNumber); 121 | UploadData += "&temperature="+String(sensor_temperature); 122 | UploadData += "&humidity="+String(sensor_humidity); 123 | UploadData += "&pressure="+String(sensor_pressure); 124 | UploadData += "&spare="+String(sensor_spare); 125 | UploadData += "&sensortype="+String(SensorType); 126 | Serial.println("...Information to be uploaded: "+UploadData); 127 | } 128 | 129 | -------------------------------------------------------------------------------- /ESP_SHT30_Sensor_Client_Uploader.ino: -------------------------------------------------------------------------------- 1 | /* An ESP32 or ESP8266 provides a sensor client that uploads sensor data to a sensor server 2 | The 'MIT License (MIT) Copyright (c) 2018 by David Bird'. 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, 5 | distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 6 | following conditions: 7 | The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the 8 | software use is visible to an end-user. 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | See more at http://www.dsbird.org.uk 13 | */ 14 | #ifdef ESP8266 15 | #include // Built-in 16 | #include // Built-in 17 | #else 18 | #include // Built-in 19 | #include // Built-in 20 | #endif 21 | #include // Built-in 22 | #include // https://github.com/wemos/WEMOS_SHT3x_Arduino_Library 23 | #include // Built-in 24 | 25 | // Local ESP web-server address 26 | String UploadData; 27 | float sensor_temperature, sensor_humidity, sensor_pressure, sensor_spare; 28 | 29 | /* Instructions: 30 | * 1. Check which pin your sensor is conencted to, if not a WEMOS D1 Mini, then change D4 to the pin number 31 | * 2. Change 'SensorNumber' to your requirements 32 | * 3. Change 'SensorType' to match your sensor e.g. SHT30, SHT31 or SHT35 33 | * 4. Adjust the DHT sensor type on line 8-10 above 34 | * 5. Adjust sleep time as required. Maximum of 1-hour on an ESP8266 35 | * 6. Change the sensorserver host address to match your server's setting 36 | */ 37 | 38 | // This is device 'sensor-1' adjust these definitions to make each sensor unique 39 | #define SensorNumber 3 40 | #define SensorType "SHT30-1" // You can make these sensor names unique e.g. SHT30-1 and SHT30-2, or your own choice 41 | #define SleepTime 60 // 60-secs 42 | //This is the central servers address, all sensors need to know this 43 | const char* ServerHost = "192.168.0.99"; // this is the IP address of the central sensor server 44 | 45 | WiFiClient client; 46 | 47 | #ifdef ESP8266 48 | ESP8266WiFiMulti wifiMulti; 49 | #else 50 | WiFiMulti wifiMulti; 51 | #endif 52 | 53 | SHT3X sht30(0x45); // SHT30 object to enable readings (I2C address = 0x45) 54 | 55 | void setup() { 56 | Serial.begin(115200); 57 | Serial.println("...Connecting to Wi-Fi"); 58 | WiFi.mode(WIFI_STA); 59 | // AP Wi-Fi credentials 60 | wifiMulti.addAP("ssid_for_AP_1", "your_password_for_AP_1"); 61 | wifiMulti.addAP("ssid_for_AP_2", "your_password_for_AP_2"); 62 | wifiMulti.addAP("ssid_for_AP_3", "your_password_for_AP_3"); 63 | // Etc 64 | while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above 65 | delay(250); 66 | Serial.print('.'); 67 | if (millis() > 40000) ESP.restart(); // If more than 40-secs since start-up, then Restart 68 | } 69 | Serial.print("\n\rWi-Fi connected\n\rIP address: "); 70 | Serial.println(WiFi.localIP()); 71 | Serial.println("\n\r...Wi-Fi connected"); 72 | } 73 | 74 | void loop() { 75 | sht30.get(); // Update temp and humi 76 | sensor_temperature = sht30.cTemp; 77 | sensor_humidity = sht30.humidity; 78 | while (isnan(sensor_temperature) && isnan(sensor_humidity)) { 79 | Serial.println(sensor_temperature); 80 | sensor_temperature = sht30.cTemp; 81 | sensor_humidity = sht30.humidity; 82 | } 83 | CollectUploadData(); 84 | SendHttpPOST(); 85 | Serial.println(UploadData); 86 | delay(SleepTime*1000); //Wait for next upload 87 | } 88 | 89 | void SendHttpPOST() { 90 | // Loop until we're reconnected 91 | while (!client.connected()) { 92 | Serial.print("Attempting connection..."); 93 | if (client.connect(ServerHost,80)) { 94 | Serial.println("connected"); 95 | } else { 96 | Serial.println(" try again in 500 ms"); 97 | delay(500); // Wait 0.5 seconds before retrying 98 | } 99 | } 100 | client.println("GET /"+UploadData+" HTTP/1.1\n\r"); 101 | client.println("Host: "+String(ServerHost)); 102 | delay(500); // Delay needed for ESP32 103 | client.println("Connection: close"); 104 | client.stop(); 105 | } 106 | 107 | void CollectUploadData() { 108 | Serial.println("...Submitting Upload request"); 109 | UploadData = "sensor?Sensor="+String(SensorNumber); 110 | UploadData += "&temperature="+String(sensor_temperature); 111 | UploadData += "&humidity="+String(sensor_humidity); 112 | UploadData += "&pressure="+String(sensor_pressure); 113 | UploadData += "&spare="+String(sensor_spare); 114 | UploadData += "&sensortype="+String(SensorType); 115 | Serial.println("...Information to be uploaded: "+UploadData); 116 | } 117 | 118 | -------------------------------------------------------------------------------- /ESP_Sensor_Server_01.ino: -------------------------------------------------------------------------------- 1 | /* ESP32 providing a sensor server collating sensor data uploads and siaplsy the readigns on a Web page 2 | The 'MIT License (MIT) Copyright (c) 2018 by David Bird'. 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, 5 | distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 6 | following conditions: 7 | The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the 8 | software use is visible to an end-user. 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | See more at http://www.dsbird.org.uk 13 | */ 14 | #ifdef ESP8266 15 | #include // Built-in 16 | #include // Built-in 17 | #include // Built-in 18 | #include 19 | #else 20 | #include // Built-in 21 | #include // Built-in 22 | #include // https://github.com/Pedroalbuquerque/ESP32WebServer download and place in your Libraries folder 23 | #include 24 | #endif 25 | #include // Built-in 26 | 27 | #ifdef ESP8266 28 | ESP8266WiFiMulti wifiMulti; 29 | ESP8266WebServer server(80); 30 | #else 31 | WiFiMulti wifiMulti; 32 | ESP32WebServer server(80); 33 | #endif 34 | 35 | // Adjust the following values to match your needs 36 | /////////////////////////////////////////////////////////////////////////////////////// 37 | #define ServerVersion 1.0 38 | #define number_of_sensors 16 // 20 is a practical limit 39 | #define readings_per_sensor 1 // NOTE: there must be 1 record per sensor, making this (e.g.6x2) will allow 2 readings per sensor per day, etc. But not supported in this version 40 | #define number_of_days 2 // NOTE: must be > 1, but more than 2 is not supported in this version 41 | #define PageWidth 1023 // Screen width in pixels 42 | char SensorName[number_of_sensors][16] = {"Sensor-1","Sensor-2","Sensor-3","Sensor-4","Sensor-5","Sensor-6", 43 | "Sensor-7","Sensor-8","Sensor-9","Sensor-10","Sensor-11","Sensor-12"};//NOTE: No more than 16-chars per sensor name, or adjust array size 44 | char RowName[5][16] = {"Sensor-type","Temperature","Humidity","Pressure","Spare"}; //NOTE: No more than 16-chars per row name, or adjust array size 45 | char RowUnits[5][20] = {"","°C","%","hPa",""}; //NOTE: No more than 20-chars per unit name, or adjust array size 46 | /////////////////////////////////////////////////////////////////////////////////////// 47 | 48 | char ServerLogicalName[16] = {0}; // used here http://ServerLogicalName.local 49 | // If you make ServerLogicalName = "myserver" then the address would be http://myserver.local/ 50 | 51 | typedef struct { 52 | byte sensornumber; // Sensor number provided by e.g. Sensor=3 53 | float value1; // For example Temperature 54 | float value2; // For example Humidity 55 | float value3; // For example Pressure 56 | float value4; // Spare 57 | String sensortype = "N/A"; // The sensor type e.g. an SHT30 or BMP180 58 | } sensor_record_type; 59 | 60 | sensor_record_type sensor_data[number_of_days][number_of_sensors * readings_per_sensor]; // Define the data array 61 | int sensor_reading = 0; // Default value 62 | int day_record_cnt = 1; // Default value 63 | int day_count = 1; // Default value 64 | String webpage = ""; 65 | bool AUpdate = true; // Default value 66 | 67 | IPAddress local_IP(192, 168, 0, 99); // Set your server's fixed IP address here 68 | IPAddress gateway(192, 168, 0, 1); 69 | IPAddress subnet(255, 255, 0, 0); 70 | 71 | void setup(void){ 72 | Serial.begin(115200); 73 | sprintf(ServerLogicalName,"sensorserver"); // Don't add '.local' ! 74 | Serial.println("Hostname: "+String(ServerLogicalName)); 75 | if (!WiFi.config(local_IP, gateway, subnet)) { 76 | Serial.println("STA Failed to configure"); 77 | } 78 | wifiMulti.addAP("ssid_of_AP_1", "password_for_AP_1"); // add Wi-Fi networks you want to connect to, so your SSID and your Password 79 | wifiMulti.addAP("ssid_of_AP_2", "password_for_AP_2"); 80 | wifiMulti.addAP("ssid_of_AP_3", "password_for_AP_3"); 81 | // etc 82 | Serial.println("Connecting ..."); 83 | while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above 84 | delay(250); Serial.print('.'); 85 | } 86 | Serial.print("\nConnected to "+WiFi.SSID()); // Report which SSID has been used 87 | Serial.print("IP address:\t"); 88 | Serial.println(WiFi.localIP()); // And the IP address being used, although http://ServerLogicalName.local will access the device 89 | Serial.println(""); 90 | while (WiFi.status() != WL_CONNECTED) { // Wait for connection 91 | delay(500); 92 | Serial.print("."); 93 | } 94 | if (!MDNS.begin("sensorserver")) { 95 | Serial.println("Error setting up MDNS responder!"); 96 | while(1){ 97 | delay(1000); 98 | } 99 | } 100 | ///////////////////////////// Request commands 101 | server.on("/", handleRoot); 102 | server.on("/test", [](){ server.send(200, "text/plain", "The server status is OK"); }); // Test the server by giving a status response 103 | server.on("/sensor", handleSensors); // Associate the handler function to the path, note /sensor only refreshes the data 104 | server.onNotFound(handleNotFound); // Handle when a client requests an unknown URI for example something other than "/") 105 | server.on("/AUpdate", auto_update); // Turn auto-update ON/OFF 106 | ///////////////////////////// End of Request commands 107 | server.begin(); 108 | Serial.println("HTTP server started"); 109 | } 110 | 111 | void loop(void){ 112 | server.handleClient(); // Listen for client connections 113 | } 114 | 115 | // Functions from here... 116 | void handleRoot() { 117 | server.send(200, "text/plain", "hello from the ESP8266 Sensor Server"); 118 | } 119 | 120 | void handleNotFound(){ 121 | server.send(404, "text/plain", "404: Not found"); // Send HTTP status 404 (Not Found) when there's no handler for the URI in the request 122 | } 123 | 124 | // This function handles many arguments e.g. http://sensorserver.local/sensor?Sensor=1&Temperature=11.1&Humidity=22.2&Pressure=1000.0mb&Value=5.0&sensortype=BME280 125 | // e.g. http://sensorserver.local/sensor?Sensor=2&Temperature=22.2&Humidity=44.4&Pressure=1002.2mb 126 | // e.g. http://sensorserver.local/sensor?Sensor=3&Temperature=33.3&Humidity=66.6&Pressure=1003.3mb 127 | // Update the sensorserver to your own and click on the links to test the server! 128 | void handleSensors() { //Server request handler 129 | webpage = ""; 130 | Serial.print("Received from: "); 131 | for (int i = 0; i <= server.args(); i++) Serial.println(server.argName(i)+" "+server.arg(i)); // Display received arguments from sensor 132 | 133 | day_record_cnt = 1; // day_record_cnt += 1; //scope for more than 1-day of records, but forced to 1 for this version! 134 | int sensor_num = server.arg(0).toInt(); 135 | if (sensor_num > number_of_sensors) sensor_num = 0; 136 | if (sensor_num !=0 ) sensor_reading = sensor_num; else sensor_reading = 1; // Each sensor has its own record, so Sensor-1 readings go in record-1, etc 137 | for (int i = 0; i <= server.args(); i++) { 138 | if (sensor_num != 0 && sensor_num <= number_of_sensors) { // If a valid sensor number then process 139 | sensor_data[day_record_cnt][sensor_reading].sensornumber = (byte)sensor_num; // Max. 255 sensors 140 | if (i == 1) sensor_data[day_record_cnt][sensor_reading].value1 = server.arg(i).toFloat(); 141 | if (i == 2) sensor_data[day_record_cnt][sensor_reading].value2 = server.arg(i).toFloat(); 142 | if (i == 3) sensor_data[day_record_cnt][sensor_reading].value3 = server.arg(i).toFloat(); 143 | if (i == 4) sensor_data[day_record_cnt][sensor_reading].value4 = server.arg(i).toFloat(); 144 | if (i == 5) sensor_data[day_record_cnt][sensor_reading].sensortype = server.arg(i); 145 | } else Serial.println("Sensor number rejected"); 146 | } 147 | #define resolution 1 // 1 = 20.2 or 2 = 20.23 or 3 = 20.234 displayed data if the sensor supports it 148 | append_page_header(); 149 | webpage += "
"; 150 | if (!ReceivedAnySensor()) { 151 | webpage += "

*** Waiting For Sensors ***

"; 152 | Serial.println("No sensors received"); 153 | } 154 | else 155 | { 156 | webpage += "
"; // Add horizontal scrolling if number of fields exceeds page width 157 | webpage += ""; // Top-left corner is blank 158 | for (int s = 0; s < number_of_sensors; s++) { 159 | if (sensor_data[day_record_cnt][s+1].sensornumber != 0 && sensor_data[1][s+1].sensornumber <= number_of_sensors) 160 | webpage += ""; 161 | } 162 | webpage += ""; 163 | for (int s = 1; s <= number_of_sensors; s++) { 164 | if (sensor_data[day_record_cnt][s].sensornumber != 0 && sensor_data[1][s].sensornumber <= number_of_sensors) 165 | webpage += ""; 166 | } 167 | webpage += ""; 168 | for (int s = 1; s <= number_of_sensors; s++) { 169 | if (sensor_data[day_record_cnt][s].sensornumber != 0 && sensor_data[1][s].sensornumber <= number_of_sensors) 170 | webpage += ""; 171 | } 172 | webpage += ""; 173 | webpage += ""; 174 | for (int s = 1; s <= number_of_sensors; s++) { 175 | if (sensor_data[day_record_cnt][s].sensornumber != 0 && sensor_data[1][s].sensornumber <= number_of_sensors) 176 | webpage += ""; 177 | } 178 | webpage += ""; 179 | webpage += ""; 180 | for (int s = 1; s <= number_of_sensors; s++) { 181 | if (sensor_data[day_record_cnt][s].sensornumber != 0 && sensor_data[1][s].sensornumber <= number_of_sensors) 182 | webpage += ""; 183 | } 184 | webpage += ""; 185 | webpage += ""; 186 | for (int s = 1; s <= number_of_sensors; s++) { 187 | if (sensor_data[day_record_cnt][s].sensornumber != 0 && sensor_data[1][s].sensornumber <= number_of_sensors) 188 | webpage += ""; 189 | } 190 | webpage += ""; 191 | webpage += ""; 192 | for (int s = 1; s <= number_of_sensors; s++) { 193 | if ((sensor_data[day_record_cnt][s].sensornumber != 0) && (sensor_data[1][s].sensornumber <= number_of_sensors)) 194 | { webpage += ""; } 195 | } 196 | webpage += ""; 197 | webpage += "
"+String(SensorName[s])+"
Sensor Number"+String(sensor_data[1][s].sensornumber)+"
"+String(RowName[0])+""+String(sensor_data[1][s].sensortype)+RowUnits[0]+"
"+String(RowName[1])+""+String(sensor_data[1][s].value1,resolution)+RowUnits[1]+"
"+String(RowName[2])+""+String(sensor_data[1][s].value2,resolution)+RowUnits[2]+"
"+String(RowName[3])+""+String(sensor_data[1][s].value3,resolution)+RowUnits[3]+"
"+String(RowName[4])+""+String(sensor_data[1][s].value4,resolution)+RowUnits[4]+"
"; 198 | } 199 | webpage += "

"; 200 | append_page_footer(); 201 | server.send(200, "text/html", webpage); //Send Response to the HTTP request 202 | } 203 | 204 | void auto_update () { // Auto-refresh of the screen, this turns it on/off 205 | AUpdate = !AUpdate; 206 | handleSensors(); 207 | } 208 | 209 | void append_page_header() { 210 | webpage = ""; 211 | if (AUpdate) webpage += ""; // 30-sec refresh time, test needed to prevent auto updates repeating some commands 212 | webpage += "Sensor Server

Sensor Server "+String(ServerVersion)+"

"; 215 | } 216 | 217 | void append_page_footer(){ // Saves repeating many lines of code for HTML page footers 218 | webpage += ""; 229 | webpage += ""; 233 | webpage += "

©"+String(char(byte(0x40>>1)))+String(char(byte(0x88>>1)))+String(char(byte(0x5c>>1)))+String(char(byte(0x98>>1)))+String(char(byte(0x5c>>1))); 234 | webpage += String(char((0x84>>1)))+String(char(byte(0xd2>>1)))+String(char(0xe4>>1))+String(char(0xc8>>1))+String(char(byte(0x40>>1))); 235 | webpage += String(char(byte(0x64/2)))+String(char(byte(0x60>>1)))+String(char(byte(0x62>>1)))+String(char(0x70>>1))+"

"; 236 | webpage += ""; 237 | } 238 | 239 | bool ReceivedAnySensor(){ 240 | bool sensor_received = false; 241 | for (int s = 1; s <= number_of_sensors; s++) { 242 | if ((sensor_data[day_record_cnt][s].sensornumber != 0) && (sensor_data[day_record_cnt][s].sensornumber <= number_of_sensors)) { sensor_received = true; } 243 | } 244 | return sensor_received; 245 | } 246 | 247 | -------------------------------------------------------------------------------- /ESP_Sensor_Server_Advanced_4.ino: -------------------------------------------------------------------------------- 1 | /* Version 1 2 | 3 | ESP32/ESP8266 Sensor Server 4 | 5 | This software, the ideas and concepts is Copyright (c) David Bird 2018. All rights to this software are reserved. 6 | 7 | Any redistribution or reproduction of any part or all of the contents in any form is prohibited other than the following: 8 | 1. You may print or download to a local hard disk extracts for your personal and non-commercial use only. 9 | 2. You may copy the content to individual third parties for their personal use, but only if you acknowledge the author David Bird as the source of the material. 10 | 3. You may not, except with my express written permission, distribute or commercially exploit the content. 11 | 4. You may not transmit it or store it in any other website or other form of electronic retrieval system for commercial purposes. 12 | 13 | The above copyright ('as annotated') notice and this permission notice shall be included in all copies or substantial portions of the Software and where the 14 | software use is visible to an end-user. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT. FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY 17 | OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | See more at http://www.dsbird.org.uk 21 | 22 | */ 23 | #ifdef ESP8266 24 | #include // Built-in 25 | #include // Built-in 26 | #include // Built-in 27 | #include 28 | #include // struct timeval 29 | #else 30 | #include // Built-in 31 | #include // Built-in 32 | #include // https://github.com/Pedroalbuquerque/ESP32WebServer download and place in your Libraries folder 33 | #include 34 | #include "FS.h" 35 | #endif 36 | #include "time.h" 37 | #include "Network.h" 38 | #include "Sys_Variables.h" 39 | #include "CSS.h" 40 | #include // Built-in 41 | #include // Built-in 42 | 43 | #ifdef ESP8266 44 | ESP8266WiFiMulti wifiMulti; 45 | ESP8266WebServer server(80); 46 | #else 47 | WiFiMulti wifiMulti; 48 | WebServer server(80); 49 | #endif 50 | 51 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 52 | void setup(void) { 53 | Serial.begin(115200); 54 | //WiFi.config(ip, gateway, subnet, dns1, dns2); 55 | if (!WiFi.config(local_IP, gateway, subnet, dns)) { 56 | Serial.println("WiFi STATION Failed to configure Correctly"); 57 | } 58 | wifiMulti.addAP(ssid_1, password_1); // add Wi-Fi networks you want to connect to, it connects from strongest to weakest signal strength 59 | wifiMulti.addAP(ssid_2, password_2); 60 | wifiMulti.addAP(ssid_3, password_3); 61 | wifiMulti.addAP(ssid_4, password_4); 62 | 63 | Serial.println("Connecting ..."); 64 | while (wifiMulti.run() != WL_CONNECTED) { // Wait for the Wi-Fi to connect: scan for Wi-Fi networks, and connect to the strongest of the networks above 65 | delay(250); Serial.print('.'); 66 | } 67 | Serial.println("\nConnected to " + WiFi.SSID() + " Use IP address: " + WiFi.localIP().toString()); // Report which SSID and IP is in use 68 | // The logical name http://fileserver.local will also access the device if you have 'Bonjour' running or your system supports multicast dns 69 | if (!MDNS.begin(servername)) { // Set your preferred server name, if you use "myserver" the address would be http://myserver.local/ 70 | Serial.println(F("Error setting up MDNS responder!")); 71 | ESP.restart(); 72 | } 73 | #ifdef ESP32 74 | SPI.begin(18, 19, 23); // (SCK,MOSI,MISO) SPI pins used by most ESP32 boards. 75 | // Note: SD_Card readers on the ESP32 will NOT work unless there is a pull-up on MISO, either do this or wire a resistor (1K to 4K7) to Vcc 76 | pinMode(19, INPUT_PULLUP); 77 | pinMode(23, INPUT_PULLUP); 78 | #endif 79 | Serial.print(F("Initializing SD card...")); 80 | if (!SD.begin(SD_CS_pin)) { // see if the card is present and can be initialised. Wemos D1 Mini SD-Card shields use D8 for CS 81 | Serial.println(F("Card failed or not present, no SD Card data logging possible...")); 82 | SD_present = false; 83 | } 84 | else 85 | { 86 | Serial.println(F("Card initialised... data logging enabled...")); 87 | SD_present = true; 88 | } 89 | // Note again: Using an ESP32 and SD Card readers requires a 1K to 4K7 pull-up to 3v3 on the MISO line, otherwise they do-not function. 90 | //---------------------------------------------------------------------- 91 | ///////////////////////////// Server Commands that will be responded to 92 | server.on("/", HomePage); 93 | server.on("/test", []() { 94 | server.send(200, "text/plain", "Server status is OK"); 95 | }); // Simple server test by providing a status response 96 | server.on("/sensor", HandleSensors); // Now associate the handler functions to the path of each function 97 | server.on("/Liveview", DisplaySensors); 98 | server.on("/Iconview", DisplayLocations); 99 | server.on("/Csetup", ChannelSetup); 100 | server.on("/AUpdate", Auto_Update); 101 | server.on("/Help", Help); 102 | server.on("/Cstream", Channel_File_Stream); 103 | server.on("/Cdownload", Channel_File_Download); 104 | server.on("/Odownload", File_Download); 105 | server.on("/Cupload", Channel_File_Upload); 106 | server.on("/Cerase", Channel_File_Erase); 107 | server.on("/Oerase", File_Erase); 108 | server.on("/upload", HTTP_POST, []() { 109 | server.send(200); 110 | }, handleFileUpload); 111 | server.on("/SDdir", SD_dir); 112 | server.on("/chart", DrawChart); 113 | server.on("/forward", MoveChartForward); 114 | server.on("/reverse", MoveChartBack); 115 | server.onNotFound(handleNotFound); // When a client requests an unknown URI for example something other than "/") 116 | ///////////////////////////// End of Request commands 117 | server.begin(); 118 | Serial.println("HTTP server started"); 119 | for (int i = 0; i < number_of_channels; i++) { 120 | ChannelData[i].ID = i; 121 | } 122 | SetupTime(); 123 | ReadChannelData(); 124 | } 125 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 126 | void loop(void) { 127 | server.handleClient(); // Listen for client connections 128 | if (data_amended) { 129 | SaveChannelData(); 130 | data_amended = false; 131 | } 132 | } 133 | // Functions from here... 134 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 135 | void HomePage() { 136 | SendHTML_Header(refresh_off); 137 | webpage += F(""); 138 | webpage += F(""); 139 | webpage += F(""); 140 | webpage += F(""); 141 | webpage += F("


"); 142 | webpage += F(""); 143 | webpage += F(""); 144 | webpage += F(""); 145 | webpage += F(""); 146 | webpage += F("


"); 147 | append_page_footer(graph_off); 148 | SendHTML_Content(); 149 | SendHTML_Stop(); 150 | webpage = ""; 151 | } 152 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 153 | void Help() { 154 | SendHTML_Header(refresh_off); 155 | webpage += F("

Sensor Server Help

"); 156 | webpage += F("

Help section: The Server collects sensor readings and stores them in a Channel and then displayed according to the Channel settings.\ 157 | Data fields are numeric and can be interpreted as the units of your choice. All data from sensors is saved sequentially to the SD-Card and there is a file \ 158 | for each Channel.

"); 159 | webpage += F("

For example, Channel-1 data is saved in a file called '1.txt' and Channel-2 in '2.txt' and so-on. The contents of a Channel file can be streamed \ 160 | to the web-browser, graphed, downloaded, deleted or amended and then uploaded back again.


"); 161 | webpage += F(""); 162 | webpage += F(""); 163 | webpage += F(""); 164 | webpage += F(""); 165 | webpage += F(""); 166 | webpage += F(""); 167 | webpage += F(""); 168 | webpage += F(""); 169 | webpage += F(""); 170 | webpage += F(""); 171 | webpage += F(""); 172 | webpage += F(""); 173 | webpage += F(""); 174 | webpage += F(""); 175 | webpage += F(""); 176 | webpage += F(""); 177 | webpage += F("
Use the menu options to:
'Refresh'Toggle on/off automatic screen refresh of 30-secs
'View Channels'Shows sensor data for each channel as it is received
'View Locations'Shows an Icon and data for each channel as it is received
'Graph Channels'Graph Channel Readings
'Setup Channels'Edit the Channel Name, Description, Sensor Type and Units
'Stream Channel Data'Stream Channel Data to your browser
'Download Channel Data'Download Channel Data to a file
'[Download] File'Download any file
After download into Excel use the formula '=(((DataCell/60)/60)/24)+DATE(1970,1,1)' to convert UNIX time to a Date-Time
e.g If A1 = 1517059610 (unix time) and A2 is empty use A2 = (((A1/60)/60)/24)+DATE(1970,1,1)' now A2 = HH:MM;SS-DD/MM/YY
Set the format of cell A2 to Custom, then dd/mm/yyyy hh:mm
'Upload Channel Data'Upload Channel Data file
'Erase Channel Data'Erase Channel Data file
'[Erase] File'Erase any file
'File Directory'List files on the SD-Card

"); 178 | SendHTML_Content(); 179 | append_page_footer(graph_off); 180 | SendHTML_Content(); 181 | SendHTML_Stop(); 182 | webpage = ""; 183 | } 184 | //~~~~~~~~~~~~~~la~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 185 | void ChannelSetup() { 186 | if (server.args() > 0 ) { // Arguments were received 187 | data_amended = true; 188 | for (byte ArgNum = 0; ArgNum <= server.args(); ArgNum++) { 189 | if (server.argName(ArgNum) == "chan_name") { 190 | ChannelData[channel_number].Name = server.arg(ArgNum); 191 | } 192 | if (server.argName(ArgNum) == "chan_desc") { 193 | ChannelData[channel_number].Description = server.arg(ArgNum); 194 | } 195 | if (server.argName(ArgNum) == "chan_type") { 196 | ChannelData[channel_number].Type = server.arg(ArgNum); 197 | } 198 | if (server.argName(ArgNum) == "chan_field1") { 199 | ChannelData[channel_number].Field1 = server.arg(ArgNum); 200 | } 201 | if (server.argName(ArgNum) == "chan_field1_units") { 202 | ChannelData[channel_number].Field1_Units = server.arg(ArgNum); 203 | } 204 | if (server.argName(ArgNum) == "chan_field2") { 205 | ChannelData[channel_number].Field2 = server.arg(ArgNum); 206 | } 207 | if (server.argName(ArgNum) == "chan_field2_units") { 208 | ChannelData[channel_number].Field2_Units = server.arg(ArgNum); 209 | } 210 | if (server.argName(ArgNum) == "chan_field3") { 211 | ChannelData[channel_number].Field3 = server.arg(ArgNum); 212 | } 213 | if (server.argName(ArgNum) == "chan_field3_units") { 214 | ChannelData[channel_number].Field3_Units = server.arg(ArgNum); 215 | } 216 | if (server.argName(ArgNum) == "chan_field4") { 217 | ChannelData[channel_number].Field4 = server.arg(ArgNum); 218 | } 219 | if (server.argName(ArgNum) == "chan_field4_units") { 220 | ChannelData[channel_number].Field4_Units = server.arg(ArgNum); 221 | } 222 | if (server.argName(ArgNum) == "iconname") { 223 | ChannelData[channel_number].IconName = server.arg(ArgNum); 224 | } 225 | ChannelData[channel_number].Created = TimeNow(); 226 | } 227 | } 228 | if (server.hasArg("edit_c1")) { 229 | channel_number = 1; 230 | ChannelEditor(channel_number); 231 | } 232 | else if (server.hasArg("edit_c2")) { 233 | channel_number = 2; 234 | ChannelEditor(channel_number); 235 | } 236 | else if (server.hasArg("edit_c3")) { 237 | channel_number = 3; 238 | ChannelEditor(channel_number); 239 | } 240 | else if (server.hasArg("edit_c4")) { 241 | channel_number = 4; 242 | ChannelEditor(channel_number); 243 | } 244 | else if (server.hasArg("edit_c5")) { 245 | channel_number = 5; 246 | ChannelEditor(channel_number); 247 | } 248 | else if (server.hasArg("edit_c6")) { 249 | channel_number = 6; 250 | ChannelEditor(channel_number); 251 | } 252 | else if (server.hasArg("edit_c7")) { 253 | channel_number = 7; 254 | ChannelEditor(channel_number); 255 | } 256 | else if (server.hasArg("edit_c8")) { 257 | channel_number = 8; 258 | ChannelEditor(channel_number); 259 | } 260 | else if (server.hasArg("edit_c9")) { 261 | channel_number = 9; 262 | ChannelEditor(channel_number); 263 | } 264 | else if (server.hasArg("edit_c10")) { 265 | channel_number = 10; 266 | ChannelEditor(channel_number); 267 | } 268 | else if (server.hasArg("edit_c11")) { 269 | channel_number = 11; 270 | ChannelEditor(channel_number); 271 | } 272 | else if (server.hasArg("edit_c12")) { 273 | channel_number = 12; // NOTE: *** Add more channels here to match the 'number_of_channels' value 274 | ChannelEditor(channel_number); 275 | } 276 | else 277 | { 278 | SendHTML_Header(refresh_off); 279 | webpage += F("

Channel Setup/Editor


"); 280 | webpage += F(""); 281 | webpage += F(""); 282 | webpage += F(""); 283 | for (byte cn = 1; cn < number_of_channels; cn++) { 284 | webpage += F(""; 286 | webpage += ""; 287 | if (ChannelData[cn].Updated == BaseTime) webpage += F(""); 288 | else 289 | { 290 | webpage += ""; 292 | } 293 | } 294 | webpage += F("
Channel IDNameDescriptionCreated on:Last Updated at:File Size
"); 285 | webpage += String(ChannelData[cn].ID) + "" + ChannelData[cn].Name + "" + ChannelData[cn].Description + "" + Time(ChannelData[cn].Created).substring(9) + "-
" + Time(ChannelData[cn].Updated) + ""; 291 | webpage += String(file_size(String(cn))) + "
"); 295 | webpage += F("

Select channel to View/Edit

"); 296 | webpage += F("
"); 297 | for (byte in_select = 1; in_select < number_of_channels; in_select++) { 298 | webpage += F(""; 299 | } 300 | webpage += F("

"); 301 | SendHTML_Content(); 302 | append_page_footer(graph_off); 303 | SendHTML_Content(); 304 | SendHTML_Stop(); 305 | webpage = ""; 306 | } 307 | } 308 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 309 | void ChannelEditor(int channel_number) { 310 | SendHTML_Header(refresh_off); 311 | webpage += F("

Channel Editor

"); 312 | webpage += F(""); 313 | webpage += F(""; 315 | //--- 316 | webpage += F(""; 317 | webpage += F(""; 319 | //--- 320 | webpage += F(""; 321 | webpage += F(""; 322 | //--- 323 | webpage += F(""; 324 | webpage += F(""; 325 | //--- 326 | webpage += F(""; 327 | webpage += F(""; 328 | webpage += F(""; 329 | SendHTML_Content(); 330 | //--- 331 | webpage += F(""; 332 | webpage += F(""; 333 | webpage += F(""; 334 | //--- 335 | webpage += F(""; 336 | webpage += F(""; 337 | webpage += F(""; 338 | //--- 339 | webpage += F(""; 340 | webpage += F(""; 341 | webpage += F(""; 342 | //--- 343 | webpage += F(""; 344 | webpage += F(""; 345 | //--- 346 | webpage += F("
ID:"); 314 | webpage += String(ChannelData[channel_number].ID) + "Edit EntriesEdit Units
Name:"); webpage += ChannelData[channel_number].Name + "
Description:"); webpage += ChannelData[channel_number].Description + "
Sensor Type:"); webpage += ChannelData[channel_number].Type + "
Field-1 Name:"); webpage += ChannelData[channel_number].Field1 + "
Field-2 Name:"); webpage += ChannelData[channel_number].Field2 + "
Field-3 Name:"); webpage += ChannelData[channel_number].Field3 + "
Field-4 Name:"); webpage += ChannelData[channel_number].Field4 + "
Icon Name:"); webpage += ChannelData[channel_number].IconName + "
"); 347 | webpage += F("

"); 348 | webpage += F("[Back]

"); 349 | append_page_footer(graph_off); 350 | SendHTML_Content(); 351 | SendHTML_Stop(); 352 | webpage = ""; 353 | if (data_amended) SaveChannelData(); 354 | } 355 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 356 | boolean isValidNumber(String str) { 357 | str.trim(); 358 | if (!(str.charAt(0) == '+' || str.charAt(0) == '-' || isDigit(str.charAt(0)))) return false; // Failed if not starting with a unary +- sign or a number 359 | for (byte i = 1; i < str.length(); i++) { 360 | if (!(isDigit(str.charAt(i)) || str.charAt(i) == '.')) return false; // Anything other than a number or . is a failure 361 | } 362 | return true; 363 | } 364 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 365 | void handleNotFound() { 366 | if (SD_present && loadFromSdCard(server.uri())) return; // Process a file if requested 367 | ReportCommandNotFound(""); // go back to home page if erroneous command entered 368 | } 369 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 370 | // This function handles many arguments e.g. http://sensorserver.local/sensor?Sensor=1&Temperature=11.1&Humidity=22.2&Pressure=1000.0mb&Value=5.0&sensortype=BME280 371 | // e.g. http://sensorserver.local/sensor?Sensor=2&Temperature=22.2&Humidity=44.4&Pressure=1002.2mb 372 | // e.g. http://sensorserver.local/sensor?Sensor=3&Temperature=33.3&Humidity=66.6&Pressure=1003.3mb 373 | // Update the links above to your own name e.g. sensorserver becomes 'myserver.local' and click on the links to test the server! 374 | void HandleSensors() { //Server request handler 375 | Serial.println("Handle sensor reception"); 376 | if (server.argName(0) != "") Serial.print("Received from: "); 377 | for (int i = 0; i <= server.args(); i++) Serial.println(server.argName(i) + " " + server.arg(i)); // Display received arguments from sensor 378 | int sensor_num = server.arg(0).toInt(); 379 | if (sensor_num > number_of_channels) sensor_num = 0; // Channel-0 is a null channel, assigned when incorrect data is received 380 | if ((sensor_num >= 1 && sensor_num < number_of_channels) && ((server.argName(0) == "Sensor") || (server.argName(0) == "sensor"))) { 381 | ChannelData[sensor_num].Updated = TimeNow(); // A valid sensor has been received, so update time-received record 382 | sensor_reading = sensor_num; // Each sensor has its own record, so Sensor-1 readings go in record-1, etc 383 | for (int i = 0; i <= server.args(); i++) { 384 | if (sensor_num != 0 && sensor_num <= number_of_channels) { // If a valid sensor number then process 385 | SensorData[sensor_reading].sensornumber = (byte)sensor_num; // Max. 255 sensors 386 | if (i == 1) SensorData[sensor_reading].value1 = server.arg(i).toFloat(); 387 | if (i == 2) SensorData[sensor_reading].value2 = server.arg(i).toFloat(); 388 | if (i == 3) SensorData[sensor_reading].value3 = server.arg(i).toFloat(); 389 | if (i == 4) SensorData[sensor_reading].value4 = server.arg(i).toFloat(); 390 | if (i == 5) SensorData[sensor_reading].sensortype = server.arg(i); 391 | } 392 | else Serial.println("Sensor number rejected"); 393 | } 394 | SensorData[sensor_reading].readingtime = TimeNow(); 395 | if (SD_present) { // If the SD-Card is present and board fitted then append the next reading to the log file called 'datalog.txt' 396 | File dataFile = SD.open("/" + String(sensor_num) + ".txt", FILE_READ); // Check to see if a file exists 397 | if (!dataFile) { // If not update its creation date 398 | ChannelData[sensor_num].Created = TimeNow(); 399 | } 400 | dataFile.close(); 401 | #ifdef ESP8266 402 | dataFile = SD.open("/" + String(sensor_num) + ".txt", FILE_WRITE); //On the ESP8266 FILE_WRITE opens file for writing and move to the end of the file 403 | #else 404 | dataFile = SD.open("/" + String(sensor_num) + ".txt", FILE_APPEND); //On the ESP32 it needs FILE_APPEND to open file for writing and movs to the end of the file 405 | #endif 406 | if (dataFile) { // if the file is available, write to it 407 | dataFile.print(SensorData[sensor_reading].readingtime); dataFile.print(char(9)); // TAB delimited data 408 | dataFile.print(SensorData[sensor_reading].value1); dataFile.print(char(9)); 409 | dataFile.print(SensorData[sensor_reading].value2); dataFile.print(char(9)); 410 | dataFile.print(SensorData[sensor_reading].value3); dataFile.print(char(9)); 411 | dataFile.print(SensorData[sensor_reading].value4); dataFile.print(char(9)); 412 | dataFile.println(SensorData[sensor_reading].sensortype); 413 | } 414 | dataFile.close(); 415 | } 416 | } 417 | DisplaySensors(); 418 | } 419 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 420 | void DisplaySensors() { 421 | byte resolution = 1; // 1 = 20.2 or 2 = 20.23 or 3 = 20.234 displayed data if the sensor supports the resolution 422 | SendHTML_Header(refresh_on); 423 | webpage = F("
"); 424 | if (!ReceivedAnySensor()) { 425 | webpage += F("

*** Waiting For Data Reception ***

"); 426 | Serial.println("No sensors received"); 427 | } 428 | else 429 | { 430 | webpage += F("

Current Sensor Readings

"); 431 | webpage += F("
"); // Add horizontal scrolling if number of fields exceeds page width 432 | webpage += F(""); 433 | // NOTE: ****** You might think there is a more efficient way to do the next section, there is, except using a FOR loop, but the ESP32 gives stack errors, it's a compiler error!! 434 | if (SensorData[0].sensornumber != 0 && SensorData[0].sensornumber <= number_of_channels) webpage += ""; 435 | if (SensorData[1].sensornumber != 0 && SensorData[1].sensornumber <= number_of_channels) webpage += ""; 436 | if (SensorData[2].sensornumber != 0 && SensorData[2].sensornumber <= number_of_channels) webpage += ""; 437 | if (SensorData[3].sensornumber != 0 && SensorData[3].sensornumber <= number_of_channels) webpage += ""; 438 | if (SensorData[4].sensornumber != 0 && SensorData[4].sensornumber <= number_of_channels) webpage += ""; 439 | if (SensorData[5].sensornumber != 0 && SensorData[5].sensornumber <= number_of_channels) webpage += ""; 440 | if (SensorData[6].sensornumber != 0 && SensorData[6].sensornumber <= number_of_channels) webpage += ""; 441 | // if (SensorData[7].sensornumber != 0 && SensorData[7].sensornumber <= number_of_channels) webpage += ""; 442 | // if (SensorData[8].sensornumber != 0 && SensorData[8].sensornumber <= number_of_channels) webpage += ""; 443 | // if (SensorData[9].sensornumber != 0 && SensorData[9].sensornumber <= number_of_channels) webpage += ""; 444 | // if (SensorData[10].sensornumber != 0 && SensorData[10].sensornumber <= number_of_channels) webpage += ""; 445 | // if (SensorData[11].sensornumber != 0 && SensorData[11].sensornumber <= number_of_channels) webpage += ""; 446 | // if (SensorData[12].sensornumber != 0 && SensorData[12].sensornumber <= number_of_channels) webpage += ""; 447 | webpage += F(""); 448 | if ((SensorData[0].sensornumber != 0) && (SensorData[0].sensornumber <= number_of_channels)) webpage += ""; 449 | if ((SensorData[1].sensornumber != 0) && (SensorData[1].sensornumber <= number_of_channels)) webpage += ""; 450 | if ((SensorData[2].sensornumber != 0) && (SensorData[2].sensornumber <= number_of_channels)) webpage += ""; 451 | if ((SensorData[3].sensornumber != 0) && (SensorData[3].sensornumber <= number_of_channels)) webpage += ""; 452 | if ((SensorData[4].sensornumber != 0) && (SensorData[4].sensornumber <= number_of_channels)) webpage += ""; 453 | if ((SensorData[5].sensornumber != 0) && (SensorData[5].sensornumber <= number_of_channels)) webpage += ""; 454 | if ((SensorData[6].sensornumber != 0) && (SensorData[6].sensornumber <= number_of_channels)) webpage += ""; 455 | // if ((SensorData[7].sensornumber != 0) && (SensorData[7].sensornumber <= number_of_channels)) webpage += ""; 456 | // if ((SensorData[8].sensornumber != 0) && (SensorData[8].sensornumber <= number_of_channels)) webpage += ""; 457 | // if ((SensorData[9].sensornumber != 0) && (SensorData[9].sensornumber <= number_of_channels)) webpage += ""; 458 | // if ((SensorData[10].sensornumber != 0) && (SensorData[10].sensornumber <= number_of_channels)) webpage += ""; 459 | // if ((SensorData[11].sensornumber != 0) && (SensorData[11].sensornumber <= number_of_channels)) webpage += ""; 460 | // if ((SensorData[12].sensornumber != 0) && (SensorData[12].sensornumber <= number_of_channels)) webpage += ""; 461 | webpage += F(""); 462 | if (SensorData[0].sensornumber != 0 && SensorData[0].sensornumber <= number_of_channels) webpage += ""; 463 | if (SensorData[1].sensornumber != 0 && SensorData[1].sensornumber <= number_of_channels) webpage += ""; 464 | if (SensorData[2].sensornumber != 0 && SensorData[2].sensornumber <= number_of_channels) webpage += ""; 465 | if (SensorData[3].sensornumber != 0 && SensorData[3].sensornumber <= number_of_channels) webpage += ""; 466 | if (SensorData[4].sensornumber != 0 && SensorData[4].sensornumber <= number_of_channels) webpage += ""; 467 | if (SensorData[5].sensornumber != 0 && SensorData[5].sensornumber <= number_of_channels) webpage += ""; 468 | if (SensorData[6].sensornumber != 0 && SensorData[6].sensornumber <= number_of_channels) webpage += ""; 469 | // if (SensorData[7].sensornumber != 0 && SensorData[7].sensornumber <= number_of_channels) webpage += ""; 470 | // if (SensorData[8].sensornumber != 0 && SensorData[8].sensornumber <= number_of_channels) webpage += ""; 471 | // if (SensorData[9].sensornumber != 0 && SensorData[9].sensornumber <= number_of_channels) webpage += ""; 472 | // if (SensorData[10].sensornumber != 0 && SensorData[10].sensornumber <= number_of_channels) webpage += ""; 473 | // if (SensorData[11].sensornumber != 0 && SensorData[11].sensornumber <= number_of_channels) webpage += ""; 474 | // if (SensorData[12].sensornumber != 0 && SensorData[12].sensornumber <= number_of_channels) webpage += ""; 475 | webpage += F(""); 476 | if (SensorData[0].sensornumber != 0 && SensorData[0].sensornumber <= number_of_channels) webpage += ""; 477 | if (SensorData[1].sensornumber != 0 && SensorData[1].sensornumber <= number_of_channels) webpage += ""; 478 | if (SensorData[2].sensornumber != 0 && SensorData[2].sensornumber <= number_of_channels) webpage += ""; 479 | if (SensorData[3].sensornumber != 0 && SensorData[3].sensornumber <= number_of_channels) webpage += ""; 480 | if (SensorData[4].sensornumber != 0 && SensorData[4].sensornumber <= number_of_channels) webpage += ""; 481 | if (SensorData[5].sensornumber != 0 && SensorData[5].sensornumber <= number_of_channels) webpage += ""; 482 | if (SensorData[6].sensornumber != 0 && SensorData[6].sensornumber <= number_of_channels) webpage += ""; 483 | // if (SensorData[7].sensornumber != 0 && SensorData[7].sensornumber <= number_of_channels) webpage += ""; 484 | // if (SensorData[8].sensornumber != 0 && SensorData[8].sensornumber <= number_of_channels) webpage += ""; 485 | // if (SensorData[9].sensornumber != 0 && SensorData[9].sensornumber <= number_of_channels) webpage += ""; 486 | // if (SensorData[10].sensornumber != 0 && SensorData[10].sensornumber <= number_of_channels) webpage += ""; 487 | // if (SensorData[11].sensornumber != 0 && SensorData[11].sensornumber <= number_of_channels) webpage += ""; 488 | // if (SensorData[12].sensornumber != 0 && SensorData[12].sensornumber <= number_of_channels) webpage += ""; 489 | SendHTML_Content(); 490 | webpage += F(""); 491 | if (SensorData[0].sensornumber != 0 && SensorData[0].sensornumber <= number_of_channels) webpage += ""; 492 | if (SensorData[1].sensornumber != 0 && SensorData[1].sensornumber <= number_of_channels) webpage += ""; 493 | if (SensorData[2].sensornumber != 0 && SensorData[2].sensornumber <= number_of_channels) webpage += ""; 494 | if (SensorData[3].sensornumber != 0 && SensorData[3].sensornumber <= number_of_channels) webpage += ""; 495 | if (SensorData[4].sensornumber != 0 && SensorData[4].sensornumber <= number_of_channels) webpage += ""; 496 | if (SensorData[5].sensornumber != 0 && SensorData[5].sensornumber <= number_of_channels) webpage += ""; 497 | if (SensorData[6].sensornumber != 0 && SensorData[6].sensornumber <= number_of_channels) webpage += ""; 498 | // if (SensorData[7].sensornumber != 0 && SensorData[7].sensornumber <= number_of_channels) webpage += ""; 499 | // if (SensorData[8].sensornumber != 0 && SensorData[8].sensornumber <= number_of_channels) webpage += ""; 500 | // if (SensorData[9].sensornumber != 0 && SensorData[9].sensornumber <= number_of_channels) webpage += ""; 501 | // if (SensorData[10].sensornumber != 0 && SensorData[10].sensornumber <= number_of_channels) webpage += ""; 502 | // if (SensorData[11].sensornumber != 0 && SensorData[11].sensornumber <= number_of_channels) webpage += ""; 503 | // if (SensorData[12].sensornumber != 0 && SensorData[12].sensornumber <= number_of_channels) webpage += ""; 504 | webpage += F(""); 505 | if (SensorData[0].sensornumber != 0 && SensorData[0].sensornumber <= number_of_channels) webpage += ""; 506 | if (SensorData[1].sensornumber != 0 && SensorData[1].sensornumber <= number_of_channels) webpage += ""; 507 | if (SensorData[2].sensornumber != 0 && SensorData[2].sensornumber <= number_of_channels) webpage += ""; 508 | if (SensorData[3].sensornumber != 0 && SensorData[3].sensornumber <= number_of_channels) webpage += ""; 509 | if (SensorData[4].sensornumber != 0 && SensorData[4].sensornumber <= number_of_channels) webpage += ""; 510 | if (SensorData[5].sensornumber != 0 && SensorData[5].sensornumber <= number_of_channels) webpage += ""; 511 | if (SensorData[6].sensornumber != 0 && SensorData[6].sensornumber <= number_of_channels) webpage += ""; 512 | // if (SensorData[7].sensornumber != 0 && SensorData[7].sensornumber <= number_of_channels) webpage += ""; 513 | // if (SensorData[8].sensornumber != 0 && SensorData[8].sensornumber <= number_of_channels) webpage += ""; 514 | // if (SensorData[9].sensornumber != 0 && SensorData[9].sensornumber <= number_of_channels) webpage += ""; 515 | // if (SensorData[10].sensornumber != 0 && SensorData[10].sensornumber <= number_of_channels) webpage += ""; 516 | // if (SensorData[11].sensornumber != 0 && SensorData[11].sensornumber <= number_of_channels) webpage += ""; 517 | // if (SensorData[12].sensornumber != 0 && SensorData[12].sensornumber <= number_of_channels) webpage += ""; 518 | webpage += F(""); 519 | if ((SensorData[0].sensornumber != 0) && (SensorData[0].sensornumber <= number_of_channels)) webpage += ""; 520 | if ((SensorData[1].sensornumber != 0) && (SensorData[1].sensornumber <= number_of_channels)) webpage += ""; 521 | if ((SensorData[2].sensornumber != 0) && (SensorData[2].sensornumber <= number_of_channels)) webpage += ""; 522 | if ((SensorData[3].sensornumber != 0) && (SensorData[3].sensornumber <= number_of_channels)) webpage += ""; 523 | if ((SensorData[4].sensornumber != 0) && (SensorData[4].sensornumber <= number_of_channels)) webpage += ""; 524 | if ((SensorData[5].sensornumber != 0) && (SensorData[5].sensornumber <= number_of_channels)) webpage += ""; 525 | if ((SensorData[6].sensornumber != 0) && (SensorData[6].sensornumber <= number_of_channels)) webpage += ""; 526 | // if ((SensorData[7].sensornumber != 0) && (SensorData[7].sensornumber <= number_of_channels)) webpage += ""; 527 | // if ((SensorData[8].sensornumber != 0) && (SensorData[8].sensornumber <= number_of_channels)) webpage += ""; 528 | // if ((SensorData[9].sensornumber != 0) && (SensorData[9].sensornumber <= number_of_channels)) webpage += ""; 529 | // if ((SensorData[10].sensornumber != 0) && (SensorData[10].sensornumber <= number_of_channels)) webpage += ""; 530 | // if ((SensorData[11].sensornumber != 0) && (SensorData[11].sensornumber <= number_of_channels)) webpage += ""; 531 | // if ((SensorData[12].sensornumber != 0) && (SensorData[12].sensornumber <= number_of_channels)) webpage += ""; 532 | webpage += F(""); 533 | if ((SensorData[0].sensornumber != 0) && (SensorData[0].sensornumber <= number_of_channels)) webpage += ""; 534 | if ((SensorData[1].sensornumber != 0) && (SensorData[1].sensornumber <= number_of_channels)) webpage += ""; 535 | if ((SensorData[2].sensornumber != 0) && (SensorData[2].sensornumber <= number_of_channels)) webpage += ""; 536 | if ((SensorData[3].sensornumber != 0) && (SensorData[3].sensornumber <= number_of_channels)) webpage += ""; 537 | if ((SensorData[4].sensornumber != 0) && (SensorData[4].sensornumber <= number_of_channels)) webpage += ""; 538 | if ((SensorData[5].sensornumber != 0) && (SensorData[5].sensornumber <= number_of_channels)) webpage += ""; 539 | if ((SensorData[6].sensornumber != 0) && (SensorData[6].sensornumber <= number_of_channels)) webpage += ""; 540 | // if ((SensorData[7].sensornumber != 0) && (SensorData[7].sensornumber <= number_of_channels)) webpage += ""; 541 | // if ((SensorData[8].sensornumber != 0) && (SensorData[8].sensornumber <= number_of_channels)) webpage += ""; 542 | // if ((SensorData[9].sensornumber != 0) && (SensorData[9].sensornumber <= number_of_channels)) webpage += ""; 543 | // if ((SensorData[10].sensornumber != 0) && (SensorData[10].sensornumber <= number_of_channels)) webpage += ""; 544 | // if ((SensorData[11].sensornumber != 0) && (SensorData[11].sensornumber <= number_of_channels)) webpage += ""; 545 | // if ((SensorData[12].sensornumber != 0) && (SensorData[12].sensornumber <= number_of_channels)) webpage += ""; 546 | webpage += F("
Sensor Name"+String(ChannelData[SensorData[0].sensornumber].Name)+""+String(ChannelData[SensorData[1].sensornumber].Name)+""+String(ChannelData[SensorData[2].sensornumber].Name)+""+String(ChannelData[SensorData[3].sensornumber].Name)+""+String(ChannelData[SensorData[4].sensornumber].Name)+""+String(ChannelData[SensorData[5].sensornumber].Name)+""+String(ChannelData[SensorData[6].sensornumber].Name)+""+String(ChannelData[SensorData[7].sensornumber].Name)+""+String(ChannelData[SensorData[8].sensornumber].Name)+""+String(ChannelData[SensorData[9].sensornumber].Name)+""+String(ChannelData[SensorData[10].sensornumber].Name)+""+String(ChannelData[SensorData[11].sensornumber].Name)+""+String(ChannelData[SensorData[12].sensornumber].Name)+"
Sensor Number" + String(SensorData[0].sensornumber) + "" + String(SensorData[1].sensornumber) + "" + String(SensorData[2].sensornumber) + "" + String(SensorData[3].sensornumber) + "" + String(SensorData[4].sensornumber) + "" + String(SensorData[5].sensornumber) + "" + String(SensorData[6].sensornumber) + "" + String(SensorData[7].sensornumber) + "" + String(SensorData[8].sensornumber) + "" + String(SensorData[9].sensornumber) + "" + String(SensorData[10].sensornumber) + "" + String(SensorData[11].sensornumber) + "" + String(SensorData[12].sensornumber) + "
Type" + ChannelData[0].Type + "" + ChannelData[1].Type + "" + ChannelData[2].Type + "" + ChannelData[3].Type + "" + ChannelData[4].Type + "" + ChannelData[5].Type + "" + ChannelData[6].Type + "" + ChannelData[7].Type + "" + ChannelData[8].Type + "" + ChannelData[9].Type + "" + ChannelData[10].Type + "" + ChannelData[11].Type + "" + ChannelData[12].Type + "
Field-1" + String(SensorData[0].value1, resolution) + ChannelData[0].Field1_Units + "" + String(SensorData[1].value1, resolution) + ChannelData[1].Field1_Units + "" + String(SensorData[2].value1, resolution) + ChannelData[2].Field1_Units + "" + String(SensorData[3].value1, resolution) + ChannelData[3].Field1_Units + "" + String(SensorData[4].value1, resolution) + ChannelData[4].Field1_Units + "" + String(SensorData[5].value1, resolution) + ChannelData[5].Field1_Units + "" + String(SensorData[6].value1, resolution) + ChannelData[6].Field1_Units + "" + String(SensorData[7].value1, resolution) + ChannelData[7].Field1_Units + "" + String(SensorData[8].value1, resolution) + ChannelData[8].Field1_Units + "" + String(SensorData[9].value1, resolution) + ChannelData[9].Field1_Units + "" + String(SensorData[10].value1, resolution) + ChannelData[10].Field1_Units + "" + String(SensorData[11].value1, resolution) + ChannelData[11].Field1_Units + "" + String(SensorData[12].value1, resolution) + ChannelData[12].Field1_Units + "
Field-2" + String(SensorData[0].value2, resolution) + ChannelData[0].Field2_Units + "" + String(SensorData[1].value2, resolution) + ChannelData[1].Field2_Units + "" + String(SensorData[2].value2, resolution) + ChannelData[2].Field2_Units + "" + String(SensorData[3].value2, resolution) + ChannelData[3].Field2_Units + "" + String(SensorData[4].value2, resolution) + ChannelData[4].Field2_Units + "" + String(SensorData[5].value2, resolution) + ChannelData[5].Field2_Units + "" + String(SensorData[6].value2, resolution) + ChannelData[6].Field2_Units + "" + String(SensorData[7].value2, resolution) + ChannelData[7].Field2_Units + "" + String(SensorData[8].value2, resolution) + ChannelData[8].Field2_Units + "" + String(SensorData[9].value2, resolution) + ChannelData[9].Field2_Units + "" + String(SensorData[10].value2, resolution) + ChannelData[10].Field2_Units + "" + String(SensorData[11].value2, resolution) + ChannelData[11].Field2_Units + "" + String(SensorData[12].value2, resolution) + ChannelData[12].Field2_Units + "
Field-3" + String(SensorData[0].value3, resolution) + ChannelData[0].Field3_Units + "" + String(SensorData[1].value3, resolution) + ChannelData[1].Field3_Units + "" + String(SensorData[2].value3, resolution) + ChannelData[2].Field3_Units + "" + String(SensorData[3].value3, resolution) + ChannelData[3].Field3_Units + "" + String(SensorData[4].value3, resolution) + ChannelData[4].Field3_Units + "" + String(SensorData[5].value3, resolution) + ChannelData[5].Field3_Units + "" + String(SensorData[6].value3, resolution) + ChannelData[6].Field3_Units + "" + String(SensorData[7].value3, resolution) + ChannelData[7].Field3_Units + "" + String(SensorData[8].value3, resolution) + ChannelData[8].Field3_Units + "" + String(SensorData[9].value3, resolution) + ChannelData[9].Field3_Units + "" + String(SensorData[10].value3, resolution) + ChannelData[10].Field3_Units + "" + String(SensorData[11].value3, resolution) + ChannelData[11].Field3_Units + "" + String(SensorData[12].value3, resolution) + ChannelData[12].Field3_Units + "
Field-4" + String(SensorData[0].value4, resolution) + ChannelData[0].Field4_Units + "" + String(SensorData[1].value4, resolution) + ChannelData[1].Field4_Units + "" + String(SensorData[2].value4, resolution) + ChannelData[2].Field4_Units + "" + String(SensorData[3].value4, resolution) + ChannelData[3].Field4_Units + "" + String(SensorData[4].value4, resolution) + ChannelData[4].Field4_Units + "" + String(SensorData[5].value4, resolution) + ChannelData[5].Field4_Units + "" + String(SensorData[6].value4, resolution) + ChannelData[6].Field4_Units + "" + String(SensorData[7].value4, resolution) + ChannelData[7].Field4_Units + "" + String(SensorData[8].value4, resolution) + ChannelData[8].Field4_Units + "" + String(SensorData[9].value4, resolution) + ChannelData[9].Field4_Units + "" + String(SensorData[10].value4, resolution) + ChannelData[10].Field4_Units + "" + String(SensorData[11].value4, resolution) + ChannelData[11].Field4_Units + "" + String(SensorData[12].value4, resolution) + ChannelData[12].Field4_Units + "
Updated" + Time(SensorData[0].readingtime).substring(0, 8) + "" + Time(SensorData[1].readingtime).substring(0, 8) + "" + Time(SensorData[2].readingtime).substring(0, 8) + "" + Time(SensorData[3].readingtime).substring(0, 8) + "" + Time(SensorData[4].readingtime).substring(0, 8) + "" + Time(SensorData[5].readingtime).substring(0, 8) + "" + Time(SensorData[6].readingtime).substring(0, 8) + "" + Time(SensorData[7].readingtime).substring(0, 8) + "" + Time(SensorData[8].readingtime).substring(0, 8) + "" + Time(SensorData[9].readingtime).substring(0, 8) + "" + Time(SensorData[10].readingtime).substring(0, 8) + "" + Time(SensorData[11].readingtime).substring(0, 8) + "" + Time(SensorData[12].readingtime).substring(0, 8) + "
"); 547 | } 548 | webpage += F("

"); 549 | SendHTML_Content(); 550 | append_page_footer(graph_off); 551 | SendHTML_Content(); 552 | SendHTML_Stop(); 553 | webpage = ""; 554 | } 555 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 556 | void DisplayLocations() { 557 | #define resolution 1 // 1 = 20.2 or 2 = 20.23 or 3 = 20.234 displayed data if the sensor supports the resolution 558 | SendHTML_Header(refresh_on); 559 | webpage += F("
"); 560 | if (!ReceivedAnySensor()) { 561 | webpage += F("

*** Waiting For Data Reception ***

"); 562 | } 563 | else 564 | { 565 | webpage += F("

Current Sensor Readings

"); 566 | webpage += F("
"); // Add horizontal scrolling if number of fields exceeds page width 567 | webpage += F(""); 568 | for (int s = 1; s <= number_of_channels; s++) { 569 | if (SensorData[s].sensornumber != 0 && SensorData[s].sensornumber <= number_of_channels) 570 | { 571 | webpage += F(""); 574 | } 575 | } 576 | webpage += F(""); 577 | for (int s = 1; s <= number_of_channels; s++) { 578 | if (SensorData[s].sensornumber != 0 && SensorData[s].sensornumber <= number_of_channels) 579 | { 580 | webpage += F(""); 582 | } 583 | } 584 | webpage += F(""); 585 | for (int s = 1; s <= number_of_channels; s++) { 586 | if (SensorData[s].sensornumber != 0 && SensorData[s].sensornumber <= number_of_channels) 587 | { 588 | if (ChannelData[s].Field1 != "") { 589 | webpage += ""; 590 | } 591 | } 592 | else webpage += F(""); 593 | } 594 | SendHTML_Content(); 595 | webpage += F(""); 596 | for (int s = 1; s <= number_of_channels; s++) { 597 | if (SensorData[s].sensornumber != 0 && SensorData[s].sensornumber <= number_of_channels) 598 | { 599 | if (ChannelData[s].Field2 != "") { 600 | webpage += ""; 601 | } 602 | } 603 | else webpage += F(""); 604 | } 605 | webpage += F(""); 606 | for (int s = 1; s <= number_of_channels; s++) { 607 | if (SensorData[s].sensornumber != 0 && SensorData[s].sensornumber <= number_of_channels) 608 | { 609 | if (ChannelData[s].Field3 != "") { 610 | webpage += ""; 611 | } 612 | } 613 | else webpage += F(""); 614 | } 615 | webpage += F(""); 616 | for (int s = 1; s <= number_of_channels; s++) { 617 | if (SensorData[s].sensornumber != 0 && SensorData[s].sensornumber <= number_of_channels) { 618 | if (ChannelData[s].Field4 != "") 619 | { 620 | webpage += ""; 621 | } 622 | } else webpage += F(""); 623 | } 624 | webpage += F(""); 625 | for (int s = 1; s <= number_of_channels; s++) { 626 | if ((SensorData[s].sensornumber != 0) && (SensorData[s].sensornumber <= number_of_channels)) 627 | webpage += ""; 628 | } 629 | webpage += F("
"); 572 | webpage += ChannelData[SensorData[s].sensornumber].Name; 573 | webpage += F("
" + String(SensorData[s].value1, resolution) + ChannelData[s].Field1_Units + "
" + String(SensorData[s].value2, resolution) + ChannelData[s].Field2_Units + "
" + String(SensorData[s].value3, resolution) + ChannelData[s].Field3_Units + "
" + String(SensorData[s].value4, resolution) + ChannelData[s].Field4_Units + "
" + Time(SensorData[s].readingtime).substring(0, 8) + "
"); 630 | } 631 | webpage += F("

"); 632 | append_page_footer(graph_off); 633 | SendHTML_Content(); 634 | SendHTML_Stop(); 635 | webpage = ""; 636 | } 637 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 638 | void Auto_Update () { // Auto-refresh of the screen, this turns it on/off 639 | AUpdate = !AUpdate; 640 | HomePage(); 641 | } 642 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 643 | bool ReceivedAnySensor() { 644 | bool sensor_received = false; 645 | for (int s = 1; s <= number_of_channels; s++) { 646 | if ((SensorData[s].sensornumber != 0) && (SensorData[s].sensornumber <= number_of_channels)) { 647 | sensor_received = true; 648 | } 649 | } 650 | return sensor_received; 651 | } 652 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 653 | void ReadChannelData() { 654 | if (SD_present) { // If the SD-Card is present and board fitted then append the next reading to the log file called 'datalog.txt' 655 | File dataFile = SD.open("/chandata.txt", FILE_READ); 656 | int cn = 1; 657 | if (dataFile) { // if the file is available, read it 658 | String in_record; 659 | while (dataFile.available() && cn < number_of_channels) { 660 | // Note the trim function is essential for graphing to work! Fields are padded out with spaces otherwise, I don't know why... 661 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].ID = in_record.toInt(); 662 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].Name = in_record; 663 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].Description = in_record; 664 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].Type = in_record; 665 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].Field1 = in_record; 666 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].Field1_Units = in_record; 667 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].Field2 = in_record; 668 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].Field2_Units = in_record; 669 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].Field3 = in_record; 670 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].Field3_Units = in_record; 671 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].Field4 = in_record; 672 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].Field4_Units = in_record; 673 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].IconName = in_record; 674 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].Created = in_record.toInt(); 675 | in_record = dataFile.readStringUntil(','); in_record.trim(); ChannelData[cn].Updated = in_record.toInt(); 676 | cn++; 677 | } 678 | } else ReportSDNotPresent(); 679 | dataFile.close(); 680 | } else ReportSDNotPresent(); 681 | } 682 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 683 | void SaveChannelData() { 684 | if (SD_present) { // If the SD-Card is present and board fitted then append the next reading to the log file called 'datalog.txt' 685 | if (!SD.remove("/chandata.txt")) Serial.println(F("Failed to delete Channel Setting files")); 686 | File dataFile = SD.open("/chandata.txt", FILE_WRITE); 687 | if (dataFile) { // Save all possible channel data 688 | for (int cn = 1; cn < number_of_channels; cn++) { 689 | dataFile.println(String(ChannelData[cn].ID) + ","); 690 | dataFile.println(ChannelData[cn].Name + ","); 691 | dataFile.println(ChannelData[cn].Description + ","); 692 | dataFile.println(ChannelData[cn].Type + ","); 693 | dataFile.println(ChannelData[cn].Field1 + ","); 694 | dataFile.println(ChannelData[cn].Field1_Units + ","); 695 | dataFile.println(ChannelData[cn].Field2 + ","); 696 | dataFile.println(ChannelData[cn].Field2_Units + ","); 697 | dataFile.println(ChannelData[cn].Field3 + ","); 698 | dataFile.println(ChannelData[cn].Field3_Units + ","); 699 | dataFile.println(ChannelData[cn].Field4 + ","); 700 | dataFile.println(ChannelData[cn].Field4_Units + ","); 701 | dataFile.println(ChannelData[cn].IconName + ","); 702 | dataFile.println(String(ChannelData[cn].Created) + ","); 703 | dataFile.println(String(ChannelData[cn].Updated) + ","); 704 | } 705 | } else ReportSDNotPresent(); 706 | dataFile.close(); 707 | } else ReportSDNotPresent(); 708 | } 709 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 710 | void Channel_File_Stream() { 711 | if (server.args() > 0 ) { // Arguments were received 712 | if (server.hasArg("stream")) { 713 | if (server.arg(0) == String(number_of_channels)) SD_file_stream("chandata"); 714 | } else SD_file_stream(server.arg(0)); 715 | } 716 | else SelectInput(refresh_off, "Channel File Stream", "Select a Channel to Stream", "Cstream", "stream", graph_on); 717 | } 718 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 719 | void SD_file_stream(String filename) { 720 | if (SD_present) { 721 | File dataFile = SD.open("/" + filename + ".txt", FILE_READ); // Now read data from SD Card 722 | if (dataFile) { 723 | if (dataFile.available()) { // If data is available and present 724 | String dataType = "application/octet-stream"; 725 | if (server.streamFile(dataFile, dataType) != dataFile.size()) { 726 | Serial.print(F("Sent less data than expected!")); 727 | } 728 | } 729 | dataFile.close(); // close the file: 730 | } else ReportFileNotPresent("Cstream"); 731 | } else ReportSDNotPresent(); 732 | } 733 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 734 | void Channel_File_Download() { 735 | if (server.args() > 0 ) { // Arguments were received 736 | if (server.hasArg("download")) { 737 | if (server.arg(0) == String(number_of_channels)) SD_file_download("chandata", !open_download); 738 | } else SD_file_download(server.arg(0), !open_download); 739 | } 740 | else SelectInput(refresh_off, "Channel File Download", "Select a Channel to Download", "Cdownload", "download", graph_on); 741 | } 742 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 743 | void File_Download() { 744 | if (server.args() > 0 ) { // Arguments were received 745 | Serial.println(server.arg(0)); 746 | if (server.hasArg("odownload")) SD_file_download(server.arg(0), open_download); 747 | } 748 | else OpenSelectInput("Enter a File Name to Download", "Odownload", "odownload"); 749 | } 750 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 751 | void SD_file_download(String filename, bool download_mode) { 752 | if (SD_present) { 753 | File download = SD.open("/" + filename + (download_mode ? "" : ".txt"), FILE_READ); // Now read data from SD Card 754 | if (download) { 755 | if (!download_mode) filename += ".txt"; 756 | server.sendHeader("Content-Type", "text/text"); 757 | server.sendHeader("Content-Disposition", "attachment; filename=" + filename); 758 | server.sendHeader("Connection", "close"); 759 | server.streamFile(download, "application/octet-stream"); 760 | download.close(); 761 | } else ReportFileNotPresent("Cdownload"); 762 | } else ReportSDNotPresent(); 763 | } 764 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 765 | void Channel_File_Upload() { 766 | append_page_header(refresh_off); 767 | webpage += F("

Select File to Upload

"); 768 | webpage += F("
"); 769 | webpage += F("
"); 770 | webpage += F("

"); 771 | webpage += F("[Back]

"); 772 | append_page_footer(graph_off); 773 | server.send(200, "text/html", webpage); 774 | } 775 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 776 | File UploadFile; 777 | void handleFileUpload() { // upload a new file to the Filing system 778 | HTTPUpload& uploadfile = server.upload(); // See https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WebServer/srcv 779 | // For further information on 'status' structure, there are other reasons such as a failed transfer that could be used 780 | if (uploadfile.status == UPLOAD_FILE_START) 781 | { 782 | String filename = uploadfile.filename; 783 | if (!filename.startsWith("/")) filename = "/" + filename; 784 | Serial.print("Upload File Name: "); Serial.println(filename); 785 | SD.remove(filename); // Remove a previous version, otherwise data is appended the file again 786 | UploadFile = SD.open(filename, FILE_WRITE); // Open the file for writing in SPIFFS (create it, if doesn't exist) 787 | filename = String(); 788 | } 789 | else if (uploadfile.status == UPLOAD_FILE_WRITE) 790 | { 791 | if (UploadFile) UploadFile.write(uploadfile.buf, uploadfile.currentSize); // Write the received bytes to the file 792 | } 793 | else if (uploadfile.status == UPLOAD_FILE_END) 794 | { 795 | if (UploadFile) // If the file was successfully created 796 | { 797 | UploadFile.close(); // Close the file again 798 | Serial.print("Upload Size: "); Serial.println(uploadfile.totalSize); 799 | webpage = ""; 800 | append_page_header(refresh_off); 801 | webpage += F("

File was successfully uploaded

"); 802 | webpage += F("

Uploaded File Name: "); webpage += uploadfile.filename + "

"; 803 | webpage += F("

File Size: "); webpage += file_size(uploadfile.totalSize) + "


"; 804 | append_page_footer(graph_off); 805 | server.send(200, "text/html", webpage); 806 | } 807 | else 808 | { 809 | ReportCouldNotCreateFile("Cupload"); 810 | } 811 | } 812 | } 813 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 814 | void Channel_File_Erase() { 815 | if (server.args() > 0 ) { // Arguments were received 816 | if (server.hasArg("erase")) { 817 | if (server.arg(0) == String(number_of_channels)) SD_file_erase("chandata", !open_erase); 818 | } else SD_file_erase(server.arg(0), !open_erase); 819 | ChannelDataReset(server.arg(0).toInt()); 820 | SaveChannelData(); 821 | } 822 | else SelectInput(refresh_off, "Channel File Erase", "Select a Channel to Erase", "Cerase", "erase", graph_off); 823 | } 824 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 825 | void File_Erase() { 826 | if (server.args() > 0 ) { // Arguments were received 827 | Serial.println(server.arg(0)); 828 | if (server.hasArg("oerase")) SD_file_erase(server.arg(0), open_erase); 829 | } 830 | else OpenSelectInput("Enter a File Name to Erase", "Oerase", "oerase"); 831 | } 832 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 833 | void SD_file_erase(String filename, bool erase_mode) { // Erase the datalog file 834 | if (SD_present) { 835 | append_page_header(refresh_off); 836 | webpage += F("

Channel File Erase

"); 837 | File dataFile = SD.open("/" + filename + (erase_mode ? "" : ".txt"), FILE_READ); // Now read data from SD Card 838 | if (dataFile) 839 | { 840 | if (SD.remove("/" + filename + (erase_mode ? "" : ".txt"))) { 841 | Serial.println(F("File deleted successfully")); 842 | webpage += "

FILE: '" + filename + (erase_mode ? "" : ".txt") + "' has been erased

"; 843 | if (erase_mode) webpage += F("[Back]

"); else webpage += F("[Back]

"); 844 | } 845 | else 846 | { 847 | webpage += F("

Channel File was not deleted - error

"); 848 | webpage += F("[Back]

"); 849 | } 850 | } else ReportFileNotPresent("Cerase"); 851 | append_page_footer(graph_off); 852 | server.send(200, "text/html", webpage); 853 | webpage = ""; 854 | } else ReportSDNotPresent(); 855 | } 856 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 857 | void SD_dir() { 858 | if (SD_present) { 859 | File root = SD.open("/"); 860 | if (root) { 861 | root.rewindDirectory(); 862 | SendHTML_Header(refresh_off); 863 | webpage += F("

SD Card Contents

"); 864 | webpage += F(""); 865 | webpage += F(""); 866 | printDirectory("/", 0); 867 | webpage += F("
Name/TypeFile/DirSize

"); 868 | SendHTML_Content(); 869 | root.close(); 870 | } 871 | else 872 | { 873 | SendHTML_Header(refresh_off); 874 | webpage += F("

No Channel Files Found

"); 875 | } 876 | append_page_footer(graph_off); 877 | SendHTML_Content(); 878 | SendHTML_Stop(); // Stop is needed because no content length was sent 879 | } else ReportSDNotPresent(); 880 | } 881 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 882 | void printDirectory(const char * dirname, uint8_t levels) { 883 | Serial.printf("Listing directory: %s\n", dirname); 884 | File root = SD.open(dirname); 885 | #ifdef ESP8266 886 | root.rewindDirectory(); //Only needed for ESP8266 887 | #endif 888 | if (!root) { 889 | Serial.println("Failed to open directory"); 890 | return; 891 | } 892 | if (!root.isDirectory()) { 893 | Serial.println("Not a directory"); 894 | return; 895 | } 896 | File file = root.openNextFile(); 897 | while (file) { 898 | if (webpage.length() > 1000) { 899 | SendHTML_Content(); 900 | } 901 | if (file.isDirectory()) { 902 | Serial.println(file.name()); 903 | webpage += "" + String(file.isDirectory() ? "Dir" : "File") + "" + String(file.name()) + ""; 904 | printDirectory(file.name(), levels - 1); 905 | } 906 | else 907 | { 908 | webpage += "" + String(file.name()) + ""; 909 | webpage += "" + String(file.isDirectory() ? "Dir" : "File") + ""; 910 | webpage += "" + file_size(file.size()) + ""; 911 | } 912 | file = root.openNextFile(); 913 | } 914 | file.close(); 915 | } 916 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 917 | String file_size(int bytes) { 918 | String fsize = ""; 919 | if (bytes < 1024) fsize = String(bytes) + " B"; 920 | else if (bytes < (1024 * 1024)) fsize = String(bytes / 1024.0, 3) + " KB"; 921 | else if (bytes < (1024 * 1024 * 1024)) fsize = String(bytes / 1024.0 / 1024.0, 3) + " MB"; 922 | else fsize = String(bytes / 1024.0 / 1024.0 / 1024.0, 3) + " GB"; 923 | return fsize; 924 | } 925 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 926 | void DrawChart() { 927 | graph_start = 0; 928 | graph_end = display_records; 929 | if (server.args() > 0 ) { // Arguments were received 930 | if (server.hasArg("draw")) drawchart(server.arg(0)); // parameter is sensor number 931 | } 932 | else SelectInput(refresh_off, "Channel Graph", "Select a Channel to Graph", "chart", "draw", graph_off); 933 | } 934 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 935 | void drawchart(String filename) { // required to enable parameters with the call 936 | graph_filename = filename; // global variable requirement 937 | channel_number = filename.toInt(); 938 | readingCnt = CountFileRecords(filename); 939 | GetandGraphData(); 940 | } 941 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 942 | void GetDataForGraph(String filename, int start) { 943 | int recordCnt = 0, displayCnt = 0; 944 | String stype; 945 | graph_sensor = filename.toInt(); 946 | if (SD_present) { 947 | File dataFile = SD.open("/" + filename + ".txt", FILE_READ); // Now read data from SD Card 948 | if (dataFile) { 949 | while (dataFile.available() && displayCnt < display_records) { // if the file is available, read from it 950 | DisplayData[displayCnt].ltime = dataFile.parseInt(); // 1517052559 21.58 43.71 0.00 0.00 SHT30-1 typically 951 | DisplayData[displayCnt].field1 = dataFile.parseFloat(); 952 | DisplayData[displayCnt].field2 = dataFile.parseFloat(); 953 | DisplayData[displayCnt].field3 = dataFile.parseFloat(); 954 | DisplayData[displayCnt].field4 = dataFile.parseFloat(); 955 | stype = dataFile.readStringUntil('\n'); // Needed to complete a record read 956 | if (recordCnt >= start) displayCnt++; 957 | recordCnt++; 958 | } 959 | } else ReportFileNotPresent("chart"); 960 | dataFile.close(); 961 | if (recordCnt == 0) displayCnt = 1; // In case the file is empty 962 | if (start > recordCnt) displayCnt = recordCnt; // In case the file is empty or there were not enough records. 963 | graph_start = start; 964 | graph_end = displayCnt; // Number of records to display 965 | } else ReportSDNotPresent(); 966 | } 967 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 968 | void MoveChartForward() { 969 | graph_start += graph_step; 970 | graph_end += graph_step; 971 | if (graph_end > readingCnt) { 972 | graph_end = readingCnt; 973 | graph_start = readingCnt - display_records; 974 | if (graph_start < 0 ) graph_start = 0; 975 | } 976 | if ((graph_start > readingCnt - graph_step) && (readingCnt - graph_step > 0) ) graph_start = readingCnt - graph_step; 977 | GetandGraphData(); 978 | } 979 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 980 | void MoveChartBack() { 981 | graph_start -= graph_step; 982 | graph_end -= graph_step; 983 | if (graph_start < 0) graph_start = 0; 984 | GetandGraphData(); 985 | } 986 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 987 | void GetandGraphData() { 988 | GetDataForGraph(graph_filename, graph_start); 989 | GraphSelectedData(DisplayData, 990 | graph_start, 991 | graph_end, 992 | ChannelData[graph_sensor].Name); 993 | } 994 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 995 | void ConstructGraph(record_type displayed_SensorData[], int start, int number_of_records, String title, String ytitle, String yunits, String graphname) { 996 | webpage = ""; 997 | // https://developers.google.com/loader/ // https://developers.google.com/chart/interactive/docs/basic_load_libs 998 | // https://developers.google.com/chart/interactive/docs/basic_preparing_data 999 | // https://developers.google.com/chart/interactive/docs/reference#google.visualization.arraytodatatable and See appendix-A 1000 | // data format is: [field-name,field-name,field-name] then [data,data,data], e.g. [12, 20.5, 70.3] 1001 | webpage += F(""); 1002 | webpage += F(""); 1049 | SendHTML_Content(); 1050 | } 1051 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1052 | void GraphSelectedData(record_type displayed_SensorData[], 1053 | int start, 1054 | int number_of_records, 1055 | String title) 1056 | { 1057 | // See google charts api for more details. To load the APIs, include the following script in the header of your web page. 1058 | // 1059 | // See https://developers.google.com/chart/interactive/docs/basic_load_libs 1060 | bool graph1_on = false, graph2_on = false, graph3_on = false, graph4_on = false; 1061 | String ytitle, yunits; 1062 | SendHTML_Header(refresh_off); 1063 | webpage += F("

"); webpage += ChannelData[graph_sensor].Name + " (" + String(readingCnt) + "-readings)

"; 1064 | SendHTML_Content(); 1065 | if (ChannelData[channel_number].Field1 != "") { 1066 | graphfield = one; 1067 | graphcolour = "red"; 1068 | ytitle = ChannelData[graph_sensor].Field1; 1069 | yunits = ChannelData[graph_sensor].Field1_Units; 1070 | ConstructGraph(displayed_SensorData, start, number_of_records, title + " " + ytitle, ytitle, yunits, "graph1"); // 'graph1' is e.g. the tag to match the
statements below 1071 | graph1_on = true; 1072 | } 1073 | //----------------- 1074 | if (ChannelData[channel_number].Field2 != "") { 1075 | graphfield = two; 1076 | graphcolour = "blue"; 1077 | ytitle = ChannelData[graph_sensor].Field2; 1078 | yunits = ChannelData[graph_sensor].Field2_Units; 1079 | ConstructGraph(displayed_SensorData, start, number_of_records, title + " " + ytitle, ytitle, yunits, "graph2"); 1080 | graph2_on = true; 1081 | } 1082 | //----------------- 1083 | if (ChannelData[channel_number].Field3 != "") { 1084 | graphfield = three; 1085 | graphcolour = "green"; 1086 | ytitle = ChannelData[graph_sensor].Field3; 1087 | yunits = ChannelData[graph_sensor].Field3_Units; 1088 | ConstructGraph(displayed_SensorData, start, number_of_records, title + " " + ytitle, ytitle, yunits, "graph3"); 1089 | graph3_on = true; 1090 | } 1091 | //----------------- 1092 | if (ChannelData[channel_number].Field4 != "") { 1093 | graphfield = four; 1094 | graphcolour = "orange"; 1095 | ytitle = ChannelData[graph_sensor].Field4; 1096 | yunits = ChannelData[graph_sensor].Field4_Units; 1097 | ConstructGraph(displayed_SensorData, start, number_of_records, title + " " + ytitle, ytitle, yunits, "graph4"); 1098 | graph4_on = true; 1099 | } 1100 | //----------------- 1101 | webpage += F("
"); 1102 | webpage += F("
"); 1103 | if (graph1_on) webpage += F("
"); 1104 | if (graph2_on) webpage += F("
"); 1105 | if (graph3_on) webpage += F("
"); 1106 | if (graph4_on) webpage += F("
"); 1107 | webpage += F("
"); 1108 | webpage += F("
[Back]
"); 1109 | SendHTML_Content(); // Send Content 1110 | append_page_footer(graph_on); 1111 | SendHTML_Content(); // Send footer 1112 | SendHTML_Stop(); // Stop is needed because no content length was sent 1113 | } 1114 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1115 | void SendHTML_Header(bool refresh_mode) { 1116 | server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); 1117 | server.sendHeader("Pragma", "no-cache"); 1118 | server.sendHeader("Connection", "Keep-Alive"); 1119 | server.sendHeader("Expires", "-1"); 1120 | server.setContentLength(CONTENT_LENGTH_UNKNOWN); 1121 | server.send(200, "text/html", ""); // Empty content inhibits Content-length header 1122 | append_page_header(refresh_mode); 1123 | server.sendContent(webpage); 1124 | server.sendContent("\n\r"); // A blank line seperates the header 1125 | webpage = ""; 1126 | } 1127 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1128 | void SendHTML_Content() { 1129 | server.sendContent(webpage); 1130 | webpage = ""; 1131 | } 1132 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1133 | void SendHTML_Stop() { 1134 | server.sendContent(""); 1135 | server.client().stop(); // Stop is needed because no content length was sent 1136 | } 1137 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1138 | void SelectInput(bool refresh_mode, String heading1, String heading2, String command, String arg_calling_name, bool graph_mode_off) { 1139 | SendHTML_Header(refresh_off); 1140 | webpage += F("

"); webpage += heading2 + "

"; 1141 | webpage += F(""; // Must match the calling argument e.g. '/chart' calls '/chart' after selection but with arguments 1142 | for (byte cn = 1; cn < number_of_channels; cn++) { 1143 | webpage += F(""; 1144 | } 1145 | if (graph_mode_off) { 1146 | webpage += F(""; 1148 | } 1149 | webpage += F("

"); 1150 | append_page_footer(graph_off); 1151 | SendHTML_Content(); 1152 | SendHTML_Stop(); 1153 | webpage = ""; 1154 | } 1155 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1156 | void OpenSelectInput(String heading1, String command, String arg_calling_name) { 1157 | SendHTML_Header(refresh_off); 1158 | webpage += F("

"); webpage += heading1 + "

"; 1159 | webpage += F(""; // Must match the calling argument e.g. '/chart' calls '/chart' after selection but with arguments! 1160 | webpage += F("
"); 1161 | webpage += F("

"); 1162 | append_page_footer(graph_off); 1163 | SendHTML_Content(); 1164 | SendHTML_Stop(); 1165 | webpage = ""; 1166 | } 1167 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1168 | String file_size(String filename) { // Display file size of the datalog file 1169 | String ftxtsize = ""; 1170 | if (SD_present) { 1171 | File dataFile = SD.open("/" + filename + ".txt", FILE_READ); // Now read data from SD Card 1172 | if (dataFile) { 1173 | int bytes = dataFile.size(); 1174 | if (bytes < 1024) ftxtsize = String(bytes) + " B"; 1175 | else if (bytes < (1024 * 1024)) ftxtsize = String(bytes / 1024.0) + " KB"; 1176 | else if (bytes < (1024 * 1024 * 1024)) ftxtsize = String(bytes / 1024.0 / 1024.0) + " MB"; 1177 | else ftxtsize = String(bytes / 1024.0 / 1024.0 / 1024.0) + " GB"; 1178 | } 1179 | else ftxtsize = ""; 1180 | dataFile.close(); 1181 | return ftxtsize; 1182 | } else ReportSDNotPresent(); 1183 | return ""; 1184 | } 1185 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1186 | void ReportSDNotPresent() { 1187 | append_page_header(refresh_off); 1188 | webpage += F("

No SD Card present

"); 1189 | webpage += F("[Back]

"); 1190 | append_page_footer(graph_off); 1191 | server.send(200, "text/html", webpage); 1192 | webpage = ""; 1193 | } 1194 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1195 | void ReportFileNotPresent(String target) { 1196 | append_page_header(refresh_off); 1197 | webpage += F("

File does not exist

"); 1198 | webpage += F("[Back]

"; 1199 | append_page_footer(graph_off); 1200 | server.send(200, "text/html", webpage); 1201 | webpage = ""; 1202 | } 1203 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1204 | void ReportCommandNotFound(String target) { 1205 | append_page_header(refresh_off); 1206 | webpage += F("

Function does not exist

"); 1207 | webpage += F("[Back]

"; 1208 | append_page_footer(graph_off); 1209 | server.send(200, "text/html", webpage); 1210 | webpage = ""; 1211 | } 1212 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1213 | void ReportCouldNotCreateFile(String target) { 1214 | append_page_header(refresh_off); 1215 | webpage += F("

Could Not Create Uploaded File (write-protected?)

"); 1216 | webpage += F("[Back]

"; 1217 | append_page_footer(graph_off); 1218 | server.send(200, "text/html", webpage); 1219 | webpage = ""; 1220 | } 1221 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1222 | int CountFileRecords(String filename) { 1223 | int recordCnt = 0, temp_read_int = 0; 1224 | float temp_read_flt = 0; 1225 | String temp_read_txt = ""; 1226 | if (SD_present) { 1227 | File dataFile = SD.open("/" + filename + ".txt", FILE_READ); // Now read data from SD Card 1228 | if (dataFile) { 1229 | while (dataFile.available()) { // if the file is available, read from it 1230 | recordCnt++; 1231 | temp_read_int = dataFile.parseInt(); // 1517052559 21.58 43.71 0.00 0.00 SHT30-1 typically 1232 | temp_read_flt = dataFile.parseFloat(); 1233 | temp_read_flt = dataFile.parseFloat(); 1234 | temp_read_flt = dataFile.parseFloat(); 1235 | temp_read_flt = dataFile.parseFloat(); 1236 | temp_read_txt = dataFile.readStringUntil('\n'); // Needed to complete a record read 1237 | } 1238 | } 1239 | dataFile.close(); 1240 | return recordCnt; 1241 | } else ReportSDNotPresent(); 1242 | return 0; 1243 | } 1244 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1245 | void SetupTime() { 1246 | timeval tv = {BaseTime, 0 }; // 00:00:00 01/01/2018 1247 | timezone tz = { 0 , 0 }; 1248 | settimeofday(&tv, &tz); 1249 | configTime(0, 0, "pool.ntp.org"); 1250 | setenv("TZ", "GMT0BST,M3.5.0/2,M10.5.0/2", 1); 1251 | tzset(); 1252 | time_t tnow = time(nullptr); 1253 | delay(2000); 1254 | Serial.print(F("\nWaiting for time...")); 1255 | tnow = time(nullptr); 1256 | Serial.println("Time set " + Time(tnow)); 1257 | } 1258 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1259 | String Time(int unix_time) { 1260 | struct tm *now_tm; 1261 | int hour, min, second, day, month, year; 1262 | // timeval tv = {unix_time,0}; 1263 | time_t tm = unix_time; 1264 | now_tm = localtime(&tm); 1265 | hour = now_tm->tm_hour; 1266 | min = now_tm->tm_min; 1267 | second = now_tm->tm_sec; 1268 | day = now_tm->tm_mday; 1269 | month = now_tm->tm_mon + 1; 1270 | year = 1900 + now_tm->tm_year; // To get just YY information 1271 | //String days[7] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; 1272 | time_str = (hour < 10 ? "0" + String(hour) : String(hour)) + ":" + (min < 10 ? "0" + String(min) : String(min)) + ":" + (second < 10 ? "0" + String(second) : String(second)) + "-"; 1273 | time_str += (day < 10 ? "0" + String(day) : String(day)) + "/" + (month < 10 ? "0" + String(month) : String(month)) + "/" + (year < 10 ? "0" + String(year) : String(year)); // HH:MM:SS 05/07/17 1274 | //Serial.println(time_str); 1275 | return time_str; 1276 | } 1277 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1278 | int TimeNow() { 1279 | time_t tnow = time(nullptr); 1280 | return tnow; 1281 | } 1282 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1283 | void ChannelDataReset(byte CN) { 1284 | ChannelData[CN].ID = CN; 1285 | ChannelData[CN].Name = "Name-TBA"; 1286 | ChannelData[CN].Description = "Sensor Readings"; 1287 | ChannelData[CN].Type = "e.g.SHT30"; 1288 | ChannelData[CN].Field1 = "e.g.Temperature"; 1289 | ChannelData[CN].Field1_Units = "°C"; 1290 | ChannelData[CN].Field2 = "e.g.Humidity"; 1291 | ChannelData[CN].Field2_Units = "%"; 1292 | ChannelData[CN].Field3 = "e.g.Pressure"; 1293 | ChannelData[CN].Field3_Units = "hPa"; 1294 | ChannelData[CN].Field4 = "Unused"; 1295 | ChannelData[CN].Field4_Units = ""; 1296 | ChannelData[CN].IconName = "building.png"; 1297 | ChannelData[CN].Created = BaseTime; 1298 | ChannelData[CN].Updated = BaseTime; 1299 | } 1300 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1301 | // Read file from SD Card and display it 1302 | bool loadFromSdCard(String filename) { 1303 | String dataType = "text/plain"; 1304 | if (filename.endsWith(".htm")) dataType = "text/html"; 1305 | else if (filename.endsWith(".html")) dataType = "text/html"; 1306 | else if (filename.endsWith(".css")) dataType = "text/css"; 1307 | else if (filename.endsWith(".png")) dataType = "image/png"; 1308 | else if (filename.endsWith(".gif")) dataType = "image/gif"; 1309 | else if (filename.endsWith(".jpg")) dataType = "image/jpeg"; 1310 | else if (filename.endsWith(".bmp")) dataType = "image/bmp"; 1311 | else if (filename.endsWith(".ico")) dataType = "image/x-icon"; 1312 | Serial.println(filename); 1313 | File dataFile = SD.open(filename.c_str()); 1314 | if (!dataFile) return false; 1315 | if (server.hasArg("download")) dataType = "application/octet-stream"; 1316 | if (server.streamFile(dataFile, dataType) != dataFile.size()) { 1317 | Serial.println("Sent less data than expected!"); 1318 | } 1319 | dataFile.close(); 1320 | return true; 1321 | } 1322 | -------------------------------------------------------------------------------- /Licence.txt: -------------------------------------------------------------------------------- 1 | This software, the ideas and concepts is Copyright (c) David Bird 2014 and beyond. 2 | 3 | All rights to this software are reserved. 4 | 5 | It is prohibited to redistribute or reproduce of any part or all of the software contents in any form other than the following: 6 | 7 | 1. You may print or download to a local hard disk extracts for your personal and non-commercial use only. 8 | 9 | 2. You may copy the content to individual third parties for their personal use, but only if you acknowledge the author David Bird as the source of the material. 10 | 11 | 3. You may not, except with my express written permission, distribute or commercially exploit the content. 12 | 13 | 4. You may not transmit it or store it in any other website or other form of electronic retrieval system for commercial purposes. 14 | 15 | 5. You MUST include all of this copyright and permission notice ('as annotated') and this shall be included in all copies or substantial portions of the software and where the software use is visible to an end-user. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS" FOR PRIVATE USE ONLY, IT IS NOT FOR COMMERCIAL USE IN WHOLE OR PART OR CONCEPT. 18 | 19 | FOR PERSONAL USE IT IS SUPPLIED WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | 21 | IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Network.h: -------------------------------------------------------------------------------- 1 | // Adjust the following values to match your needs 2 | // ----------------------------------------------- 3 | #define servername "sensorserver2" // Set your server's logical name here e.g. if myserver then address is http://myserver.local/ 4 | IPAddress local_IP(192, 168, 0, 175); // Set your server's fixed IP address here 5 | IPAddress gateway(192, 168, 0, 1); // Set your network Gateway usually your Router base address 6 | IPAddress subnet(255, 255, 255, 0); 7 | IPAddress dns(192,168,0,1); // Set your network DNS usually your Router base address 8 | const char ssid_1[] = "your_SSID1"; 9 | const char password_1[] = "your_PASSWORD_for SSID1"; 10 | 11 | const char ssid_2[] = "your_SID2"; 12 | const char password_2[] = "your_PASSWORD_for SSID2"; 13 | 14 | const char ssid_3[] = "your_SSID3"; 15 | const char password_3[] = "your_PASSWORD_for SSID3"; 16 | 17 | const char ssid_4[] = "your_SSID4"; 18 | const char password_4[] = "your_PASSWORD_for SSID4"; 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP-Sensor-Server-Client 2 | A sensor server that receives and displays on a webpage client data, usually from an ESP configured as a sensor 3 | 4 | 1. Download the files to your IDE location. 5 | 2. Locate the files referenced in the Server and Clients, download those and place in your Libraries folder 6 | 3. Choose an IP address for your Server e.g. 192.168.0.99 7 | 4. Edit the Server IP address accordingly. 8 | 5. Test the Server by complining and uploading to either an ESP8266 or ESP32, the code adapts accordingly. Make sure you choose the correct board type! 9 | 6. Test the server by typing this in a browser address bar: 10 | http://sensorserver.local/sensor?Sensor=1&temperature=21.2&humidity=50.1&pressure=1001&spare=0&sensortype="Mine" 11 | 12 | Or if your PC does not have 'Bonjour' installed, this is needed to resolve mult-cast DNS packets and resolves the address sesnorserver.local to the IP address, in which case enter in the browser address bar: 13 | 14 | http://192.168.0.99/sensor?Sensor=1&temperature=21.2&humidity=50.1&pressure=1001&spare=0&sensortype="Mine" 15 | 16 | For Clients, find the example IP address of 192.168.0.99 and edit this to match your choice of IP address. 17 | 18 | For the Clients, wire your sensors accordingly and change the sensor pins in the Source code to match your pins. 19 | 20 | Compile and upload 21 | 22 | Adjust the names and types and units as desired. 23 | 24 | To see the sensors use http://sensorserver.local/sensor or http://192.168.0.99/sensor 25 | 26 | To test the server use http://sensorserver.local/ or http://192.168.0.99/ 27 | 28 | -------------------------------------------------------------------------------- /Sys_Variables.h: -------------------------------------------------------------------------------- 1 | #define ServerVersion "1.0" 2 | String webpage = ""; 3 | bool AUpdate = true; // Default value is On 4 | const byte number_of_channels = 7; // **** MAXIMUM Of 12 and ensure this is the required Number of Channels + 1 e.g. for 6 channels set this value to 7 5 | // NOTE: ******************* FOR EACH increase or decrease in number_of_channels change lines 441 onwards accordingly, otherwise there will be compilation errors out-of-bounds 6 | // This is until the compiler errors can be fixed. Currently 1.0.0 for ESP32 7 | 8 | #define display_records 500 // 500 is the maximum of readings that can be displayed on the graph 9 | #define graph_step 250 // The amount the graph is moved forwards/backwards 10 | #define BaseTime 1514764800 // 00:00:00 01/01/2018 11 | #ifdef ESP8266 12 | #define SD_CS_pin D8 // The pin on Wemos D1 Mini for SD Card Chip-Select 13 | #else 14 | #define SD_CS_pin 5 // The pin on MH-T Live ESP32 (version of Wemos D1 Mini) for SD Card Chip-Select 15 | #endif 16 | String time_str; 17 | bool refresh_on = true; 18 | bool refresh_off = false; 19 | bool data_amended = false; 20 | String graph_filename = ""; 21 | bool graph_on = true; 22 | bool graph_off = false; 23 | bool open_erase = true; // Enables only Channel Data files to be deleted 24 | bool open_download = true; // Enables only Channel Data files to be downloaded 25 | enum fieldselector {one, two, three, four}; 26 | String graphcolour = "red"; 27 | int readingCnt = 0; 28 | fieldselector graphfield = one; 29 | 30 | /////////////////////////////////////////////////////////////////////////////////////// 31 | typedef struct { 32 | int ID = 0; 33 | String Name = "Name-TBA"; 34 | String Description = "Sensor Readings"; 35 | String Type = "SHT30"; 36 | String Field1 = "Temperature"; 37 | String Field1_Units = "°C"; 38 | String Field2 = "Humidity"; 39 | String Field2_Units = "%"; 40 | String Field3 = "Pressure"; 41 | String Field3_Units = "hPa"; 42 | String Field4 = "Unused"; 43 | String Field4_Units = ""; 44 | String IconName = "building.png"; 45 | int Created = BaseTime; 46 | int Updated = BaseTime; 47 | } sensor_details_type; 48 | 49 | sensor_details_type ChannelData[number_of_channels]; 50 | 51 | typedef struct { 52 | byte sensornumber; // Sensor number provided by e.g. Sensor=3 53 | float value1; // For example Temperature 54 | float value2; // For example Humidity 55 | float value3; // For example Pressure 56 | float value4; // Spare 57 | String sensortype = "N/A"; // The sensor type e.g. an SHT30 or BMP180 58 | int readingtime = BaseTime; 59 | } sensor_record_type; // total bytes per record = (1+4+4+4+4+String(6)+4) = 27Bytes ~ 4-years of records/MByte at 24 readings/day 60 | 61 | sensor_record_type SensorData[number_of_channels]; // Define the data array 62 | 63 | int channel_number = 0; 64 | int sensor_reading = 0; // Default value 65 | int day_count = 1; // Default value 66 | int graph_sensor = 1; 67 | bool SD_present; 68 | 69 | int graph_start, graph_end, index_ptr=0; 70 | 71 | typedef struct { 72 | int ltime; // Time reading arrived 73 | float field1; // usually Temperature values 74 | float field2; // usually Humidity values 75 | float field3; // usually Pressure values 76 | float field4; // spare 77 | } record_type; 78 | 79 | record_type DisplayData[display_records]; // Define the data array for display on a graph 80 | -------------------------------------------------------------------------------- /icons/List: -------------------------------------------------------------------------------- 1 | 2 | These are the icons for the Sensor-Server Advanced code. 3 | 4 | The server supports icons files of the type PNG, JPG, GIF and BMP. 5 | 6 | FIle names must conform to the standrad 8.3 so '12345678.123' or filename.bmp, etc. 7 | 8 | No more than 8 characters for the filename and no more than 3 characters for the file extension. 9 | -------------------------------------------------------------------------------- /icons/bedroom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G6EJD/ESP-Sensor-Server-and-Clients/8f4fe7ad55105b95750aa8b81e2790870602e59a/icons/bedroom.png -------------------------------------------------------------------------------- /icons/building.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G6EJD/ESP-Sensor-Server-and-Clients/8f4fe7ad55105b95750aa8b81e2790870602e59a/icons/building.png -------------------------------------------------------------------------------- /icons/conserve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G6EJD/ESP-Sensor-Server-and-Clients/8f4fe7ad55105b95750aa8b81e2790870602e59a/icons/conserve.png -------------------------------------------------------------------------------- /icons/dining.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G6EJD/ESP-Sensor-Server-and-Clients/8f4fe7ad55105b95750aa8b81e2790870602e59a/icons/dining.png -------------------------------------------------------------------------------- /icons/factory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G6EJD/ESP-Sensor-Server-and-Clients/8f4fe7ad55105b95750aa8b81e2790870602e59a/icons/factory.png -------------------------------------------------------------------------------- /icons/garage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G6EJD/ESP-Sensor-Server-and-Clients/8f4fe7ad55105b95750aa8b81e2790870602e59a/icons/garage.png -------------------------------------------------------------------------------- /icons/outdoors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G6EJD/ESP-Sensor-Server-and-Clients/8f4fe7ad55105b95750aa8b81e2790870602e59a/icons/outdoors.png -------------------------------------------------------------------------------- /icons/study.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G6EJD/ESP-Sensor-Server-and-Clients/8f4fe7ad55105b95750aa8b81e2790870602e59a/icons/study.png --------------------------------------------------------------------------------