diff --git a/Arduino/McLighting/McLighting.ino b/Arduino/McLighting/McLighting.ino index a937ef0..708b4fd 100644 --- a/Arduino/McLighting/McLighting.ino +++ b/Arduino/McLighting/McLighting.ino @@ -1,953 +1,957 @@ -#include "definitions.h" - -// *************************************************************************** -// Load libraries for: WebServer / WiFiManager / WebSockets -// *************************************************************************** -#include //https://github.com/esp8266/Arduino - -// needed for library WiFiManager -#include -#include -#include //https://github.com/tzapu/WiFiManager - -#include -#include -#include -#include - -#include //https://github.com/Links2004/arduinoWebSockets -#include - -// OTA -#ifdef ENABLE_OTA - #include - #include -#endif - -// MQTT -#ifdef ENABLE_MQTT - #include - #ifdef ENABLE_HOMEASSISTANT - #include - #endif - - WiFiClient espClient; - PubSubClient mqtt_client(espClient); -#endif - -#ifdef ENABLE_AMQTT - #include //https://github.com/marvinroger/async-mqtt-client - //https://github.com/me-no-dev/ESPAsyncTCP - #ifdef ENABLE_HOMEASSISTANT - #include - #endif - - AsyncMqttClient amqttClient; - WiFiEventHandler wifiConnectHandler; - WiFiEventHandler wifiDisconnectHandler; -#endif - - -// *************************************************************************** -// Instanciate HTTP(80) / WebSockets(81) Server -// *************************************************************************** -ESP8266WebServer server(80); -WebSocketsServer webSocket = WebSocketsServer(81); - -#ifdef HTTP_OTA -#include -ESP8266HTTPUpdateServer httpUpdater; -#endif - -#ifdef USE_NEOANIMATIONFX -// *************************************************************************** -// Load libraries / Instanciate NeoAnimationFX library -// *************************************************************************** -// https://github.com/debsahu/NeoAnimationFX -#include -#define NEOMETHOD NeoPBBGRB800 - -NEOMETHOD neoStrip(NUMLEDS); -NeoAnimationFX 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 -// *************************************************************************** -// https://github.com/kitesurfer1404/WS2812FX -#include -WS2812FX strip = WS2812FX(NUMLEDS, PIN, NEO_GRB + NEO_KHZ800); - -// Parameter 1 = number of pixels in strip -// Parameter 2 = Arduino pin number (most are valid) -// Parameter 3 = pixel type flags, add together as needed: -// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) -// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) -// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products) -// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2) - -// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across -// 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 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 - int state = digitalRead(BUILTIN_LED); // get the current state of GPIO1 pin - digitalWrite(BUILTIN_LED, !state); // set pin to the opposite state -} - -#ifdef ENABLE_STATE_SAVE_EEPROM - // *************************************************************************** - // EEPROM helper - // *************************************************************************** - String readEEPROM(int offset, int len) { - String res = ""; - for (int i = 0; i < len; ++i) - { - res += char(EEPROM.read(i + offset)); - //DBG_OUTPUT_PORT.println(char(EEPROM.read(i + offset))); - } - DBG_OUTPUT_PORT.printf("readEEPROM(): %s\n", res.c_str()); - return res; - } - - void writeEEPROM(int offset, int len, String value) { - DBG_OUTPUT_PORT.printf("writeEEPROM(): %s\n", value.c_str()); - for (int i = 0; i < len; ++i) - { - if (i < value.length()) { - EEPROM.write(i + offset, value[i]); - } else { - EEPROM.write(i + offset, NULL); - } - } - } -#endif - -// *************************************************************************** -// Saved state handling -// *************************************************************************** -// https://stackoverflow.com/questions/9072320/split-string-into-string-array -String getValue(String data, char separator, int index) -{ - int found = 0; - int strIndex[] = {0, -1}; - int maxIndex = data.length()-1; - - for(int i=0; i<=maxIndex && found<=index; i++){ - if(data.charAt(i)==separator || i==maxIndex){ - found++; - strIndex[0] = strIndex[1]+1; - strIndex[1] = (i == maxIndex) ? i+1 : i; - } - } - - return found>index ? data.substring(strIndex[0], strIndex[1]) : ""; -} - -// *************************************************************************** -// Callback for WiFiManager library when config mode is entered -// *************************************************************************** -//gets called when WiFiManager enters configuration mode -void configModeCallback (WiFiManager *myWiFiManager) { - DBG_OUTPUT_PORT.println("Entered config mode"); - DBG_OUTPUT_PORT.println(WiFi.softAPIP()); - //if you used auto generated SSID, print it - DBG_OUTPUT_PORT.println(myWiFiManager->getConfigPortalSSID()); - //entered config mode, make led toggle faster - ticker.attach(0.2, tick); - - uint16_t i; - for (i = 0; i < strip.numPixels(); i++) { - strip.setPixelColor(i, 0, 0, 255); - } - strip.show(); -} - -//callback notifying us of the need to save config -void saveConfigCallback () { - DBG_OUTPUT_PORT.println("Should save config"); - shouldSaveConfig = true; -} - -// *************************************************************************** -// Include: Webserver -// *************************************************************************** -#include "spiffs_webserver.h" - -// *************************************************************************** -// Include: Request handlers -// *************************************************************************** -#include "request_handlers.h" - -// *************************************************************************** -// Include: Color modes -// *************************************************************************** -#include "colormodes.h" - -// *************************************************************************** -// MAIN -// *************************************************************************** -void setup() { -// system_update_cpu_freq(160); - - DBG_OUTPUT_PORT.begin(115200); - EEPROM.begin(512); - - // set builtin led pin as output - pinMode(BUILTIN_LED, OUTPUT); - // button pin setup -#ifdef ENABLE_BUTTON - pinMode(BUTTON,INPUT_PULLUP); -#endif - // 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(HOSTNAME)); - - // *************************************************************************** - // Setup: Neopixel - // *************************************************************************** - strip.init(); - strip.setBrightness(brightness); - strip.setSpeed(convertSpeed(ws2812fx_speed)); - //strip.setMode(FX_MODE_RAINBOW_CYCLE); - strip.setColor(main_color.red, main_color.green, main_color.blue); - strip.start(); - - // *************************************************************************** - // Setup: WiFiManager - // *************************************************************************** - // 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 - #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 - readEEPROM(64, 6).toCharArray(mqtt_port, 6); // 64-69 - readEEPROM(70, 32).toCharArray(mqtt_user, 32); // 70-101 - readEEPROM(102, 32).toCharArray(mqtt_pass, 32); // 102-133 - DBG_OUTPUT_PORT.printf("MQTT host: %s\n", mqtt_host); - DBG_OUTPUT_PORT.printf("MQTT port: %s\n", mqtt_port); - 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); - WiFiManagerParameter custom_mqtt_pass("pass", "MQTT pass", mqtt_pass, 32); - #endif - - //Local intialization. Once its business is done, there is no need to keep it around - WiFiManager wifiManager; - //reset settings - for testing - //wifiManager.resetSettings(); - - //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode - wifiManager.setAPCallback(configModeCallback); - - #if defined(ENABLE_MQTT) or defined(ENABLE_AMQTT) - //set config save notify callback - wifiManager.setSaveConfigCallback(saveConfigCallback); - - //add all your parameters here - wifiManager.addParameter(&custom_mqtt_host); - wifiManager.addParameter(&custom_mqtt_port); - wifiManager.addParameter(&custom_mqtt_user); - 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" - //and goes into a blocking loop awaiting configuration - if (!wifiManager.autoConnect(HOSTNAME)) { - DBG_OUTPUT_PORT.println("failed to connect and hit timeout"); - //reset and try again, or maybe put it to deep sleep - ESP.reset(); - delay(1000); - } - - #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()); - strcpy(mqtt_user, custom_mqtt_user.getValue()); - 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"); - - writeEEPROM(0, 64, mqtt_host); // 0-63 - writeEEPROM(64, 6, mqtt_port); // 64-69 - writeEEPROM(70, 32, mqtt_user); // 70-101 - writeEEPROM(102, 32, mqtt_pass); // 102-133 - writeEEPROM(134, 1, "1"); // 134 --> always "1" - 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 :)"); - ticker.detach(); - //keep LED on - digitalWrite(BUILTIN_LED, LOW); - - - // *************************************************************************** - // Configure OTA - // *************************************************************************** - #ifdef ENABLE_OTA - DBG_OUTPUT_PORT.println("Arduino OTA activated."); - - // Port defaults to 8266 - ArduinoOTA.setPort(8266); - - // Hostname defaults to esp8266-[ChipID] - ArduinoOTA.setHostname(HOSTNAME); - - // No authentication by default - // ArduinoOTA.setPassword("admin"); - - // Password can be set with it's md5 value as well - // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3 - // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3"); - - ArduinoOTA.onStart([]() { - DBG_OUTPUT_PORT.println("Arduino OTA: Start updating"); - }); - ArduinoOTA.onEnd([]() { - DBG_OUTPUT_PORT.println("Arduino OTA: End"); - }); - ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { - DBG_OUTPUT_PORT.printf("Arduino OTA Progress: %u%%\r", (progress / (total / 100))); - }); - ArduinoOTA.onError([](ota_error_t error) { - DBG_OUTPUT_PORT.printf("Arduino OTA Error[%u]: ", error); - if (error == OTA_AUTH_ERROR) DBG_OUTPUT_PORT.println("Arduino OTA: Auth Failed"); - else if (error == OTA_BEGIN_ERROR) DBG_OUTPUT_PORT.println("Arduino OTA: Begin Failed"); - else if (error == OTA_CONNECT_ERROR) DBG_OUTPUT_PORT.println("Arduino OTA: Connect Failed"); - else if (error == OTA_RECEIVE_ERROR) DBG_OUTPUT_PORT.println("Arduino OTA: Receive Failed"); - else if (error == OTA_END_ERROR) DBG_OUTPUT_PORT.println("Arduino OTA: End Failed"); - }); - - ArduinoOTA.begin(); - DBG_OUTPUT_PORT.println(""); - #endif - - - // *************************************************************************** - // Configure MQTT - // *************************************************************************** - #ifdef ENABLE_MQTT - if (mqtt_host != "" && String(mqtt_port).toInt() > 0) { - snprintf(mqtt_intopic, sizeof mqtt_intopic, "%s/in", HOSTNAME); - snprintf(mqtt_outtopic, sizeof mqtt_outtopic, "%s/out", HOSTNAME); - - DBG_OUTPUT_PORT.printf("MQTT active: %s:%d\n", mqtt_host, String(mqtt_port).toInt()); - - mqtt_client.setServer(mqtt_host, String(mqtt_port).toInt()); - mqtt_client.setCallback(mqtt_callback); - } - #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 - // *************************************************************************** - bool mdns_result = MDNS.begin(HOSTNAME); - - DBG_OUTPUT_PORT.print("Open http://"); - DBG_OUTPUT_PORT.print(WiFi.localIP()); - DBG_OUTPUT_PORT.println("/ to open McLighting."); - - DBG_OUTPUT_PORT.print("Use http://"); - DBG_OUTPUT_PORT.print(HOSTNAME); - DBG_OUTPUT_PORT.println(".local/ when you have Bonjour installed."); - - DBG_OUTPUT_PORT.print("New users: Open http://"); - DBG_OUTPUT_PORT.print(WiFi.localIP()); - DBG_OUTPUT_PORT.println("/upload to upload the webpages first."); - - DBG_OUTPUT_PORT.println(""); - - - // *************************************************************************** - // Setup: WebSocket server - // *************************************************************************** - webSocket.begin(); - webSocket.onEvent(webSocketEvent); - - // *************************************************************************** - // Setup: SPIFFS Webserver handler - // *************************************************************************** - //list directory - server.on("/list", HTTP_GET, handleFileList); - //load editor - server.on("/edit", HTTP_GET, []() { - if (!handleFileRead("/edit.htm")) server.send(404, "text/plain", "FileNotFound"); - }); - //create file - server.on("/edit", HTTP_PUT, handleFileCreate); - //delete file - server.on("/edit", HTTP_DELETE, handleFileDelete); - //first callback is called after the request has ended with all parsed arguments - //second callback handles file uploads at that location - server.on("/edit", HTTP_POST, []() { - server.send(200, "text/plain", ""); - }, handleFileUpload); - //get heap status, analog input value and all GPIO statuses in one json call - server.on("/esp_status", HTTP_GET, []() { - String json = "{"; - json += "\"heap\":" + String(ESP.getFreeHeap()); - // json += ", \"analog\":" + String(analogRead(A0)); - // json += ", \"gpio\":" + String((uint32_t)(((GPI | GPO) & 0xFFFF) | ((GP16I & 0x01) << 16))); - json += "}"; - server.send(200, "text/json", json); - json = String(); - }); - - - //called when the url is not defined here - //use it to load content from SPIFFS - server.onNotFound([]() { - if (!handleFileRead(server.uri())) - handleNotFound(); - }); - - server.on("/upload", handleMinimalUpload); - - server.on("/restart", []() { - DBG_OUTPUT_PORT.printf("/restart\n"); - server.send(200, "text/plain", "restarting..." ); - ESP.restart(); - }); - - server.on("/reset_wlan", []() { - DBG_OUTPUT_PORT.printf("/reset_wlan\n"); - server.send(200, "text/plain", "Resetting WLAN and restarting..." ); - WiFiManager wifiManager; - wifiManager.resetSettings(); - ESP.restart(); - }); - - server.on("/start_config_ap", []() { - DBG_OUTPUT_PORT.printf("/start_config_ap\n"); - server.send(200, "text/plain", "Starting config AP ..." ); - WiFiManager wifiManager; - wifiManager.startConfigPortal(HOSTNAME); - }); - - - // *************************************************************************** - // Setup: SPIFFS Webserver handler - // *************************************************************************** - server.on("/set_brightness", []() { - if (server.arg("c").toInt() > 0) { - brightness = (int) server.arg("c").toInt() * 2.55; - } else { - brightness = server.arg("p").toInt(); - } - if (brightness > 255) { - brightness = 255; - } - if (brightness < 0) { - 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(); - }); - - server.on("/get_brightness", []() { - String str_brightness = String((int) (brightness / 2.55)); - server.send(200, "text/plain", str_brightness ); - DBG_OUTPUT_PORT.print("/get_brightness: "); - DBG_OUTPUT_PORT.println(str_brightness); - }); - - server.on("/set_speed", []() { - if (server.arg("d").toInt() >= 0) { - 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(); - }); - - server.on("/get_speed", []() { - String str_speed = String(ws2812fx_speed); - server.send(200, "text/plain", str_speed ); - DBG_OUTPUT_PORT.print("/get_speed: "); - DBG_OUTPUT_PORT.println(str_speed); - }); - - server.on("/get_switch", []() { - server.send(200, "text/plain", (mode == OFF) ? "0" : "1" ); - DBG_OUTPUT_PORT.printf("/get_switch: %s\n", (mode == OFF) ? "0" : "1"); - }); - - server.on("/get_color", []() { - String rgbcolor = String(main_color.red, HEX) + String(main_color.green, HEX) + String(main_color.blue, HEX); - server.send(200, "text/plain", rgbcolor ); - DBG_OUTPUT_PORT.print("/get_color: "); - DBG_OUTPUT_PORT.println(rgbcolor); - }); - - server.on("/status", []() { - getStatusJSON(); - }); - - server.on("/off", []() { - exit_func = true; - 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", []() { - exit_func = true; - 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", []() { - exit_func = true; - 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", []() { - exit_func = true; - 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", []() { - exit_func = true; - 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", []() { - exit_func = true; - 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", []() { - exit_func = true; - 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", []() { - exit_func = true; - 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", []() { - getModesJSON(); - }); - - server.on("/set_mode", []() { - 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 - - server.begin(); - - // Start MDNS service - if (mdns_result) { - MDNS.addService("http", "tcp", 80); - } - #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); - if (chk == "STA") { - DBG_OUTPUT_PORT.printf("Found saved state: %s\n", saved_state_string.c_str()); - setModeByStateString(saved_state_string); - } - sprintf(last_state, "STA|%2d|%3d|%3d|%3d|%3d|%3d|%3d", mode, ws2812fx_mode, ws2812fx_speed, brightness, main_color.red, main_color.green, main_color.blue); - #endif -} - - -void loop() { - #ifdef ENABLE_BUTTON - button(); - #endif - server.handleClient(); - webSocket.loop(); - - #ifdef ENABLE_OTA - ArduinoOTA.handle(); - #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 - if (mode == SET_MODE) { - DBG_OUTPUT_PORT.printf("SET_MODE: %d %d\n", ws2812fx_mode, mode); - strip.setMode(ws2812fx_mode); - mode = HOLD; - } - if (mode == OFF) { -// strip.setColor(0,0,0); -// strip.setMode(FX_MODE_STATIC); - if(strip.isRunning()) strip.stop(); //should clear memory - // mode = HOLD; - } - if (mode == ALL) { - strip.setColor(main_color.red, main_color.green, main_color.blue); - 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); - mode = HOLD; - } - if (mode == RAINBOW) { - strip.setMode(FX_MODE_RAINBOW); - mode = HOLD; - } - if (mode == RAINBOWCYCLE) { - strip.setMode(FX_MODE_RAINBOW_CYCLE); - mode = HOLD; - } - if (mode == THEATERCHASE) { - strip.setColor(main_color.red, main_color.green, main_color.blue); - strip.setMode(FX_MODE_THEATER_CHASE); - mode = HOLD; - } - if (mode == TWINKLERANDOM) { - strip.setColor(main_color.red, main_color.green, main_color.blue); - strip.setMode(FX_MODE_TWINKLE_RANDOM); - mode = HOLD; - } - if (mode == THEATERCHASERAINBOW) { - strip.setMode(FX_MODE_THEATER_CHASE_RAINBOW); - 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(); - } - - // Only for modes with WS2812FX functionality - if (mode != TV && mode != CUSTOM) { - 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_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); - - if (strcmp(current_state, last_state) != 0) { - // DBG_OUTPUT_PORT.printf("STATE CHANGED: %s / %s\n", last_state, current_state); - strcpy(last_state, current_state); - time_statechange = millis(); - state_save_requested = true; - } - if (state_save_requested && time_statechange + timeout_statechange_save <= millis()) { - time_statechange = 0; - state_save_requested = false; - writeEEPROM(256, 32, last_state); // 256 --> last_state (reserved 32 bytes) - EEPROM.commit(); - } - #endif -} +#include "definitions.h" + +// *************************************************************************** +// Load libraries for: WebServer / WiFiManager / WebSockets +// *************************************************************************** +#include //https://github.com/esp8266/Arduino + +// needed for library WiFiManager +#include +#include +#include //https://github.com/tzapu/WiFiManager + +#include +#include +#include +#include + +#include //https://github.com/Links2004/arduinoWebSockets +#include + +// OTA +#ifdef ENABLE_OTA + #include + #include +#endif + +// MQTT +#ifdef ENABLE_MQTT + #include + #ifdef ENABLE_HOMEASSISTANT + #include + #endif + + WiFiClient espClient; + PubSubClient mqtt_client(espClient); +#endif + +#ifdef ENABLE_AMQTT + #include //https://github.com/marvinroger/async-mqtt-client + //https://github.com/me-no-dev/ESPAsyncTCP + #ifdef ENABLE_HOMEASSISTANT + #include + #endif + + AsyncMqttClient amqttClient; + WiFiEventHandler wifiConnectHandler; + WiFiEventHandler wifiDisconnectHandler; +#endif + + +// *************************************************************************** +// Instanciate HTTP(80) / WebSockets(81) Server +// *************************************************************************** +ESP8266WebServer server(80); +WebSocketsServer webSocket = WebSocketsServer(81); + +#ifdef HTTP_OTA +#include +ESP8266HTTPUpdateServer httpUpdater; +#endif + +#ifdef USE_NEOANIMATIONFX +// *************************************************************************** +// Load libraries / Instanciate NeoAnimationFX library +// *************************************************************************** +// https://github.com/debsahu/NeoAnimationFX +#include +#define NEOMETHOD NeoPBBGRB800 + +NEOMETHOD neoStrip(NUMLEDS); +NeoAnimationFX 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 +// *************************************************************************** +// https://github.com/kitesurfer1404/WS2812FX +#include +WS2812FX strip = WS2812FX(NUMLEDS, PIN, NEO_GRB + NEO_KHZ800); + +// Parameter 1 = number of pixels in strip +// Parameter 2 = Arduino pin number (most are valid) +// Parameter 3 = pixel type flags, add together as needed: +// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) +// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) +// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products) +// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2) + +// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across +// 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 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 + int state = digitalRead(BUILTIN_LED); // get the current state of GPIO1 pin + digitalWrite(BUILTIN_LED, !state); // set pin to the opposite state +} + +#ifdef ENABLE_STATE_SAVE_EEPROM + // *************************************************************************** + // EEPROM helper + // *************************************************************************** + String readEEPROM(int offset, int len) { + String res = ""; + for (int i = 0; i < len; ++i) + { + res += char(EEPROM.read(i + offset)); + //DBG_OUTPUT_PORT.println(char(EEPROM.read(i + offset))); + } + DBG_OUTPUT_PORT.printf("readEEPROM(): %s\n", res.c_str()); + return res; + } + + void writeEEPROM(int offset, int len, String value) { + DBG_OUTPUT_PORT.printf("writeEEPROM(): %s\n", value.c_str()); + for (int i = 0; i < len; ++i) + { + if (i < value.length()) { + EEPROM.write(i + offset, value[i]); + } else { + EEPROM.write(i + offset, NULL); + } + } + } +#endif + +// *************************************************************************** +// Saved state handling +// *************************************************************************** +// https://stackoverflow.com/questions/9072320/split-string-into-string-array +String getValue(String data, char separator, int index) +{ + int found = 0; + int strIndex[] = {0, -1}; + int maxIndex = data.length()-1; + + for(int i=0; i<=maxIndex && found<=index; i++){ + if(data.charAt(i)==separator || i==maxIndex){ + found++; + strIndex[0] = strIndex[1]+1; + strIndex[1] = (i == maxIndex) ? i+1 : i; + } + } + + return found>index ? data.substring(strIndex[0], strIndex[1]) : ""; +} + +// *************************************************************************** +// Callback for WiFiManager library when config mode is entered +// *************************************************************************** +//gets called when WiFiManager enters configuration mode +void configModeCallback (WiFiManager *myWiFiManager) { + DBG_OUTPUT_PORT.println("Entered config mode"); + DBG_OUTPUT_PORT.println(WiFi.softAPIP()); + //if you used auto generated SSID, print it + DBG_OUTPUT_PORT.println(myWiFiManager->getConfigPortalSSID()); + //entered config mode, make led toggle faster + ticker.attach(0.2, tick); + + uint16_t i; + for (i = 0; i < strip.numPixels(); i++) { + strip.setPixelColor(i, 0, 0, 255); + } + strip.show(); +} + +//callback notifying us of the need to save config +void saveConfigCallback () { + DBG_OUTPUT_PORT.println("Should save config"); + shouldSaveConfig = true; +} + +// *************************************************************************** +// Include: Webserver +// *************************************************************************** +#include "spiffs_webserver.h" + +// *************************************************************************** +// Include: Request handlers +// *************************************************************************** +#include "request_handlers.h" + +// *************************************************************************** +// Include: Color modes +// *************************************************************************** +#include "colormodes.h" + +// *************************************************************************** +// MAIN +// *************************************************************************** +void setup() { +// system_update_cpu_freq(160); + + DBG_OUTPUT_PORT.begin(115200); + EEPROM.begin(512); + + // set builtin led pin as output + pinMode(BUILTIN_LED, OUTPUT); + // button pin setup +#ifdef ENABLE_BUTTON + pinMode(BUTTON,INPUT_PULLUP); +#endif + // 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(HOSTNAME)); + + // *************************************************************************** + // Setup: Neopixel + // *************************************************************************** + strip.init(); + strip.setBrightness(brightness); + strip.setSpeed(convertSpeed(ws2812fx_speed)); + //strip.setMode(FX_MODE_RAINBOW_CYCLE); + strip.setColor(main_color.red, main_color.green, main_color.blue); + strip.start(); + + // *************************************************************************** + // Setup: WiFiManager + // *************************************************************************** + // 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 + #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 + readEEPROM(64, 6).toCharArray(mqtt_port, 6); // 64-69 + readEEPROM(70, 32).toCharArray(mqtt_user, 32); // 70-101 + readEEPROM(102, 32).toCharArray(mqtt_pass, 32); // 102-133 + DBG_OUTPUT_PORT.printf("MQTT host: %s\n", mqtt_host); + DBG_OUTPUT_PORT.printf("MQTT port: %s\n", mqtt_port); + 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); + WiFiManagerParameter custom_mqtt_pass("pass", "MQTT pass", mqtt_pass, 32); + #endif + + //Local intialization. Once its business is done, there is no need to keep it around + WiFiManager wifiManager; + //reset settings - for testing + //wifiManager.resetSettings(); + + //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode + wifiManager.setAPCallback(configModeCallback); + + #if defined(ENABLE_MQTT) or defined(ENABLE_AMQTT) + //set config save notify callback + wifiManager.setSaveConfigCallback(saveConfigCallback); + + //add all your parameters here + wifiManager.addParameter(&custom_mqtt_host); + wifiManager.addParameter(&custom_mqtt_port); + wifiManager.addParameter(&custom_mqtt_user); + 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" + //and goes into a blocking loop awaiting configuration + if (!wifiManager.autoConnect(HOSTNAME)) { + DBG_OUTPUT_PORT.println("failed to connect and hit timeout"); + //reset and try again, or maybe put it to deep sleep + ESP.reset(); + delay(1000); + } + + #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()); + strcpy(mqtt_user, custom_mqtt_user.getValue()); + 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"); + + writeEEPROM(0, 64, mqtt_host); // 0-63 + writeEEPROM(64, 6, mqtt_port); // 64-69 + writeEEPROM(70, 32, mqtt_user); // 70-101 + writeEEPROM(102, 32, mqtt_pass); // 102-133 + writeEEPROM(134, 1, "1"); // 134 --> always "1" + 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 :)"); + ticker.detach(); + //keep LED on + digitalWrite(BUILTIN_LED, LOW); + + + // *************************************************************************** + // Configure OTA + // *************************************************************************** + #ifdef ENABLE_OTA + DBG_OUTPUT_PORT.println("Arduino OTA activated."); + + // Port defaults to 8266 + ArduinoOTA.setPort(8266); + + // Hostname defaults to esp8266-[ChipID] + ArduinoOTA.setHostname(HOSTNAME); + + // No authentication by default + // ArduinoOTA.setPassword("admin"); + + // Password can be set with it's md5 value as well + // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3 + // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3"); + + ArduinoOTA.onStart([]() { + DBG_OUTPUT_PORT.println("Arduino OTA: Start updating"); + }); + ArduinoOTA.onEnd([]() { + DBG_OUTPUT_PORT.println("Arduino OTA: End"); + }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + DBG_OUTPUT_PORT.printf("Arduino OTA Progress: %u%%\r", (progress / (total / 100))); + }); + ArduinoOTA.onError([](ota_error_t error) { + DBG_OUTPUT_PORT.printf("Arduino OTA Error[%u]: ", error); + if (error == OTA_AUTH_ERROR) DBG_OUTPUT_PORT.println("Arduino OTA: Auth Failed"); + else if (error == OTA_BEGIN_ERROR) DBG_OUTPUT_PORT.println("Arduino OTA: Begin Failed"); + else if (error == OTA_CONNECT_ERROR) DBG_OUTPUT_PORT.println("Arduino OTA: Connect Failed"); + else if (error == OTA_RECEIVE_ERROR) DBG_OUTPUT_PORT.println("Arduino OTA: Receive Failed"); + else if (error == OTA_END_ERROR) DBG_OUTPUT_PORT.println("Arduino OTA: End Failed"); + }); + + ArduinoOTA.begin(); + DBG_OUTPUT_PORT.println(""); + #endif + + + // *************************************************************************** + // Configure MQTT + // *************************************************************************** + #ifdef ENABLE_MQTT + if (mqtt_host != "" && String(mqtt_port).toInt() > 0) { + snprintf(mqtt_intopic, sizeof mqtt_intopic, "%s/in", HOSTNAME); + snprintf(mqtt_outtopic, sizeof mqtt_outtopic, "%s/out", HOSTNAME); + + DBG_OUTPUT_PORT.printf("MQTT active: %s:%d\n", mqtt_host, String(mqtt_port).toInt()); + + mqtt_client.setServer(mqtt_host, String(mqtt_port).toInt()); + mqtt_client.setCallback(mqtt_callback); + } + #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 + // *************************************************************************** + bool mdns_result = MDNS.begin(HOSTNAME); + + DBG_OUTPUT_PORT.print("Open http://"); + DBG_OUTPUT_PORT.print(WiFi.localIP()); + DBG_OUTPUT_PORT.println("/ to open McLighting."); + + DBG_OUTPUT_PORT.print("Use http://"); + DBG_OUTPUT_PORT.print(HOSTNAME); + DBG_OUTPUT_PORT.println(".local/ when you have Bonjour installed."); + + DBG_OUTPUT_PORT.print("New users: Open http://"); + DBG_OUTPUT_PORT.print(WiFi.localIP()); + DBG_OUTPUT_PORT.println("/upload to upload the webpages first."); + + DBG_OUTPUT_PORT.println(""); + + + // *************************************************************************** + // Setup: WebSocket server + // *************************************************************************** + webSocket.begin(); + webSocket.onEvent(webSocketEvent); + + // *************************************************************************** + // Setup: SPIFFS Webserver handler + // *************************************************************************** + //list directory + server.on("/list", HTTP_GET, handleFileList); + //load editor + server.on("/edit", HTTP_GET, []() { + if (!handleFileRead("/edit.htm")) server.send(404, "text/plain", "FileNotFound"); + }); + //create file + server.on("/edit", HTTP_PUT, handleFileCreate); + //delete file + server.on("/edit", HTTP_DELETE, handleFileDelete); + //first callback is called after the request has ended with all parsed arguments + //second callback handles file uploads at that location + server.on("/edit", HTTP_POST, []() { + server.send(200, "text/plain", ""); + }, handleFileUpload); + //get heap status, analog input value and all GPIO statuses in one json call + server.on("/esp_status", HTTP_GET, []() { + String json = "{"; + json += "\"heap\":" + String(ESP.getFreeHeap()); + // json += ", \"analog\":" + String(analogRead(A0)); + // json += ", \"gpio\":" + String((uint32_t)(((GPI | GPO) & 0xFFFF) | ((GP16I & 0x01) << 16))); + json += "}"; + server.send(200, "text/json", json); + json = String(); + }); + + + //called when the url is not defined here + //use it to load content from SPIFFS + server.onNotFound([]() { + if (!handleFileRead(server.uri())) + handleNotFound(); + }); + + server.on("/upload", handleMinimalUpload); + + server.on("/restart", []() { + DBG_OUTPUT_PORT.printf("/restart\n"); + server.send(200, "text/plain", "restarting..." ); + ESP.restart(); + }); + + server.on("/reset_wlan", []() { + DBG_OUTPUT_PORT.printf("/reset_wlan\n"); + server.send(200, "text/plain", "Resetting WLAN and restarting..." ); + WiFiManager wifiManager; + wifiManager.resetSettings(); + ESP.restart(); + }); + + server.on("/start_config_ap", []() { + DBG_OUTPUT_PORT.printf("/start_config_ap\n"); + server.send(200, "text/plain", "Starting config AP ..." ); + WiFiManager wifiManager; + wifiManager.startConfigPortal(HOSTNAME); + }); + + + // *************************************************************************** + // Setup: SPIFFS Webserver handler + // *************************************************************************** + server.on("/set_brightness", []() { + if (server.arg("c").toInt() > 0) { + brightness = (int) server.arg("c").toInt() * 2.55; + } else { + brightness = server.arg("p").toInt(); + } + if (brightness > 255) { + brightness = 255; + } + if (brightness < 0) { + 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(); + }); + + server.on("/get_brightness", []() { + String str_brightness = String((int) (brightness / 2.55)); + server.send(200, "text/plain", str_brightness ); + DBG_OUTPUT_PORT.print("/get_brightness: "); + DBG_OUTPUT_PORT.println(str_brightness); + }); + + server.on("/set_speed", []() { + if (server.arg("d").toInt() >= 0) { + 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(); + }); + + server.on("/get_speed", []() { + String str_speed = String(ws2812fx_speed); + server.send(200, "text/plain", str_speed ); + DBG_OUTPUT_PORT.print("/get_speed: "); + DBG_OUTPUT_PORT.println(str_speed); + }); + + server.on("/get_switch", []() { + server.send(200, "text/plain", (mode == OFF) ? "0" : "1" ); + DBG_OUTPUT_PORT.printf("/get_switch: %s\n", (mode == OFF) ? "0" : "1"); + }); + + server.on("/get_color", []() { + String rgbcolor = String(main_color.red, HEX) + String(main_color.green, HEX) + String(main_color.blue, HEX); + server.send(200, "text/plain", rgbcolor ); + DBG_OUTPUT_PORT.print("/get_color: "); + DBG_OUTPUT_PORT.println(rgbcolor); + }); + + server.on("/status", []() { + getStatusJSON(); + }); + + server.on("/off", []() { + exit_func = true; + 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", []() { + exit_func = true; + 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", []() { + exit_func = true; + 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", []() { + exit_func = true; + 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", []() { + exit_func = true; + 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", []() { + exit_func = true; + 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", []() { + exit_func = true; + 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", []() { + exit_func = true; + 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", []() { + getModesJSON(); + }); + + server.on("/set_mode", []() { + 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_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); + if (chk == "STA") { + DBG_OUTPUT_PORT.printf("Found saved state: %s\n", saved_state_string.c_str()); + setModeByStateString(saved_state_string); + } + sprintf(last_state, "STA|%2d|%3d|%3d|%3d|%3d|%3d|%3d", mode, ws2812fx_mode, ws2812fx_speed, brightness, main_color.red, main_color.green, main_color.blue); + #endif +} + + +void loop() { + #ifdef ENABLE_BUTTON + button(); + #endif + server.handleClient(); + webSocket.loop(); + + #ifdef ENABLE_OTA + ArduinoOTA.handle(); + #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 + if (mode == SET_MODE) { + DBG_OUTPUT_PORT.printf("SET_MODE: %d %d\n", ws2812fx_mode, mode); + strip.setMode(ws2812fx_mode); + mode = HOLD; + } + if (mode == OFF) { +// strip.setColor(0,0,0); +// strip.setMode(FX_MODE_STATIC); + if(strip.isRunning()) strip.stop(); //should clear memory + // mode = HOLD; + } + if (mode == ALL) { + strip.setColor(main_color.red, main_color.green, main_color.blue); + 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); + mode = HOLD; + } + if (mode == RAINBOW) { + strip.setMode(FX_MODE_RAINBOW); + mode = HOLD; + } + if (mode == RAINBOWCYCLE) { + strip.setMode(FX_MODE_RAINBOW_CYCLE); + mode = HOLD; + } + if (mode == THEATERCHASE) { + strip.setColor(main_color.red, main_color.green, main_color.blue); + strip.setMode(FX_MODE_THEATER_CHASE); + mode = HOLD; + } + if (mode == TWINKLERANDOM) { + strip.setColor(main_color.red, main_color.green, main_color.blue); + strip.setMode(FX_MODE_TWINKLE_RANDOM); + mode = HOLD; + } + if (mode == THEATERCHASERAINBOW) { + strip.setMode(FX_MODE_THEATER_CHASE_RAINBOW); + 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(); + } + + // Only for modes with WS2812FX functionality + if (mode != TV && mode != CUSTOM) { + 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_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); + + if (strcmp(current_state, last_state) != 0) { + // DBG_OUTPUT_PORT.printf("STATE CHANGED: %s / %s\n", last_state, current_state); + strcpy(last_state, current_state); + time_statechange = millis(); + state_save_requested = true; + } + if (state_save_requested && time_statechange + timeout_statechange_save <= millis()) { + time_statechange = 0; + state_save_requested = false; + writeEEPROM(256, 32, last_state); // 256 --> last_state (reserved 32 bytes) + EEPROM.commit(); + } + #endif +} diff --git a/Arduino/McLighting/definitions.h b/Arduino/McLighting/definitions.h index b665ead..33c16b8 100644 --- a/Arduino/McLighting/definitions.h +++ b/Arduino/McLighting/definitions.h @@ -1,21 +1,22 @@ -//#define USE_NEOANIMATIONFX // Uses NeoAnimationFX, PIN is ignored & set to RX/GPIO3 -#define USE_WS2812FX // Uses WS2812FX +//#define USE_NEOANIMATIONFX // Uses NeoAnimationFX, PIN is ignored & set to RX/GPIO3, see: https://github.com/debsahu/NeoAnimationFX +#define USE_WS2812FX // Uses WS2812FX, see: https://github.com/kitesurfer1404/WS2812FX // Neopixel -#define PIN 14 // PIN (14 / D5) where neopixel / WS2811 strip is attached -#define NUMLEDS 300 // Number of leds in the strip +#define PIN D1 // 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 BUTTON 0 // Input pin (4 / D2) for switching the LED strip on / off, connect this PIN to ground to trigger button. +#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 ESP8266HTTPUpdateServer OTA code. -//#define ENABLE_OTA // If defined, enable Arduino 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 +//#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 + #if defined(USE_NEOANIMATIONFX) and defined(USE_WS2812FX) #error "Cant have both NeoAnimationFX and WS2812FX enabled. Choose either one." #endif @@ -30,7 +31,7 @@ const char HOSTNAME[] = "McLighting01"; // Friedly hostname #endif // parameters for automatically cycling favorite patterns -uint32_t autoParams[][4] = { // color, speed, mode, duration (seconds) +uint32_t autoParams[][4] = { // color, speed, mode, duration (seconds) {0xff0000, 200, 1, 5.0}, // blink red for 5 seconds {0x00ff00, 200, 3, 10.0}, // wipe green for 10 seconds {0x0000ff, 200, 11, 5.0}, // dual scan blue for 5 seconds @@ -68,13 +69,12 @@ uint32_t autoParams[][4] = { // color, speed, mode, duration (seconds) uint16_t color_temp = 327; // min is 154 and max is 500 #endif - const char mqtt_clientid[] = "NeoPixelStrip01"; // MQTT ClientID - - char mqtt_host[64] = ""; - char mqtt_port[6] = ""; - char mqtt_user[32] = ""; - char mqtt_pass[32] = ""; + const char mqtt_clientid[] = "NeoPixelsStrip"; // MQTT ClientID + char mqtt_host[64] = ""; + char mqtt_port[6] = ""; + char mqtt_user[32] = ""; + char mqtt_pass[32] = ""; #endif @@ -134,4 +134,4 @@ LEDState main_color = { 255, 0, 0 }; // Store the "main color" of the strip use byte KeyPressCount = 0; byte prevKeyState = HIGH; // button is active low boolean buttonState = false; -#endif +#endif diff --git a/Arduino/McLighting/request_handlers.h b/Arduino/McLighting/request_handlers.h index bf980bc..543639e 100644 --- a/Arduino/McLighting/request_handlers.h +++ b/Arduino/McLighting/request_handlers.h @@ -1363,4 +1363,4 @@ bool readStateFS() { updateFS = false; return false; } -#endif +#endif diff --git a/README.md b/README.md index da0e882..69d0b50 100644 --- a/README.md +++ b/README.md @@ -6,28 +6,50 @@ > Because of it's open architecture and APIs it's easy to build new clients for different platforms (iOS, Android, Windows Universal Apps, Siri/Cortana integration, ...). +[![Demo video WebClient](https://j.gifs.com/kRPrzN.gif)](https://youtu.be/rc6QVHKAXBs) + +[![Demo video Apple Homekit integration](https://j.gifs.com/gJP2o6.gif)](https://youtu.be/4JnGXZaPnrw) + ___ -Update 15.02.2018: +Update 07.04.2018: +And even more changes to McLighting! Most of them were contributed by user @debsahu. Thank you! +- AMQTT is now the default MQTT library, it's a bit more lightweight and stable. You can still use PubSubClient if you want to. +- You can use @debsahu great NeoAnimationFX library as a alternative to WS2812FX. It's based on the NeoPixelBus instead of Adafruits NeoPixel library. It can handle longer strips more efficient. If you want, give it a try. WS2812FX is still the default. +- Some more changes regarding Homeassistant integration. +Please see the [Wiki](https://github.com/toblum/McLighting/wiki/Software-installation) for details on the required libraries. +If you have problems with the new version, let us know. You can get the last version [here](https://github.com/toblum/McLighting/tree/Before_AMQTT_NeoAnimationFX). + +I'm also working on a alternative web interface for McLighting in the meanwhile, but it may take some more time. +For the german users: McLighting was used in [Kliemannsland](https://youtu.be/3TUjszkS3bY?t=1211) (a funny web show) when they built a really big Neopixel installation. + +Update 18.03.2018: +The code for integration with homeassistant was merged into master. It's currently active by default. You can safely disable it in definitions.h when use do not want to use it, or want to use McLighting on a small ESP_01. +There are some informations in the [Wiki](https://github.com/toblum/McLighting/wiki/Homeassistant-integration). + +Update 17.02.2018: +User @debsahu contributed code for integration with homeassistant. It's currently in a separate branch (https://github.com/toblum/McLighting/tree/feature/ha_integration). If you're using Homeassistant, please try it out and give feedback. +User @FabLab-Luenn created a version of McLighting (https://github.com/FabLab-Luenen/McLighting) for 6812 and other RGBW strips. Give it a try, if you own such strips. +A thank you goes to all contributors. + +Update 12. / 15.02.2018: +Added Home Assistant Support using MQTT Light. A better implementation would be using MQTT Light JSON. Replaced Home Assistant Support using MQTT Light to MQTT JSON Light. -Update 12.02.2018: -Added Home Assistant Support using MQTT Light. A better implementation would be using MQTT Light JSON. - -Update 31.01.2018: +Update 31.01.2018: User @codmpm did a very professional McLighting installation and even designed his own PCBs. He has a great writeup for his project at: https://allgeek.de/2018/01/29/esp8266-neopixel-controller/ (in german). -Update 27.01.2018: +Update 27.01.2018: Many people asked if it's possible to connect more than one strip (currently not) or at least "sync" multiple McLighting nodes. Although it may be possible to connect more then one WS2812 strip to the same data pin (works in many cases, you just have to try), syncing many McLighting instances would be a benefit. This could easily be achieved done by software like [NodeRed](https://nodered.org/). I added a example flow to demonstrate that [here](https://github.com/toblum/McLighting/blob/master/clients/node_red/websocket_proxy.json). Have a look at the short video [here](https://youtu.be/g3CHtG9c520). -Update 21.01.2018: +Update 21.01.2018: User @szepnorbee contributed code for button control. Thank you! It's merged into the master branch now. There is a short manual for configuration [here](https://github.com/toblum/McLighting/wiki/Button-control). -Update 06.01.2018: +Update 06.01.2018: After som etesting I merged the "feature/save_state" banch into master, so everybody should now be able to use this new functionality. Basically McLighting now saves the current mode to EEPROM and restores the setting on reboot. So you wont need to select your favorite mode again. If you don't want to use this, you can disable it in definitions.h. ~~Some people noticed that there are currently problems compiling McLighting whe using ESP8266 core in version 2.4.0. This is due to a [problem](https://github.com/kitesurfer1404/WS2812FX/issues/58) with WS2812FX when using this version. For the moment you can stick to the 2.4.0 RC2 (also easily available via the boards manager).~~ (fixed now ) Funny! McLighting was featured in the german radio show ["Netzbasteln"](https://www.deutschlandfunknova.de/beitrag/netzbasteln-wolkenlampe-mit-cloud-anschluss) on Deutschlandfunk Nova with a nice audio tutorial. -Update 16.12.2017: +Update 16.12.2017: There was a breaking change in the WS2812FX library: Speeds have a new format (65535-0 instead of 0-255). I released a new version that converts the speeds settings. Please use the latest [WS2812FX library](https://github.com/kitesurfer1404/WS2812FX) (14.12.2017 or later) if use have an existing version installed. I got many messages from people who use McLighting for own projects. User Brian Lough built a lighting system for his wedding and made a nice instruction video for his build: https://goo.gl/NbfKi8 @@ -35,25 +57,25 @@ Update 30.09.2017: Thanks to [@moose4lord](https://github.com/moose4lord) Mclighting works with the newest version of WS1812FX and has a possibility to define autocycle patterns [Wiki](https://github.com/toblum/McLighting/wiki/Autocycling). Thank for contributing to McLighting everyone! I was also informed of a new project that is loosely based on McLighting: [Responsive_LED_Control](https://github.com/doctormord/Responsive_LED_Control) That looks very promising. -Update 07.08.2017: +Update 07.08.2017: As requested by many of you, McLighting now also features MQTT support. Thanks at @LeonVos for his attempts on this. I implemented the same API as used in WebSockets now for MQTT. Please have a look here for details: https://github.com/toblum/McLighting/wiki/MQTT-API I will try to add a new instruction video soon. Many of you also took McLighting and adapted the software according your needs. This is great. I found some videos on YouTube that show these projects. I collected them here: https://goo.gl/yG7M4h If you have done something similar with McLighting, please drop me a note. I'm always interested in what you've done with it. -Update 19.02.2017: +Update 19.02.2017: Added OTA support as promised by @markbajaj. Minor other improvements. -Update 05.02.2017: +Update 05.02.2017: After a long time I was able to work a bit on McLighting v2 and it's finally out now. The main difference, among minor improvements and library updates, is the usage of the great WS2812FX library for color animations. It brings a lot (almost 50!) of new animations. The API changed a little bit, because the speed can now be set as a value from 0 to 255, not the delay anymore. So the web inferface had to change accordingly. The new animation mode have to be set also by their number, instead of a dedicated url. The list of all animation modes can also be received by the API. All existing API endpoints are kept for downward compatibility. So you should be able to use the new version without big changes. The original version is kept as branch "mclighting_legacy". Documentation will be updated soon. -Update 04.01.2017: +Update 04.01.2017: Now, there are two forks of McLighting (using the famous FastLED library). I did not notice it first, because I currently do not receive notification e-mails by Github (I have no idea why). Maybe you want to give them also a try, I will definitely do so as soon as I find time. https://github.com/russp81/LEDLAMP_FASTLEDs And this one was also forked: https://github.com/jake-b/Griswold-LED-Controller -Update 12.08.2016: +Update 12.08.2016: There is now a [gitter.im](https://gitter.im/mclighting/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link) chat room for this project. Update 11.06.2016: @@ -61,11 +83,6 @@ Today I presented the project at [Pi and More 9](https://piandmore.de/) and got ___ -[![Demo video WebClient](https://j.gifs.com/kRPrzN.gif)](https://youtu.be/rc6QVHKAXBs) - -[![Demo video Apple Homekit integration](https://j.gifs.com/gJP2o6.gif)](https://youtu.be/4JnGXZaPnrw) - - ## The Hardware The project ist based on the famous ESP8266 microcontroller and WD2811/WS2812 LED strips. There are many variations of the ESP chip out there, but I chose the NodeMCU dev board, because it's powered by micro USB and has a voltage converter included to power the ESP which uses 3.3V. @@ -111,15 +128,14 @@ I hope I didn't miss any sources and mentioned every author. In case I forgot so ## Todos - [x] MQTT support -- [ ] Support multiple strips and control them separately or together +- [ ] Support multiple strips and control them separately or together [Issue](https://github.com/toblum/McLighting/issues/118) - [ ] Save favourite effects? [Issue](https://github.com/toblum/McLighting/issues/35) -- [ ] Make number of pixels, MQTT and PIN configurable via front end [Issue](https://github.com/toblum/McLighting/issues/93) -- [ ] OTA update [Issue](https://github.com/toblum/McLighting/issues/93) +- [ ] Make number of pixels, MQTT and PIN configurable via front end [Issue](https://github.com/toblum/McLighting/issues/93) and [Issue](https://github.com/toblum/McLighting/issues/101) +- [x] OTA update [Issue](https://github.com/toblum/McLighting/issues/93) - [ ] Bundle webpages instead of SPIFFS [Issue](https://github.com/toblum/McLighting/issues/93) - [ ] Remove old / wrong EEPROM settings completely (https://github.com/toblum/McLighting/issues/92) - [x] Fix issue with websockets connection problems -- [ ] Add support for 433MHz wireless socket using the [RC switch](https://github.com/sui77/rc-switch) library. -- [ ] Switch to the [NeoPixelBus library](https://github.com/Makuna/NeoPixelBus/wiki) +- [x] Switch to the [NeoPixelBus library](https://github.com/Makuna/NeoPixelBus/wiki) - [x] Use the led strip for status information in connection phase - [x] Enhance the documentation - [x] Stability improvements @@ -129,9 +145,11 @@ I hope I didn't miss any sources and mentioned every author. In case I forgot so - [x] Button control [Issue](https://github.com/toblum/McLighting/issues/36) - [x] Retain last state [Issue](https://github.com/toblum/McLighting/issues/47) - [ ] Additional clients -- [ ] If no wifi, at least enable button mode. [Issue](https://github.com/toblum/McLighting/issues/88) +- [ ] If no wifi, at least enable button mode. - [ ] Also enable McLighting in Wifi AP mode. -- [ ] Make a set of NodeRed nodes. +- [x] Make a set of NodeRed nodes. +- [ ] Multiple buttons/GPIO Inputs. [Issue](https://github.com/toblum/McLighting/issues/119) +- [ ] Music visualizer / Bring back ArtNet [Issue](https://github.com/toblum/McLighting/issues/111) ## Licence