/arduinoLightStrip/arduinoLightStrip.ino |
@@ -0,0 +1,327 @@ |
/*************************************************************************/ |
/* Copyright (C) 2021 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 |
|
// Define LED strip PWM pins. |
#if defined(ARDUINO_ARCH_ESP8266) |
#define R_PIN D5 |
#define G_PIN D6 |
#define B_PIN D7 |
#elif defined(ARDUINO_ARCH_ESP32) |
#define R_PIN 33 |
#define G_PIN 34 |
#define B_PIN 35 |
#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); |
|
/*************************************************************************/ |
/* Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 */ |
/*************************************************************************/ |
int wasMapValueToRange(int v, int xMin, int xMax, int yMin, int yMax) { |
return yMin + ( |
( yMax - yMin ) * ( v - xMin ) / ( xMax - xMin ) |
); |
} |
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; |
} |
|
Serial.println("Message deserialized...."); |
|
// Ignore message with no identifier in the payload. |
if (!doc.containsKey("id")) { |
return; |
} |
|
// Do not listen to self. |
String id = (const char *)doc["id"]; |
if (id == String(MQTT_CLIENT_ID().c_str())) { |
return; |
} |
|
// Set the pin values while mapping from RGB to [0, 3.3]V (PWMRANGE) |
const int r = wasMapValueToRange((const int)doc["r"], 0, 255, 0, PWMRANGE); |
analogWrite(R_PIN, r); |
|
const int g = wasMapValueToRange((const int)doc["g"], 0, 255, 0, PWMRANGE); |
analogWrite(G_PIN, g); |
|
const int b = wasMapValueToRange((const int)doc["b"], 0, 255, 0, PWMRANGE); |
analogWrite(B_PIN, b); |
|
Serial.println("R: " + String(r) + ", G: " + String(g) + ", B: " + String(b)); |
|
// Announce the action. |
StaticJsonDocument<256> msg; |
msg["R"] = r; |
msg["G"] = g; |
msg["B"] = b; |
|
char msgPublish[256]; |
serializeJson(msg, msgPublish); |
mqttClient.publish(MQTT_TOPIC().c_str(), (const char*) msgPublish); |
} |
|
bool mqttConnect() { |
Serial.println("Attempting to connect to MQTT broker: " + String(mqtt_host)); |
mqttClient.setServer(mqtt_host, mqtt_port); |
|
StaticJsonDocument<256> msg; |
char msgPublish[256]; |
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"; |
serializeJson(msg, msgPublish); |
mqttClient.publish(MQTT_TOPIC().c_str(), (const char*) msgPublish); |
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"; |
serializeJson(msg, msgPublish); |
mqttClient.publish(MQTT_TOPIC().c_str(), (const char*) msgPublish); |
return true; |
} |
else { |
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); |
|
// Initialize pins. |
pinMode(R_PIN, OUTPUT); |
analogWrite(R_PIN, 0); |
pinMode(G_PIN, OUTPUT); |
analogWrite(G_PIN, 0); |
pinMode(B_PIN, OUTPUT); |
analogWrite(B_PIN, 0); |
|
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; |
} |
} |