/arduinoPinToggle/arduinoPinToggle.ino |
@@ -1,9 +1,7 @@ |
/*************************************************************************/ |
/* Copyright (C) 2023 Wizardry and Steamworks - License: GNU GPLv3 */ |
/* Copyright (C) 2020 Wizardry and Steamworks - License: GNU GPLv3 */ |
/*************************************************************************/ |
|
// Removing comment for debugging over the first serial port. |
// #define DEBUG 1 |
// The AP to connect to via Wifi. |
#define STA_SSID "" |
// The AP Wifi password. |
@@ -31,7 +29,7 @@ |
#if defined(ARDUINO_ARCH_ESP8266) |
#define GET_CHIP_ID() (ESP.getChipId()) |
#elif defined(ARDUINO_ARCH_ESP32) |
#define GET_CHIP_ID() ((uint16_t)(ESP.getEfuseMac() >> 32)) |
#define GET_CHIP_ID() ((uint16_t)(ESP.getEfuseMac()>>32)) |
#endif |
|
// Miscellaneous defines. |
@@ -56,6 +54,15 @@ |
#include <SPIFFS.h> |
#endif |
|
const char *sta_ssid = STA_SSID; |
const char *sta_psk = STA_PSK; |
const char *mqtt_host = MQTT_HOST; |
const char *mqtt_username = MQTT_USERNAME; |
const char *mqtt_password = MQTT_PASSWORD; |
const int mqtt_port = MQTT_PORT; |
const char *ota_password = OTA_PASSWORD; |
const int ota_port = OTA_PORT; |
|
WiFiClient espClient; |
PubSubClient mqttClient(espClient); |
|
@@ -66,131 +73,85 @@ |
int PINS[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, |
12, 13, 14, 15, 16, 17, 18, 19, 21, 22, |
23, 25, 26, 27, 32, 33, 34, 35, 36, 37, |
38, 39 }; |
38, 39 |
}; |
#endif |
|
String mqttSerialize(StaticJsonDocument<256> msg) { |
char output[256]; |
serializeJson(msg, output, 256); |
return String(output); |
const char* mqttSerialize(StaticJsonDocument<255> msg) { |
String message; |
serializeJson(msg, message); |
size_t length = message.length() + 1; |
char payload[length]; |
message.toCharArray(payload, length); |
|
return (const char*) payload; |
} |
|
void mqttCallback(char *topic, byte *payload, unsigned int length) { |
String msgTopic = String(topic); |
// payload is not null terminated and casting will not work |
char msgPayload[length + 1]; |
snprintf(msgPayload, length + 1, "%s", payload); |
#ifdef DEBUG |
Serial.println("Message received on topic: " + String(topic) + " with payload: " + String(msgPayload)); |
#endif |
String msgPayload = (const char *)payload; |
Serial.println("Message received on topic: " + msgTopic + " with payload: " + msgPayload); |
|
// Parse the payload sent to the MQTT topic as a JSON document. |
StaticJsonDocument<256> doc; |
#ifdef DEBUG |
StaticJsonDocument<255> doc; |
Serial.println("Deserializing message...."); |
#endif |
DeserializationError error = deserializeJson(doc, msgPayload); |
if (error) { |
#ifdef DEBUG |
Serial.println("Failed to parse MQTT payload as JSON: " + String(error.c_str())); |
#endif |
return; |
} |
|
// Do not process messages without an action key. |
if (!doc.containsKey("action")) { |
// Do not process messages with an action key. |
if (doc.containsKey("action")) { |
return; |
} |
|
String action = (const char *)doc["action"]; |
if (action == "set") { |
String state = (const char *)doc["state"]; |
const int pin = (const int)doc["pin"]; |
#ifdef DEBUG |
Serial.println("Setting pin: " + String(pin) + " to state: " + String(state)); |
#endif |
pinMode(PINS[pin], OUTPUT); |
String state = (const char *)doc["state"]; |
const int pin = (const int)doc["pin"]; |
|
if (state == "on") { |
digitalWrite(PINS[pin], HIGH); |
int status = digitalRead(PINS[pin]); |
#ifdef DEBUG |
Serial.println("Pin " + String(pin) + " state is now: " + String(status)); |
#endif |
return; |
} |
|
digitalWrite(PINS[pin], LOW); |
int status = digitalRead(PINS[pin]); |
#ifdef DEBUG |
Serial.println("Pin " + String(pin) + " state is now: " + String(status)); |
#endif |
Serial.println("Setting pin: " + String(pin) + " to state: " + String(state)); |
// Announce the action. |
StaticJsonDocument<255> msg; |
msg["pin"] = pin; |
msg["state"] = state; |
msg["action"] = "set"; |
mqttClient.publish(MQTT_TOPIC().c_str(), mqttSerialize(msg)); |
pinMode(PINS[pin], OUTPUT); |
if (state == "on") { |
digitalWrite(PINS[pin], HIGH); |
Serial.println("Pin set to HIGH"); |
return; |
} |
|
if (action == "get") { |
const int pin = (const int)doc["pin"]; |
#ifdef DEBUG |
Serial.println("Getting pin: " + String(pin) + " state."); |
#endif |
int status = digitalRead(PINS[pin]); |
#ifdef DEBUG |
Serial.println("Pin " + String(pin) + " state is now: " + String(status)); |
#endif |
// Announce the action. |
StaticJsonDocument<256> msg; |
msg["pin"] = pin; |
switch (status) { |
case 1: |
msg["state"] = "on"; |
break; |
case 0: |
msg["state"] = "off"; |
break; |
default: |
msg["state"] = "unknown"; |
break; |
} |
|
mqttClient.publish(MQTT_TOPIC().c_str(), mqttSerialize(msg).c_str()); |
return; |
} |
digitalWrite(PINS[pin], LOW); |
Serial.println("Pin set to LOW"); |
} |
|
bool mqttConnect() { |
#ifdef DEBUG |
Serial.println("Attempting to connect to MQTT broker: " + String(MQTT_HOST)); |
#endif |
mqttClient.setServer(MQTT_HOST, MQTT_PORT); |
Serial.println("Attempting to connect to MQTT broker: " + String(mqtt_host)); |
mqttClient.setServer(mqtt_host, mqtt_port); |
|
StaticJsonDocument<256> msg; |
if (mqttClient.connect(MQTT_CLIENT_ID().c_str(), MQTT_USERNAME, MQTT_PASSWORD)) { |
#ifdef DEBUG |
StaticJsonDocument<255> msg; |
if (mqttClient.connect(MQTT_CLIENT_ID().c_str(), mqtt_username, mqtt_password)) { |
Serial.println("Established connection with MQTT broker using client ID: " + MQTT_CLIENT_ID()); |
#endif |
mqttClient.setCallback(mqttCallback); |
msg["action"] = "connected"; |
mqttClient.publish(MQTT_TOPIC().c_str(), mqttSerialize(msg).c_str()); |
#ifdef DEBUG |
mqttClient.publish(MQTT_TOPIC().c_str(), mqttSerialize(msg)); |
Serial.println("Attempting to subscribe to MQTT topic: " + MQTT_TOPIC()); |
#endif |
if (!mqttClient.subscribe(MQTT_TOPIC().c_str())) { |
#ifdef DEBUG |
Serial.println("Failed to subscribe to MQTT topic: " + MQTT_TOPIC()); |
#endif |
return false; |
} |
#ifdef DEBUG |
Serial.println("Subscribed to MQTT topic: " + MQTT_TOPIC()); |
#endif |
msg.clear(); |
msg["action"] = "subscribed"; |
mqttClient.publish(MQTT_TOPIC().c_str(), mqttSerialize(msg).c_str()); |
mqttClient.publish(MQTT_TOPIC().c_str(), mqttSerialize(msg)); |
return true; |
} |
#ifdef DEBUG |
Serial.println("Connection to MQTT broker failed with MQTT client state: " + String(mqttClient.state())); |
#endif |
else { |
Serial.println("Connection to MQTT broker failed with MQTT client state: " + String(mqttClient.state())); |
} |
|
return false; |
} |
|
@@ -212,9 +173,7 @@ |
|
void setup() { |
Serial.begin(115200); |
#ifdef DEBUG |
Serial.println("Booted, setting up Wifi in 10s..."); |
#endif |
delay(10000); |
|
WiFi.mode(WIFI_STA); |
@@ -223,38 +182,32 @@ |
#elif defined(ARDUINO_ARCH_ESP32) |
WiFi.setHostname(HOSTNAME().c_str()); |
#endif |
WiFi.begin(STA_SSID, STA_PSK); |
WiFi.begin(sta_ssid, sta_psk); |
while (WiFi.waitForConnectResult() != WL_CONNECTED) { |
#ifdef DEBUG |
Serial.println("Failed to connect to Wifi, rebooting in 5s..."); |
#endif |
delay(5000); |
ESP.restart(); |
} |
#ifdef DEBUG |
|
Serial.print("Connected to Wifi: "); |
#endif |
Serial.println(WiFi.localIP()); |
#ifdef DEBUG |
|
Serial.println("Setting up OTA in 10s..."); |
#endif |
delay(10000); |
|
// Port defaults to 8266 |
ArduinoOTA.setPort(OTA_PORT); |
ArduinoOTA.setPort(ota_port); |
|
// Hostname defaults to esp-[ChipID] |
ArduinoOTA.setHostname(HOSTNAME().c_str()); |
|
// Set the OTA password |
ArduinoOTA.setPassword(OTA_PASSWORD); |
ArduinoOTA.setPassword(ota_password); |
|
ArduinoOTA.onStart([]() { |
switch (ArduinoOTA.getCommand()) { |
case U_FLASH: // Sketch |
#ifdef DEBUG |
case U_FLASH: // Sketch |
Serial.println("OTA start updating sketch."); |
#endif |
break; |
#if defined(ARDUINO_ARCH_ESP8266) |
case U_FS: |
@@ -261,22 +214,16 @@ |
#elif defined(ARDUINO_ARCH_ESP32) |
case U_SPIFFS: |
#endif |
#ifdef DEBUG |
Serial.println("OTA start updating filesystem."); |
#endif |
SPIFFS.end(); |
break; |
default: |
#ifdef DEBUG |
Serial.println("Unknown OTA update type."); |
#endif |
break; |
} |
}); |
ArduinoOTA.onEnd([]() { |
#ifdef DEBUG |
Serial.println("OTA update complete."); |
#endif |
SPIFFS.begin(); |
#if defined(ARDUINO_ARCH_ESP8266) |
// For what it's worth, check the filesystem on ESP8266. |
@@ -285,44 +232,28 @@ |
ESP.restart(); |
}); |
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { |
#ifdef DEBUG |
Serial.printf("OTA update progress: %u%%\r", (progress / (total / 100))); |
#endif |
}); |
ArduinoOTA.onError([](ota_error_t error) { |
#ifdef DEBUG |
Serial.printf("OTA update error [%u]: ", error); |
#endif |
switch (error) { |
case OTA_AUTH_ERROR: |
#ifdef DEBUG |
Serial.println("OTA authentication failed"); |
#endif |
break; |
case OTA_BEGIN_ERROR: |
#ifdef DEBUG |
Serial.println("OTA begin failed"); |
#endif |
break; |
case OTA_CONNECT_ERROR: |
#ifdef DEBUG |
Serial.println("OTA connect failed"); |
#endif |
break; |
case OTA_RECEIVE_ERROR: |
#ifdef DEBUG |
Serial.println("OTA receive failed"); |
#endif |
break; |
case OTA_END_ERROR: |
#ifdef DEBUG |
Serial.println("OTA end failed"); |
#endif |
break; |
default: |
#ifdef DEBUG |
Serial.println("Unknown OTA failure"); |
#endif |
break; |
} |
ESP.restart(); |
@@ -330,13 +261,11 @@ |
ArduinoOTA.begin(); |
|
// Set up MQTT client. |
mqttClient.setServer(MQTT_HOST, MQTT_PORT); |
mqttClient.setServer(mqtt_host, mqtt_port); |
mqttClient.setCallback(mqttCallback); |
|
// Touchdown. |
#ifdef DEBUG |
Serial.println("Setup complete."); |
#endif |
} |
|
void loop() { |
@@ -351,15 +280,11 @@ |
delay(1); |
break; |
case WL_NO_SHIELD: |
#ifdef DEBUG |
Serial.println("No Wifi shield present."); |
#endif |
goto DEFAULT_CASE; |
break; |
case WL_NO_SSID_AVAIL: |
#ifdef DEBUG |
Serial.println("Configured SSID not found."); |
#endif |
goto DEFAULT_CASE; |
break; |
// Temporary statuses indicating transitional states. |
@@ -372,12 +297,10 @@ |
case WL_CONNECTION_LOST: |
case WL_DISCONNECTED: |
default: |
#ifdef DEBUG |
Serial.println("Wifi connection failed with status: " + String(wifiStatus)); |
#endif |
DEFAULT_CASE: |
delay(10000); |
ESP.restart(); |
break; |
} |
} |
} |