Browse Source

add temperature compensation. refactoring. dark-mode. debug-log with websockets.

Thomas Buck 1 year ago
parent
commit
a627ec29e1
21 changed files with 2068 additions and 1179 deletions
  1. 50
    0
      include/DebugLog.h
  2. 24
    1
      include/config.h
  3. 28
    0
      include/html.h
  4. 21
    0
      include/influx.h
  5. 34
    0
      include/memory.h
  6. 20
    0
      include/mqtt.h
  7. 41
    0
      include/sensors.h
  8. 30
    0
      include/servers.h
  9. 8
    0
      platformio.ini
  10. 76
    0
      src/DebugLog.cpp
  11. 6
    5
      src/SimpleInflux.cpp
  12. 64
    42
      src/SimpleUpdater.cpp
  13. 435
    0
      src/html.cpp
  14. 290
    0
      src/influx.cpp
  15. 34
    1120
      src/main.cpp
  16. 130
    0
      src/memory.cpp
  17. 180
    0
      src/mqtt.cpp
  18. 10
    0
      src/relais.cpp
  19. 357
    0
      src/sensors.cpp
  20. 230
    0
      src/servers.cpp
  21. 0
    11
      test/README

+ 50
- 0
include/DebugLog.h View File

@@ -0,0 +1,50 @@
1
+/*
2
+ * DebugLog.h
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#ifndef _DEBUG_LOG_H_
15
+#define _DEBUG_LOG_H_
16
+
17
+#include <Arduino.h>
18
+
19
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
20
+#include <CircularBuffer.h>
21
+#define DEBUG_LOG_HISTORY_SIZE 1024
22
+#endif
23
+
24
+class DebugLog {
25
+public:
26
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
27
+    String getBuffer(void);
28
+#endif
29
+    
30
+    void write(char c);
31
+    void print(String s);
32
+    void print(int n);
33
+    
34
+    void println(void);
35
+    void println(String s);
36
+    void println(int n);
37
+    
38
+private:
39
+    void sendToTargets(String s);
40
+    
41
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
42
+    void addToBuffer(String s);
43
+    
44
+    CircularBuffer<char, DEBUG_LOG_HISTORY_SIZE> buffer;
45
+#endif
46
+};
47
+
48
+extern DebugLog debug;
49
+
50
+#endif // _DEBUG_LOG_H_

+ 24
- 1
include/config.h View File

@@ -15,10 +15,12 @@
15 15
 #define __ESP_ENV_CONFIG__
16 16
 
17 17
 // Sketch version
18
-#define ESP_ENV_VERSION "0.5.0"
18
+#define ESP_ENV_VERSION "0.6.0"
19 19
 
20 20
 // location of sensor, used in DB and hostname
21 21
 //#define SENSOR_LOCATION_LIVINGROOM
22
+//#define SENSOR_LOCATION_LIVINGROOM_WORKSPACE
23
+//#define SENSOR_LOCATION_LIVINGROOM_TV
22 24
 //#define SENSOR_LOCATION_BEDROOM
23 25
 //#define SENSOR_LOCATION_BATHROOM
24 26
 //#define SENSOR_LOCATION_GREENHOUSE
@@ -38,10 +40,13 @@
38 40
 #define INFLUXDB_HOST "INFLUX_IP_HERE"
39 41
 #define INFLUXDB_PORT 8086
40 42
 #define INFLUXDB_DATABASE "roomsensorsdiy"
43
+#define INFLUX_MAX_ERRORS_RESET 10
41 44
 
42 45
 // all given in milliseconds
43 46
 #define SERVER_HANDLE_INTERVAL 10
47
+#define SENSOR_HANDLE_INTERVAL (5 * 1000)
44 48
 #define DB_WRITE_INTERVAL (30 * 1000)
49
+#define MQTT_WRITE_INTERVAL (30 * 1000)
45 50
 #define LED_BLINK_INTERVAL (2 * 1000)
46 51
 #define LED_INIT_BLINK_INTERVAL 500
47 52
 #define LED_CONNECT_BLINK_INTERVAL 250
@@ -50,16 +55,28 @@
50 55
 
51 56
 #if defined(SENSOR_LOCATION_LIVINGROOM)
52 57
 #define SENSOR_LOCATION "livingroom"
58
+#define SENSOR_ID SENSOR_LOCATION
59
+#elif defined(SENSOR_LOCATION_LIVINGROOM_WORKSPACE)
60
+#define SENSOR_LOCATION "livingroom"
61
+#define SENSOR_ID "livingroom-workspace"
62
+#elif defined(SENSOR_LOCATION_LIVINGROOM_TV)
63
+#define SENSOR_LOCATION "livingroom"
64
+#define SENSOR_ID "livingroom-tv"
53 65
 #elif defined(SENSOR_LOCATION_BEDROOM)
54 66
 #define SENSOR_LOCATION "bedroom"
67
+#define SENSOR_ID SENSOR_LOCATION
55 68
 #elif defined(SENSOR_LOCATION_BATHROOM)
56 69
 #define SENSOR_LOCATION "bathroom"
70
+#define SENSOR_ID SENSOR_LOCATION
57 71
 #elif defined(SENSOR_LOCATION_GREENHOUSE)
58 72
 #define SENSOR_LOCATION "greenhouse"
73
+#define SENSOR_ID SENSOR_LOCATION
59 74
 #elif defined(SENSOR_LOCATION_TESTING)
60 75
 #define SENSOR_LOCATION "testing"
76
+#define SENSOR_ID SENSOR_LOCATION
61 77
 #else
62 78
 #define SENSOR_LOCATION "unknown"
79
+#define SENSOR_ID SENSOR_LOCATION
63 80
 #endif
64 81
 
65 82
 #if defined(RELAIS_SERIAL) || defined(RELAIS_GPIO)
@@ -70,4 +87,10 @@
70 87
 #define FEATURE_MOISTURE
71 88
 #endif
72 89
 
90
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
91
+#define BUILTIN_LED_PIN 1
92
+#elif defined(ARDUINO_ARCH_AVR)
93
+#define BUILTIN_LED_PIN 13
94
+#endif
95
+
73 96
 #endif // __ESP_ENV_CONFIG__

+ 28
- 0
include/html.h View File

@@ -0,0 +1,28 @@
1
+/*
2
+ * html.h
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#ifndef __HTML_H__
15
+#define __HTML_H__
16
+
17
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
18
+
19
+void handlePage(int mode = -1, int id = 0);
20
+void handleReset();
21
+
22
+#else
23
+
24
+void handlePage(WiFiClient &client, int mode = -1, int id = 0);
25
+
26
+#endif
27
+
28
+#endif // __HTML_H__

+ 21
- 0
include/influx.h View File

@@ -0,0 +1,21 @@
1
+/*
2
+ * influx.h
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#ifndef __INFLUX_H__
15
+#define __INFLUX_H__
16
+
17
+void initInflux();
18
+void runInflux();
19
+void writeDatabase();
20
+
21
+#endif // __INFLUX_H__

+ 34
- 0
include/memory.h View File

@@ -0,0 +1,34 @@
1
+/*
2
+ * memory.h
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#ifndef __ESP_ENV_MEMORY__
15
+#define __ESP_ENV_MEMORY__
16
+
17
+struct ConfigMemory {
18
+    double sht_temp_off;
19
+    double bme1_temp_off;
20
+    double bme2_temp_off;
21
+
22
+    ConfigMemory() {
23
+        sht_temp_off = 0.0;
24
+        bme1_temp_off = 0.0;
25
+        bme2_temp_off = 0.0;
26
+    }
27
+};
28
+
29
+ConfigMemory mem_read();
30
+void mem_write(ConfigMemory mem);
31
+
32
+extern ConfigMemory config;
33
+
34
+#endif // __ESP_ENV_MEMORY__

+ 20
- 0
include/mqtt.h View File

@@ -0,0 +1,20 @@
1
+/*
2
+ * mqtt.h
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#ifndef __MQTT_H__
15
+#define __MQTT_H__
16
+
17
+void initMQTT();
18
+void runMQTT();
19
+
20
+#endif // __MQTT_H__

+ 41
- 0
include/sensors.h View File

@@ -0,0 +1,41 @@
1
+/*
2
+ * sensors.h
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#ifndef __SENSORS_H__
15
+#define __SENSORS_H__
16
+
17
+float bme1_temp(void);
18
+float bme2_temp(void);
19
+float bme1_humid(void);
20
+float bme2_humid(void);
21
+float bme1_pressure(void);
22
+float bme2_pressure(void);
23
+float sht_temp(void);
24
+float sht_humid(void);
25
+extern bool found_bme1, found_bme2, found_sht;
26
+
27
+#ifdef ENABLE_CCS811
28
+float ccs1_eco2(void);
29
+float ccs1_tvoc(void);
30
+float ccs2_eco2(void);
31
+float ccs2_tvoc(void);
32
+extern bool found_ccs1, found_ccs2;
33
+extern bool ccs1_data_valid, ccs2_data_valid;
34
+extern int ccs1_error_code, ccs2_error_code;
35
+#endif // ENABLE_CCS811
36
+
37
+void handleCalibrate();
38
+void initSensors();
39
+void runSensors();
40
+
41
+#endif // __SENSORS_H__

+ 30
- 0
include/servers.h View File

@@ -0,0 +1,30 @@
1
+/*
2
+ * servers.h
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#ifndef __SERVERS_H__
15
+#define __SERVERS_H__
16
+
17
+void initServers(String hostname);
18
+void runServers();
19
+
20
+void wifi_send_websocket(String s);
21
+
22
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
23
+#include "SimpleUpdater.h"
24
+extern UPDATE_WEB_SERVER server;
25
+#elif defined(ARDUINO_ARCH_AVR)
26
+#include <WiFiLink.h>
27
+extern WiFiServer server;
28
+#endif
29
+
30
+#endif // __SERVERS_H__

+ 8
- 0
platformio.ini View File

@@ -24,6 +24,8 @@ lib_deps =
24 24
     Adafruit BME280 Library
25 25
     https://github.com/adafruit/Adafruit_CCS811
26 26
     https://github.com/knolleary/pubsubclient.git#2d228f2f862a95846c65a8518c79f48dfc8f188c
27
+    https://github.com/rlogiacco/CircularBuffer
28
+    https://github.com/Links2004/arduinoWebSockets
27 29
 
28 30
 [env:esp8266relais]
29 31
 platform = espressif8266
@@ -40,6 +42,8 @@ lib_deps =
40 42
     Adafruit Unified Sensor
41 43
     Adafruit BME280 Library
42 44
     https://github.com/knolleary/pubsubclient.git#2d228f2f862a95846c65a8518c79f48dfc8f188c
45
+    https://github.com/rlogiacco/CircularBuffer
46
+    https://github.com/Links2004/arduinoWebSockets
43 47
 
44 48
 [env:esp32env]
45 49
 platform = platformio/espressif32@3.5.0
@@ -61,6 +65,8 @@ lib_deps =
61 65
     https://github.com/tobiasschuerg/InfluxDB-Client-for-Arduino.git#66ed5d031caab6953cc79b407a4b49d33b1126dc
62 66
     https://github.com/adafruit/Adafruit_CCS811
63 67
     https://github.com/knolleary/pubsubclient.git#2d228f2f862a95846c65a8518c79f48dfc8f188c
68
+    https://github.com/rlogiacco/CircularBuffer
69
+    https://github.com/Links2004/arduinoWebSockets
64 70
 
65 71
 [env:esp32moisture]
66 72
 platform = platformio/espressif32@3.5.0
@@ -83,6 +89,8 @@ lib_deps =
83 89
     https://github.com/tobiasschuerg/InfluxDB-Client-for-Arduino.git#66ed5d031caab6953cc79b407a4b49d33b1126dc
84 90
     https://github.com/adafruit/Adafruit_CCS811
85 91
     https://github.com/knolleary/pubsubclient.git#2d228f2f862a95846c65a8518c79f48dfc8f188c
92
+    https://github.com/rlogiacco/CircularBuffer
93
+    https://github.com/Links2004/arduinoWebSockets
86 94
 
87 95
 [env:arduinomoisture]
88 96
 platform = atmelavr

+ 76
- 0
src/DebugLog.cpp View File

@@ -0,0 +1,76 @@
1
+/*
2
+ * DebugLog.cpp
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#include <Arduino.h>
15
+
16
+#include "DebugLog.h"
17
+
18
+DebugLog debug;
19
+
20
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
21
+
22
+void wifi_send_websocket(String s);
23
+
24
+String DebugLog::getBuffer(void) {
25
+    String r;
26
+    for (unsigned int i = 0; i < buffer.size(); i++) {
27
+        r += buffer[i];
28
+    }
29
+    return r;
30
+}
31
+
32
+void DebugLog::addToBuffer(String s) {
33
+    for (unsigned int i = 0; i < s.length(); i++) {
34
+        buffer.push(s[i]);
35
+    }
36
+}
37
+
38
+#endif
39
+
40
+void DebugLog::sendToTargets(String s) {
41
+    Serial.print(s);
42
+    
43
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
44
+    s = "log:" + s;
45
+    wifi_send_websocket(s);
46
+#endif
47
+}
48
+
49
+void DebugLog::write(char c) {
50
+    print(String(c));
51
+}
52
+
53
+void DebugLog::print(String s) {
54
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
55
+    addToBuffer(s);
56
+#endif
57
+    
58
+    sendToTargets(s);
59
+}
60
+
61
+void DebugLog::print(int n) {
62
+    print(String(n));
63
+}
64
+
65
+void DebugLog::println(void) {
66
+    print(String(F("\r\n")));
67
+}
68
+
69
+void DebugLog::println(String s) {
70
+    s += String(F("\r\n"));
71
+    print(s);
72
+}
73
+
74
+void DebugLog::println(int n) {
75
+    println(String(n));
76
+}

+ 6
- 5
src/SimpleInflux.cpp View File

@@ -12,6 +12,7 @@
12 12
  */
13 13
 
14 14
 #include <Arduino.h>
15
+#include "DebugLog.h"
15 16
 #include "SimpleInflux.h"
16 17
 
17 18
 #if defined(ARDUINO_ARCH_AVR)
@@ -43,8 +44,8 @@ void InfluxData::addValue(const char *name, double value) {
43 44
 boolean Influxdb::write(InfluxData &data) {
44 45
 #if defined(ARDUINO_ARCH_AVR)
45 46
 
46
-    Serial.print(F("Writing "));
47
-    Serial.println(data.dataName());
47
+    debug.print(F("Writing "));
48
+    debug.println(data.dataName());
48 49
 
49 50
     client.stop();
50 51
 
@@ -103,7 +104,7 @@ boolean Influxdb::write(InfluxData &data) {
103 104
             if (client.available()) {
104 105
                 char c = client.read();
105 106
                 if (c != '\r') {
106
-                    Serial.write(c);
107
+                    debug.write(c);
107 108
                 }
108 109
 
109 110
                 if (compare_off == compare_to.length()) {
@@ -132,10 +133,10 @@ boolean Influxdb::write(InfluxData &data) {
132 133
         }
133 134
 
134 135
         client.stop();
135
-        Serial.println(contains_error ? F("Request failed") : F("Request Done"));
136
+        debug.println(contains_error ? F("Request failed") : F("Request Done"));
136 137
         return !contains_error;
137 138
     } else {
138
-        Serial.println(F("Error connecting"));
139
+        debug.println(F("Error connecting"));
139 140
         return false; // failed
140 141
     }
141 142
 #else

+ 64
- 42
src/SimpleUpdater.cpp View File

@@ -11,8 +11,6 @@
11 11
  * ----------------------------------------------------------------------------
12 12
  */
13 13
 
14
-#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
15
-
16 14
 #if defined(ARDUINO_ARCH_ESP8266)
17 15
 #include <ESP8266HTTPUpdateServer.h>
18 16
 #elif defined(ARDUINO_ARCH_ESP32)
@@ -25,46 +23,68 @@
25 23
 
26 24
 void SimpleUpdater::get(void) {
27 25
     String uploadPage = F(
28
-        "<html><head>"
29
-        "<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
30
-        "<title>SimpleUpdater ESP32</title>"
31
-        "</head><body>"
32
-        "<h1>SimpleUpdater</h1>"
33
-        "<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
34
-        "<input type='file' name='update'>"
35
-        "<input type='submit' value='Update'>"
36
-        "</form>"
37
-        "<div id='prg'>progress: 0%</div>"
38
-        "<a href=\"/\">Back to Main Page</a>"
39
-        "<script>"
40
-        "$('form').submit(function(e){"
41
-        "e.preventDefault();"
42
-        "var form = $('#upload_form')[0];"
43
-        "var data = new FormData(form);"
44
-        " $.ajax({"
45
-        "url: '/update',"
46
-        "type: 'POST',"
47
-        "data: data,"
48
-        "contentType: false,"
49
-        "processData:false,"
50
-        "xhr: function() {"
51
-        "var xhr = new window.XMLHttpRequest();"
52
-        "xhr.upload.addEventListener('progress', function(evt) {"
53
-        "if (evt.lengthComputable) {"
54
-        "var per = evt.loaded / evt.total;"
55
-        "$('#prg').html('progress: ' + Math.round(per*100) + '%');"
56
-        "}"
57
-        "}, false);"
58
-        "return xhr;"
59
-        "},"
60
-        "success:function(d, s) {"
61
-        "console.log('success!')" 
62
-        "},"
63
-        "error: function (a, b, c) {"
64
-        "}"
65
-        "});"
66
-        "});"
67
-        "</script>"
26
+        "<!DOCTYPE html>\n"
27
+        "<html><head>\n"
28
+        "<meta charset='utf-8'/>\n"
29
+        "<meta name='viewport' content='width=device-width, initial-scale=1'/>\n"
30
+        "<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>\n"
31
+        "<title>SimpleUpdater ESP32</title>\n"
32
+        "<style>\n"
33
+        "@media (prefers-color-scheme: dark) {\n"
34
+            "body {\n"
35
+                "background-color: black;\n"
36
+                "color: white;\n"
37
+            "}\n"
38
+        "}\n"
39
+        "</style>\n"
40
+        "</head><body>\n"
41
+
42
+        "<h1>SimpleUpdater</h1>\n"
43
+        "<p>Select the update file. If you have built this project with PlatformIO, you can find a firmware.bin in the .pio folder.</p>\n"
44
+        "<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>\n"
45
+        "<input type='file' name='update' accept='.bin'>\n"
46
+        "<input type='submit' value='Update'>\n"
47
+        "</form><br>\n"
48
+        "<div id='prg'>progress: 0%</div>\n"
49
+        "<p>After the update is finished, you will automatically be redirected to the main page.</p>\n"
50
+        "<a href=\"/\">Back to Main Page</a>\n"
51
+
52
+        "<script>\n"
53
+        "$('form').submit(function(e){\n"
54
+            "e.preventDefault();\n"
55
+            "var form = $('#upload_form')[0];\n"
56
+            "var data = new FormData(form);\n"
57
+            " $.ajax({\n"
58
+                "url: '/update',\n"
59
+                "type: 'POST',\n"
60
+                "data: data,\n"
61
+                "contentType: false,\n"
62
+                "processData:false,\n"
63
+                "xhr: function() {\n"
64
+                    "var xhr = new window.XMLHttpRequest();\n"
65
+                    "xhr.upload.addEventListener('progress', function(evt) {\n"
66
+                        "if (evt.lengthComputable) {\n"
67
+                            "var per = evt.loaded / evt.total;\n"
68
+                            "$('#prg').html('progress: ' + Math.round(per*100) + '%');\n"
69
+                        "}\n"
70
+                    "}, false);\n"
71
+                    "return xhr;\n"
72
+                "},\n"
73
+                "success: function(d, s) {\n"
74
+                    "$('#prg').html('progress: success! redirecting...');\n"
75
+                    "setTimeout(function() {\n"
76
+                        "window.location.href = '/';\n"
77
+                    "}, 3000);\n"
78
+                "},\n"
79
+                "error: function(a, b, c) {\n"
80
+                    "$('#prg').html('progress: finished! redirecting...');\n"
81
+                    "setTimeout(function() {\n"
82
+                        "window.location.href = '/';\n"
83
+                    "}, 1000);\n"
84
+                "}\n"
85
+            "});\n"
86
+        "});\n"
87
+        "</script>\n"
68 88
         "</body></html>"
69 89
     );
70 90
     
@@ -102,6 +122,8 @@ void SimpleUpdater::postUpload(void) {
102 122
 
103 123
 #endif
104 124
 
125
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
126
+
105 127
 void SimpleUpdater::setup(UPDATE_WEB_SERVER *_server) {
106 128
     if (_server == NULL) {
107 129
         return;

+ 435
- 0
src/html.cpp View File

@@ -0,0 +1,435 @@
1
+/*
2
+ * html.cpp
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#include <Arduino.h>
15
+
16
+#if defined(ARDUINO_ARCH_ESP8266)
17
+
18
+#include <ESP8266WiFi.h>
19
+#include <ESP8266WebServer.h>
20
+#include <ESP8266mDNS.h>
21
+#define ESP_PLATFORM_NAME "ESP8266"
22
+
23
+#elif defined(ARDUINO_ARCH_ESP32)
24
+
25
+#include <WiFi.h>
26
+#include <WebServer.h>
27
+#include <ESPmDNS.h>
28
+#define ESP_PLATFORM_NAME "ESP32"
29
+
30
+#elif defined(ARDUINO_ARCH_AVR)
31
+
32
+#include <UnoWiFiDevEdSerial1.h>
33
+#include <WiFiLink.h>
34
+#define ESP_PLATFORM_NAME "Uno WiFi"
35
+
36
+#endif
37
+
38
+#include "config.h"
39
+#include "DebugLog.h"
40
+#include "sensors.h"
41
+#include "servers.h"
42
+#include "memory.h"
43
+#include "relais.h"
44
+#include "moisture.h"
45
+#include "html.h"
46
+
47
+#if defined(ARDUINO_ARCH_AVR)
48
+#define ARDUINO_SEND_PARTIAL_PAGE() do { \
49
+        size_t len = message.length(), off = 0; \
50
+        while (off < len) { \
51
+            if ((len - off) >= 50) { \
52
+                client.write(message.c_str() + off, 50); \
53
+                off += 50; \
54
+            } else { \
55
+                client.write(message.c_str() + off, len - off); \
56
+                off = len; \
57
+            } \
58
+        } \
59
+        message = ""; \
60
+    } while (false);
61
+#else
62
+#define ARDUINO_SEND_PARTIAL_PAGE() while (false) { }
63
+#endif
64
+
65
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
66
+void handlePage(int mode, int id) {
67
+#else
68
+void handlePage(WiFiClient &client, int mode, int id) {
69
+#endif
70
+    String message;
71
+
72
+    message += F("<!DOCTYPE html>");
73
+    message += F("<html><head>");
74
+    message += F("<meta charset='utf-8'/>");
75
+    message += F("<meta name='viewport' content='width=device-width, initial-scale=1'/>");
76
+    message += F("<title>" ESP_PLATFORM_NAME " Environment Sensor</title>");
77
+    message += F("<style>");
78
+    message += F(".log {\n");
79
+    message += F(    "max-height: 300px;\n");
80
+    message += F(    "padding: 0 1.0em;\n");
81
+    message += F(    "max-width: 1200px;\n");
82
+    message += F(    "margin: auto;\n");
83
+    message += F(    "margin-top: 1.5em;\n");
84
+    message += F(    "border: 1px dashed black;\n");
85
+    message += F(    "font-family: monospace;\n");
86
+    message += F(    "overflow-y: scroll;\n");
87
+    message += F(    "word-break: break-all;\n");
88
+    message += F("}\n");
89
+    message += F("#logbuf {\n");
90
+    message += F(    "white-space: break-spaces;\n");
91
+    message += F("}\n");
92
+    message += F("@media (prefers-color-scheme: dark) {");
93
+    message += F(    "body {");
94
+    message += F(        "background-color: black;");
95
+    message += F(        "color: white;");
96
+    message += F(    "}");
97
+    message += F(    ".log {\n");
98
+    message += F(        "border-color: white;");
99
+    message += F(    "}");
100
+    message += F("}");
101
+    message += F("</style>");
102
+    message += F("</head><body>");
103
+    message += F("<h1>" ESP_PLATFORM_NAME " Environment Sensor</h1>");
104
+    message += F("\n<p>\n");
105
+    message += F("Version: ");
106
+    message += ESP_ENV_VERSION;
107
+    message += F("\n<br>\n");
108
+    message += F("Location: ");
109
+    message += SENSOR_LOCATION;
110
+    message += F("\n<br>\n");
111
+    message += F("ID: ");
112
+    message += SENSOR_ID;
113
+
114
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
115
+    message += F("\n<br>\n");
116
+    message += F("MAC: ");
117
+    message += WiFi.macAddress();
118
+#endif
119
+
120
+    message += F("\n</p>\n");
121
+
122
+    ARDUINO_SEND_PARTIAL_PAGE();
123
+
124
+#if defined(ARDUINO_ARCH_ESP8266)
125
+    
126
+    message += F("<p>");
127
+    message += F("Reset reason: ");
128
+    message += ESP.getResetReason();
129
+    message += F("<br>");
130
+    message += F("Free heap: ");
131
+    message += String(ESP.getFreeHeap());
132
+    message += F(" (");
133
+    message += String(ESP.getHeapFragmentation());
134
+    message += F("% fragmentation)");
135
+    message += F("<br>");
136
+    message += F("Free sketch space: ");
137
+    message += String(ESP.getFreeSketchSpace());
138
+    message += F("<br>");
139
+    message += F("Flash chip real size: ");
140
+    message += String(ESP.getFlashChipRealSize());
141
+
142
+    if (ESP.getFlashChipSize() != ESP.getFlashChipRealSize()) {
143
+        message += F("<br>");
144
+        message += F("WARNING: sdk chip size (");
145
+        message += (ESP.getFlashChipSize());
146
+        message += F(") does not match!");
147
+    }
148
+    
149
+    message += F("</p>");
150
+    
151
+#elif defined(ARDUINO_ARCH_ESP32)
152
+
153
+    message += F("<p>");
154
+    message += F("Free heap: ");
155
+    message += String(ESP.getFreeHeap() / 1024.0);
156
+    message += F("k<br>");
157
+    message += F("Free sketch space: ");
158
+    message += String(ESP.getFreeSketchSpace() / 1024.0);
159
+    message += F("k<br>");
160
+    message += F("Flash chip size: ");
161
+    message += String(ESP.getFlashChipSize() / 1024.0);
162
+    message += F("k</p><hr>");
163
+    
164
+#endif
165
+
166
+    message += F("\n<p>\n");
167
+    if (found_bme1) {
168
+        message += F("BME280 Low:");
169
+        message += F("\n<br>\n");
170
+        message += F("Temperature: ");
171
+        message += String(bme1_temp());
172
+        message += F("\n<br>\n");
173
+        message += F("Humidity: ");
174
+        message += String(bme1_humid());
175
+        message += F("\n<br>\n");
176
+        message += F("Pressure: ");
177
+        message += String(bme1_pressure());
178
+        message += F("\n<br>\n");
179
+        message += F("Offset: ");
180
+        message += String(config.bme1_temp_off);
181
+        message += F("\n<br>\n");
182
+        message += F("<form method=\"GET\" action=\"/calibrate\">");
183
+        message += F("<input type=\"text\" name=\"bme1\" placeholder=\"Real Temp.\">");
184
+        message += F("<input type=\"submit\" value=\"Calibrate\">");
185
+        message += F("</form>");
186
+    } else {
187
+        message += F("BME280 (low) not connected!");
188
+    }
189
+    message += F("\n</p><hr>\n");
190
+
191
+    message += F("\n<p>\n");
192
+    if (found_bme2) {
193
+        message += F("BME280 High:");
194
+        message += F("\n<br>\n");
195
+        message += F("Temperature: ");
196
+        message += String(bme2_temp());
197
+        message += F("\n<br>\n");
198
+        message += F("Humidity: ");
199
+        message += String(bme2_humid());
200
+        message += F("\n<br>\n");
201
+        message += F("Pressure: ");
202
+        message += String(bme2_pressure());
203
+        message += F("\n<br>\n");
204
+        message += F("Offset: ");
205
+        message += String(config.bme2_temp_off);
206
+        message += F("\n<br>\n");
207
+        message += F("<form method=\"GET\" action=\"/calibrate\">");
208
+        message += F("<input type=\"text\" name=\"bme2\" placeholder=\"Real Temp.\">");
209
+        message += F("<input type=\"submit\" value=\"Calibrate\">");
210
+        message += F("</form>");
211
+    } else {
212
+        message += F("BME280 (high) not connected!");
213
+    }
214
+    message += F("\n</p><hr>\n");
215
+
216
+    ARDUINO_SEND_PARTIAL_PAGE();
217
+
218
+    message += F("\n<p>\n");
219
+    if (found_sht) {
220
+        message += F("SHT21:");
221
+        message += F("\n<br>\n");
222
+        message += F("Temperature: ");
223
+        message += String(sht_temp());
224
+        message += F("\n<br>\n");
225
+        message += F("Humidity: ");
226
+        message += String(sht_humid());
227
+        message += F("\n<br>\n");
228
+        message += F("Offset: ");
229
+        message += String(config.sht_temp_off);
230
+        message += F("\n<br>\n");
231
+        message += F("<form method=\"GET\" action=\"/calibrate\">");
232
+        message += F("<input type=\"text\" name=\"sht\" placeholder=\"Real Temp.\">");
233
+        message += F("<input type=\"submit\" value=\"Calibrate\">");
234
+        message += F("</form>");
235
+    } else {
236
+        message += F("SHT21 not connected!");
237
+    }
238
+    message += F("\n</p><hr>\n");
239
+
240
+#ifdef ENABLE_CCS811
241
+
242
+    message += F("\n<p>\n");
243
+    if (found_ccs1) {
244
+        message += F("CCS811 Low:");
245
+        message += F("\n<br>\n");
246
+        message += F("eCO2: ");
247
+        message += String(ccs1_eco2());
248
+        message += F("ppm");
249
+        message += F("\n<br>\n");
250
+        message += F("TVOC: ");
251
+        message += String(ccs1_tvoc());
252
+        message += F("ppb");
253
+
254
+        if (!ccs1_data_valid) {
255
+            message += F("\n<br>\n");
256
+            message += F("Data invalid (");
257
+            message += String(ccs1_error_code);
258
+            message += F(")!");
259
+        }
260
+    } else {
261
+        message += F("CCS811 (Low) not connected!");
262
+    }
263
+    message += F("\n</p><hr>\n");
264
+
265
+    message += F("\n<p>\n");
266
+    if (found_ccs2) {
267
+        message += F("CCS811 High:");
268
+        message += F("\n<br>\n");
269
+        message += F("eCO2: ");
270
+        message += String(ccs2_eco2());
271
+        message += F("ppm");
272
+        message += F("\n<br>\n");
273
+        message += F("TVOC: ");
274
+        message += String(ccs2_tvoc());
275
+        message += F("ppb");
276
+
277
+        if (!ccs2_data_valid) {
278
+            message += F("\n<br>\n");
279
+            message += F("Data invalid (");
280
+            message += String(ccs2_error_code);
281
+            message += F(")!");
282
+        }
283
+    } else {
284
+        message += F("CCS811 (High) not connected!");
285
+    }
286
+    message += F("\n</p><hr>\n");
287
+
288
+#endif // ENABLE_CCS811
289
+
290
+    ARDUINO_SEND_PARTIAL_PAGE();
291
+
292
+#ifdef FEATURE_MOISTURE
293
+    for (int i = 0; i < moisture_count(); i++) {
294
+        int moisture = moisture_read(i);
295
+        if (moisture < moisture_max()) {
296
+            message += F("\n<p>\n");
297
+            message += F("Sensor ");
298
+            message += String(i + 1);
299
+            message += F(":\n<br>\n");
300
+            message += F("Moisture: ");
301
+            message += String(moisture);
302
+            message += F(" / ");
303
+            message += String(moisture_max());
304
+            message += F("\n</p>\n");
305
+        }
306
+    }
307
+    
308
+    if (moisture_count() <= 0) {
309
+        message += F("\n<p>\n");
310
+        message += F("No moisture sensors configured!");
311
+        message += F("\n</p>\n");
312
+    }
313
+
314
+    ARDUINO_SEND_PARTIAL_PAGE();
315
+#endif // FEATURE_MOISTURE
316
+
317
+#ifdef FEATURE_RELAIS
318
+    message += F("\n<p>\n");
319
+    for (int i = 0; i < relais_count(); i++) {
320
+        message += String(F("<a href=\"/on?id=")) + String(i) + String(F("\">Relais ")) + String(i) + String(F(" On (")) + relais_name(i) + String(F(")</a><br>\n"));
321
+        message += String(F("<a href=\"/off?id=")) + String(i) + String(F("\">Relais ")) + String(i) + String(F(" Off (")) + relais_name(i) + String(F(")</a><br>\n"));
322
+    }
323
+    message += String(F("<a href=\"/on?id=")) + String(relais_count()) + String(F("\">All Relais On</a><br>\n"));
324
+    message += String(F("<a href=\"/off?id=")) + String(relais_count()) + String(F("\">All Relais Off</a><br>\n"));
325
+    message += F("</p>\n");
326
+
327
+    if ((mode >= 0) && (mode <= 1)) {
328
+        message += F("<p>");
329
+        message += F("Turned Relais ");
330
+        message += (id < relais_count()) ? String(id) : String(F("1-4"));
331
+        message += (mode ? String(F(" On")) : String(F(" Off")));
332
+        message += F("</p>\n");
333
+    }
334
+
335
+    message += F("\n<p>\n");
336
+    for (int i = 0; i < relais_count(); i++) {
337
+        message += String(F("Relais ")) + String(i) + String(F(" (")) + relais_name(i) + String(F(") = ")) + (relais_get(i) ? String(F("On")) : String(F("Off"))) + String(F("<br>\n"));
338
+    }
339
+    message += F("</p>\n");
340
+
341
+    ARDUINO_SEND_PARTIAL_PAGE();
342
+#endif // FEATURE_RELAIS
343
+
344
+    if (mode == 42) {
345
+        message += F("<p>New calibration value saved!</p>\n");
346
+    }
347
+
348
+#if ! defined(ARDUINO_ARCH_AVR)
349
+    message += F("<p>");
350
+    message += F("Try <a href=\"/update\">/update</a> for OTA firmware updates!");
351
+    message += F("</p><p>");
352
+    message += F("Try <a href=\"/reset\">/reset</a> to reset ESP!");
353
+    message += F("</p>");
354
+#endif
355
+    
356
+    message += F("<p>");
357
+#ifdef ENABLE_INFLUXDB_LOGGING
358
+    message += F("InfluxDB: ");
359
+    message += INFLUXDB_DATABASE;
360
+    message += F(" @ ");
361
+    message += INFLUXDB_HOST;
362
+    message += F(":");
363
+    message += String(INFLUXDB_PORT);
364
+#else
365
+    message += F("InfluxDB logging not enabled!");
366
+#endif
367
+    message += F("</p>");
368
+
369
+    message += F("<p>Uptime: ");
370
+    message += String(millis() / 1000);
371
+    message += F(" sec.</p>");
372
+
373
+#if ! defined(ARDUINO_ARCH_AVR)
374
+    message += F("<hr><p>Debug Log:</p>");
375
+    message += F("<div class='log'><pre id='logbuf'>");
376
+    message += debug.getBuffer();
377
+    message += F("</pre></div>");
378
+#endif
379
+
380
+    message += F("</body>");
381
+
382
+#if ! defined(ARDUINO_ARCH_AVR)
383
+    message += F("<script type='text/javascript'>\n");
384
+    message += F("var socket = new WebSocket('ws://' + window.location.hostname + ':81');\n");
385
+    message += F("socket.onmessage = function(e) {");
386
+    message += F(    "var log = document.getElementById('logbuf');");
387
+    message += F(    "var div = document.getElementsByClassName('log')[0];");
388
+    message += F(    "log.innerHTML += e.data.substring(4);");
389
+    message += F(    "if (log.innerHTML.length > (1024 * 1024)) {");
390
+    message += F(        "log.innerHTML = log.innerHTML.substring(1024 * 1024);");
391
+    message += F(    "}");
392
+    message += F(    "div.scrollTop = div.scrollHeight;");
393
+    message += F("};\n");
394
+    message += F("var hist = document.getElementsByClassName('log')[0];\n");
395
+    message += F("hist.scrollTop = hist.scrollHeight;\n");
396
+    message += F("</script>\n");
397
+#endif
398
+
399
+    message += F("</html>");
400
+
401
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
402
+    server.send(200, "text/html", message);
403
+#else
404
+    ARDUINO_SEND_PARTIAL_PAGE();
405
+#endif
406
+}
407
+
408
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
409
+void handleReset() {
410
+    String message;
411
+    message += F("<!DOCTYPE html>");
412
+    message += F("<html><head>");
413
+    message += F("<meta charset='utf-8'/>");
414
+    message += F("<meta name='viewport' content='width=device-width, initial-scale=1'/>");
415
+    message += F("<meta http-equiv='refresh' content='10; URL=/'/>");
416
+    message += F("<title>" ESP_PLATFORM_NAME " Environment Sensor</title>");
417
+    message += F("<style>");
418
+    message += F("@media (prefers-color-scheme: dark) {");
419
+    message += F(    "body {");
420
+    message += F(        "background-color: black;");
421
+    message += F(        "color: white;");
422
+    message += F(    "}");
423
+    message += F("}");
424
+    message += F("</style>");
425
+    message += F("</head><body>");
426
+    message += F("<p>Resetting in 2s...</p>");
427
+    message += F("<p>Auto redirect in 10s. Please retry manually on error.</p>");
428
+    message += F("<p>Go <a href=\"/\">back</a> to start.</p>");
429
+    message += F("</body></html>");
430
+    server.send(200, "text/html", message);
431
+
432
+    delay(2000);
433
+    ESP.restart();
434
+}
435
+#endif

+ 290
- 0
src/influx.cpp View File

@@ -0,0 +1,290 @@
1
+/*
2
+ * influx.cpp
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#include <Arduino.h>
15
+
16
+#include "config.h"
17
+#include "DebugLog.h"
18
+#include "sensors.h"
19
+#include "relais.h"
20
+#include "moisture.h"
21
+#include "influx.h"
22
+
23
+#ifdef ENABLE_INFLUXDB_LOGGING
24
+
25
+#if defined(ARDUINO_ARCH_ESP8266)
26
+
27
+#include <ESP8266WiFi.h>
28
+#include <ESP8266WebServer.h>
29
+#include <ESP8266mDNS.h>
30
+
31
+#elif defined(ARDUINO_ARCH_ESP32)
32
+
33
+#include <WiFi.h>
34
+#include <WebServer.h>
35
+#include <ESPmDNS.h>
36
+
37
+#elif defined(ARDUINO_ARCH_AVR)
38
+
39
+#include <UnoWiFiDevEdSerial1.h>
40
+#include <WiFiLink.h>
41
+
42
+#endif
43
+
44
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
45
+#include <InfluxDb.h>
46
+#else
47
+#include "SimpleInflux.h"
48
+#endif
49
+
50
+static Influxdb influx(INFLUXDB_HOST, INFLUXDB_PORT);
51
+static int error_count = 0;
52
+static unsigned long last_db_write_time = 0;
53
+
54
+void initInflux() {
55
+    debug.println(F("Influx"));
56
+    influx.setDb(INFLUXDB_DATABASE);
57
+}
58
+
59
+void runInflux() {
60
+    unsigned long time = millis();
61
+
62
+    if ((time - last_db_write_time) >= DB_WRITE_INTERVAL) {
63
+        last_db_write_time = time;
64
+        writeDatabase();
65
+    }
66
+
67
+#ifdef INFLUX_MAX_ERRORS_RESET
68
+    if (error_count >= INFLUX_MAX_ERRORS_RESET) {
69
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
70
+        ESP.restart();
71
+#else
72
+        // TODO implement watchdog reset for AVR
73
+#endif
74
+    }
75
+#endif // INFLUX_MAX_ERRORS_RESET
76
+}
77
+
78
+static boolean writeMeasurement(InfluxData &measurement) {
79
+    boolean success = influx.write(measurement);
80
+    if (!success) {
81
+        error_count++;
82
+        for (int i = 0; i < 10; i++) {
83
+            digitalWrite(BUILTIN_LED_PIN, LOW); // LED on
84
+            delay(LED_ERROR_BLINK_INTERVAL);
85
+            digitalWrite(BUILTIN_LED_PIN, HIGH); // LED off
86
+            delay(LED_ERROR_BLINK_INTERVAL);
87
+        }
88
+    }
89
+    return success;
90
+}
91
+
92
+void writeDatabase() {
93
+#if defined(ARDUINO_ARCH_AVR)
94
+    debug.println(F("Writing to InfluxDB"));
95
+
96
+    InfluxData measurement("");
97
+#endif
98
+
99
+    if (found_bme1) {
100
+#if defined(ARDUINO_ARCH_AVR)
101
+        measurement.clear();
102
+        measurement.setName("environment");
103
+#else
104
+        InfluxData measurement("environment");
105
+#endif
106
+
107
+        measurement.addTag("location", SENSOR_LOCATION);
108
+        measurement.addTag("location-id", SENSOR_ID);
109
+        measurement.addTag("placement", "1");
110
+        measurement.addTag("sensor", "bme280");
111
+
112
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
113
+        measurement.addTag("device", WiFi.macAddress());
114
+#endif
115
+
116
+        measurement.addValue("temperature", bme1_temp());
117
+        measurement.addValue("pressure", bme1_pressure());
118
+        measurement.addValue("humidity", bme1_humid());
119
+
120
+        debug.println(F("Writing bme1"));
121
+        writeMeasurement(measurement);
122
+        debug.println(F("Done!"));
123
+    }
124
+
125
+    if (found_bme2) {
126
+#if defined(ARDUINO_ARCH_AVR)
127
+        measurement.clear();
128
+        measurement.setName("environment");
129
+#else
130
+        InfluxData measurement("environment");
131
+#endif
132
+
133
+        measurement.addTag("location", SENSOR_LOCATION);
134
+        measurement.addTag("location-id", SENSOR_ID);
135
+        measurement.addTag("placement", "2");
136
+        measurement.addTag("sensor", "bme280");
137
+
138
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
139
+        measurement.addTag("device", WiFi.macAddress());
140
+#endif
141
+
142
+        measurement.addValue("temperature", bme2_temp());
143
+        measurement.addValue("pressure", bme2_pressure());
144
+        measurement.addValue("humidity", bme2_humid());
145
+
146
+        debug.println(F("Writing bme2"));
147
+        writeMeasurement(measurement);
148
+        debug.println(F("Done!"));
149
+    }
150
+
151
+    if (found_sht) {
152
+#if defined(ARDUINO_ARCH_AVR)
153
+        measurement.clear();
154
+        measurement.setName("environment");
155
+#else
156
+        InfluxData measurement("environment");
157
+#endif
158
+
159
+        measurement.addTag("location", SENSOR_LOCATION);
160
+        measurement.addTag("location-id", SENSOR_ID);
161
+        measurement.addTag("sensor", "sht21");
162
+
163
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
164
+        measurement.addTag("device", WiFi.macAddress());
165
+#endif
166
+
167
+        measurement.addValue("temperature", sht_temp());
168
+        measurement.addValue("humidity", sht_humid());
169
+
170
+        debug.println(F("Writing sht"));
171
+        writeMeasurement(measurement);
172
+        debug.println(F("Done!"));
173
+    }
174
+
175
+#ifdef ENABLE_CCS811
176
+
177
+    if (found_ccs1) {
178
+#if defined(ARDUINO_ARCH_AVR)
179
+        measurement.clear();
180
+        measurement.setName("environment");
181
+#else
182
+        InfluxData measurement("environment");
183
+#endif
184
+
185
+        measurement.addTag("location", SENSOR_LOCATION);
186
+        measurement.addTag("location-id", SENSOR_ID);
187
+        measurement.addTag("placement", "1");
188
+        measurement.addTag("sensor", "ccs811");
189
+
190
+        String err(ccs1_error_code);
191
+        measurement.addTag("error", err);
192
+
193
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
194
+        measurement.addTag("device", WiFi.macAddress());
195
+#endif
196
+
197
+        measurement.addValue("eco2", ccs1_eco2());
198
+        measurement.addValue("tvoc", ccs1_tvoc());
199
+
200
+        debug.println(F("Writing ccs1"));
201
+        writeMeasurement(measurement);
202
+        debug.println(F("Done!"));
203
+    }
204
+
205
+    if (found_ccs2) {
206
+#if defined(ARDUINO_ARCH_AVR)
207
+        measurement.clear();
208
+        measurement.setName("environment");
209
+#else
210
+        InfluxData measurement("environment");
211
+#endif
212
+
213
+        measurement.addTag("location", SENSOR_LOCATION);
214
+        measurement.addTag("location-id", SENSOR_ID);
215
+        measurement.addTag("placement", "2");
216
+        measurement.addTag("sensor", "ccs811");
217
+
218
+        String err(ccs2_error_code);
219
+        measurement.addTag("error", err);
220
+
221
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
222
+        measurement.addTag("device", WiFi.macAddress());
223
+#endif
224
+
225
+        measurement.addValue("eco2", ccs2_eco2());
226
+        measurement.addValue("tvoc", ccs2_tvoc());
227
+
228
+        debug.println(F("Writing ccs2"));
229
+        writeMeasurement(measurement);
230
+        debug.println(F("Done!"));
231
+    }
232
+
233
+#endif // ENABLE_CCS811
234
+
235
+#ifdef FEATURE_MOISTURE
236
+    for (int i = 0; i < moisture_count(); i++) {
237
+        int moisture = moisture_read(i);
238
+        if (moisture < moisture_max()) {
239
+#if defined(ARDUINO_ARCH_AVR)
240
+            measurement.clear();
241
+            measurement.setName("moisture");
242
+#else
243
+            InfluxData measurement("moisture");
244
+#endif
245
+
246
+            measurement.addTag("location", SENSOR_LOCATION);
247
+            measurement.addTag("location-id", SENSOR_ID);
248
+            String sensor(i + 1, DEC);
249
+            measurement.addTag("sensor", sensor);
250
+
251
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
252
+            measurement.addTag("device", WiFi.macAddress());
253
+#endif
254
+
255
+            measurement.addValue("value", moisture);
256
+            measurement.addValue("maximum", moisture_max());
257
+
258
+            debug.print(F("Writing moisture "));
259
+            debug.println(i);
260
+            writeMeasurement(measurement);
261
+            debug.println(F("Done!"));
262
+        }
263
+    }
264
+#endif // FEATURE_MOISTURE
265
+
266
+#ifdef FEATURE_RELAIS
267
+    for (int i = 0; i < relais_count(); i++) {
268
+        InfluxData measurement("relais");
269
+        measurement.addTag("location", SENSOR_LOCATION);
270
+        measurement.addTag("location-id", SENSOR_ID);
271
+        measurement.addTag("id", String(i));
272
+        measurement.addTag("name", relais_name(i));
273
+
274
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
275
+        measurement.addTag("device", WiFi.macAddress());
276
+#endif
277
+
278
+        measurement.addValue("state", relais_get(i));
279
+        writeMeasurement(measurement);
280
+    }
281
+#endif // FEATURE_RELAIS
282
+}
283
+
284
+#else
285
+
286
+void initInflux() { }
287
+void runInflux() { }
288
+void writeDatabase() { }
289
+
290
+#endif // ENABLE_INFLUXDB_LOGGING

+ 34
- 1120
src/main.cpp
File diff suppressed because it is too large
View File


+ 130
- 0
src/memory.cpp View File

@@ -0,0 +1,130 @@
1
+/*
2
+ * memory.cpp
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#include <Arduino.h>
15
+#include <EEPROM.h>
16
+
17
+#include "DebugLog.h"
18
+#include "memory.h"
19
+
20
+ConfigMemory mem_read() {
21
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
22
+    EEPROM.begin(sizeof(ConfigMemory) + 1);
23
+#endif
24
+
25
+    ConfigMemory cm;
26
+    uint8_t cs = 0x42;
27
+    uint8_t *us = (uint8_t *)((void *)&cm);
28
+    for (unsigned int i = 0; i < sizeof(ConfigMemory); i++) {
29
+        uint8_t v = EEPROM.read(i);
30
+        cs ^= v;
31
+        us[i] = v;
32
+    }
33
+
34
+    uint8_t checksum = EEPROM.read(sizeof(ConfigMemory));
35
+
36
+    if (cs == checksum) {
37
+        return cm;
38
+    } else {
39
+        debug.print(F("Config checksum mismatch: "));
40
+        debug.print(cs);
41
+        debug.print(F(" != "));
42
+        debug.println(checksum);
43
+
44
+        ConfigMemory empty;
45
+        return empty;
46
+    }
47
+}
48
+
49
+void mem_write(ConfigMemory mem) {
50
+    uint8_t cs = 0x42;
51
+    const uint8_t *us = (const uint8_t *)((const void *)&mem);
52
+    for (unsigned int i = 0; i < sizeof(ConfigMemory); i++) {
53
+        uint8_t v = us[i];
54
+        EEPROM.write(i, v);
55
+        cs ^= v;
56
+    }
57
+
58
+    EEPROM.write(sizeof(ConfigMemory), cs);
59
+
60
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
61
+    EEPROM.commit();
62
+#endif
63
+
64
+    debug.print(F("Written config with checksum "));
65
+    debug.println(cs);
66
+}
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+/*
75
+0
76
+0
77
+0
78
+0
79
+0
80
+0
81
+0
82
+0
83
+0
84
+0
85
+0
86
+0
87
+30
88
+5
89
+22
90
+192
91
+0
92
+0
93
+0
94
+0
95
+0
96
+0
97
+0
98
+0
99
+205
100
+
101
+
102
+
103
+
104
+
105
+0
106
+0
107
+0
108
+0
109
+0
110
+0
111
+0
112
+0
113
+0
114
+0
115
+0
116
+0
117
+184
118
+158
119
+19
120
+192
121
+0
122
+0
123
+0
124
+0
125
+0
126
+0
127
+0
128
+0
129
+245
130
+*/

+ 180
- 0
src/mqtt.cpp View File

@@ -0,0 +1,180 @@
1
+/*
2
+ * mqtt.cpp
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#include <Arduino.h>
15
+
16
+#include "config.h"
17
+#include "DebugLog.h"
18
+#include "sensors.h"
19
+#include "relais.h"
20
+#include "influx.h"
21
+#include "mqtt.h"
22
+
23
+#ifdef ENABLE_MQTT
24
+
25
+#if defined(ARDUINO_ARCH_ESP8266)
26
+
27
+#include <ESP8266WiFi.h>
28
+#include <ESP8266WebServer.h>
29
+#include <ESP8266mDNS.h>
30
+
31
+#elif defined(ARDUINO_ARCH_ESP32)
32
+
33
+#include <WiFi.h>
34
+#include <WebServer.h>
35
+#include <ESPmDNS.h>
36
+
37
+#elif defined(ARDUINO_ARCH_AVR)
38
+
39
+#include <UnoWiFiDevEdSerial1.h>
40
+#include <WiFiLink.h>
41
+
42
+#endif
43
+
44
+#include <PubSubClient.h>
45
+
46
+WiFiClient mqttClient;
47
+PubSubClient mqtt(mqttClient);
48
+static unsigned long last_mqtt_reconnect_time = 0;
49
+static unsigned long last_mqtt_write_time = 0;
50
+
51
+static void writeMQTT() {
52
+    if (!mqtt.connected()) {
53
+        return;
54
+    }
55
+
56
+    if (found_bme1) {
57
+        mqtt.publish(SENSOR_LOCATION "/temperature", String(bme1_temp()).c_str(), true);
58
+        mqtt.publish(SENSOR_LOCATION "/humidity", String(bme1_humid()).c_str(), true);
59
+        mqtt.publish(SENSOR_LOCATION "/pressure", String(bme1_pressure()).c_str(), true);
60
+    } else if (found_bme2) {
61
+        mqtt.publish(SENSOR_LOCATION "/temperature", String(bme2_temp()).c_str(), true);
62
+        mqtt.publish(SENSOR_LOCATION "/humidity", String(bme2_humid()).c_str(), true);
63
+        mqtt.publish(SENSOR_LOCATION "/pressure", String(bme2_pressure()).c_str(), true);
64
+    } else if (found_sht) {
65
+        mqtt.publish(SENSOR_LOCATION "/temperature", String(sht_temp()).c_str(), true);
66
+        mqtt.publish(SENSOR_LOCATION "/humidity", String(sht_humid()).c_str(), true);
67
+    }
68
+
69
+#ifdef ENABLE_CCS811
70
+    if (found_ccs1) {
71
+        mqtt.publish(SENSOR_LOCATION "/eco2", String(ccs1_eco2()).c_str(), true);
72
+        mqtt.publish(SENSOR_LOCATION "/tvoc", String(ccs1_tvoc()).c_str(), true);
73
+    } else if (found_ccs2) {
74
+        mqtt.publish(SENSOR_LOCATION "/eco2", String(ccs2_eco2()).c_str(), true);
75
+        mqtt.publish(SENSOR_LOCATION "/tvoc", String(ccs2_tvoc()).c_str(), true);
76
+    }
77
+#endif // ENABLE_CCS811
78
+}
79
+
80
+static void mqttCallback(char* topic, byte* payload, unsigned int length) {
81
+#ifdef FEATURE_RELAIS
82
+    int state = 0;
83
+    int id = -1;
84
+
85
+    String ts(topic), ps((char *)payload);
86
+
87
+    String our_topic(SENSOR_LOCATION);
88
+    our_topic += "/";
89
+
90
+    if (!ts.startsWith(our_topic)) {
91
+        debug.print(F("Unknown MQTT room "));
92
+        debug.println(ts);
93
+        return;
94
+    }
95
+
96
+    String ids = ts.substring(our_topic.length());
97
+    for (int i = 0; i < relais_count(); i++) {
98
+        if (ids == relais_name(i)) {
99
+            id = i;
100
+            break;
101
+        }
102
+    }
103
+
104
+    if (id < 0) {
105
+        debug.print(F("Unknown MQTT topic "));
106
+        debug.println(ts);
107
+        return;
108
+    }
109
+
110
+    if (ps.indexOf("on") != -1) {
111
+        state = 1;
112
+    } else if (ps.indexOf("off") != -1) {
113
+        state = 0;
114
+    } else {
115
+        return;
116
+    }
117
+
118
+    if ((id >= 0) && (id < relais_count())) {
119
+        debug.print(F("Turning "));
120
+        debug.print(state ? "on" : "off");
121
+        debug.print(F(" relais "));
122
+        debug.println(id);
123
+
124
+        relais_set(id, state);
125
+
126
+        writeDatabase();
127
+    }
128
+#endif // FEATURE_RELAIS
129
+}
130
+
131
+static void mqttReconnect() {
132
+    // Create a random client ID
133
+    String clientId = F("ESP-" SENSOR_ID "-");
134
+    clientId += String(random(0xffff), HEX);
135
+
136
+    // Attempt to connect
137
+#if defined(MQTT_USER) && defined(MQTT_PASS)
138
+    if (mqtt.connect(clientId.c_str(), MQTT_USER, MQTT_PASS)) {
139
+#else
140
+    if (mqtt.connect(clientId.c_str())) {
141
+#endif
142
+
143
+#ifdef FEATURE_RELAIS
144
+        for (int i = 0; i < relais_count(); i++) {
145
+            String topic(SENSOR_LOCATION);
146
+            topic += String("/") + relais_name(i);
147
+            mqtt.subscribe(topic.c_str());
148
+        }
149
+#endif // FEATURE_RELAIS
150
+    }
151
+}
152
+
153
+void initMQTT() {
154
+    debug.println(F("MQTT"));
155
+    mqtt.setServer(MQTT_HOST, MQTT_PORT);
156
+    mqtt.setCallback(mqttCallback);
157
+}
158
+
159
+void runMQTT() {
160
+    unsigned long time = millis();
161
+
162
+    if ((time - last_mqtt_write_time) >= MQTT_WRITE_INTERVAL) {
163
+        last_mqtt_write_time = time;
164
+        writeMQTT();
165
+    }
166
+
167
+    if (!mqtt.connected() && ((millis() - last_mqtt_reconnect_time) >= MQTT_RECONNECT_INTERVAL)) {
168
+        last_mqtt_reconnect_time = millis();
169
+        mqttReconnect();
170
+    }
171
+
172
+    mqtt.loop();
173
+}
174
+
175
+#else
176
+
177
+void initMQTT() { }
178
+void runMQTT() { }
179
+
180
+#endif // ENABLE_MQTT

+ 10
- 0
src/relais.cpp View File

@@ -38,6 +38,16 @@ static String names[SERIAL_RELAIS_COUNT] = {
38 38
     String("light_big"),
39 39
     String("relais_2"),
40 40
     String("fan")
41
+#elif defined(SENSOR_LOCATION_LIVINGROOM_WORKSPACE)
42
+    String("light_pc"),
43
+    String("light_bench"),
44
+    String("relais_w2"),
45
+    String("relais_w3")
46
+#elif defined(SENSOR_LOCATION_LIVINGROOM_TV)
47
+    String("light_amp"),
48
+    String("light_box"),
49
+    String("light_kitchen"),
50
+    String("relais_t3")
41 51
 #else
42 52
     String("relais_0"),
43 53
     String("relais_1"),

+ 357
- 0
src/sensors.cpp View File

@@ -0,0 +1,357 @@
1
+/*
2
+ * sensors.cpp
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#include <Arduino.h>
15
+
16
+#include <Adafruit_BME280.h>
17
+#include <SHT2x.h>
18
+
19
+#ifdef ENABLE_CCS811
20
+#include <Adafruit_CCS811.h>
21
+#endif // ENABLE_CCS811
22
+
23
+#include "config.h"
24
+#include "DebugLog.h"
25
+#include "memory.h"
26
+#include "servers.h"
27
+#include "html.h"
28
+#include "sensors.h"
29
+
30
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
31
+#include <Wire.h>
32
+#endif
33
+
34
+#define SHT_I2C_ADDRESS HTDU21D_ADDRESS
35
+#define BME_I2C_ADDRESS_1 0x76
36
+#define BME_I2C_ADDRESS_2 0x77
37
+#define CCS811_ADDRESS_1 0x5A
38
+#define CCS811_ADDRESS_2 0x5B
39
+
40
+#if defined(ARDUINO_ARCH_ESP8266)
41
+
42
+#define I2C_SDA_PIN 2
43
+#define I2C_SCL_PIN 0
44
+
45
+static TwoWire Wire2;
46
+static SHT2x sht(SHT_I2C_ADDRESS, &Wire2);
47
+
48
+#elif defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_AVR)
49
+
50
+static SHT2x sht(SHT_I2C_ADDRESS, &Wire);
51
+
52
+#endif
53
+
54
+static Adafruit_BME280 bme1, bme2;
55
+
56
+bool found_bme1 = false;
57
+bool found_bme2 = false;
58
+bool found_sht = false;
59
+
60
+#ifdef ENABLE_CCS811
61
+static Adafruit_CCS811 ccs1, ccs2;
62
+bool found_ccs1 = false;
63
+bool found_ccs2 = false;
64
+bool ccs1_data_valid = false;
65
+bool ccs2_data_valid = false;
66
+int ccs1_error_code = 0;
67
+int ccs2_error_code = 0;
68
+#endif // ENABLE_CCS811
69
+
70
+static unsigned long last_sensor_handle_time = 0;
71
+
72
+float bme1_temp(void) {
73
+    while (1) {
74
+        float a = bme1.readTemperature();
75
+        float b = bme1.readTemperature();
76
+        
77
+        if ((a > b) && ((a - b) < 2.0)) {
78
+            return (a + b) / 2.0;
79
+        }
80
+        
81
+        if ((a < b) && ((b - a) < 2.0)) {
82
+            return (a + b) / 2.0;
83
+        }
84
+    }
85
+    return 0.0;
86
+}
87
+
88
+float bme2_temp(void) {
89
+    while (1) {
90
+        float a = bme2.readTemperature();
91
+        float b = bme2.readTemperature();
92
+        
93
+        if ((a > b) && ((a - b) < 2.0)) {
94
+            return (a + b) / 2.0;
95
+        }
96
+        
97
+        if ((a < b) && ((b - a) < 2.0)) {
98
+            return (a + b) / 2.0;
99
+        }
100
+    }
101
+    return 0.0;
102
+}
103
+
104
+float bme1_humid(void) {
105
+    while (1) {
106
+        float a = bme1.readHumidity();
107
+        float b = bme1.readHumidity();
108
+        
109
+        if ((a > b) && ((a - b) < 2.0)) {
110
+            return (a + b) / 2.0;
111
+        }
112
+        
113
+        if ((a < b) && ((b - a) < 2.0)) {
114
+            return (a + b) / 2.0;
115
+        }
116
+    }
117
+    return 0.0;
118
+}
119
+
120
+float bme2_humid(void) {
121
+    while (1) {
122
+        float a = bme2.readHumidity();
123
+        float b = bme2.readHumidity();
124
+        
125
+        if ((a > b) && ((a - b) < 2.0)) {
126
+            return (a + b) / 2.0;
127
+        }
128
+        
129
+        if ((a < b) && ((b - a) < 2.0)) {
130
+            return (a + b) / 2.0;
131
+        }
132
+    }
133
+    return 0.0;
134
+}
135
+
136
+float bme1_pressure(void) {
137
+    while (1) {
138
+        float a = bme1.readPressure();
139
+        float b = bme1.readPressure();
140
+        
141
+        if ((a > b) && ((a - b) < 2.0)) {
142
+            return (a + b) / 2.0;
143
+        }
144
+        
145
+        if ((a < b) && ((b - a) < 2.0)) {
146
+            return (a + b) / 2.0;
147
+        }
148
+    }
149
+    return 0.0;
150
+}
151
+
152
+float bme2_pressure(void) {
153
+    while (1) {
154
+        float a = bme2.readPressure();
155
+        float b = bme2.readPressure();
156
+        
157
+        if ((a > b) && ((a - b) < 2.0)) {
158
+            return (a + b) / 2.0;
159
+        }
160
+        
161
+        if ((a < b) && ((b - a) < 2.0)) {
162
+            return (a + b) / 2.0;
163
+        }
164
+    }
165
+    return 0.0;
166
+}
167
+
168
+float sht_temp(void) {
169
+    while (1) {
170
+        float a = sht.GetTemperature() + config.sht_temp_off;
171
+        float b = sht.GetTemperature() + config.sht_temp_off;
172
+        
173
+        if ((a > b) && ((a - b) < 2.0)) {
174
+            return (a + b) / 2.0;
175
+        }
176
+        
177
+        if ((a < b) && ((b - a) < 2.0)) {
178
+            return (a + b) / 2.0;
179
+        }
180
+    }
181
+    return 0.0;
182
+}
183
+
184
+float sht_humid(void) {
185
+    while (1) {
186
+        float a = sht.GetHumidity();
187
+        float b = sht.GetHumidity();
188
+        
189
+        if ((a > b) && ((a - b) < 2.0)) {
190
+            return (a + b) / 2.0;
191
+        }
192
+        
193
+        if ((a < b) && ((b - a) < 2.0)) {
194
+            return (a + b) / 2.0;
195
+        }
196
+    }
197
+    return 0.0;
198
+}
199
+
200
+#ifdef ENABLE_CCS811
201
+
202
+float ccs1_eco2(void) {
203
+    return ccs1.geteCO2();
204
+}
205
+
206
+float ccs1_tvoc(void) {
207
+    return ccs1.getTVOC();
208
+}
209
+
210
+float ccs2_eco2(void) {
211
+    return ccs2.geteCO2();
212
+}
213
+
214
+float ccs2_tvoc(void) {
215
+    return ccs2.getTVOC();
216
+}
217
+
218
+static void ccs_update() {
219
+    if (found_ccs1) {
220
+        if (ccs1.available()) {
221
+            if (found_bme1) {
222
+                ccs1.setEnvironmentalData(bme1_humid(), bme1_temp());
223
+            } else if (found_bme2) {
224
+                ccs1.setEnvironmentalData(bme2_humid(), bme2_temp());
225
+            } else if (found_sht) {
226
+                ccs1.setEnvironmentalData(sht_humid(), sht_temp());
227
+            }
228
+
229
+            ccs1_error_code = ccs1.readData();
230
+            ccs1_data_valid = (ccs1_error_code == 0);
231
+        }
232
+    }
233
+
234
+    if (found_ccs2) {
235
+        if (ccs2.available()) {
236
+            if (found_bme1) {
237
+                ccs2.setEnvironmentalData(bme1_humid(), bme1_temp());
238
+            } else if (found_bme2) {
239
+                ccs2.setEnvironmentalData(bme2_humid(), bme2_temp());
240
+            } else if (found_sht) {
241
+                ccs2.setEnvironmentalData(sht_humid(), sht_temp());
242
+            }
243
+
244
+            ccs2_error_code = ccs2.readData();
245
+            ccs2_data_valid = (ccs2_error_code == 0);
246
+        }
247
+    }
248
+}
249
+
250
+#endif // ENABLE_CCS811
251
+
252
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
253
+void handleCalibrate() {
254
+    bool diff = false;
255
+
256
+    if (server.hasArg("bme1")) {
257
+        diff = true;
258
+        String off_string = server.arg("bme1");
259
+        double real_temp = off_string.toDouble();
260
+        double meas_temp = bme1_temp() - config.bme1_temp_off;
261
+        config.bme1_temp_off = real_temp - meas_temp;
262
+    }
263
+
264
+    if (server.hasArg("bme2")) {
265
+        diff = true;
266
+        String off_string = server.arg("bme2");
267
+        double real_temp = off_string.toDouble();
268
+        double meas_temp = bme2_temp() - config.bme2_temp_off;
269
+        config.bme2_temp_off = real_temp - meas_temp;
270
+    }
271
+
272
+    if (server.hasArg("sht")) {
273
+        diff = true;
274
+        String off_string = server.arg("sht");
275
+        double real_temp = off_string.toDouble();
276
+        double meas_temp = sht_temp() - config.sht_temp_off;
277
+        config.sht_temp_off = real_temp - meas_temp;
278
+    }
279
+
280
+    if (diff) {
281
+        if (found_bme1) {
282
+            bme1.setTemperatureCompensation(config.bme1_temp_off);
283
+        }
284
+
285
+        if (found_bme2) {
286
+            bme2.setTemperatureCompensation(config.bme2_temp_off);
287
+        }
288
+
289
+        mem_write(config);
290
+        handlePage(42);
291
+    } else {
292
+        handlePage();
293
+    }
294
+}
295
+#endif
296
+
297
+void initSensors() {
298
+    // Init I2C and try to connect to sensors
299
+#if defined(ARDUINO_ARCH_ESP8266)
300
+
301
+    debug.println(F("Wire2"));
302
+    Wire2.begin(I2C_SDA_PIN, I2C_SCL_PIN);
303
+
304
+    debug.println(F("BME"));
305
+    found_bme1 = (!bme1.begin(BME_I2C_ADDRESS_1, &Wire2)) ? false : true;
306
+    found_bme2 = (!bme2.begin(BME_I2C_ADDRESS_2, &Wire2)) ? false : true;
307
+
308
+#ifdef ENABLE_CCS811
309
+    debug.println(F("CCS"));
310
+    found_ccs1 = ccs1.begin(CCS811_ADDRESS_1, &Wire2);
311
+    found_ccs2 = ccs2.begin(CCS811_ADDRESS_2, &Wire2);
312
+#endif // ENABLE_CCS811
313
+
314
+#elif defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_AVR)
315
+
316
+#if defined(ARDUINO_ARCH_ESP32)
317
+    debug.println(F("Wire"));
318
+    Wire.begin();
319
+#endif
320
+
321
+    debug.println(F("BME"));
322
+    found_bme1 = (!bme1.begin(BME_I2C_ADDRESS_1, &Wire)) ? false : true;
323
+    found_bme2 = (!bme2.begin(BME_I2C_ADDRESS_2, &Wire)) ? false : true;
324
+
325
+#ifdef ENABLE_CCS811
326
+    debug.println(F("CCS"));
327
+    found_ccs1 = ccs1.begin(CCS811_ADDRESS_1, &Wire);
328
+    found_ccs2 = ccs2.begin(CCS811_ADDRESS_2, &Wire);
329
+#endif // ENABLE_CCS811
330
+
331
+#endif
332
+
333
+    debug.println(F("SHT"));
334
+    found_sht = sht.GetAlive();
335
+
336
+    // initialize temperature offsets
337
+    if (found_bme1) {
338
+        bme1.setTemperatureCompensation(config.bme1_temp_off);
339
+    }
340
+    if (found_bme2) {
341
+        bme2.setTemperatureCompensation(config.bme2_temp_off);
342
+    }
343
+}
344
+
345
+void runSensors() {
346
+    unsigned long time = millis();
347
+    
348
+    if ((time - last_sensor_handle_time) >= SENSOR_HANDLE_INTERVAL) {
349
+        last_sensor_handle_time = time;
350
+
351
+#ifdef ENABLE_CCS811
352
+        if (found_ccs1 || found_ccs2) {
353
+            ccs_update();
354
+        }
355
+#endif // ENABLE_CCS811
356
+    }
357
+}

+ 230
- 0
src/servers.cpp View File

@@ -0,0 +1,230 @@
1
+/*
2
+ * servers.cpp
3
+ *
4
+ * ESP8266 / ESP32 Environmental Sensor
5
+ *
6
+ * ----------------------------------------------------------------------------
7
+ * "THE BEER-WARE LICENSE" (Revision 42):
8
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
9
+ * you can do whatever you want with this stuff. If we meet some day, and you
10
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
11
+ * ----------------------------------------------------------------------------
12
+ */
13
+
14
+#include <Arduino.h>
15
+
16
+#if defined(ARDUINO_ARCH_ESP8266)
17
+
18
+#include <ESP8266WiFi.h>
19
+#include <ESP8266WebServer.h>
20
+#include <ESP8266mDNS.h>
21
+
22
+#elif defined(ARDUINO_ARCH_ESP32)
23
+
24
+#include <WiFi.h>
25
+#include <WebServer.h>
26
+#include <ESPmDNS.h>
27
+
28
+#endif
29
+
30
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
31
+
32
+#include <WebSocketsServer.h>
33
+#include "SimpleUpdater.h"
34
+
35
+UPDATE_WEB_SERVER server(80);
36
+SimpleUpdater updater;
37
+WebSocketsServer socket(81);
38
+
39
+#elif defined(ARDUINO_ARCH_AVR)
40
+
41
+#include <UnoWiFiDevEdSerial1.h>
42
+#include <WiFiLink.h>
43
+
44
+WiFiServer server(80);
45
+
46
+#endif
47
+
48
+#include "config.h"
49
+#include "DebugLog.h"
50
+#include "moisture.h"
51
+#include "sensors.h"
52
+#include "relais.h"
53
+#include "memory.h"
54
+#include "influx.h"
55
+#include "mqtt.h"
56
+#include "html.h"
57
+
58
+static unsigned long last_server_handle_time = 0;
59
+
60
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
61
+void wifi_send_websocket(String s) {
62
+    socket.broadcastTXT(s);
63
+}
64
+#endif
65
+
66
+#ifdef FEATURE_RELAIS
67
+
68
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
69
+static void handleOn() {
70
+#else
71
+static void handleOn(WiFiClient &client) {
72
+#endif
73
+    String id_string = server.arg("id");
74
+    int id = id_string.toInt();
75
+
76
+    if ((id >= 0) && (id < relais_count())) {
77
+        relais_set(id, 1);
78
+    } else {
79
+        for (int i = 0; i < relais_count(); i++) {
80
+            relais_set(i, 1);
81
+        }
82
+    }
83
+
84
+    writeDatabase();
85
+
86
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
87
+    handlePage(1, id);
88
+#else
89
+    handlePage(client, 1, id);
90
+#endif
91
+}
92
+
93
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
94
+static void handleOff() {
95
+#else
96
+static void handleOff(WiFiClient &client) {
97
+#endif
98
+    String id_string = server.arg("id");
99
+    int id = id_string.toInt();
100
+
101
+    if ((id >= 0) && (id < relais_count())) {
102
+        relais_set(id, 0);
103
+    } else {
104
+        for (int i = 0; i < relais_count(); i++) {
105
+            relais_set(i, 0);
106
+        }
107
+    }
108
+
109
+    writeDatabase();
110
+
111
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
112
+    handlePage(0, id);
113
+#else
114
+    handlePage(client, 0, id);
115
+#endif
116
+}
117
+
118
+#endif // FEATURE_RELAIS
119
+
120
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
121
+static void handleRoot() {
122
+    handlePage();
123
+#else
124
+static void handleRoot(WiFiClient &client) {
125
+    handlePage(client);
126
+#endif
127
+}
128
+
129
+void initServers(String hostname) {
130
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
131
+    // Setup HTTP Server
132
+    debug.println(F("HTTP"));
133
+    MDNS.begin(hostname.c_str());
134
+    updater.setup(&server);
135
+    server.on("/", handleRoot);
136
+    server.on("/reset", handleReset);
137
+    server.on("/calibrate", handleCalibrate);
138
+
139
+#ifdef FEATURE_RELAIS
140
+    server.on("/on", handleOn);
141
+    server.on("/off", handleOff);
142
+#endif // FEATURE_RELAIS
143
+
144
+    MDNS.addService("http", "tcp", 80);
145
+
146
+    socket.begin();
147
+#endif
148
+
149
+    server.begin();
150
+}
151
+
152
+#if defined(ARDUINO_ARCH_AVR)
153
+static void http_server() {
154
+    // listen for incoming clients
155
+    WiFiClient client = server.available();
156
+    if (client) {
157
+        debug.println(F("new http client"));
158
+
159
+        // an http request ends with a blank line
160
+        boolean currentLineIsBlank = true;
161
+
162
+        while (client.connected()) {
163
+            if (client.available()) {
164
+                char c = client.read();
165
+                debug.write(c);
166
+
167
+                // if you've gotten to the end of the line (received a newline
168
+                // character) and the line is blank, the http request has ended,
169
+                // so you can send a reply
170
+                if ((c == '\n') && currentLineIsBlank) {
171
+                    // send a standard http response header
172
+                    client.println(F("HTTP/1.1 200 OK"));
173
+                    client.println(F("Content-Type: text/html"));
174
+                    client.println(F("Connection: close"));
175
+                    client.println();
176
+
177
+                    // TODO parse path and handle different pages
178
+                    handleRoot(client);
179
+
180
+                    break;
181
+                }
182
+
183
+                if (c == '\n') {
184
+                    // you're starting a new line
185
+                    currentLineIsBlank = true;
186
+                } else if (c != '\r') {
187
+                    // you've gotten a character on the current line
188
+                    currentLineIsBlank = false;
189
+                }
190
+            }
191
+        }
192
+
193
+        // give the web browser time to receive the data
194
+        delay(10);
195
+
196
+        // close the connection
197
+        client.stop();
198
+        debug.println(F("http client disconnected"));
199
+    }
200
+}
201
+#endif
202
+
203
+static void handleServers() {
204
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
205
+    server.handleClient();
206
+#else
207
+    http_server();
208
+#endif
209
+    
210
+#if defined(ARDUINO_ARCH_ESP8266)
211
+    MDNS.update();
212
+#endif
213
+}
214
+
215
+void runServers() {
216
+    unsigned long time = millis();
217
+
218
+    if ((time - last_server_handle_time) >= SERVER_HANDLE_INTERVAL) {
219
+        last_server_handle_time = time;
220
+        handleServers();
221
+
222
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
223
+        socket.loop();
224
+#endif
225
+    }
226
+    
227
+    runSensors();
228
+    runMQTT();
229
+    runInflux();
230
+}

+ 0
- 11
test/README View File

@@ -1,11 +0,0 @@
1
-
2
-This directory is intended for PIO Unit Testing and project tests.
3
-
4
-Unit Testing is a software testing method by which individual units of
5
-source code, sets of one or more MCU program modules together with associated
6
-control data, usage procedures, and operating procedures, are tested to
7
-determine whether they are fit for use. Unit testing finds problems early
8
-in the development cycle.
9
-
10
-More information about PIO Unit Testing:
11
-- https://docs.platformio.org/page/plus/unit-testing.html

Loading…
Cancel
Save