Merge branch 'debsahu/master'

Support for AMQTT, NEOANIMATIONFX
This commit is contained in:
Tobias Blum 2018-04-07 21:57:21 +02:00
commit 9b7ca2ac47
4 changed files with 1157 additions and 396 deletions

View file

@ -35,6 +35,18 @@
PubSubClient mqtt_client(espClient);
#endif
#ifdef ENABLE_AMQTT
#include <AsyncMqttClient.h> //https://github.com/marvinroger/async-mqtt-client
//https://github.com/me-no-dev/ESPAsyncTCP
#ifdef ENABLE_HOMEASSISTANT
#include <ArduinoJson.h>
#endif
AsyncMqttClient amqttClient;
WiFiEventHandler wifiConnectHandler;
WiFiEventHandler wifiDisconnectHandler;
#endif
// ***************************************************************************
// Instanciate HTTP(80) / WebSockets(81) Server
@ -47,6 +59,25 @@ WebSocketsServer webSocket = WebSocketsServer(81);
ESP8266HTTPUpdateServer httpUpdater;
#endif
#ifdef USE_NEOANIMATIONFX
// ***************************************************************************
// Load libraries / Instanciate NeoAnimationFX library
// ***************************************************************************
// https://github.com/debsahu/NeoAnimationFX
#include <NeoAnimationFX.h>
#define NEOMETHOD NeoPBBGRB800
NEOMETHOD neoStrip(NUMLEDS);
NeoAnimationFX<NEOMETHOD> strip(neoStrip);
// Uses Pin RX / GPIO3 (Only pin that is supported, due to hardware limitations)
// NEOMETHOD NeoPBBGRB800 uses GRB config 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEOMETHOD NeoPBBGRB400 uses GRB config 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEOMETHOD NeoPBBRGB800 uses RGB config 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEOMETHOD NeoPBBRGB400 uses RGB config 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
#endif
#ifdef USE_WS2812FX
// ***************************************************************************
// Load libraries / Instanciate WS2812FX library
// ***************************************************************************
@ -66,14 +97,23 @@ WS2812FX strip = WS2812FX(NUMLEDS, PIN, NEO_GRB + NEO_KHZ800);
// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input
// and minimize distance between Arduino and first pixel. Avoid connecting
// on a live circuit...if you must, connect GND first.
#endif
// ***************************************************************************
// Load library "ticker" for blinking status led
// ***************************************************************************
#include <Ticker.h>
Ticker ticker;
#ifdef ENABLE_HOMEASSISTANT
Ticker ha_send_data;
#endif
#ifdef ENABLE_AMQTT
Ticker mqttReconnectTimer;
Ticker wifiReconnectTimer;
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
Ticker spiffs_save_state;
#endif
void tick()
{
//toggle state
@ -81,7 +121,7 @@ void tick()
digitalWrite(BUILTIN_LED, !state); // set pin to the opposite state
}
#ifdef ENABLE_STATE_SAVE_EEPROM
// ***************************************************************************
// EEPROM helper
// ***************************************************************************
@ -107,7 +147,7 @@ void writeEEPROM(int offset, int len, String value) {
}
}
}
#endif
// ***************************************************************************
// Saved state handling
@ -130,7 +170,6 @@ String getValue(String data, char separator, int index)
return found>index ? data.substring(strIndex[0], strIndex[1]) : "";
}
// ***************************************************************************
// Callback for WiFiManager library when config mode is entered
// ***************************************************************************
@ -171,12 +210,12 @@ void saveConfigCallback () {
// ***************************************************************************
#include "colormodes.h"
// ***************************************************************************
// MAIN
// ***************************************************************************
void setup() {
// system_update_cpu_freq(160);
DBG_OUTPUT_PORT.begin(115200);
EEPROM.begin(512);
@ -189,6 +228,23 @@ void setup() {
// start ticker with 0.5 because we start in AP mode and try to connect
ticker.attach(0.5, tick);
// ***************************************************************************
// Setup: SPIFFS
// ***************************************************************************
SPIFFS.begin();
{
Dir dir = SPIFFS.openDir("/");
while (dir.next()) {
String fileName = dir.fileName();
size_t fileSize = dir.fileSize();
DBG_OUTPUT_PORT.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str());
}
FSInfo fs_info;
SPIFFS.info(fs_info);
DBG_OUTPUT_PORT.printf("FS Usage: %d/%d bytes\n\n", fs_info.usedBytes, fs_info.totalBytes);
}
wifi_station_set_hostname(const_cast<char*>(HOSTNAME));
// ***************************************************************************
@ -207,7 +263,10 @@ void setup() {
// The extra parameters to be configured (can be either global or just in the setup)
// After connecting, parameter.getValue() will get you the configured value
// id/name placeholder/prompt default length
#ifdef ENABLE_MQTT
#if defined(ENABLE_MQTT) or defined(ENABLE_AMQTT)
#if defined(ENABLE_STATE_SAVE_SPIFFS)
(readConfigFS()) ? DBG_OUTPUT_PORT.println("WiFiManager config FS Read success!"): DBG_OUTPUT_PORT.println("WiFiManager config FS Read failure!");
#else
String settings_available = readEEPROM(134, 1);
if (settings_available == "1") {
readEEPROM(0, 64).toCharArray(mqtt_host, 64); // 0-63
@ -219,7 +278,7 @@ void setup() {
DBG_OUTPUT_PORT.printf("MQTT user: %s\n", mqtt_user);
DBG_OUTPUT_PORT.printf("MQTT pass: %s\n", mqtt_pass);
}
#endif
WiFiManagerParameter custom_mqtt_host("host", "MQTT hostname", mqtt_host, 64);
WiFiManagerParameter custom_mqtt_port("port", "MQTT port", mqtt_port, 6);
WiFiManagerParameter custom_mqtt_user("user", "MQTT user", mqtt_user, 32);
@ -234,7 +293,7 @@ void setup() {
//set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
wifiManager.setAPCallback(configModeCallback);
#ifdef ENABLE_MQTT
#if defined(ENABLE_MQTT) or defined(ENABLE_AMQTT)
//set config save notify callback
wifiManager.setSaveConfigCallback(saveConfigCallback);
@ -245,6 +304,8 @@ void setup() {
wifiManager.addParameter(&custom_mqtt_pass);
#endif
WiFi.setSleepMode(WIFI_NONE_SLEEP);
//fetches ssid and pass and tries to connect
//if it does not connect it starts an access point with the specified name
//here "AutoConnectAP"
@ -256,7 +317,7 @@ void setup() {
delay(1000);
}
#ifdef ENABLE_MQTT
#if defined(ENABLE_MQTT) or defined(ENABLE_AMQTT)
//read updated parameters
strcpy(mqtt_host, custom_mqtt_host.getValue());
strcpy(mqtt_port, custom_mqtt_port.getValue());
@ -264,6 +325,9 @@ void setup() {
strcpy(mqtt_pass, custom_mqtt_pass.getValue());
//save the custom parameters to FS
#if defined(ENABLE_STATE_SAVE_SPIFFS)
(writeConfigFS(shouldSaveConfig)) ? DBG_OUTPUT_PORT.println("WiFiManager config FS Save success!"): DBG_OUTPUT_PORT.println("WiFiManager config FS Save failure!");
#else if defined(ENABLE_STATE_SAVE_EEPROM)
if (shouldSaveConfig) {
DBG_OUTPUT_PORT.println("Saving WiFiManager config");
@ -275,6 +339,12 @@ void setup() {
EEPROM.commit();
}
#endif
#endif
#ifdef ENABLE_AMQTT
wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect);
wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect);
#endif
//if you get here you have connected to the WiFi
DBG_OUTPUT_PORT.println("connected...yeey :)");
@ -340,6 +410,21 @@ void setup() {
}
#endif
#ifdef ENABLE_AMQTT
if (mqtt_host != "" && String(mqtt_port).toInt() > 0) {
amqttClient.onConnect(onMqttConnect);
amqttClient.onDisconnect(onMqttDisconnect);
amqttClient.onMessage(onMqttMessage);
amqttClient.setServer(mqtt_host, String(mqtt_port).toInt());
amqttClient.setClientId(mqtt_clientid);
connectToMqtt();
}
#endif
// #ifdef ENABLE_HOMEASSISTANT
// ha_send_data.attach(5, tickerSendState); // Send HA data back only every 5 sec
// #endif
// ***************************************************************************
// Setup: MDNS responder
@ -367,24 +452,6 @@ void setup() {
webSocket.begin();
webSocket.onEvent(webSocketEvent);
// ***************************************************************************
// Setup: SPIFFS
// ***************************************************************************
SPIFFS.begin();
{
Dir dir = SPIFFS.openDir("/");
while (dir.next()) {
String fileName = dir.fileName();
size_t fileSize = dir.fileSize();
DBG_OUTPUT_PORT.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str());
}
FSInfo fs_info;
SPIFFS.info(fs_info);
DBG_OUTPUT_PORT.printf("FS Usage: %d/%d bytes\n\n", fs_info.usedBytes, fs_info.totalBytes);
}
// ***************************************************************************
// Setup: SPIFFS Webserver handler
// ***************************************************************************
@ -462,7 +529,19 @@ void setup() {
brightness = 0;
}
strip.setBrightness(brightness);
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK %") + String(brightness)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK %") + String(brightness)).c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
getStatusJSON();
});
@ -478,6 +557,15 @@ void setup() {
ws2812fx_speed = server.arg("d").toInt();
ws2812fx_speed = constrain(ws2812fx_speed, 0, 255);
strip.setSpeed(convertSpeed(ws2812fx_speed));
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ?") + String(ws2812fx_speed)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ?") + String(ws2812fx_speed)).c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
}
getStatusJSON();
@ -511,6 +599,18 @@ void setup() {
mode = OFF;
getArgs();
getStatusJSON();
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String("OK =off").c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("OK =off").c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = false;
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
});
server.on("/all", []() {
@ -518,6 +618,18 @@ void setup() {
mode = ALL;
getArgs();
getStatusJSON();
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String("OK =all").c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("OK =all").c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
});
server.on("/wipe", []() {
@ -525,6 +637,18 @@ void setup() {
mode = WIPE;
getArgs();
getStatusJSON();
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String("OK =wipe").c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("OK =wipe").c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
});
server.on("/rainbow", []() {
@ -532,6 +656,18 @@ void setup() {
mode = RAINBOW;
getArgs();
getStatusJSON();
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String("OK =rainbow").c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("OK =rainbow").c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
});
server.on("/rainbowCycle", []() {
@ -539,6 +675,18 @@ void setup() {
mode = RAINBOWCYCLE;
getArgs();
getStatusJSON();
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String("OK =rainbowCycle").c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("OK =rainbowCycle").c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
});
server.on("/theaterchase", []() {
@ -546,6 +694,37 @@ void setup() {
mode = THEATERCHASE;
getArgs();
getStatusJSON();
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String("OK =theaterchase").c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("OK =theaterchase").c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
});
server.on("/twinkleRandom", []() {
exit_func = true;
mode = TWINKLERANDOM;
getArgs();
getStatusJSON();
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String("OK =twinkleRandom").c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("OK =twinkleRandom").c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
});
server.on("/theaterchaseRainbow", []() {
@ -553,6 +732,18 @@ void setup() {
mode = THEATERCHASERAINBOW;
getArgs();
getStatusJSON();
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String("OK =theaterchaseRainbow").c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("OK =theaterchaseRainbow").c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
});
server.on("/tv", []() {
@ -560,6 +751,18 @@ void setup() {
mode = TV;
getArgs();
getStatusJSON();
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String("OK =tv").c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("OK =tv").c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
});
server.on("/get_modes", []() {
@ -570,20 +773,40 @@ void setup() {
getArgs();
mode = SET_MODE;
getStatusJSON();
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK /") + String(ws2812fx_mode).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK /") + String(ws2812fx_mode)).c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
});
#ifdef HTTP_OTA
httpUpdater.setup(&server,"/update");
#endif
#ifdef HTTP_OTA
httpUpdater.setup(&server, "/update");
#endif
server.begin();
// Start MDNS service
if (mdns_result) {
MDNS.addService("http", "tcp", 80);
}
#ifdef ENABLE_STATE_SAVE
#ifdef ENABLE_STATE_SAVE_SPIFFS
(readStateFS()) ? DBG_OUTPUT_PORT.println(" Success!") : DBG_OUTPUT_PORT.println(" Failure!");
#endif
#ifdef ENABLE_STATE_SAVE_EEPROM
// Load state string from EEPROM
String saved_state_string = readEEPROM(256, 32);
String chk = getValue(saved_state_string, '|', 0);
@ -608,13 +831,32 @@ void loop() {
#endif
#ifdef ENABLE_MQTT
if (WiFi.status() != WL_CONNECTED) {
#ifdef ENABLE_HOMEASSISTANT
ha_send_data.detach();
#endif
DBG_OUTPUT_PORT.println("WiFi disconnected, reconnecting!");
WiFi.disconnect();
WiFi.setSleepMode(WIFI_NONE_SLEEP);
WiFi.mode(WIFI_STA);
WiFi.begin();
} else {
if (mqtt_host != "" && String(mqtt_port).toInt() > 0 && mqtt_reconnect_retries < MQTT_MAX_RECONNECT_TRIES) {
if (!mqtt_client.connected()) {
#ifdef ENABLE_HOMEASSISTANT
ha_send_data.detach();
#endif
DBG_OUTPUT_PORT.println("MQTT disconnected, reconnecting!");
mqtt_reconnect();
} else {
mqtt_client.loop();
}
}
}
#endif
#ifdef ENABLE_HOMEASSISTANT
// if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
if (new_ha_mqtt_msg) sendState();
#endif
// Simple statemachine that handles the different modes
@ -624,8 +866,9 @@ void loop() {
mode = HOLD;
}
if (mode == OFF) {
strip.setColor(0,0,0);
strip.setMode(FX_MODE_STATIC);
// strip.setColor(0,0,0);
// strip.setMode(FX_MODE_STATIC);
if(strip.isRunning()) strip.stop(); //should clear memory
// mode = HOLD;
}
if (mode == ALL) {
@ -633,6 +876,18 @@ void loop() {
strip.setMode(FX_MODE_STATIC);
mode = HOLD;
}
if (mode == SETCOLOR) {
strip.setColor(main_color.red, main_color.green, main_color.blue);
mode = HOLD;
}
if (mode == SETSPEED) {
strip.setSpeed(convertSpeed(ws2812fx_speed));
// mode = HOLD;
}
if (mode == BRIGHTNESS) {
strip.setBrightness(brightness);
mode = HOLD;
}
if (mode == WIPE) {
strip.setColor(main_color.red, main_color.green, main_color.blue);
strip.setMode(FX_MODE_COLOR_WIPE);
@ -661,11 +916,13 @@ void loop() {
mode = HOLD;
}
if (mode == HOLD || mode == CUSTOM) {
if(!strip.isRunning()) strip.start();
if (exit_func) {
exit_func = false;
}
}
if (mode == TV) {
if(!strip.isRunning()) strip.start();
tv();
}
@ -674,8 +931,13 @@ void loop() {
strip.service();
}
#ifdef ENABLE_STATE_SAVE_SPIFFS
if (updateStateFS) {
(writeStateFS()) ? DBG_OUTPUT_PORT.println(" Success!") : DBG_OUTPUT_PORT.println(" Failure!");
}
#endif
#ifdef ENABLE_STATE_SAVE
#ifdef ENABLE_STATE_SAVE_EEPROM
// Check for state changes
sprintf(current_state, "STA|%2d|%3d|%3d|%3d|%3d|%3d|%3d", mode, strip.getMode(), ws2812fx_speed, brightness, main_color.red, main_color.green, main_color.blue);

View file

@ -1,16 +1,31 @@
//#define USE_NEOANIMATIONFX // Uses NeoAnimationFX, PIN is ignored & set to RX/GPIO3
#define USE_WS2812FX // Uses WS2812FX
// Neopixel
#define PIN 5 // PIN (5 / D1) where neopixel / WS2811 strip is attached
#define PIN 5 // PIN (14 / D5) where neopixel / WS2811 strip is attached
#define NUMLEDS 24 // Number of leds in the strip
// #define BUILTIN_LED 2 // ESP-12F has the built in LED on GPIO2, see https://github.com/esp8266/Arduino/issues/2192
#define BUILTIN_LED 2 // ESP-12F has the built in LED on GPIO2, see https://github.com/esp8266/Arduino/issues/2192
#define BUTTON 4 // Input pin (4 / D2) for switching the LED strip on / off, connect this PIN to ground to trigger button.
const char HOSTNAME[] = "McLighting01"; // Friedly hostname
//#define HTTP_OTA // If defined, enable Added ESP8266HTTPUpdateServer
#define ENABLE_OTA // If defined, enable Arduino OTA code.
#define ENABLE_MQTT // If defined, enable MQTT client code, see: https://github.com/toblum/McLighting/wiki/MQTT-API
#define ENABLE_HOMEASSISTANT // If defined, enable Homeassistant integration, ENABLE_MQTT must be active
// #define ENABLE_BUTTON // If defined, enable button handling code, see: https://github.com/toblum/McLighting/wiki/Button-control
#define ENABLE_BUTTON // If defined, enable button handling code, see: https://github.com/toblum/McLighting/wiki/Button-control
#if defined(USE_NEOANIMATIONFX) and defined(USE_WS2812FX)
#error "Cant have both NeoAnimationFX and WS2812FX enabled. Choose either one."
#endif
#if !defined(USE_NEOANIMATIONFX) and !defined(USE_WS2812FX)
#error "Need to either use NeoAnimationFX and WS2812FX mode."
#endif
#if defined(ENABLE_MQTT) and defined(ENABLE_AMQTT)
#error "Cant have both PubSubClient and AsyncMQTT enabled. Choose either one."
#endif
#if ( (defined(ENABLE_HOMEASSISTANT) and !defined(ENABLE_MQTT)) and (defined(ENABLE_HOMEASSISTANT) and !defined(ENABLE_AMQTT)) )
#error "To use HA, you have to either enable PubCubClient or AsyncMQTT"
#endif
// parameters for automatically cycling favorite patterns
uint32_t autoParams[][4] = { // color, speed, mode, duration (seconds)
@ -20,6 +35,7 @@ uint32_t autoParams[][4] = { // color, speed, mode, duration (seconds)
{0x0000ff, 200, 42, 15.0} // fireworks for 15 seconds
};
#if defined(ENABLE_MQTT) or defined(ENABLE_AMQTT)
#ifdef ENABLE_MQTT
#define MQTT_MAX_PACKET_SIZE 512
#define MQTT_MAX_RECONNECT_TRIES 4
@ -27,26 +43,36 @@ uint32_t autoParams[][4] = { // color, speed, mode, duration (seconds)
int mqtt_reconnect_retries = 0;
char mqtt_intopic[strlen(HOSTNAME) + 4 + 5]; // Topic in will be: <HOSTNAME>/in
char mqtt_outtopic[strlen(HOSTNAME) + 5 + 5]; // Topic out will be: <HOSTNAME>/out
uint8_t qossub = 0; // PubSubClient can sub qos 0 or 1
#endif
#ifdef ENABLE_AMQTT
String mqtt_intopic = String(HOSTNAME) + "/in";
String mqtt_outtopic = String(HOSTNAME) + "/out";
uint8_t qossub = 0; // AMQTT can sub qos 0 or 1 or 2
uint8_t qospub = 0; // AMQTT can pub qos 0 or 1 or 2
#endif
#ifdef ENABLE_HOMEASSISTANT
String mqtt_ha = "home/" + String(HOSTNAME) + "_ha/";
String mqtt_ha_state_in = mqtt_ha + "state/in";
String mqtt_ha_state_out = mqtt_ha + "state/out";
String mqtt_ha_speed = mqtt_ha + "speed";
const char* on_cmd = "ON";
const char* off_cmd = "OFF";
bool stateOn = false;
bool animation_on = false;
String effectString = "Static";
bool new_ha_mqtt_msg = false;
uint16_t color_temp = 327; // min is 154 and max is 500
#endif
const char * mqtt_clientid = HOSTNAME; // Set ClientID to HOSTNAME to be unique
const char mqtt_clientid[] = "NeoPixelsStrip"; // MQTT ClientID
char mqtt_host[64] = "";
char mqtt_port[6] = "";
char mqtt_user[32] = "";
char mqtt_pass[32] = "";
#endif
@ -56,7 +82,7 @@ uint32_t autoParams[][4] = { // color, speed, mode, duration (seconds)
#define DBG_OUTPUT_PORT Serial // Set debug output port
// List of all color modes
enum MODE { SET_MODE, HOLD, OFF, ALL, WIPE, RAINBOW, RAINBOWCYCLE, THEATERCHASE, TWINKLERANDOM, THEATERCHASERAINBOW, TV, CUSTOM };
enum MODE { SET_MODE, HOLD, OFF, ALL, SETCOLOR, SETSPEED, BRIGHTNESS, WIPE, RAINBOW, RAINBOWCYCLE, THEATERCHASE, TWINKLERANDOM, THEATERCHASERAINBOW, TV, CUSTOM };
MODE mode = RAINBOW; // Standard mode that is active when software starts
@ -80,14 +106,18 @@ typedef struct ledstate LEDState; // Define the datatype LEDState
LEDState ledstates[NUMLEDS]; // Get an array of led states to store the state of the whole strip
LEDState main_color = { 255, 0, 0 }; // Store the "main color" of the strip used in single color modes
#define ENABLE_STATE_SAVE // If defined, save state on reboot
#ifdef ENABLE_STATE_SAVE
#define ENABLE_STATE_SAVE_SPIFFS // If defined, saves state on SPIFFS
//#define ENABLE_STATE_SAVE_EEPROM // If defined, save state on reboot
#ifdef ENABLE_STATE_SAVE_EEPROM
char current_state[32]; // Keeps the current state representation
char last_state[32]; // Save the last state as string representation
unsigned long time_statechange = 0; // Time when the state last changed
int timeout_statechange_save = 5000; // Timeout in ms to wait before state is saved
bool state_save_requested = false; // State has to be saved after timeout
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
bool updateStateFS = false;
#endif
// Button handling
#ifdef ENABLE_BUTTON
@ -103,3 +133,13 @@ LEDState main_color = { 255, 0, 0 }; // Store the "main color" of the strip use
byte prevKeyState = HIGH; // button is active low
boolean buttonState = false;
#endif
#define PIN 14 // PIN (14 / D5) where neopixel / WS2811 strip is attached
#define NUMLEDS 300 // Number of leds in the strip
#define BUILTIN_LED 2 // ESP-12F has the built in LED on GPIO2, see https://github.com/esp8266/Arduino/issues/2192
#define BUTTON 0 // Input pin (4 / D2) for switching the LED strip on / off, connect this PIN to ground to trigger button.
#define HTTP_OTA // If defined, enable ESP8266HTTPUpdateServer OTA code.
//#define ENABLE_OTA // If defined, enable Arduino OTA code.
#define ENABLE_AMQTT // If defined, enable Async MQTT code, see: https://github.com/marvinroger/async-mqtt-client
//#define ENABLE_MQTT // If defined, enable MQTT client code, see: https://github.com/toblum/McLighting/wiki/MQTT-API
const char * mqtt_clientid = HOSTNAME; // Set ClientID to HOSTNAME to be unique

View file

@ -1,6 +1,17 @@
// ***************************************************************************
// Request handlers
// ***************************************************************************
#ifdef ENABLE_HOMEASSISTANT
void tickerSendState(){
new_ha_mqtt_msg = true;
}
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
void tickerSpiffsSaveState(){
updateStateFS = true;
}
#endif
void getArgs() {
if (server.arg("rgb") != "") {
uint32_t rgb = (uint32_t) strtol(server.arg("rgb").c_str(), NULL, 16);
@ -40,8 +51,9 @@ void getArgs() {
}
long convertSpeed(int mcl_speed) {
long ws2812_speed = mcl_speed * 256;
uint16_t convertSpeed(uint8_t mcl_speed) {
//long ws2812_speed = mcl_speed * 256;
uint16_t ws2812_speed = 61760 * (exp(0.0002336 * mcl_speed) - exp(-0.03181 * mcl_speed));
ws2812_speed = SPEED_MAX - ws2812_speed;
if (ws2812_speed < SPEED_MIN) {
ws2812_speed = SPEED_MIN;
@ -286,33 +298,6 @@ void getModesJSON() {
server.send ( 200, "application/json", listModesJSON() );
}
#ifdef ENABLE_HOMEASSISTANT
/********************************** START SEND STATE*****************************************/
void sendState() {
StaticJsonBuffer<JSON_OBJECT_SIZE(10)> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
root["state"] = (stateOn) ? on_cmd : off_cmd;
JsonObject& color = root.createNestedObject("color");
color["r"] = main_color.red;
color["g"] = main_color.green;
color["b"] = main_color.blue;
root["brightness"] = brightness;
char modeName[30];
strncpy_P(modeName, (PGM_P)strip.getModeName(strip.getMode()), sizeof(modeName)); // copy from progmem
root["effect"] = modeName;
char buffer[root.measureLength() + 1];
root.printTo(buffer, sizeof(buffer));
mqtt_client.publish(mqtt_ha_state_out.c_str(), buffer, true);
}
#endif
// ***************************************************************************
// HTTP request handlers
// ***************************************************************************
@ -408,9 +393,18 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght
handleSetMainColor(payload);
DBG_OUTPUT_PORT.printf("Set main color to: [%u] [%u] [%u]\n", main_color.red, main_color.green, main_color.blue);
webSocket.sendTXT(num, "OK");
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
sendState();
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
}
@ -421,6 +415,15 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght
strip.setSpeed(convertSpeed(ws2812fx_speed));
DBG_OUTPUT_PORT.printf("WS: Set speed to: [%u]\n", ws2812fx_speed);
webSocket.sendTXT(num, "OK");
#ifdef ENABLE_HOMEASSISTANT
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
}
// % ==> Set brightness
@ -430,9 +433,18 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght
DBG_OUTPUT_PORT.printf("WS: Set brightness to: [%u]\n", brightness);
strip.setBrightness(brightness);
webSocket.sendTXT(num, "OK");
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
sendState();
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
}
@ -440,9 +452,18 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght
if (payload[0] == '*') {
handleSetAllMode(payload);
webSocket.sendTXT(num, "OK");
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
sendState();
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
}
@ -450,18 +471,36 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght
if (payload[0] == '!') {
handleSetSingleLED(payload, 1);
webSocket.sendTXT(num, "OK");
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
}
// + ==> Set multiple LED in the given colors
if (payload[0] == '+') {
handleSetDifferentColors(payload);
webSocket.sendTXT(num, "OK");
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
}
// + ==> Set range of LEDs in the given color
if (payload[0] == 'R') {
handleRangeDifferentColors(payload);
webSocket.sendTXT(num, "OK");
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
}
// = ==> Activate named mode
@ -473,8 +512,17 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght
DBG_OUTPUT_PORT.printf("Activated mode [%u]!\n", mode);
webSocket.sendTXT(num, "OK");
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
sendState();
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
}
@ -485,6 +533,13 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght
String json = listStatusJSON();
DBG_OUTPUT_PORT.println(json);
webSocket.sendTXT(num, json);
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, listStatusJSON());
#endif
#ifdef ENABLE_AMQTT
String liststat = (String) listStatusJSON();
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, liststat.c_str());
#endif
}
// ~ ==> Get WS2812 modes.
@ -494,15 +549,36 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght
String json = listModesJSON();
DBG_OUTPUT_PORT.println(json);
webSocket.sendTXT(num, json);
#ifdef ENABLE_MQTT
DBG_OUTPUT_PORT.printf("Error: Not implemented. Message too large for pubsubclient.");
mqtt_client.publish(mqtt_outtopic, "ERROR: Not implemented. Message too large for pubsubclient.");
//String json_modes = listModesJSON();
//DBG_OUTPUT_PORT.printf(json_modes.c_str());
//int res = mqtt_client.publish(mqtt_outtopic, json_modes.c_str(), json_modes.length());
//DBG_OUTPUT_PORT.printf("Result: %d / %d", res, json_modes.length());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("ERROR: Not implemented. Message too large for AsyncMQTT.").c_str());
#endif
}
// / ==> Set WS2812 mode.
if (payload[0] == '/') {
handleSetWS2812FXMode(payload);
webSocket.sendTXT(num, "OK");
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
sendState();
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
}
@ -533,24 +609,25 @@ void checkForRequests() {
// ***************************************************************************
// MQTT callback / connection handler
// ***************************************************************************
#ifdef ENABLE_MQTT
#if defined(ENABLE_MQTT) or defined(ENABLE_AMQTT)
#ifdef ENABLE_HOMEASSISTANT
void temp2rgb(unsigned int kelvin) {
LEDState temp2rgb(unsigned int kelvin) {
int tmp_internal = kelvin / 100.0;
LEDState tmp_color;
// red
if (tmp_internal <= 66) {
main_color.red = 255;
tmp_color.red = 255;
} else {
float tmp_red = 329.698727446 * pow(tmp_internal - 60, -0.1332047592);
if (tmp_red < 0) {
main_color.red = 0;
tmp_color.red = 0;
} else if (tmp_red > 255) {
main_color.red = 255;
tmp_color.red = 255;
} else {
main_color.red = tmp_red;
tmp_color.red = tmp_red;
}
}
@ -558,142 +635,203 @@ void checkForRequests() {
if (tmp_internal <= 66) {
float tmp_green = 99.4708025861 * log(tmp_internal) - 161.1195681661;
if (tmp_green < 0) {
main_color.green = 0;
tmp_color.green = 0;
} else if (tmp_green > 255) {
main_color.green = 255;
tmp_color.green = 255;
} else {
main_color.green = tmp_green;
tmp_color.green = tmp_green;
}
} else {
float tmp_green = 288.1221695283 * pow(tmp_internal - 60, -0.0755148492);
if (tmp_green < 0) {
main_color.green = 0;
tmp_color.green = 0;
} else if (tmp_green > 255) {
main_color.green = 255;
tmp_color.green = 255;
} else {
main_color.green = tmp_green;
tmp_color.green = tmp_green;
}
}
// blue
if (tmp_internal >= 66) {
main_color.blue = 255;
tmp_color.blue = 255;
} else if (tmp_internal <= 19) {
main_color.blue = 0;
tmp_color.blue = 0;
} else {
float tmp_blue = 138.5177312231 * log(tmp_internal - 10) - 305.0447927307;
if (tmp_blue < 0) {
main_color.blue = 0;
tmp_color.blue = 0;
} else if (tmp_blue > 255) {
main_color.blue = 255;
tmp_color.blue = 255;
} else {
main_color.blue = tmp_blue;
tmp_color.blue = tmp_blue;
}
}
return tmp_color;
}
//handleSetWS2812FXMode((uint8_t *) "/0");
strip.setColor(main_color.red, main_color.green, main_color.blue);
strip.start();
void sendState() {
const size_t bufferSize = JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(6);
//StaticJsonBuffer<bufferSize> jsonBuffer;
DynamicJsonBuffer jsonBuffer(bufferSize);
JsonObject& root = jsonBuffer.createObject();
root["state"] = (stateOn) ? on_cmd : off_cmd;
JsonObject& color = root.createNestedObject("color");
color["r"] = main_color.red;
color["g"] = main_color.green;
color["b"] = main_color.blue;
root["brightness"] = brightness;
root["color_temp"] = color_temp;
root["speed"] = ws2812fx_speed;
char modeName[30];
strncpy_P(modeName, (PGM_P)strip.getModeName(strip.getMode()), sizeof(modeName)); // copy from progmem
root["effect"] = modeName;
char buffer[root.measureLength() + 1];
root.printTo(buffer, sizeof(buffer));
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_ha_state_out.c_str(), buffer, true);
DBG_OUTPUT_PORT.printf("MQTT: Send [%s]: %s\n", mqtt_ha_state_out.c_str(), buffer);
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_ha_state_out.c_str(), 1, true, buffer);
DBG_OUTPUT_PORT.printf("MQTT: Send [%s]: %s\n", mqtt_ha_state_out.c_str(), buffer);
#endif
new_ha_mqtt_msg = false;
ha_send_data.detach();
DBG_OUTPUT_PORT.printf("Heap size: %u\n", ESP.getFreeHeap());
}
bool processJson(char* message) {
StaticJsonBuffer<JSON_OBJECT_SIZE(10)> jsonBuffer;
const size_t bufferSize = JSON_OBJECT_SIZE(3) + JSON_OBJECT_SIZE(5) + 150;
//StaticJsonBuffer<bufferSize> jsonBuffer;
DynamicJsonBuffer jsonBuffer(bufferSize);
JsonObject& root = jsonBuffer.parseObject(message);
if (!root.success()) {
Serial.println("parseObject() failed");
DBG_OUTPUT_PORT.println("parseObject() failed");
return false;
}
//DBG_OUTPUT_PORT.println("JSON ParseObject() done!");
if (root.containsKey("state")) {
if (strcmp(root["state"], on_cmd) == 0 and !(animation_on)) {
const char* state_in = root["state"];
if (strcmp(state_in, on_cmd) == 0 and !(animation_on)) {
stateOn = true;
mode = ALL;
strip.setColor(main_color.red, main_color.green, main_color.blue);
strip.start();
}
else if (strcmp(root["state"], off_cmd) == 0) {
else if (strcmp(state_in, off_cmd) == 0) {
stateOn = false;
mode = OFF;
animation_on = false;
strip.start();
mode = OFF;
return true;
}
}
if (root.containsKey("color")) {
main_color.red = root["color"]["r"];
main_color.green = root["color"]["g"];
main_color.blue = root["color"]["b"];
//handleSetWS2812FXMode((uint8_t *) "/0");
strip.setColor(main_color.red, main_color.green, main_color.blue);
strip.start();
JsonObject& color = root["color"];
main_color.red = (uint8_t) color["r"];
main_color.green = (uint8_t) color["g"];
main_color.blue = (uint8_t) color["b"];
mode = SETCOLOR;
}
if (root.containsKey("speed")) {
uint8_t json_speed = constrain((uint8_t) root["speed"], 0, 255);
if (json_speed != ws2812fx_speed) {
ws2812fx_speed = json_speed;
mode = SETSPEED;
}
}
if (root.containsKey("color_temp")) {
//temp comes in as mireds, need to convert to kelvin then to RGB
int color_temp = root["color_temp"];
color_temp = (uint16_t) root["color_temp"];
unsigned int kelvin = 1000000 / color_temp;
temp2rgb(kelvin);
main_color = temp2rgb(kelvin);
mode = SETCOLOR;
}
if (root.containsKey("brightness")) {
const char * brightness_json = root["brightness"];
uint8_t b = (uint8_t) strtol((const char *) &brightness_json[0], NULL, 10);
brightness = constrain(b, 0, 255);
strip.setBrightness(brightness);
mode = BRIGHTNESS;
}
if (root.containsKey("effect")) {
animation_on = true;
effectString = String((const char *)root["effect"]);
String effectString = root["effect"].asString();
for (uint8_t i = 0; i < strip.getModeCount(); i++) {
if(String(strip.getModeName(i)) == effectString) {
mode = HOLD;
strip.setColor(main_color.red, main_color.green, main_color.blue);
strip.setMode(i);
strip.start();
mode = SET_MODE;
ws2812fx_mode = i;
break;
}
}
}
jsonBuffer.clear();
return true;
}
#endif
#ifdef ENABLE_AMQTT
void onMqttMessage(char* topic, char* payload_in, AsyncMqttClientMessageProperties properties, size_t length, size_t index, size_t total) {
DBG_OUTPUT_PORT.print("MQTT: Recieved ["); DBG_OUTPUT_PORT.print(topic);
// DBG_OUTPUT_PORT.print("]: "); DBG_OUTPUT_PORT.println(payload_in);
uint8_t * payload = (uint8_t *) malloc(length + 1);
memcpy(payload, payload_in, length);
payload[length] = NULL;
DBG_OUTPUT_PORT.printf("]: %s\n", payload);
#endif
#ifdef ENABLE_MQTT
void mqtt_callback(char* topic, byte* payload_in, unsigned int length) {
uint8_t * payload = (uint8_t *)malloc(length + 1);
memcpy(payload, payload_in, length);
payload[length] = NULL;
DBG_OUTPUT_PORT.printf("MQTT: Message arrived [%s]\n", payload);
#endif
#ifdef ENABLE_HOMEASSISTANT
if (strcmp(topic, mqtt_ha_state_in.c_str()) == 0) {
if (!processJson((char*)payload)) {
return;
}
sendState();
} else if (strcmp(topic, mqtt_ha_speed.c_str()) == 0) {
uint8_t d = (uint8_t) strtol((const char *) &payload[0], NULL, 10);
ws2812fx_speed = constrain(d, 0, 255);
strip.setSpeed(convertSpeed(ws2812fx_speed));
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
#ifdef ENABLE_MQTT
} else if (strcmp(topic, (char *)mqtt_intopic) == 0) {
#endif
#ifdef ENABLE_AMQTT
} else if (strcmp(topic, mqtt_intopic.c_str()) == 0) {
#endif
#endif
// # ==> Set main color
if (payload[0] == '#') {
handleSetMainColor(payload);
DBG_OUTPUT_PORT.printf("MQTT: Set main color to [%u] [%u] [%u]\n", main_color.red, main_color.green, main_color.blue);
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
sendState();
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
}
@ -703,7 +841,15 @@ void checkForRequests() {
ws2812fx_speed = constrain(d, 0, 255);
strip.setSpeed(convertSpeed(ws2812fx_speed));
DBG_OUTPUT_PORT.printf("MQTT: Set speed to [%u]\n", ws2812fx_speed);
#ifdef ENABLE_HOMEASSISTANT
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
}
// % ==> Set brightness
@ -712,10 +858,18 @@ void checkForRequests() {
brightness = constrain(b, 0, 255);
strip.setBrightness(brightness);
DBG_OUTPUT_PORT.printf("MQTT: Set brightness to [%u]\n", brightness);
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
sendState();
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
}
@ -723,10 +877,18 @@ void checkForRequests() {
if (payload[0] == '*') {
handleSetAllMode(payload);
DBG_OUTPUT_PORT.printf("MQTT: Set main color and light all LEDs [%s]\n", payload);
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
sendState();
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
}
@ -734,20 +896,35 @@ void checkForRequests() {
if (payload[0] == '!') {
handleSetSingleLED(payload, 1);
DBG_OUTPUT_PORT.printf("MQTT: Set single LED in given color [%s]\n", payload);
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
}
// + ==> Set multiple LED in the given colors
if (payload[0] == '+') {
handleSetDifferentColors(payload);
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
}
// R ==> Set range of LEDs in the given colors
if (payload[0] == 'R') {
handleRangeDifferentColors(payload);
DBG_OUTPUT_PORT.printf("MQTT: Set range of LEDS to single color: [%s]\n", payload);
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
}
// = ==> Activate named mode
@ -755,9 +932,17 @@ void checkForRequests() {
String str_mode = String((char *) &payload[0]);
handleSetNamedMode(str_mode);
DBG_OUTPUT_PORT.printf("MQTT: Activate named mode [%s]\n", payload);
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
sendState();
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
}
@ -765,7 +950,13 @@ void checkForRequests() {
if (payload[0] == '$') {
DBG_OUTPUT_PORT.printf("MQTT: Get status info.\n");
DBG_OUTPUT_PORT.println("MQTT: Out: " + String(listStatusJSON()));
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, listStatusJSON());
#endif
#ifdef ENABLE_AMQTT
String liststat = (String) listStatusJSON();
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, liststat.c_str());
#endif
}
// ~ ==> Get WS2812 modes.
@ -773,6 +964,7 @@ void checkForRequests() {
// Hint: https://github.com/knolleary/pubsubclient/issues/110
if (payload[0] == '~') {
DBG_OUTPUT_PORT.printf("MQTT: Get WS2812 modes.\n");
#ifdef ENABLE_MQTT
DBG_OUTPUT_PORT.printf("Error: Not implemented. Message too large for pubsubclient.");
mqtt_client.publish(mqtt_outtopic, "ERROR: Not implemented. Message too large for pubsubclient.");
//String json_modes = listModesJSON();
@ -780,16 +972,28 @@ void checkForRequests() {
//int res = mqtt_client.publish(mqtt_outtopic, json_modes.c_str(), json_modes.length());
//DBG_OUTPUT_PORT.printf("Result: %d / %d", res, json_modes.length());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("ERROR: Not implemented. Message too large for AsyncMQTT.").c_str());
#endif
}
// / ==> Set WS2812 mode.
if (payload[0] == '/') {
handleSetWS2812FXMode(payload);
DBG_OUTPUT_PORT.printf("MQTT: Set WS2812 mode [%s]\n", payload);
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String(String("OK ") + String((char *)payload)).c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
sendState();
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
}
@ -799,11 +1003,12 @@ void checkForRequests() {
free(payload);
}
#ifdef ENABLE_MQTT
void mqtt_reconnect() {
// Loop until we're reconnected
while (!mqtt_client.connected() && mqtt_reconnect_retries < MQTT_MAX_RECONNECT_TRIES) {
mqtt_reconnect_retries++;
DBG_OUTPUT_PORT.printf("Attempting MQTT connection %d / %d (%s) ...\n", mqtt_reconnect_retries, MQTT_MAX_RECONNECT_TRIES, mqtt_clientid);
DBG_OUTPUT_PORT.printf("Attempting MQTT connection %d / %d ...\n", mqtt_reconnect_retries, MQTT_MAX_RECONNECT_TRIES);
// Attempt to connect
if (mqtt_client.connect(mqtt_clientid, mqtt_user, mqtt_pass)) {
DBG_OUTPUT_PORT.println("MQTT connected!");
@ -813,11 +1018,10 @@ void checkForRequests() {
strcat(message, HOSTNAME);
mqtt_client.publish(mqtt_outtopic, message);
// ... and resubscribe
mqtt_client.subscribe(mqtt_intopic);
mqtt_client.subscribe(mqtt_intopic, qossub, qossub);
#ifdef ENABLE_HOMEASSISTANT
DBG_OUTPUT_PORT.printf("Homeassistant MQTT topic in: %s\n", mqtt_ha_state_in.c_str());
mqtt_client.subscribe(mqtt_ha_state_in.c_str());
mqtt_client.subscribe(mqtt_ha_speed.c_str());
ha_send_data.detach();
mqtt_client.subscribe(mqtt_ha_state_in.c_str(), qossub);
#endif
DBG_OUTPUT_PORT.printf("MQTT topic in: %s\n", mqtt_intopic);
@ -835,6 +1039,76 @@ void checkForRequests() {
}
}
#endif
#ifdef ENABLE_AMQTT
void connectToWifi() {
DBG_OUTPUT_PORT.println("Re-connecting to Wi-Fi...");
WiFi.setSleepMode(WIFI_NONE_SLEEP);
WiFi.mode(WIFI_STA);
WiFi.begin();
}
void connectToMqtt() {
DBG_OUTPUT_PORT.println("Connecting to MQTT...");
amqttClient.connect();
}
void onWifiConnect(const WiFiEventStationModeGotIP& event) {
DBG_OUTPUT_PORT.println("Connected to Wi-Fi.");
connectToMqtt();
}
void onWifiDisconnect(const WiFiEventStationModeDisconnected& event) {
DBG_OUTPUT_PORT.println("Disconnected from Wi-Fi.");
#ifdef ENABLE_HOMEASSISTANT
ha_send_data.detach();
#endif
mqttReconnectTimer.detach(); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
wifiReconnectTimer.once(2, connectToWifi);
}
void onMqttConnect(bool sessionPresent) {
DBG_OUTPUT_PORT.println("Connected to MQTT.");
DBG_OUTPUT_PORT.print("Session present: ");
DBG_OUTPUT_PORT.println(sessionPresent);
char * message = new char[18 + strlen(HOSTNAME) + 1];
strcpy(message, "McLighting ready: ");
strcat(message, HOSTNAME);
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, message);
//Subscribe
uint16_t packetIdSub1 = amqttClient.subscribe((char *)mqtt_intopic.c_str(), qossub);
DBG_OUTPUT_PORT.printf("Subscribing at QoS %d, packetId: ", qossub); DBG_OUTPUT_PORT.println(packetIdSub1);
#ifdef ENABLE_HOMEASSISTANT
ha_send_data.detach();
uint16_t packetIdSub2 = amqttClient.subscribe((char *)mqtt_ha_state_in.c_str(), qossub);
DBG_OUTPUT_PORT.printf("Subscribing at QoS %d, packetId: ", qossub); DBG_OUTPUT_PORT.println(packetIdSub2);
#endif
}
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
DBG_OUTPUT_PORT.print("Disconnected from MQTT, reason: ");
if (reason == AsyncMqttClientDisconnectReason::TLS_BAD_FINGERPRINT) {
DBG_OUTPUT_PORT.println("Bad server fingerprint.");
} else if (reason == AsyncMqttClientDisconnectReason::TCP_DISCONNECTED) {
DBG_OUTPUT_PORT.println("TCP Disconnected.");
} else if (reason == AsyncMqttClientDisconnectReason::MQTT_UNACCEPTABLE_PROTOCOL_VERSION) {
DBG_OUTPUT_PORT.println("Bad server fingerprint.");
} else if (reason == AsyncMqttClientDisconnectReason::MQTT_IDENTIFIER_REJECTED) {
DBG_OUTPUT_PORT.println("MQTT Identifier rejected.");
} else if (reason == AsyncMqttClientDisconnectReason::MQTT_SERVER_UNAVAILABLE) {
DBG_OUTPUT_PORT.println("MQTT server unavailable.");
} else if (reason == AsyncMqttClientDisconnectReason::MQTT_MALFORMED_CREDENTIALS) {
DBG_OUTPUT_PORT.println("MQTT malformed credentials.");
} else if (reason == AsyncMqttClientDisconnectReason::MQTT_NOT_AUTHORIZED) {
DBG_OUTPUT_PORT.println("MQTT not authorized.");
} else if (reason == AsyncMqttClientDisconnectReason::ESP8266_NOT_ENOUGH_SPACE) {
DBG_OUTPUT_PORT.println("Not enough space on esp8266.");
}
if (WiFi.isConnected()) {
mqttReconnectTimer.once(5, connectToMqtt);
}
}
#endif
#endif
// ***************************************************************************
@ -854,10 +1128,16 @@ void checkForRequests() {
buttonState = false;
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String("OK =off").c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("OK =off").c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = false;
sendState();
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
}
}
@ -868,10 +1148,16 @@ void checkForRequests() {
setModeByStateString(BTN_MODE_MEDIUM);
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String("OK =fire flicker").c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("OK =fire flicker").c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
sendState();
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
}
@ -881,10 +1167,16 @@ void checkForRequests() {
setModeByStateString(BTN_MODE_LONG);
#ifdef ENABLE_MQTT
mqtt_client.publish(mqtt_outtopic, String("OK =fireworks random").c_str());
#endif
#ifdef ENABLE_AMQTT
amqttClient.publish(mqtt_outtopic.c_str(), qospub, false, String("OK =fireworks random").c_str());
#endif
#ifdef ENABLE_HOMEASSISTANT
stateOn = true;
sendState();
if(!ha_send_data.active()) ha_send_data.once(5, tickerSendState);
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
if(!spiffs_save_state.active()) spiffs_save_state.once(3, tickerSpiffsSaveState);
#endif
}
@ -918,3 +1210,157 @@ void checkForRequests() {
}
}
#endif
#ifdef ENABLE_STATE_SAVE_SPIFFS
bool updateFS = false;
// Write configuration to FS JSON
bool writeConfigFS(bool saveConfig){
if (saveConfig) {
//FS save
updateFS = true;
DBG_OUTPUT_PORT.print("Saving config: ");
DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(4));
// StaticJsonBuffer<JSON_OBJECT_SIZE(4)> jsonBuffer;
JsonObject& json = jsonBuffer.createObject();
json["mqtt_host"] = mqtt_host;
json["mqtt_port"] = mqtt_port;
json["mqtt_user"] = mqtt_user;
json["mqtt_pass"] = mqtt_pass;
// SPIFFS.remove("/config.json") ? DBG_OUTPUT_PORT.println("removed file") : DBG_OUTPUT_PORT.println("failed removing file");
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) DBG_OUTPUT_PORT.println("failed to open config file for writing");
json.printTo(DBG_OUTPUT_PORT);
json.printTo(configFile);
configFile.close();
updateFS = false;
return true;
//end save
} else {
DBG_OUTPUT_PORT.println("SaveConfig is False!");
return false;
}
}
// Read search_str to FS
bool readConfigFS() {
//read configuration from FS JSON
updateFS = true;
if (SPIFFS.exists("/config.json")) {
//file exists, reading and loading
DBG_OUTPUT_PORT.print("Reading config file... ");
File configFile = SPIFFS.open("/config.json", "r");
if (configFile) {
DBG_OUTPUT_PORT.println("Opened!");
size_t size = configFile.size();
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(4)+300);
// StaticJsonBuffer<JSON_OBJECT_SIZE(4)+300> jsonBuffer;
JsonObject& json = jsonBuffer.parseObject(buf.get());
DBG_OUTPUT_PORT.print("Config: ");
json.printTo(DBG_OUTPUT_PORT);
if (json.success()) {
DBG_OUTPUT_PORT.println(" Parsed!");
strcpy(mqtt_host, json["mqtt_host"]);
strcpy(mqtt_port, json["mqtt_port"]);
strcpy(mqtt_user, json["mqtt_user"]);
strcpy(mqtt_pass, json["mqtt_pass"]);
updateFS = false;
return true;
} else {
DBG_OUTPUT_PORT.println("Failed to load json config");
}
} else {
DBG_OUTPUT_PORT.println("Failed to open /config.json");
}
} else {
DBG_OUTPUT_PORT.println("Coudnt find config.json");
}
//end read
updateFS = false;
return false;
}
bool writeStateFS(){
updateFS = true;
//save the strip state to FS JSON
DBG_OUTPUT_PORT.print("Saving cfg: ");
DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(7));
// StaticJsonBuffer<JSON_OBJECT_SIZE(7)> jsonBuffer;
JsonObject& json = jsonBuffer.createObject();
json["mode"] = static_cast<int>(mode);
json["strip_mode"] = (int) strip.getMode();
json["brightness"] = brightness;
json["speed"] = ws2812fx_speed;
json["red"] = main_color.red;
json["green"] = main_color.green;
json["blue"] = main_color.blue;
// SPIFFS.remove("/state.json") ? DBG_OUTPUT_PORT.println("removed file") : DBG_OUTPUT_PORT.println("failed removing file");
File configFile = SPIFFS.open("/stripstate.json", "w");
if (!configFile) {
DBG_OUTPUT_PORT.println("Failed!");
updateFS = false;
spiffs_save_state.detach();
updateStateFS = false;
return false;
}
json.printTo(DBG_OUTPUT_PORT);
json.printTo(configFile);
configFile.close();
updateFS = false;
spiffs_save_state.detach();
updateStateFS = false;
return true;
//end save
}
bool readStateFS() {
//read strip state from FS JSON
updateFS = true;
//if (resetsettings) { SPIFFS.begin(); SPIFFS.remove("/config.json"); SPIFFS.format(); delay(1000);}
if (SPIFFS.exists("/stripstate.json")) {
//file exists, reading and loading
DBG_OUTPUT_PORT.print("Read cfg: ");
File configFile = SPIFFS.open("/stripstate.json", "r");
if (configFile) {
size_t size = configFile.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(7)+200);
// StaticJsonBuffer<JSON_OBJECT_SIZE(7)+200> jsonBuffer;
JsonObject& json = jsonBuffer.parseObject(buf.get());
json.printTo(DBG_OUTPUT_PORT);
if (json.success()) {
mode = static_cast<MODE>((int) json["mode"]);
ws2812fx_mode = json["strip_mode"];
brightness = json["brightness"];
ws2812fx_speed = json["speed"];
main_color.red = json["red"];
main_color.green = json["green"];
main_color.blue = json["blue"];
strip.setMode(ws2812fx_mode);
strip.setSpeed(convertSpeed(ws2812fx_speed));
strip.setBrightness(brightness);
strip.setColor(main_color.red, main_color.green, main_color.blue);
updateFS = false;
return true;
} else {
DBG_OUTPUT_PORT.println("Failed to parse JSON!");
}
} else {
DBG_OUTPUT_PORT.println("Failed to open \"/stripstate.json\"");
}
} else {
DBG_OUTPUT_PORT.println("Coudnt find \"/stripstate.json\"");
}
//end read
updateFS = false;
return false;
}
#endif

View file

@ -3,6 +3,7 @@ light:
name: "NeoPixel LEDs"
state_topic: "home/McLighting01_ha/state/out"
command_topic: "home/McLighting01_ha/state/in"
on_command_type: 'first'
effect: true
effect_list:
######
@ -62,6 +63,7 @@ light:
- "Tricolor Chase"
- "ICU"
brightness: true
color_temp: true
rgb: true
optimistic: false
qos: 0
@ -77,7 +79,7 @@ input_number:
automation:
- id: 71938579813759813757
alias: NeoPixel Animation Speed
alias: NeoPixel Animation Speed Send
initial_state: true
hide_entity: false
trigger:
@ -85,7 +87,18 @@ automation:
platform: state
action:
- data_template:
payload_template: '{{ trigger.to_state.state | int }}'
payload_template: '{"speed": {{ trigger.to_state.state | int }}}'
retain: true
topic: home/McLighting01_ha/speed
topic: home/McLighting01_ha/state/in
service: mqtt.publish
- id: 93786598732698756967
alias: NeoPixel Animation Speed Receive
trigger:
- platform: mqtt
topic: home/McLighting01_ha/state/out
action:
- data_template:
entity_id: input_number.neopixel_animation_speed
value: '{{ trigger.payload_json.speed | int }}'
service: input_number.set_value