Browse Source

initial commit

Thomas Buck 2 years ago
commit
e1f79337c0
10 changed files with 1028 additions and 0 deletions
  1. 1
    0
      .gitignore
  2. 39
    0
      include/README
  3. 52
    0
      include/SimpleUpdater.h
  4. 72
    0
      include/config.h
  5. 21
    0
      include/relais.h
  6. 46
    0
      lib/README
  7. 29
    0
      platformio.ini
  8. 133
    0
      src/SimpleUpdater.cpp
  9. 545
    0
      src/main.cpp
  10. 90
    0
      src/relais.cpp

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
1
+.pio

+ 39
- 0
include/README View File

@@ -0,0 +1,39 @@
1
+
2
+This directory is intended for project header files.
3
+
4
+A header file is a file containing C declarations and macro definitions
5
+to be shared between several project source files. You request the use of a
6
+header file in your project source file (C, C++, etc) located in `src` folder
7
+by including it, with the C preprocessing directive `#include'.
8
+
9
+```src/main.c
10
+
11
+#include "header.h"
12
+
13
+int main (void)
14
+{
15
+ ...
16
+}
17
+```
18
+
19
+Including a header file produces the same results as copying the header file
20
+into each source file that needs it. Such copying would be time-consuming
21
+and error-prone. With a header file, the related declarations appear
22
+in only one place. If they need to be changed, they can be changed in one
23
+place, and programs that include the header file will automatically use the
24
+new version when next recompiled. The header file eliminates the labor of
25
+finding and changing all the copies as well as the risk that a failure to
26
+find one copy will result in inconsistencies within a program.
27
+
28
+In C, the usual convention is to give header files names that end with `.h'.
29
+It is most portable to use only letters, digits, dashes, and underscores in
30
+header file names, and at most one dot.
31
+
32
+Read more about using header files in official GCC documentation:
33
+
34
+* Include Syntax
35
+* Include Operation
36
+* Once-Only Headers
37
+* Computed Includes
38
+
39
+https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

+ 52
- 0
include/SimpleUpdater.h View File

@@ -0,0 +1,52 @@
1
+/*
2
+ * SimpleUpdater.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_SIMPLE_UPDATER__
15
+#define __ESP_SIMPLE_UPDATER__
16
+
17
+#if defined(ARDUINO_ARCH_ESP8266)
18
+#include <ESP8266WebServer.h>
19
+#include <ESP8266HTTPUpdateServer.h>
20
+#define UPDATE_WEB_SERVER ESP8266WebServer
21
+#elif defined(ARDUINO_ARCH_ESP32)
22
+#include <WebServer.h>
23
+#define UPDATE_WEB_SERVER WebServer
24
+#else
25
+#error Platform not supported!
26
+#endif
27
+
28
+class SimpleUpdater {
29
+public:
30
+    SimpleUpdater(String _uri = String("/update"));
31
+    void setup(UPDATE_WEB_SERVER *_server);
32
+    
33
+private:
34
+
35
+#if defined(ARDUINO_ARCH_ESP8266)
36
+    
37
+    ESP8266HTTPUpdateServer updateServer;
38
+
39
+#elif defined(ARDUINO_ARCH_ESP32)
40
+    
41
+    void get(void);
42
+    void postResult(void);
43
+    void postUpload(void);
44
+    
45
+#endif
46
+    
47
+    String uri;
48
+    UPDATE_WEB_SERVER *server;
49
+};
50
+
51
+#endif // __ESP_SIMPLE_UPDATER__
52
+

+ 72
- 0
include/config.h View File

@@ -0,0 +1,72 @@
1
+/*
2
+ * config.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_CONFIG__
15
+#define __ESP_ENV_CONFIG__
16
+
17
+// Sketch version
18
+const char* esp_relais_version = "0.1.0";
19
+
20
+// location of sensor, used in DB and hostname
21
+//const char* sensor_location = "livingroom";
22
+//const char* sensor_location = "bedroom";
23
+const char* sensor_location = "bathroom";
24
+//const char* sensor_location = "kitchen";
25
+//const char* sensor_location = "hallway";
26
+//const char* sensor_location = "tent";
27
+//const char* sensor_location = "storage";
28
+//const char* sensor_location = "greenhouse";
29
+//const char* sensor_location = "test";
30
+
31
+#define SENSOR_HOSTNAME_PREFIX "relais-"
32
+
33
+// WiFi AP settings
34
+const char* ssid = "INSERT_SSID_HERE";
35
+const char* password = "INSERT_PASSPHRASE_HERE";
36
+
37
+// MQTT settings
38
+#define MQTT_HOST "10.23.42.14"
39
+#define MQTT_PORT 1883
40
+
41
+// InfluxDB settings
42
+#define INFLUXDB_HOST "10.23.42.14"
43
+#define INFLUXDB_PORT 8086
44
+#define INFLUXDB_DATABASE "roomsensorsdiy"
45
+
46
+// feature selection
47
+//#define ENABLE_MQTT
48
+//#define ENABLE_INFLUXDB_LOGGING
49
+#define ENABLE_NTP
50
+
51
+// all given in milliseconds
52
+#define SERVER_HANDLE_INTERVAL 10
53
+#define AUTO_TIMING_INTERVAL (15UL * 60UL * 1000UL)
54
+#define LED_BLINK_INTERVAL (2 * 1000)
55
+#define LED_INIT_BLINK_INTERVAL 500
56
+#define LED_CONNECT_BLINK_INTERVAL 250
57
+#define LED_ERROR_BLINK_INTERVAL 100
58
+
59
+#if defined(ARDUINO_ARCH_ESP8266)
60
+
61
+#define RELAIS_COUNT 4
62
+#define ESP_PLATFORM_NAME "ESP8266"
63
+
64
+#elif defined(ARDUINO_ARCH_ESP32)
65
+
66
+#define RELAIS_COUNT 10
67
+#define ESP_PLATFORM_NAME "ESP32"
68
+
69
+#endif
70
+
71
+#endif // __ESP_ENV_CONFIG__
72
+

+ 21
- 0
include/relais.h View File

@@ -0,0 +1,21 @@
1
+/*
2
+ * relais.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_RELAIS_ACTOR__
15
+#define __ESP_RELAIS_ACTOR__
16
+
17
+int relais_count(void);
18
+void relais_set(int relais, int state);
19
+void relais_init(void);
20
+
21
+#endif // __ESP_RELAIS_ACTOR__

+ 46
- 0
lib/README View File

@@ -0,0 +1,46 @@
1
+
2
+This directory is intended for project specific (private) libraries.
3
+PlatformIO will compile them to static libraries and link into executable file.
4
+
5
+The source code of each library should be placed in a an own separate directory
6
+("lib/your_library_name/[here are source files]").
7
+
8
+For example, see a structure of the following two libraries `Foo` and `Bar`:
9
+
10
+|--lib
11
+|  |
12
+|  |--Bar
13
+|  |  |--docs
14
+|  |  |--examples
15
+|  |  |--src
16
+|  |     |- Bar.c
17
+|  |     |- Bar.h
18
+|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
19
+|  |
20
+|  |--Foo
21
+|  |  |- Foo.c
22
+|  |  |- Foo.h
23
+|  |
24
+|  |- README --> THIS FILE
25
+|
26
+|- platformio.ini
27
+|--src
28
+   |- main.c
29
+
30
+and a contents of `src/main.c`:
31
+```
32
+#include <Foo.h>
33
+#include <Bar.h>
34
+
35
+int main (void)
36
+{
37
+  ...
38
+}
39
+
40
+```
41
+
42
+PlatformIO Library Dependency Finder will find automatically dependent
43
+libraries scanning project source files.
44
+
45
+More information about PlatformIO Library Dependency Finder
46
+- https://docs.platformio.org/page/librarymanager/ldf.html

+ 29
- 0
platformio.ini View File

@@ -0,0 +1,29 @@
1
+;PlatformIO Project Configuration File
2
+;
3
+;   Build options: build flags, source filter
4
+;   Upload options: custom upload port, speed and extra flags
5
+;   Library options: dependencies, extra library storages
6
+;   Advanced options: extra scripting
7
+;
8
+; Please visit documentation for the other options and examples
9
+; https://docs.platformio.org/page/projectconf.html
10
+
11
+[env:esp8266]
12
+platform = espressif8266
13
+board = esp01_1m
14
+framework = arduino
15
+lib_deps =
16
+    ESP8266 Influxdb
17
+    https://github.com/knolleary/pubsubclient.git#2d228f2f862a95846c65a8518c79f48dfc8f188c
18
+    https://github.com/arduino-libraries/NTPClient
19
+
20
+[env:esp32]
21
+platform = espressif32
22
+board = esp32dev
23
+framework = arduino
24
+upload_protocol = esptool
25
+lib_deps =
26
+    https://github.com/tobiasschuerg/InfluxDB-Client-for-Arduino.git#66ed5d031caab6953cc79b407a4b49d33b1126dc
27
+    https://github.com/knolleary/pubsubclient.git#2d228f2f862a95846c65a8518c79f48dfc8f188c
28
+    https://github.com/arduino-libraries/NTPClient
29
+

+ 133
- 0
src/SimpleUpdater.cpp View File

@@ -0,0 +1,133 @@
1
+/*
2
+ * SimpleUpdater.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
+#if defined(ARDUINO_ARCH_ESP8266)
15
+#include <ESP8266HTTPUpdateServer.h>
16
+#elif defined(ARDUINO_ARCH_ESP32)
17
+#include <Update.h>
18
+#endif
19
+
20
+#include "SimpleUpdater.h"
21
+
22
+#if defined(ARDUINO_ARCH_ESP32)
23
+
24
+void SimpleUpdater::get(void) {
25
+    String uploadPage = F(
26
+        "<html><head>"
27
+        "<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
28
+        "<title>SimpleUpdater ESP32</title>"
29
+        "</head><body>"
30
+        "<h1>SimpleUpdater</h1>"
31
+        "<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
32
+        "<input type='file' name='update'>"
33
+        "<input type='submit' value='Update'>"
34
+        "</form>"
35
+        "<div id='prg'>progress: 0%</div>"
36
+        "<a href=\"/\">Back to Main Page</a>"
37
+        "<script>"
38
+        "$('form').submit(function(e){"
39
+        "e.preventDefault();"
40
+        "var form = $('#upload_form')[0];"
41
+        "var data = new FormData(form);"
42
+        " $.ajax({"
43
+        "url: '/update',"
44
+        "type: 'POST',"
45
+        "data: data,"
46
+        "contentType: false,"
47
+        "processData:false,"
48
+        "xhr: function() {"
49
+        "var xhr = new window.XMLHttpRequest();"
50
+        "xhr.upload.addEventListener('progress', function(evt) {"
51
+        "if (evt.lengthComputable) {"
52
+        "var per = evt.loaded / evt.total;"
53
+        "$('#prg').html('progress: ' + Math.round(per*100) + '%');"
54
+        "}"
55
+        "}, false);"
56
+        "return xhr;"
57
+        "},"
58
+        "success:function(d, s) {"
59
+        "console.log('success!')" 
60
+        "},"
61
+        "error: function (a, b, c) {"
62
+        "}"
63
+        "});"
64
+        "});"
65
+        "</script>"
66
+        "</body></html>"
67
+    );
68
+    
69
+    server->send(200, "text/html", uploadPage);
70
+}
71
+
72
+void SimpleUpdater::postResult(void) {
73
+    server->sendHeader("Connection", "close");
74
+    server->send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
75
+    ESP.restart();
76
+}
77
+
78
+void SimpleUpdater::postUpload(void) {
79
+    HTTPUpload& upload = server->upload();
80
+    if (upload.status == UPLOAD_FILE_START) {
81
+        Serial.printf("Update: %s\n", upload.filename.c_str());
82
+        // start with max available size
83
+        if (!Update.begin(UPDATE_SIZE_UNKNOWN)) {
84
+            Update.printError(Serial);
85
+        }
86
+    } else if (upload.status == UPLOAD_FILE_WRITE) {
87
+        // flashing firmware to ESP
88
+        if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
89
+            Update.printError(Serial);
90
+        }
91
+    } else if (upload.status == UPLOAD_FILE_END) {
92
+        // true to set the size to the current progress
93
+        if (Update.end(true)) {
94
+            Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
95
+        } else {
96
+            Update.printError(Serial);
97
+        }
98
+    }
99
+}
100
+
101
+#endif
102
+
103
+void SimpleUpdater::setup(UPDATE_WEB_SERVER *_server) {
104
+    if (_server == NULL) {
105
+        return;
106
+    }
107
+    
108
+    server = _server;
109
+    
110
+#if defined(ARDUINO_ARCH_ESP8266)
111
+    
112
+    updateServer.setup(server);
113
+    
114
+#elif defined(ARDUINO_ARCH_ESP32)
115
+    
116
+    server->on(uri.c_str(), HTTP_POST, [this]() {
117
+        postResult();
118
+    }, [this]() {
119
+        postUpload();
120
+    });
121
+    
122
+    server->on(uri.c_str(), HTTP_GET, [this]() {
123
+        get();
124
+    });
125
+    
126
+#endif
127
+}
128
+
129
+SimpleUpdater::SimpleUpdater(String _uri) {
130
+    uri = _uri;
131
+    server = NULL;
132
+}
133
+

+ 545
- 0
src/main.cpp View File

@@ -0,0 +1,545 @@
1
+/*
2
+ * main.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 <Wire.h>
16
+#include <PubSubClient.h>
17
+
18
+#if defined(ARDUINO_ARCH_ESP8266)
19
+
20
+#include <ESP8266WiFi.h>
21
+#include <ESP8266WebServer.h>
22
+#include <ESP8266mDNS.h>
23
+
24
+#elif defined(ARDUINO_ARCH_ESP32)
25
+
26
+#include <WiFi.h>
27
+#include <WebServer.h>
28
+#include <ESPmDNS.h>
29
+
30
+#endif
31
+
32
+#include "config.h"
33
+#include "relais.h"
34
+#include "SimpleUpdater.h"
35
+
36
+#define BUILTIN_LED_PIN 1
37
+
38
+UPDATE_WEB_SERVER server(80);
39
+SimpleUpdater updater;
40
+
41
+#ifdef ENABLE_MQTT
42
+WiFiClient mqttClient;
43
+PubSubClient mqtt(mqttClient);
44
+#endif // ENABLE_MQTT
45
+
46
+#ifdef ENABLE_INFLUXDB_LOGGING
47
+#include <InfluxDb.h>
48
+
49
+Influxdb influx(INFLUXDB_HOST, INFLUXDB_PORT);
50
+
51
+#define INFLUX_MAX_ERRORS_RESET 10
52
+int error_count = 0;
53
+#endif // ENABLE_INFLUXDB_LOGGING
54
+
55
+#ifdef ENABLE_NTP
56
+#include <WiFiUdp.h>
57
+#include <NTPClient.h>
58
+
59
+WiFiUDP ntpUDP;
60
+NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 2 * 60 * 60, 5 * 60 * 1000);
61
+#endif // ENABLE_NTP
62
+
63
+unsigned long last_server_handle_time = 0;
64
+unsigned long last_db_write_time = 0;
65
+unsigned long last_led_blink_time = 0;
66
+unsigned long last_timing_check_time = 0;
67
+
68
+String relais_name[RELAIS_COUNT] = {
69
+    String(" (Small Light)"),
70
+    String(" (Big Light)"),
71
+    String(""),
72
+    String(" (Fan)"),
73
+};
74
+
75
+bool relais_state[RELAIS_COUNT] = { false, false, false, false };
76
+String auto_set_state = String("");
77
+
78
+#ifdef ENABLE_NTP
79
+void handleAutoTimingCheck(void) {
80
+    relais_state[3] = true; // always keep fan on
81
+
82
+    if ((timeClient.getHours() >= 20) || (timeClient.getHours() < 10)) {
83
+        // small light from 20:00 to 10:00
84
+        relais_state[0] = true;
85
+        relais_state[1] = false;
86
+        auto_set_state = F("Small Light at Night (") + timeClient.getFormattedTime() + F(")");
87
+    } else {
88
+        // big light from 10:00 to 20:00
89
+        relais_state[0] = false;
90
+        relais_state[1] = true;
91
+        auto_set_state = F("Big Light while Daylight (") + timeClient.getFormattedTime() + F(")");
92
+    }
93
+
94
+    for (int i = 0; i < RELAIS_COUNT; i++) {
95
+        relais_set(i, relais_state[i] ? 1 : 0);
96
+    }
97
+}
98
+#endif // ENABLE_NTP
99
+
100
+void handlePage(int mode = -1, int id = 0) {
101
+    String message = F("<html><head>\n");
102
+    message += F("<title>" ESP_PLATFORM_NAME " MQTT Relais</title>\n");
103
+    message += F("</head><body>\n");
104
+    message += F("<h1>" ESP_PLATFORM_NAME " MQTT Relais</h1>\n");
105
+
106
+    message += F("\n<p>\n");
107
+    for (int i = 0; i < RELAIS_COUNT; i++) {
108
+        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"));
109
+        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"));
110
+    }
111
+    message += String(F("<a href=\"/on?id=")) + String(RELAIS_COUNT) + String(F("\">All Relais On</a><br>\n"));
112
+    message += String(F("<a href=\"/off?id=")) + String(RELAIS_COUNT) + String(F("\">All Relais Off</a><br>\n"));
113
+    message += F("</p>\n");
114
+
115
+    if (mode >= 0) {
116
+        message += F("<p>");
117
+        message += F("Turned Relais ");
118
+        message += (id < RELAIS_COUNT) ? String(id) : String(F("1-4"));
119
+        message += (mode ? String(F(" On")) : String(F(" Off")));
120
+        message += F("</p>\n");
121
+    }
122
+
123
+    message += F("\n<p>\n");
124
+    for (int i = 0; i < RELAIS_COUNT; i++) {
125
+        message += String(F("Relais ")) + String(i) + String(F(" = ")) + (relais_state[i] ? String(F("On")) : String(F("Off"))) + String(F("<br>\n"));
126
+    }
127
+    message += F("</p>\n");
128
+
129
+#ifdef ENABLE_NTP
130
+    message += F("<p>Time from NTP: ");
131
+    message += timeClient.getFormattedTime();
132
+    message += F("</p>\n");
133
+
134
+    message += F("<p>Auto-State: ");
135
+    message += auto_set_state;
136
+    message += F("</p>\n");
137
+
138
+    unsigned long next_min = (AUTO_TIMING_INTERVAL - (millis() - last_timing_check_time)) / (1000 * 60);
139
+    message += F("<p>Next Auto-State in ");
140
+    message += String(next_min);
141
+    message += F("min</p>\n");
142
+#endif // ENABLE_NTP
143
+
144
+    message += F("\n<p>\n");
145
+    message += F("Version: ");
146
+    message += esp_relais_version;
147
+    message += F("\n<br>\n");
148
+    message += F("Location: ");
149
+    message += sensor_location;
150
+    message += F("\n<br>\n");
151
+    message += F("MAC: ");
152
+    message += WiFi.macAddress();
153
+    message += F("\n</p>\n");
154
+
155
+#if defined(ARDUINO_ARCH_ESP8266)
156
+    
157
+    message += F("\n<p>\n");
158
+    message += F("Reset reason: ");
159
+    message += ESP.getResetReason();
160
+    message += F("\n<br>\n");
161
+    message += F("Free heap: ");
162
+    message += String(ESP.getFreeHeap());
163
+    message += F(" (");
164
+    message += String(ESP.getHeapFragmentation());
165
+    message += F("% fragmentation)");
166
+    message += F("\n<br>\n");
167
+    message += F("Free sketch space: ");
168
+    message += String(ESP.getFreeSketchSpace());
169
+    message += F("\n<br>\n");
170
+    message += F("Flash chip real size: ");
171
+    message += String(ESP.getFlashChipRealSize());
172
+
173
+    if (ESP.getFlashChipSize() != ESP.getFlashChipRealSize()) {
174
+        message += F("\n<br>\n");
175
+        message += F("WARNING: sdk chip size (");
176
+        message += (ESP.getFlashChipSize());
177
+        message += F(") does not match!");
178
+    }
179
+    
180
+    message += F("\n</p>\n");
181
+    
182
+#elif defined(ARDUINO_ARCH_ESP32)
183
+
184
+    message += F("\n<p>\n");
185
+    message += F("Free heap: ");
186
+    message += String(ESP.getFreeHeap() / 1024.0);
187
+    message += F("k\n<br>\n");
188
+    message += F("Free sketch space: ");
189
+    message += String(ESP.getFreeSketchSpace() / 1024.0);
190
+    message += F("k\n<br>\n");
191
+    message += F("Flash chip size: ");
192
+    message += String(ESP.getFlashChipSize() / 1024.0);
193
+    message += F("k\n</p>\n");
194
+    
195
+#endif
196
+
197
+    message += F("<p>\n");
198
+    message += F("Try <a href=\"/update\">/update</a> for OTA firmware updates!\n");
199
+    message += F("</p>\n");
200
+    
201
+    message += F("<p>\n");
202
+#ifdef ENABLE_INFLUXDB_LOGGING
203
+    message += F("InfluxDB: ");
204
+    message += INFLUXDB_DATABASE;
205
+    message += F(" @ ");
206
+    message += INFLUXDB_HOST;
207
+    message += F(":");
208
+    message += String(INFLUXDB_PORT);
209
+    message += F("\n");
210
+#else
211
+    message += F("InfluxDB logging not enabled!\n");
212
+#endif
213
+    message += F("</p>\n");
214
+
215
+    message += F("</body></html>\n");
216
+    server.send(200, "text/html", message);
217
+}
218
+
219
+void handleOn() {
220
+    String id_string = server.arg("id");
221
+    int id = id_string.toInt();
222
+
223
+    if ((id >= 0) && (id < RELAIS_COUNT)) {
224
+        relais_set(id, 1);
225
+        relais_state[id] = true;
226
+    } else {
227
+        for (int i = 0; i < RELAIS_COUNT; i++) {
228
+            relais_set(i, 1);
229
+            relais_state[i] = true;
230
+        }
231
+    }
232
+
233
+    // only reset to default after 15min
234
+    last_timing_check_time = millis();
235
+
236
+    handlePage(1, id);
237
+}
238
+
239
+void handleOff() {
240
+    String id_string = server.arg("id");
241
+    int id = id_string.toInt();
242
+
243
+    if ((id >= 0) && (id < RELAIS_COUNT)) {
244
+        relais_set(id, 0);
245
+        relais_state[id] = false;
246
+    } else {
247
+        for (int i = 0; i < RELAIS_COUNT; i++) {
248
+            relais_set(i, 0);
249
+            relais_state[i] = false;
250
+        }
251
+    }
252
+
253
+    // only reset to default after 15min
254
+    last_timing_check_time = millis();
255
+
256
+    handlePage(0, id);
257
+}
258
+
259
+void handleRoot() {
260
+    handlePage();
261
+}
262
+
263
+#ifdef ENABLE_MQTT
264
+void mqttCallback(char* topic, byte* payload, unsigned int length) {
265
+    int state = 0;
266
+    int id = 0;
267
+
268
+    // TODO check topic matches
269
+
270
+    if (((char)payload[0] == 't')
271
+            && ((char)payload[1] == 'u')
272
+            && ((char)payload[2] == 'r')
273
+            && ((char)payload[3] == 'n')
274
+            && ((char)payload[4] == ' ')
275
+            && ((char)payload[5] == 'o')) {
276
+        if (((char)payload[6] == 'n')
277
+            && ((char)payload[7] == ' ')) {
278
+            // turn on
279
+            state = 1;
280
+            id = payload[8];
281
+        } else if (((char)payload[6] == 'f')
282
+                && ((char)payload[7] == 'f')
283
+                && ((char)payload[8] == ' ')) {
284
+            // turn off
285
+            state = 0;
286
+            id = payload[9];
287
+        } else {
288
+            return;
289
+        }
290
+    } else {
291
+        return;
292
+    }
293
+
294
+    relais_set(id - 1, state);
295
+    relais_state[id - 1] = state ? true : false;
296
+}
297
+
298
+void mqttReconnect() {
299
+    // Loop until we're reconnected
300
+    //while (!mqtt.connected()) {
301
+        // Create a random client ID
302
+        String clientId = "ESP8266Client-";
303
+        clientId += String(random(0xffff), HEX);
304
+
305
+        // Attempt to connect
306
+        if (mqtt.connect(clientId.c_str())) {
307
+            // Once connected, publish an announcement...
308
+            //mqtt.publish("outTopic", "hello world");
309
+            // ... and resubscribe
310
+            mqtt.subscribe(sensor_location);
311
+        } else {
312
+            // Wait 5 seconds before retrying
313
+            delay(5000);
314
+        }
315
+    //}
316
+}
317
+#endif // ENABLE_MQTT
318
+
319
+void setup() {
320
+    pinMode(BUILTIN_LED_PIN, OUTPUT);
321
+    
322
+    relais_init();
323
+
324
+    for (int i = 0; i < RELAIS_COUNT; i++) {
325
+        relais_state[i] = false;
326
+    }
327
+
328
+    Serial.println();
329
+    Serial.println("Hello World From Relais Test");
330
+
331
+    // Blink LED for init
332
+    for (int i = 0; i < 2; i++) {
333
+        digitalWrite(BUILTIN_LED_PIN, LOW); // LED on
334
+        delay(LED_INIT_BLINK_INTERVAL);
335
+        digitalWrite(BUILTIN_LED_PIN, HIGH); // LED off
336
+        delay(LED_INIT_BLINK_INTERVAL);
337
+    }
338
+
339
+    // Build hostname string
340
+    String hostname = SENSOR_HOSTNAME_PREFIX;
341
+    hostname += sensor_location;
342
+
343
+#if defined(ARDUINO_ARCH_ESP8266)
344
+
345
+    // Connect to WiFi AP
346
+    Serial.print("Connecting to WiFi");
347
+    WiFi.hostname(hostname);
348
+    WiFi.mode(WIFI_STA);
349
+    WiFi.begin(ssid, password);
350
+    while (WiFi.status() != WL_CONNECTED) {
351
+        Serial.print(".");
352
+        delay(LED_CONNECT_BLINK_INTERVAL);
353
+        digitalWrite(BUILTIN_LED_PIN, !digitalRead(BUILTIN_LED_PIN));
354
+    }
355
+    Serial.println();
356
+    
357
+#elif defined(ARDUINO_ARCH_ESP32)
358
+
359
+    // Set hostname workaround
360
+    WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE);
361
+    WiFi.setHostname(hostname.c_str());
362
+    
363
+    // Workaround for WiFi connecting only every 2nd reset
364
+    // https://github.com/espressif/arduino-esp32/issues/2501#issuecomment-513602522
365
+    WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info) {
366
+        if (info.disconnected.reason == 202) {
367
+            esp_sleep_enable_timer_wakeup(10);
368
+            esp_deep_sleep_start();
369
+            delay(100);
370
+        }
371
+    }, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
372
+
373
+    // Connect to WiFi AP
374
+    WiFi.mode(WIFI_STA);
375
+    WiFi.begin(ssid, password);
376
+    while (WiFi.status() != WL_CONNECTED) {
377
+        delay(LED_CONNECT_BLINK_INTERVAL);
378
+        digitalWrite(BUILTIN_LED_PIN, !digitalRead(BUILTIN_LED_PIN));
379
+    }
380
+    
381
+    // Set hostname workaround
382
+    WiFi.setHostname(hostname.c_str());
383
+
384
+#endif
385
+
386
+    Serial.println("Seeding");
387
+    randomSeed(micros());
388
+
389
+#ifdef ENABLE_MQTT
390
+    Serial.println("MQTT");
391
+    mqtt.setServer(MQTT_HOST, MQTT_PORT);
392
+    mqtt.setCallback(mqttCallback);
393
+#endif // ENABLE_MQTT
394
+
395
+#ifdef ENABLE_INFLUXDB_LOGGING
396
+    // Setup InfluxDB Client
397
+    influx.setDb(INFLUXDB_DATABASE);
398
+#endif // ENABLE_INFLUXDB_LOGGING
399
+
400
+#ifdef ENABLE_NTP
401
+    timeClient.begin();
402
+#endif // ENABLE_NTP
403
+
404
+    // Setup HTTP Server
405
+    Serial.println("Server");
406
+    MDNS.begin(hostname.c_str());
407
+    updater.setup(&server);
408
+    server.on("/", handleRoot);
409
+    server.on("/on", handleOn);
410
+    server.on("/off", handleOff);
411
+    server.begin();
412
+    MDNS.addService("http", "tcp", 80);
413
+
414
+    Serial.println("Done");
415
+}
416
+
417
+void handleServers() {
418
+    server.handleClient();
419
+    
420
+#if defined(ARDUINO_ARCH_ESP8266)
421
+    MDNS.update();
422
+#endif
423
+}
424
+
425
+#ifdef ENABLE_INFLUXDB_LOGGING
426
+static boolean writeMeasurement(InfluxData &measurement) {
427
+    boolean success = influx.write(measurement);
428
+    if (!success) {
429
+        error_count++;
430
+        for (int i = 0; i < 10; i++) {
431
+            digitalWrite(BUILTIN_LED_PIN, LOW); // LED on
432
+            delay(LED_ERROR_BLINK_INTERVAL);
433
+            digitalWrite(BUILTIN_LED_PIN, HIGH); // LED off
434
+            delay(LED_ERROR_BLINK_INTERVAL);
435
+        }
436
+    }
437
+    return success;
438
+}
439
+
440
+void writeDatabase() {
441
+    if (found_bme1) {
442
+        InfluxData measurement("environment");
443
+        measurement.addTag("location", sensor_location);
444
+        measurement.addTag("placement", "1");
445
+        measurement.addTag("device", WiFi.macAddress());
446
+        measurement.addTag("sensor", "bme280");
447
+
448
+        measurement.addValue("temperature", bme1_temp());
449
+        measurement.addValue("pressure", bme1_pressure());
450
+        measurement.addValue("humidity", bme1_humid());
451
+
452
+        writeMeasurement(measurement);
453
+    }
454
+    if (found_bme2) {
455
+        InfluxData measurement("environment");
456
+        measurement.addTag("location", sensor_location);
457
+        measurement.addTag("placement", "2");
458
+        measurement.addTag("device", WiFi.macAddress());
459
+        measurement.addTag("sensor", "bme280");
460
+
461
+        measurement.addValue("temperature", bme2_temp());
462
+        measurement.addValue("pressure", bme2_pressure());
463
+        measurement.addValue("humidity", bme2_humid());
464
+
465
+        writeMeasurement(measurement);
466
+    }
467
+
468
+    if (found_sht) {
469
+        InfluxData measurement("environment");
470
+        measurement.addTag("location", sensor_location);
471
+        measurement.addTag("device", WiFi.macAddress());
472
+        measurement.addTag("sensor", "sht21");
473
+
474
+        measurement.addValue("temperature", sht_temp());
475
+        measurement.addValue("humidity", sht_humid());
476
+
477
+        writeMeasurement(measurement);
478
+    }
479
+    
480
+    for (int i = 0; i < moisture_count(); i++) {
481
+        int moisture = moisture_read(i);
482
+        if (moisture < moisture_max()) {
483
+            InfluxData measurement("moisture");
484
+            measurement.addTag("location", sensor_location);
485
+            measurement.addTag("device", WiFi.macAddress());
486
+            measurement.addTag("sensor", String(i + 1));
487
+
488
+            measurement.addValue("value", moisture);
489
+            measurement.addValue("maximum", moisture_max());
490
+
491
+            writeMeasurement(measurement);
492
+        }
493
+    }
494
+}
495
+#endif // ENABLE_INFLUXDB_LOGGING
496
+
497
+void loop() {
498
+    if ((millis() - last_server_handle_time) >= SERVER_HANDLE_INTERVAL) {
499
+        last_server_handle_time = millis();
500
+        handleServers();
501
+    }
502
+
503
+#ifdef ENABLE_NTP
504
+    timeClient.update();
505
+
506
+    if (timeClient.isTimeSet()) {
507
+        if (((millis() - last_timing_check_time) >= AUTO_TIMING_INTERVAL) || (last_timing_check_time == 0)) {
508
+            last_timing_check_time = millis();
509
+            handleAutoTimingCheck();
510
+        }
511
+    }
512
+#endif // ENABLE_NTP
513
+
514
+#ifdef ENABLE_MQTT
515
+    if (!mqtt.connected()) {
516
+        mqttReconnect();
517
+    }
518
+    mqtt.loop();
519
+#endif // ENABLE_MQTT
520
+
521
+#ifdef ENABLE_INFLUXDB_LOGGING
522
+    if ((millis() - last_db_write_time) >= DB_WRITE_INTERVAL) {
523
+        last_db_write_time = millis();
524
+        writeDatabase();
525
+    }
526
+    
527
+#ifdef INFLUX_MAX_ERRORS_RESET
528
+    if (error_count >= INFLUX_MAX_ERRORS_RESET) {
529
+        ESP.restart();
530
+    }
531
+#endif // INFLUX_MAX_ERRORS_RESET
532
+#endif // ENABLE_INFLUXDB_LOGGING
533
+
534
+    // blink heartbeat LED
535
+    if ((millis() - last_led_blink_time) >= LED_BLINK_INTERVAL) {
536
+        last_led_blink_time = millis();
537
+        digitalWrite(BUILTIN_LED_PIN, !digitalRead(BUILTIN_LED_PIN));
538
+    }
539
+    
540
+    // reset ESP every 6h to be safe
541
+    if (millis() >= (6 * 60 * 60 * 1000)) {
542
+        ESP.restart();
543
+    }
544
+}
545
+

+ 90
- 0
src/relais.cpp View File

@@ -0,0 +1,90 @@
1
+/*
2
+ * relais.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 "relais.h"
16
+
17
+#if defined(ARDUINO_ARCH_ESP8266)
18
+
19
+/*
20
+Turn OFF the first relay  : A0 01 00 A1
21
+Turn ON the first relay   : A0 01 01 A2
22
+Turn OFF the second relay : A0 02 00 A2
23
+Turn ON the second relay  : A0 02 01 A3
24
+Turn OFF the third relay  : A0 03 00 A3
25
+Turn ON the third relay   : A0 03 01 A4
26
+Turn OFF the fourth relay : A0 04 00 A4
27
+Turn ON the fourth relay  : A0 04 01 A5
28
+*/
29
+
30
+void relais_set(int relais, int state) {
31
+    if ((relais < 0) || (relais >= RELAIS_COUNT)) {
32
+        return;
33
+    }
34
+
35
+    int cmd[4];
36
+    cmd[0] = 0xA0; // command
37
+
38
+    cmd[1] = relais + 1; // relais id, 1-4
39
+    cmd[2] = state ? 1 : 0; // relais state
40
+
41
+    cmd[3] = 0; // checksum
42
+    for (int i = 0; i < 3; i++) {
43
+        cmd[3] += cmd[i];
44
+    }
45
+
46
+    for (int i = 0; i < 4; i++) {
47
+        Serial.write(cmd[i]);
48
+    }
49
+
50
+    delay(100);
51
+}
52
+
53
+void relais_init(void) {
54
+#if RELAIS_COUNT > 0
55
+    Serial.begin(115200);
56
+#endif
57
+
58
+    for (int i = 0; i < RELAIS_COUNT; i++) {
59
+        relais_set(i, 0);
60
+    }
61
+}
62
+
63
+#elif defined(ARDUINO_ARCH_ESP32)
64
+
65
+static int gpios[RELAIS_COUNT] = {
66
+    0, 15, 2, 4,
67
+    16, 17, 5, 18,
68
+    19, 23
69
+};
70
+
71
+void relais_set(int relais, int state) {
72
+    if ((relais < 0) || (relais >= RELAIS_COUNT)) {
73
+        return;
74
+    }
75
+
76
+    digitalWrite(gpios[relais], state ? LOW : HIGH);
77
+}
78
+
79
+void relais_init(void) {
80
+    for (int i = 0; i < RELAIS_COUNT; i++) {
81
+        pinMode(gpios[i], OUTPUT);
82
+        relais_set(i, 0);
83
+    }
84
+}
85
+
86
+#endif
87
+
88
+int relais_count(void) {
89
+    return RELAIS_COUNT;
90
+}

Loading…
Cancel
Save