Procházet zdrojové kódy

add ability to trigger watering cycles via MQTT

Thomas Buck před 2 roky
rodič
revize
ac92d1686c

+ 16
- 0
README.md Zobrazit soubor

@@ -20,6 +20,13 @@ Then simply run the build for all supported configurations with platformio.
20 20
 You can of course also use pio to flash your targets.
21 21
 
22 22
 There is also an optional Telegram bot integration.
23
+
24
+## Optional Networking Interfaces
25
+
26
+There are both a Telegram bot implementation, as well as an MQTT client implemented in Giess-o-mat.
27
+Telegram allows control even from the Internet, but unfortunately polling for new messages is very time consuming.
28
+So I would not recommend enabling it if you also want to use the I2C UI.
29
+
23 30
 Register a new bot with the Telegram botfather and put the token into wifi.h as TELEGRAM_TOKEN.
24 31
 Compile and run the project, then send a message to the bot.
25 32
 Look for the chat ID in the log and put it into wifi.h in TRUSTED_IDS.
@@ -27,6 +34,15 @@ Look for the chat ID in the log and put it into wifi.h in TRUSTED_IDS.
27 34
     echo '#define TELEGRAM_TOKEN "XXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"' >> include/wifi.h
28 35
     echo '#define TRUSTED_IDS { "1234", "5678" }' >> include/wifi.h
29 36
 
37
+MQTT is far less resource intensive, but also does not provide a bridge to the Internet.
38
+Enable it like this.
39
+The last two parameters are optional.
40
+
41
+    echo '#define MQTT_HOST "192.168.0.100"' >> include/wifi.h
42
+    echo '#define MQTT_PORT 1883' >> include/wifi.h
43
+    echo '#define MQTT_USER "USERNAME"' >> include/wifi.h
44
+    echo '#define MQTT_PASS "PASSWORD"' >> include/wifi.h
45
+
30 46
 ## Hardware
31 47
 
32 48
 In general, the project consists of two parts, the controller and the ui.

+ 2
- 2
include/Functionality.h Zobrazit soubor

@@ -45,10 +45,10 @@ Plants *get_plants(void);
45 45
 
46 46
 bool sm_is_idle(void);
47 47
 
48
-#ifdef TELEGRAM_TOKEN
48
+#if defined(TELEGRAM_TOKEN) || defined(MQTT_HOST)
49 49
 void sm_bot_abort(void);
50 50
 void sm_bot_start_auto(BoolField ferts, BoolField plants);
51
-#endif // TELEGRAM_TOKEN
51
+#endif // TELEGRAM_TOKEN || MQTT_HOST
52 52
 
53 53
 #endif // FUNCTION_CONTROL
54 54
 

+ 2
- 2
include/Statemachine.h Zobrazit soubor

@@ -124,10 +124,10 @@ public:
124 124
     const char *getStateName(void);
125 125
     bool isIdle(void);
126 126
 
127
-#ifdef TELEGRAM_TOKEN
127
+#if defined(TELEGRAM_TOKEN) || defined(MQTT_HOST)
128 128
     void bot_abort(void);
129 129
     void bot_start_auto(BoolField ferts, BoolField plants);
130
-#endif // TELEGRAM_TOKEN
130
+#endif // TELEGRAM_TOKEN || MQTT_HOST
131 131
     
132 132
 private:
133 133
     void switch_to(States s);

+ 2
- 1
include/config.h Zobrazit soubor

@@ -53,7 +53,8 @@
53 53
 #define LED_CONNECT_BLINK_INTERVAL 250
54 54
 #define LED_ERROR_BLINK_INTERVAL 100
55 55
 #define TELEGRAM_UPDATE_INTERVAL_SLOW (10 * 1000)
56
-#define TELEGRAM_UPDATE_INTERVAL_FAST (3 * 1000)
56
+#define TELEGRAM_UPDATE_INTERVAL_FAST (2 * 1000)
57
+#define MQTT_RECONNECT_INTERVAL (5 * 1000)
57 58
 
58 59
 // each attempt takes LED_CONNECT_BLINK_INTERVAL ms
59 60
 #define MAX_WIFI_CONNECT_ATTEMPTS 40

+ 2
- 0
platformio.ini Zobrazit soubor

@@ -24,6 +24,7 @@ lib_deps =
24 24
     https://github.com/RobTillaart/PCF8574
25 25
     https://github.com/bblanchon/ArduinoJson
26 26
     https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot
27
+    https://github.com/knolleary/pubsubclient.git#2d228f2f862a95846c65a8518c79f48dfc8f188c
27 28
 
28 29
 [env:esp32_main]
29 30
 platform = platformio/espressif32@3.5.0
@@ -40,6 +41,7 @@ lib_deps =
40 41
     https://github.com/RobTillaart/PCF8574
41 42
     https://github.com/bblanchon/ArduinoJson
42 43
     https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot
44
+    https://github.com/knolleary/pubsubclient.git#2d228f2f862a95846c65a8518c79f48dfc8f188c
43 45
 
44 46
 [env:arduino_ui]
45 47
 platform = atmelavr

+ 2
- 2
src/Functionality.cpp Zobrazit soubor

@@ -76,7 +76,7 @@ bool sm_is_idle(void) {
76 76
     return sm.isIdle();
77 77
 }
78 78
 
79
-#ifdef TELEGRAM_TOKEN
79
+#if defined(TELEGRAM_TOKEN) || defined(MQTT_HOST)
80 80
 
81 81
 void sm_bot_abort(void) {
82 82
     sm.bot_abort();
@@ -86,7 +86,7 @@ void sm_bot_start_auto(BoolField ferts, BoolField plants) {
86 86
     sm.bot_start_auto(ferts, plants);
87 87
 }
88 88
 
89
-#endif // TELEGRAM_TOKEN
89
+#endif // TELEGRAM_TOKEN || MQTT_HOST
90 90
 
91 91
 #endif // FUNCTION_CONTROL
92 92
 

+ 2
- 2
src/Statemachine.cpp Zobrazit soubor

@@ -173,7 +173,7 @@ void Statemachine::begin(void) {
173 173
     switch_to(init);
174 174
 }
175 175
 
176
-#ifdef TELEGRAM_TOKEN
176
+#if defined(TELEGRAM_TOKEN) || defined(MQTT_HOST)
177 177
 
178 178
 void Statemachine::bot_abort(void) {
179 179
     plants.abort();
@@ -244,7 +244,7 @@ void Statemachine::bot_start_auto(BoolField _ferts, BoolField _plants) {
244 244
     }
245 245
 }
246 246
 
247
-#endif // TELEGRAM_TOKEN
247
+#endif // TELEGRAM_TOKEN || MQTT_HOST
248 248
 
249 249
 void Statemachine::input(int n) {
250 250
     if (state == init) {

+ 155
- 16
src/WifiStuff.cpp Zobrazit soubor

@@ -62,8 +62,29 @@ UniversalTelegramBot bot(TELEGRAM_TOKEN, secured_client);
62 62
 unsigned long last_telegram_time = 0;
63 63
 String trusted_chat_ids[] = TRUSTED_IDS;
64 64
 
65
+enum telegram_state {
66
+    BOT_IDLE,
67
+    BOT_ASKED_FERT,
68
+    BOT_ASKED_PLANTS,
69
+    BOT_ASKED_CONFIRM
70
+};
71
+
72
+enum telegram_state bot_state = BOT_IDLE;
73
+String bot_lock = "";
74
+BoolField bot_plants(VALVE_COUNT - 1);
75
+BoolField bot_ferts(PUMP_COUNT);
76
+
65 77
 #endif // TELEGRAM_TOKEN
66 78
 
79
+#ifdef MQTT_HOST
80
+#include <PubSubClient.h>
81
+WiFiClient mqttClient;
82
+PubSubClient mqtt(mqttClient);
83
+unsigned long last_mqtt_reconnect_time = 0;
84
+BoolField mqtt_plants(VALVE_COUNT - 1);
85
+BoolField mqtt_ferts(PUMP_COUNT);
86
+#endif // MQTT_HOST
87
+
67 88
 UPDATE_WEB_SERVER server(80);
68 89
 WebSocketsServer socket = WebSocketsServer(81);
69 90
 SimpleUpdater updater;
@@ -125,20 +146,13 @@ void wifi_broadcast_state_change(const char *s) {
125 146
         bot.sendMessage(trusted_chat_ids[n], "New state: " + String(s), "");
126 147
     }
127 148
 #endif // TELEGRAM_TOKEN
149
+
150
+#ifdef MQTT_HOST
151
+    mqtt.publish("giessomat", s);
152
+#endif // MQTT_HOST
128 153
 }
129 154
 
130 155
 #ifdef TELEGRAM_TOKEN
131
-enum telegram_state {
132
-    BOT_IDLE,
133
-    BOT_ASKED_FERT,
134
-    BOT_ASKED_PLANTS,
135
-    BOT_ASKED_CONFIRM
136
-};
137
-
138
-enum telegram_state bot_state = BOT_IDLE;
139
-String bot_lock = "";
140
-BoolField bot_plants(VALVE_COUNT - 1);
141
-BoolField bot_ferts(PUMP_COUNT);
142 156
 
143 157
 unsigned long telegram_update_interval() {
144 158
     if (bot_state == BOT_IDLE) {
@@ -352,6 +366,109 @@ void telegram_hello() {
352 366
 }
353 367
 #endif // TELEGRAM_TOKEN
354 368
 
369
+#ifdef MQTT_HOST
370
+
371
+static void mqttCallback(char* topic, byte* payload, unsigned int length) {
372
+    String ts(topic), ps;
373
+    for (unsigned int i = 0; i < length; i++) {
374
+        char c = payload[i];
375
+        ps += c;
376
+    }
377
+
378
+    debug.println("MQTT Rx @ " + ts + ": " + ps);
379
+
380
+    if (ts != "giessomat") {
381
+        debug.println("MQTT: invalid topic");
382
+        return;
383
+    }
384
+
385
+    if (ps == "abort") {
386
+        if (sm_is_idle()) {
387
+            debug.println("MQTT: nothing to abort");
388
+        } else {
389
+            sm_bot_abort();
390
+            debug.println("MQTT: user abort");
391
+        }
392
+    } else {
393
+        if (ps.substring(0, 4) != "auto") {
394
+            debug.println("MQTT: invalid payload");
395
+            return;
396
+        }
397
+
398
+        mqtt_ferts.clear();
399
+        mqtt_plants.clear();
400
+
401
+        String buff;
402
+        bool at_plants = false;
403
+        for (int i = 5; i < ps.length() + 1; i++) {
404
+            if ((i == ps.length()) || (ps[i] == ' ') || (ps[i] == ',')) {
405
+                if (buff != "none") {
406
+                    int n = buff.toInt() - 1;
407
+                    if (!at_plants) {
408
+                        mqtt_ferts.set(n);
409
+                    } else {
410
+                        mqtt_plants.set(n);
411
+                    }
412
+                }
413
+                buff = "";
414
+
415
+                if ((i < ps.length()) && (ps[i] == ' ')) {
416
+                    at_plants = true;
417
+                }
418
+            } else {
419
+                buff += ps[i];
420
+            }
421
+        }
422
+
423
+        String s = "MQTT: fertilizers:";
424
+        for (int i = 0; i < PUMP_COUNT; i++) {
425
+            if (mqtt_ferts.isSet(i)) {
426
+                s += " " + String(i + 1);
427
+            }
428
+        }
429
+        debug.println(s);
430
+
431
+        s = "MQTT: plants:";
432
+        for (int i = 0; i < (VALVE_COUNT - 1); i++) {
433
+            if (mqtt_plants.isSet(i)) {
434
+                s += " " + String(i + 1);
435
+            }
436
+        }
437
+        debug.println(s);
438
+
439
+        if (mqtt_plants.countSet() <= 0) {
440
+            debug.println("MQTT: no plants selected");
441
+            return;
442
+        }
443
+
444
+#ifdef FULLAUTO_MIN_PLANT_COUNT
445
+        if (mqtt_plants.countSet() < FULLAUTO_MIN_PLANT_COUNT) {
446
+            debug.println("MQTT: not enough plants selected");
447
+            return;
448
+        }
449
+#endif
450
+
451
+        sm_bot_start_auto(mqtt_ferts, mqtt_plants);
452
+    }
453
+}
454
+
455
+static void mqttReconnect() {
456
+    // Create a random client ID
457
+    String clientId = F("giessomat-");
458
+    clientId += String(random(0xffff), HEX);
459
+
460
+    // Attempt to connect
461
+#if defined(MQTT_USER) && defined(MQTT_PASS)
462
+    if (mqtt.connect(clientId.c_str(), MQTT_USER, MQTT_PASS)) {
463
+#else
464
+    if (mqtt.connect(clientId.c_str())) {
465
+#endif
466
+        mqtt.subscribe("giessomat");
467
+    }
468
+}
469
+
470
+#endif // MQTT_HOST
471
+
355 472
 bool wifi_write_database(int duration, const char *type, int id) {
356 473
     bool success = false;
357 474
 
@@ -874,6 +991,18 @@ void handleRoot() {
874 991
     message += F("</p>\n");
875 992
 
876 993
     message += F("<p>\n");
994
+#ifdef MQTT_HOST
995
+    message += F("MQTT: ");
996
+    message += MQTT_HOST;
997
+    message += F(":");
998
+    message += String(MQTT_PORT);
999
+    message += F("\n");
1000
+#else
1001
+    message += F("MQTT not enabled!\n");
1002
+#endif
1003
+    message += F("</p>\n");
1004
+
1005
+    message += F("<p>\n");
877 1006
 #ifdef ENABLE_INFLUXDB_LOGGING
878 1007
     message += F("InfluxDB: ");
879 1008
     message += INFLUXDB_DATABASE;
@@ -1135,6 +1264,12 @@ void wifi_setup() {
1135 1264
     telegram_hello();
1136 1265
 #endif // TELEGRAM_TOKEN
1137 1266
 
1267
+#ifdef MQTT_HOST
1268
+    debug.println("WiFi: initializing MQTT");
1269
+    mqtt.setServer(MQTT_HOST, MQTT_PORT);
1270
+    mqtt.setCallback(mqttCallback);
1271
+#endif // MQTT_HOST
1272
+
1138 1273
     server.begin();
1139 1274
     MDNS.addService("http", "tcp", 80);
1140 1275
     MDNS.addService("http", "tcp", 81);
@@ -1146,11 +1281,6 @@ void wifi_setup() {
1146 1281
 }
1147 1282
 
1148 1283
 void wifi_run() {
1149
-    // reset ESP every 6h to be safe
1150
-    if ((millis() >= (6UL * 60UL * 60UL * 1000UL)) && (sm_is_idle())) {
1151
-        ESP.restart();
1152
-    }
1153
-
1154 1284
     if (!wifi_ok) {
1155 1285
         // nothing to handle
1156 1286
         return;
@@ -1184,6 +1314,15 @@ void wifi_run() {
1184 1314
         last_telegram_time = millis();
1185 1315
     }
1186 1316
 #endif // TELEGRAM_TOKEN
1317
+
1318
+#ifdef MQTT_HOST
1319
+    if (!mqtt.connected() && ((millis() - last_mqtt_reconnect_time) >= MQTT_RECONNECT_INTERVAL)) {
1320
+        last_mqtt_reconnect_time = millis();
1321
+        mqttReconnect();
1322
+    }
1323
+
1324
+    mqtt.loop();
1325
+#endif // MQTT_HOST
1187 1326
 }
1188 1327
 
1189 1328
 #endif // PLATFORM_ESP

Loading…
Zrušit
Uložit