arduino-sketches – Blame information for rev 17

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /*************************************************************************/
17 office 2 /* Copyright (C) 2023 Wizardry and Steamworks - License: GNU GPLv3 */
1 office 3 /*************************************************************************/
4  
17 office 5 // Removing comment for debugging over the first serial port.
6 // #define DEBUG 1
1 office 7 // The AP to connect to via Wifi.
8 #define STA_SSID ""
9 // The AP Wifi password.
10 #define STA_PSK ""
11 // The MQTT broker to connect to.
12 #define MQTT_HOST ""
13 // The MQTT broker username.
14 #define MQTT_USERNAME ""
15 // The MQTT broker password.
16 #define MQTT_PASSWORD ""
17 // The MQTT broker port.
18 #define MQTT_PORT 1883
19 // The default MQTT client ID is "esp-CHIPID" where CHIPID is the ESP8266
20 // or ESP32 chip identifier.
21 #define MQTT_CLIENT_ID() String("esp-" + String(GET_CHIP_ID(), HEX))
22 // The authentication password to use for OTA updates.
23 #define OTA_PASSWORD ""
24 // The OTA port on which updates take place.
25 #define OTA_PORT 8266
26 // The default topic that the sketch subscribes to is "esp/CHIPID" where
27 // CHIPID is the ESP8266 or ESP32 chip identifier.
28 #define MQTT_TOPIC() String("esp/" + String(GET_CHIP_ID(), HEX))
29  
30 // Platform specific defines.
31 #if defined(ARDUINO_ARCH_ESP8266)
32 #define GET_CHIP_ID() (ESP.getChipId())
33 #elif defined(ARDUINO_ARCH_ESP32)
17 office 34 #define GET_CHIP_ID() ((uint16_t)(ESP.getEfuseMac() >> 32))
1 office 35 #endif
36  
37 // Miscellaneous defines.
38 //#define CHIP_ID_HEX (String(GET_CHIP_ID()).c_str())
39 #define HOSTNAME() String("esp-" + String(GET_CHIP_ID(), HEX))
40  
41 // Platform specific libraries.
42 #if defined(ARDUINO_ARCH_ESP8266)
43 #include <ESP8266WiFi.h>
44 #include <ESP8266mDNS.h>
45 #elif defined(ARDUINO_ARCH_ESP32)
46 #include <WiFi.h>
47 #include <ESPmDNS.h>
48 #endif
49 // General libraries.
50 #include <WiFiUdp.h>
51 #include <ArduinoOTA.h>
52 #include <PubSubClient.h>
53 #include <ArduinoJson.h>
54 #if defined(ARDUINO_ARCH_ESP32)
55 #include <FS.h>
56 #include <SPIFFS.h>
57 #endif
58  
59 WiFiClient espClient;
60 PubSubClient mqttClient(espClient);
61  
62 // Define GPIO pins for supported architectures.
63 #if defined(ARDUINO_ARCH_ESP8266)
64 int PINS[] = { D0, D1, D2, D3, D4, D5, D6, D7, D8 };
65 #elif defined(ARDUINO_ARCH_ESP32)
66 int PINS[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
67 12, 13, 14, 15, 16, 17, 18, 19, 21, 22,
68 23, 25, 26, 27, 32, 33, 34, 35, 36, 37,
17 office 69 38, 39 };
1 office 70 #endif
71  
8 office 72 String mqttSerialize(StaticJsonDocument<256> msg) {
73 char output[256];
74 serializeJson(msg, output, 256);
75 return String(output);
1 office 76 }
77  
78 void mqttCallback(char *topic, byte *payload, unsigned int length) {
79 String msgTopic = String(topic);
4 office 80 // payload is not null terminated and casting will not work
81 char msgPayload[length + 1];
82 snprintf(msgPayload, length + 1, "%s", payload);
17 office 83 #ifdef DEBUG
4 office 84 Serial.println("Message received on topic: " + String(topic) + " with payload: " + String(msgPayload));
17 office 85 #endif
1 office 86  
87 // Parse the payload sent to the MQTT topic as a JSON document.
4 office 88 StaticJsonDocument<256> doc;
17 office 89 #ifdef DEBUG
1 office 90 Serial.println("Deserializing message....");
17 office 91 #endif
1 office 92 DeserializationError error = deserializeJson(doc, msgPayload);
93 if (error) {
17 office 94 #ifdef DEBUG
1 office 95 Serial.println("Failed to parse MQTT payload as JSON: " + String(error.c_str()));
17 office 96 #endif
1 office 97 return;
98 }
99  
8 office 100 // Do not process messages without an action key.
101 if (!doc.containsKey("action")) {
1 office 102 return;
103 }
104  
8 office 105 String action = (const char *)doc["action"];
17 office 106 if (action == "set") {
107 String state = (const char *)doc["state"];
108 const int pin = (const int)doc["pin"];
109 #ifdef DEBUG
110 Serial.println("Setting pin: " + String(pin) + " to state: " + String(state));
111 #endif
112 pinMode(PINS[pin], OUTPUT);
1 office 113  
17 office 114 if (state == "on") {
115 digitalWrite(PINS[pin], HIGH);
8 office 116 int status = digitalRead(PINS[pin]);
17 office 117 #ifdef DEBUG
8 office 118 Serial.println("Pin " + String(pin) + " state is now: " + String(status));
17 office 119 #endif
8 office 120 return;
17 office 121 }
122  
123 digitalWrite(PINS[pin], LOW);
124 int status = digitalRead(PINS[pin]);
125 #ifdef DEBUG
126 Serial.println("Pin " + String(pin) + " state is now: " + String(status));
127 #endif
128 return;
1 office 129 }
130  
17 office 131 if (action == "get") {
132 const int pin = (const int)doc["pin"];
133 #ifdef DEBUG
134 Serial.println("Getting pin: " + String(pin) + " state.");
135 #endif
136 int status = digitalRead(PINS[pin]);
137 #ifdef DEBUG
138 Serial.println("Pin " + String(pin) + " state is now: " + String(status));
139 #endif
140 // Announce the action.
141 StaticJsonDocument<256> msg;
142 msg["pin"] = pin;
143 switch (status) {
144 case 1:
145 msg["state"] = "on";
146 break;
147 case 0:
148 msg["state"] = "off";
149 break;
150 default:
151 msg["state"] = "unknown";
152 break;
153 }
8 office 154  
17 office 155 mqttClient.publish(MQTT_TOPIC().c_str(), mqttSerialize(msg).c_str());
156 return;
8 office 157 }
1 office 158 }
159  
160 bool mqttConnect() {
17 office 161 #ifdef DEBUG
162 Serial.println("Attempting to connect to MQTT broker: " + String(MQTT_HOST));
163 #endif
164 mqttClient.setServer(MQTT_HOST, MQTT_PORT);
1 office 165  
8 office 166 StaticJsonDocument<256> msg;
17 office 167 if (mqttClient.connect(MQTT_CLIENT_ID().c_str(), MQTT_USERNAME, MQTT_PASSWORD)) {
168 #ifdef DEBUG
1 office 169 Serial.println("Established connection with MQTT broker using client ID: " + MQTT_CLIENT_ID());
17 office 170 #endif
1 office 171 mqttClient.setCallback(mqttCallback);
172 msg["action"] = "connected";
8 office 173 mqttClient.publish(MQTT_TOPIC().c_str(), mqttSerialize(msg).c_str());
17 office 174 #ifdef DEBUG
1 office 175 Serial.println("Attempting to subscribe to MQTT topic: " + MQTT_TOPIC());
17 office 176 #endif
1 office 177 if (!mqttClient.subscribe(MQTT_TOPIC().c_str())) {
17 office 178 #ifdef DEBUG
1 office 179 Serial.println("Failed to subscribe to MQTT topic: " + MQTT_TOPIC());
17 office 180 #endif
1 office 181 return false;
182 }
17 office 183 #ifdef DEBUG
1 office 184 Serial.println("Subscribed to MQTT topic: " + MQTT_TOPIC());
17 office 185 #endif
1 office 186 msg.clear();
187 msg["action"] = "subscribed";
8 office 188 mqttClient.publish(MQTT_TOPIC().c_str(), mqttSerialize(msg).c_str());
1 office 189 return true;
190 }
17 office 191 #ifdef DEBUG
8 office 192 Serial.println("Connection to MQTT broker failed with MQTT client state: " + String(mqttClient.state()));
17 office 193 #endif
1 office 194 return false;
195 }
196  
197 bool loopWifiConnected() {
198 // Process OTA loop first since emergency OTA updates might be needed.
199 ArduinoOTA.handle();
200  
201 // Process MQTT client loop.
202 if (!mqttClient.connected()) {
203 // If the connection to the MQTT broker has failed then sleep before carrying on.
204 if (!mqttConnect()) {
205 return false;
206 }
207 }
208 mqttClient.loop();
209  
210 return true;
211 }
212  
213 void setup() {
214 Serial.begin(115200);
17 office 215 #ifdef DEBUG
1 office 216 Serial.println("Booted, setting up Wifi in 10s...");
17 office 217 #endif
1 office 218 delay(10000);
219  
220 WiFi.mode(WIFI_STA);
221 #if defined(ARDUINO_ARCH_ESP8266)
222 WiFi.hostname(HOSTNAME().c_str());
223 #elif defined(ARDUINO_ARCH_ESP32)
224 WiFi.setHostname(HOSTNAME().c_str());
225 #endif
17 office 226 WiFi.begin(STA_SSID, STA_PSK);
1 office 227 while (WiFi.waitForConnectResult() != WL_CONNECTED) {
17 office 228 #ifdef DEBUG
1 office 229 Serial.println("Failed to connect to Wifi, rebooting in 5s...");
17 office 230 #endif
1 office 231 delay(5000);
232 ESP.restart();
233 }
17 office 234 #ifdef DEBUG
1 office 235 Serial.print("Connected to Wifi: ");
17 office 236 #endif
1 office 237 Serial.println(WiFi.localIP());
17 office 238 #ifdef DEBUG
1 office 239 Serial.println("Setting up OTA in 10s...");
17 office 240 #endif
1 office 241 delay(10000);
242  
243 // Port defaults to 8266
17 office 244 ArduinoOTA.setPort(OTA_PORT);
1 office 245  
246 // Hostname defaults to esp-[ChipID]
247 ArduinoOTA.setHostname(HOSTNAME().c_str());
248  
249 // Set the OTA password
17 office 250 ArduinoOTA.setPassword(OTA_PASSWORD);
1 office 251  
252 ArduinoOTA.onStart([]() {
253 switch (ArduinoOTA.getCommand()) {
17 office 254 case U_FLASH: // Sketch
255 #ifdef DEBUG
1 office 256 Serial.println("OTA start updating sketch.");
17 office 257 #endif
1 office 258 break;
259 #if defined(ARDUINO_ARCH_ESP8266)
260 case U_FS:
261 #elif defined(ARDUINO_ARCH_ESP32)
262 case U_SPIFFS:
263 #endif
17 office 264 #ifdef DEBUG
1 office 265 Serial.println("OTA start updating filesystem.");
17 office 266 #endif
1 office 267 SPIFFS.end();
268 break;
269 default:
17 office 270 #ifdef DEBUG
1 office 271 Serial.println("Unknown OTA update type.");
17 office 272 #endif
1 office 273 break;
274 }
275 });
276 ArduinoOTA.onEnd([]() {
17 office 277 #ifdef DEBUG
1 office 278 Serial.println("OTA update complete.");
17 office 279 #endif
1 office 280 SPIFFS.begin();
281 #if defined(ARDUINO_ARCH_ESP8266)
282 // For what it's worth, check the filesystem on ESP8266.
283 SPIFFS.check();
284 #endif
285 ESP.restart();
286 });
287 ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
17 office 288 #ifdef DEBUG
1 office 289 Serial.printf("OTA update progress: %u%%\r", (progress / (total / 100)));
17 office 290 #endif
1 office 291 });
292 ArduinoOTA.onError([](ota_error_t error) {
17 office 293 #ifdef DEBUG
1 office 294 Serial.printf("OTA update error [%u]: ", error);
17 office 295 #endif
1 office 296 switch (error) {
297 case OTA_AUTH_ERROR:
17 office 298 #ifdef DEBUG
1 office 299 Serial.println("OTA authentication failed");
17 office 300 #endif
1 office 301 break;
302 case OTA_BEGIN_ERROR:
17 office 303 #ifdef DEBUG
1 office 304 Serial.println("OTA begin failed");
17 office 305 #endif
1 office 306 break;
307 case OTA_CONNECT_ERROR:
17 office 308 #ifdef DEBUG
1 office 309 Serial.println("OTA connect failed");
17 office 310 #endif
1 office 311 break;
312 case OTA_RECEIVE_ERROR:
17 office 313 #ifdef DEBUG
1 office 314 Serial.println("OTA receive failed");
17 office 315 #endif
1 office 316 break;
317 case OTA_END_ERROR:
17 office 318 #ifdef DEBUG
1 office 319 Serial.println("OTA end failed");
17 office 320 #endif
1 office 321 break;
322 default:
17 office 323 #ifdef DEBUG
1 office 324 Serial.println("Unknown OTA failure");
17 office 325 #endif
1 office 326 break;
327 }
328 ESP.restart();
329 });
330 ArduinoOTA.begin();
331  
332 // Set up MQTT client.
17 office 333 mqttClient.setServer(MQTT_HOST, MQTT_PORT);
1 office 334 mqttClient.setCallback(mqttCallback);
335  
336 // Touchdown.
17 office 337 #ifdef DEBUG
1 office 338 Serial.println("Setup complete.");
17 office 339 #endif
1 office 340 }
341  
342 void loop() {
343 // Check the Wifi connection status.
344 int wifiStatus = WiFi.status();
345 switch (wifiStatus) {
346 case WL_CONNECTED:
347 if (!loopWifiConnected()) {
348 delay(1000);
349 break;
350 }
351 delay(1);
352 break;
353 case WL_NO_SHIELD:
17 office 354 #ifdef DEBUG
1 office 355 Serial.println("No Wifi shield present.");
17 office 356 #endif
1 office 357 goto DEFAULT_CASE;
358 break;
359 case WL_NO_SSID_AVAIL:
17 office 360 #ifdef DEBUG
1 office 361 Serial.println("Configured SSID not found.");
17 office 362 #endif
1 office 363 goto DEFAULT_CASE;
364 break;
365 // Temporary statuses indicating transitional states.
366 case WL_IDLE_STATUS:
367 case WL_SCAN_COMPLETED:
368 delay(1000);
369 break;
370 // Fatal Wifi statuses trigger a delayed ESP restart.
371 case WL_CONNECT_FAILED:
372 case WL_CONNECTION_LOST:
373 case WL_DISCONNECTED:
374 default:
17 office 375 #ifdef DEBUG
1 office 376 Serial.println("Wifi connection failed with status: " + String(wifiStatus));
17 office 377 #endif
1 office 378 DEFAULT_CASE:
379 delay(10000);
380 ESP.restart();
381 break;
382 }
8 office 383 }