/arduinoPinToggle/arduinoPinToogle (with analog measurement).ino |
@@ -0,0 +1,351 @@ |
/*************************************************************************/ |
/* Copyright (C) 2022 Wizardry and Steamworks - License: GNU GPLv3 */ |
/*************************************************************************/ |
|
// The AP to connect to via Wifi. |
#define STA_SSID "" |
// The AP Wifi password. |
#define STA_PSK "" |
// The MQTT broker to connect to. |
#define MQTT_HOST "" |
// The MQTT broker username. |
#define MQTT_USERNAME "" |
// The MQTT broker password. |
#define MQTT_PASSWORD "" |
// The MQTT broker port. |
#define MQTT_PORT 1883 |
// The default MQTT client ID is "esp-CHIPID" where CHIPID is the ESP8266 |
// or ESP32 chip identifier. |
#define MQTT_CLIENT_ID() String("esp-" + String(GET_CHIP_ID(), HEX)) |
// The authentication password to use for OTA updates. |
#define OTA_PASSWORD "" |
// The OTA port on which updates take place. |
#define OTA_PORT 8266 |
// The default topic that the sketch subscribes to is "esp/CHIPID" where |
// CHIPID is the ESP8266 or ESP32 chip identifier. |
#define MQTT_TOPIC() String("esp/" + String(GET_CHIP_ID(), HEX)) |
|
// Platform specific defines. |
#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)) |
#endif |
|
// Miscellaneous defines. |
//#define CHIP_ID_HEX (String(GET_CHIP_ID()).c_str()) |
#define HOSTNAME() String("esp-" + String(GET_CHIP_ID(), HEX)) |
|
// Platform specific libraries. |
#if defined(ARDUINO_ARCH_ESP8266) |
#include <ESP8266WiFi.h> |
#include <ESP8266mDNS.h> |
#elif defined(ARDUINO_ARCH_ESP32) |
#include <WiFi.h> |
#include <ESPmDNS.h> |
#endif |
// General libraries. |
#include <WiFiUdp.h> |
#include <ArduinoOTA.h> |
#include <PubSubClient.h> |
#include <ArduinoJson.h> |
#if defined(ARDUINO_ARCH_ESP32) |
#include <FS.h> |
#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); |
|
// Define GPIO pins for supported architectures. |
#if defined(ARDUINO_ARCH_ESP8266) |
int PINS[] = { D0, D1, D2, D3, D4, D5, D6, D7, D8 }; |
int ANAL[] = { A0 }; |
#elif defined(ARDUINO_ARCH_ESP32) |
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 |
}; |
int ANAL[] = { 36, 39, 34, 35 }; |
#endif |
|
String mqttSerialize(StaticJsonDocument<256> msg) { |
char output[256]; |
serializeJson(msg, output, 256); |
return String(output); |
} |
|
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); |
Serial.println("Message received on topic: " + String(topic) + " with payload: " + String(msgPayload)); |
|
// Parse the payload sent to the MQTT topic as a JSON document. |
StaticJsonDocument<256> doc; |
Serial.println("Deserializing message...."); |
DeserializationError error = deserializeJson(doc, msgPayload); |
if (error) { |
Serial.println("Failed to parse MQTT payload as JSON: " + String(error.c_str())); |
return; |
} |
|
// Do not process messages without 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"]; |
|
Serial.println("Setting pin: " + String(pin) + " to state: " + String(state)); |
|
pinMode(PINS[pin], OUTPUT); |
|
if (state == "on") { |
digitalWrite(PINS[pin], HIGH); |
int status = digitalRead(PINS[pin]); |
Serial.println("Pin " + String(pin) + " state is now: " + String(status)); |
return; |
} |
|
digitalWrite(PINS[pin], LOW); |
int status = digitalRead(PINS[pin]); |
Serial.println("Pin " + String(pin) + " state is now: " + String(status)); |
return; |
} |
|
if(action == "get") { |
const int pin = (const int)doc["pin"]; |
|
Serial.println("Getting pin: " + String(pin) + " state."); |
|
int status = digitalRead(PINS[pin]); |
Serial.println("Pin " + String(pin) + " state is now: " + String(status)); |
|
// 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; |
} |
|
if(action == "measure") { |
const int pin = (const int)doc["pin"]; |
|
Serial.println("Getting analog pin: " + String(pin) + " state."); |
int analogValue = analogRead(ANAL[pin]); |
|
Serial.println("Value of analog pin " + String(pin) + " is: " + String(analogValue)); |
|
// Announce the analog value. |
StaticJsonDocument<256> msg; |
msg["pin"] = pin; |
msg["value"] = analogValue; |
|
mqttClient.publish(MQTT_TOPIC().c_str(), mqttSerialize(msg).c_str()); |
return; |
} |
} |
|
bool mqttConnect() { |
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)) { |
Serial.println("Established connection with MQTT broker using client ID: " + MQTT_CLIENT_ID()); |
mqttClient.setCallback(mqttCallback); |
msg["action"] = "connected"; |
mqttClient.publish(MQTT_TOPIC().c_str(), mqttSerialize(msg).c_str()); |
Serial.println("Attempting to subscribe to MQTT topic: " + MQTT_TOPIC()); |
if (!mqttClient.subscribe(MQTT_TOPIC().c_str())) { |
Serial.println("Failed to subscribe to MQTT topic: " + MQTT_TOPIC()); |
return false; |
} |
Serial.println("Subscribed to MQTT topic: " + MQTT_TOPIC()); |
msg.clear(); |
msg["action"] = "subscribed"; |
mqttClient.publish(MQTT_TOPIC().c_str(), mqttSerialize(msg).c_str()); |
return true; |
} |
|
Serial.println("Connection to MQTT broker failed with MQTT client state: " + String(mqttClient.state())); |
|
return false; |
} |
|
bool loopWifiConnected() { |
// Process OTA loop first since emergency OTA updates might be needed. |
ArduinoOTA.handle(); |
|
// Process MQTT client loop. |
if (!mqttClient.connected()) { |
// If the connection to the MQTT broker has failed then sleep before carrying on. |
if (!mqttConnect()) { |
return false; |
} |
} |
mqttClient.loop(); |
|
return true; |
} |
|
void setup() { |
Serial.begin(115200); |
Serial.println("Booted, setting up Wifi in 10s..."); |
delay(10000); |
|
WiFi.mode(WIFI_STA); |
#if defined(ARDUINO_ARCH_ESP8266) |
WiFi.hostname(HOSTNAME().c_str()); |
#elif defined(ARDUINO_ARCH_ESP32) |
WiFi.setHostname(HOSTNAME().c_str()); |
#endif |
WiFi.begin(sta_ssid, sta_psk); |
while (WiFi.waitForConnectResult() != WL_CONNECTED) { |
Serial.println("Failed to connect to Wifi, rebooting in 5s..."); |
delay(5000); |
ESP.restart(); |
} |
|
Serial.print("Connected to Wifi: "); |
Serial.println(WiFi.localIP()); |
|
Serial.println("Setting up OTA in 10s..."); |
delay(10000); |
|
// Port defaults to 8266 |
ArduinoOTA.setPort(ota_port); |
|
// Hostname defaults to esp-[ChipID] |
ArduinoOTA.setHostname(HOSTNAME().c_str()); |
|
// Set the OTA password |
ArduinoOTA.setPassword(ota_password); |
|
ArduinoOTA.onStart([]() { |
switch (ArduinoOTA.getCommand()) { |
case U_FLASH: // Sketch |
Serial.println("OTA start updating sketch."); |
break; |
#if defined(ARDUINO_ARCH_ESP8266) |
case U_FS: |
#elif defined(ARDUINO_ARCH_ESP32) |
case U_SPIFFS: |
#endif |
Serial.println("OTA start updating filesystem."); |
SPIFFS.end(); |
break; |
default: |
Serial.println("Unknown OTA update type."); |
break; |
} |
}); |
ArduinoOTA.onEnd([]() { |
Serial.println("OTA update complete."); |
SPIFFS.begin(); |
#if defined(ARDUINO_ARCH_ESP8266) |
// For what it's worth, check the filesystem on ESP8266. |
SPIFFS.check(); |
#endif |
ESP.restart(); |
}); |
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { |
Serial.printf("OTA update progress: %u%%\r", (progress / (total / 100))); |
}); |
ArduinoOTA.onError([](ota_error_t error) { |
Serial.printf("OTA update error [%u]: ", error); |
switch (error) { |
case OTA_AUTH_ERROR: |
Serial.println("OTA authentication failed"); |
break; |
case OTA_BEGIN_ERROR: |
Serial.println("OTA begin failed"); |
break; |
case OTA_CONNECT_ERROR: |
Serial.println("OTA connect failed"); |
break; |
case OTA_RECEIVE_ERROR: |
Serial.println("OTA receive failed"); |
break; |
case OTA_END_ERROR: |
Serial.println("OTA end failed"); |
break; |
default: |
Serial.println("Unknown OTA failure"); |
break; |
} |
ESP.restart(); |
}); |
ArduinoOTA.begin(); |
|
// Set up MQTT client. |
mqttClient.setServer(mqtt_host, mqtt_port); |
mqttClient.setCallback(mqttCallback); |
|
// Touchdown. |
Serial.println("Setup complete."); |
} |
|
void loop() { |
// Check the Wifi connection status. |
int wifiStatus = WiFi.status(); |
switch (wifiStatus) { |
case WL_CONNECTED: |
if (!loopWifiConnected()) { |
delay(1000); |
break; |
} |
delay(1); |
break; |
case WL_NO_SHIELD: |
Serial.println("No Wifi shield present."); |
goto DEFAULT_CASE; |
break; |
case WL_NO_SSID_AVAIL: |
Serial.println("Configured SSID not found."); |
goto DEFAULT_CASE; |
break; |
// Temporary statuses indicating transitional states. |
case WL_IDLE_STATUS: |
case WL_SCAN_COMPLETED: |
delay(1000); |
break; |
// Fatal Wifi statuses trigger a delayed ESP restart. |
case WL_CONNECT_FAILED: |
case WL_CONNECTION_LOST: |
case WL_DISCONNECTED: |
default: |
Serial.println("Wifi connection failed with status: " + String(wifiStatus)); |
DEFAULT_CASE: |
delay(10000); |
ESP.restart(); |
break; |
} |
} |