diff --git a/Arduino/McLighting/McLighting.ino b/Arduino/McLighting/McLighting.ino index d2b1b98..ad2cd04 100644 --- a/Arduino/McLighting/McLighting.ino +++ b/Arduino/McLighting/McLighting.ino @@ -1,4 +1,8 @@ #include "definitions.h" +// *************************************************************************** +// Load library "ticker" for blinking status led, mqtt send and save state +// *************************************************************************** +#include #include "version.h" // *************************************************************************** @@ -10,16 +14,12 @@ #include #include #include //https://github.com/tzapu/WiFiManager - -#include -#include #include -#if ENABLE_STATE_SAVE == 0 - #include -#endif + +#include #include //https://github.com/Links2004/arduinoWebSockets #include - + #if defined(ENABLE_BUTTON_GY33) // *************************************************************************** // Load libraries for GY33 and initialize color sensor @@ -28,19 +28,18 @@ GY33_MCU tcs; #endif -#include //https://github.com/bblanchon/ArduinoJson - // MQTT #if defined(ENABLE_MQTT) #if ENABLE_MQTT == 0 // *************************************************************************** // Load libraries for PubSubClient // *************************************************************************** + #include #include WiFiClient espClient; PubSubClient * mqtt_client; #endif - + #if ENABLE_MQTT == 1 // *************************************************************************** // Load libraries for Amqtt @@ -50,16 +49,15 @@ AsyncMqttClient * mqtt_client; WiFiEventHandler wifiConnectHandler; WiFiEventHandler wifiDisconnectHandler; + Ticker mqttReconnectTimer; + Ticker wifiReconnectTimer; + #endif + #if defined(ENABLE_HOMEASSISTANT) + Ticker ha_send_data; #endif #endif -#if defined(ARDUINOJSON_VERSION) - #if !(ARDUINOJSON_VERSION_MAJOR == 6 and ARDUINOJSON_VERSION_MINOR >= 8) - #error "Install ArduinoJson v6.8.x or higher" - #endif -#endif - -#if defined(ENABLE_E131) +#if defined(CUSTOM_WS2812FX_ANIMATIONS) // *************************************************************************** // Load libraries for E131 support // *************************************************************************** @@ -78,8 +76,9 @@ #endif #if defined(USE_HTML_MIN_GZ) -#include "htm_index_gz.h" -#include "htm_edit_gz.h" +#include "htm_index_gz.h" +#include "htm_edit_gz.h" +#include "html_material_icons.h" #endif @@ -106,87 +105,72 @@ WebSocketsServer webSocket = WebSocketsServer(81); // *************************************************************************** // Load and instanciate WS2812FX library // *************************************************************************** -#include // https://github.com/kitesurfer1404/WS2812FX +#include "WS2812FX.h" // https://github.com/kitesurfer1404/WS2812FX WS2812FX * strip = NULL; #if defined(USE_WS2812FX_DMA) #include #if USE_WS2812FX_DMA == 0 // Uses GPIO3/RXD0/RX, more info: https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods - #if !defined(LED_TYPE_WS2811) - NeoEsp8266Dma800KbpsMethod * dma = NULL ; //800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) + #if !defined(LED_TYPE_WS2811) + NeoPixelBus * dma = NULL ; //800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) #else - NeoEsp8266Dma400KbpsMethod * dma = NULL; //400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) + NeoPixelBus * dma = NULL; //400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) #endif #endif #if USE_WS2812FX_DMA == 1 // Uses UART1: GPIO1/TXD0/TX, more info: https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods - #if !defined(LED_TYPE_WS2811) - NeoEsp8266Uart0800KbpsMethod * dma = NULL; //800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) + #if !defined(LED_TYPE_WS2811) + NeoPixelBus * dma = NULL; //800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) #else - NeoEsp8266Uart0400KbpsMethod * dma = NULL; //400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) + NeoPixelBus * dma = NULL; //400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) #endif #endif #if USE_WS2812FX_DMA == 2 // Uses UART2: GPIO2/TXD1/D4, more info: https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods - #if !defined(LED_TYPE_WS2811) - NeoEsp8266Uart1800KbpsMethod * dma = NULL; //800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) + #if !defined(LED_TYPE_WS2811) + NeoPixelBus * dma = NULL; //800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) #else - NeoEsp8266Uart1400KbpsMethod * dma = NULL; //400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) + NeoPixelBus * dma = NULL; //400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) #endif #endif - + void initDMA(uint16_t stripSize = NUMLEDS){ if (dma != NULL) { delete(dma); } - uint8_t ledcolors = 3; - if (strstr(WS2812FXStripSettings.RGBOrder, "W") != NULL) { - ledcolors = 4; - } #if USE_WS2812FX_DMA == 0 // Uses GPIO3/RXD0/RX, more info: https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods - #if !defined(LED_TYPE_WS2811) - dma = new NeoEsp8266Dma800KbpsMethod(stripSize, ledcolors); //800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) + #if !defined(LED_TYPE_WS2811) + dma = new NeoPixelBus(stripSize); //800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) #else - dma = new NeoEsp8266Dma400KbpsMethod(stripSize, ledcolors); //400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) + dma = new NeoPixelBus(stripSize); //400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) #endif #endif #if USE_WS2812FX_DMA == 1 // Uses UART1: GPIO1/TXD0/TX, more info: https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods - #if !defined(LED_TYPE_WS2811) - dma = new NeoEsp8266Uart0800KbpsMethod(stripSize, ledcolors); //800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) + #if !defined(LED_TYPE_WS2811) + dma = new NeoPixelBus(stripSize); //800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) #else - dma = new NeoEsp8266Uart0400KbpsMethod(stripSize, ledcolors); //400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) + dma = new NeoPixelBus(stripSize); //400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) #endif #endif #if USE_WS2812FX_DMA == 2 // Uses UART2: GPIO2/TXD1/D4, more info: https://github.com/Makuna/NeoPixelBus/wiki/ESP8266-NeoMethods - #if !defined(LED_TYPE_WS2811) - dma = new NeoEsp8266Uart1800KbpsMethod(stripSize, ledcolors); //800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) + #if !defined(LED_TYPE_WS2811) + dma = new NeoPixelBus(stripSize); //800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) #else - dma = new NeoEsp8266Uart1400KbpsMethod(stripSize, ledcolors); //400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) + dma = new NeoPixelBus(stripSize); //400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) #endif #endif - dma->Initialize(); + dma->Begin(); } void DMA_Show(void) { - if(dma->IsReadyToUpdate()) { - memcpy(dma->getPixels(), strip->getPixels(), dma->getPixelsSize()); - dma->Update(false); + if(dma->CanShow()) { + memcpy(dma->Pixels(), strip->getPixels(), dma->PixelsSize()); + dma->Dirty(); + dma->Show(); } } #endif -// *************************************************************************** -// Load library "ticker" for blinking status led, mqtt send and save state -// *************************************************************************** -#include Ticker ticker; -#if defined(ENABLE_MQTT) - #if ENABLE_MQTT == 1 - Ticker mqttReconnectTimer; - Ticker wifiReconnectTimer; - #endif - #if defined(ENABLE_HOMEASSISTANT) - Ticker ha_send_data; - #endif -#endif + void tick() { //toggle state @@ -199,9 +183,6 @@ void tick() { decode_results results; #endif -Ticker settings_save_state; -Ticker settings_save_conf; - // *************************************************************************** // Saved state handling in WifiManager // *************************************************************************** @@ -248,99 +229,22 @@ void saveConfigCallback () { // *************************************************************************** #include "spiffs_webserver.h" -// *************************************************************************** -// Include: Request handlers -// *************************************************************************** -#include "request_handlers.h" - -#if defined(ENABLE_TV) -// *************************************************************************** -// Include: TV mode -// *************************************************************************** - #include "mode_tv.h" -#endif - -#if defined(CUSTOM_WS2812FX_ANIMATIONS) // *************************************************************************** // Include: Custom animations // *************************************************************************** - #include "mode_custom_ws2812fx_animations.h" // Add animations in this file -#endif +#include "mode_custom_ws2812fx_animations.h" // Add animations in this file -// function to Initialize the strip -void initStrip(uint16_t stripSize = WS2812FXStripSettings.stripSize, char RGBOrder[5] = WS2812FXStripSettings.RGBOrder, uint8_t pin = WS2812FXStripSettings.pin, uint8_t fxoptions = WS2812FXStripSettings.fxoptions ){ - DBG_OUTPUT_PORT.println("Initializing strip!"); -/*#if defined(USE_WS2812FX_DMA) - if (dma != NULL) { - delete(dma); - } -#endif*/ - if (strip != NULL) { - delete(strip); - WS2812FXStripSettings.stripSize = stripSize; - strcpy(WS2812FXStripSettings.RGBOrder, RGBOrder); - WS2812FXStripSettings.pin = pin; - WS2812FXStripSettings.fxoptions = fxoptions; - } -#if defined(ENABLE_E131) +// *************************************************************************** +// Include: helper functions +// *************************************************************************** +#include "helper_functions.h" -#endif - if (ledstates != NULL) { - delete(ledstates); - } - ledstates = new uint8_t [WS2812FXStripSettings.stripSize]; - -#if !defined(LED_TYPE_WS2811) - strip = new WS2812FX(stripSize, pin, checkRGBOrder(RGBOrder) + NEO_KHZ800); -#else - strip = new WS2812FX(stripSize, pin, checkRGBOrder(RGBOrder) + NEO_KHZ400); -#endif - // 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. - strip->init(); - #if defined(USE_WS2812FX_DMA) - initDMA(stripSize); - strip->setCustomShow(DMA_Show); - #endif - strip->setBrightness(brightness); -//parameters: index, start, stop, mode, color, speed, options - strip->setSegment(0, 0, stripSize - 1, ws2812fx_mode, hex_colors, convertSpeed(ws2812fx_speed), fxoptions); -#if defined(CUSTOM_WS2812FX_ANIMATIONS) - strip->setCustomMode(0, F("Fire 2012"), myCustomEffect0); -//strip->setCustomMode(1, F("CustEffect"), myCustomEffect1); - gReverseDirection = (WS2812FXStripSettings.fxoptions & 128); -#endif -#if defined(ENABLE_E131) - if (e131 != NULL) { delete(e131); } - e131 = new ESPAsyncE131(END_UNIVERSE - START_UNIVERSE + 1); - float universe_leds = 170.0; // a universe has only 512 (0..511) channels: 3*170 or 4*128 <= 512 - if (strstr(WS2812FXStripSettings.RGBOrder, "W") != NULL) { - //universe_leds = 128.0; - } - float float_enduni = stripSize/universe_leds; - uint8_t END_UNIVERSE = stripSize/universe_leds; - if (float_enduni > END_UNIVERSE) { - END_UNIVERSE = END_UNIVERSE +1; - } - - // if (e131.begin(E131_UNICAST)) // Listen via Unicast - if (e131->begin(E131_MULTICAST, START_UNIVERSE, END_UNIVERSE)) {// Listen via Multicast - DBG_OUTPUT_PORT.println(F("Listening for data...")); - } else { - DBG_OUTPUT_PORT.println(F("*** e131.begin failed ***")); - } -#endif -} +// *************************************************************************** +// Include: other functions +// *************************************************************************** +#include "json_functions.h" +#include "filesystem_functions.h" +#include "request_handlers.h" #if defined(ENABLE_MQTT) void initMqtt() { @@ -381,7 +285,7 @@ void initMqtt() { mqtt_client->setServer(mqtt_host, mqtt_port); mqtt_client->setCallback(onMqttMessage); #endif - #if ENABLE_MQTT == 1 + #if ENABLE_MQTT == 1 DBG_OUTPUT_PORT.printf("AMQTT active: %s:%d\r\n", mqtt_host, mqtt_port); mqtt_client->onConnect(onMqttConnect); mqtt_client->onDisconnect(onMqttDisconnect); @@ -406,9 +310,6 @@ void setup() { delay(500); DBG_OUTPUT_PORT.println(""); DBG_OUTPUT_PORT.println("Starting...Main Setup"); -#if ENABLE_STATE_SAVE == 0 - EEPROM.begin(512); -#endif // set builtin led pin as output pinMode(LED_BUILTIN, OUTPUT); // button pin setup @@ -428,7 +329,7 @@ void setup() { #endif #if defined(POWER_SUPPLY) - pinMode(POWER_SUPPLY, OUTPUT); // output to control external power supply + pinMode(POWER_SUPPLY, OUTPUT); // output to control external power supply #endif // start ticker with 0.5 because we start in AP mode and try to connect @@ -460,37 +361,30 @@ void setup() { #if defined(ENABLE_STATE_SAVE) //Strip Config - #if ENABLE_STATE_SAVE == 1 - (readConfigFS()) ? DBG_OUTPUT_PORT.println("WiFiManager config FS read success!"): DBG_OUTPUT_PORT.println("WiFiManager config FS Read failure!"); - delay(250); - (readStateFS()) ? DBG_OUTPUT_PORT.println("Strip state config FS read Success!") : DBG_OUTPUT_PORT.println("Strip state config FS read failure!"); - #endif - #if ENABLE_STATE_SAVE == 0 - (setConfByConfString(readEEPROM(0, 222)))? DBG_OUTPUT_PORT.println("WiFiManager config EEPROM read success!"): DBG_OUTPUT_PORT.println("WiFiManager config EEPROM Read failure!"); - delay(250); - (setModeByStateString(readEEPROM(256, 66)))? DBG_OUTPUT_PORT.println("Strip state config EEPROM read Success!") : DBG_OUTPUT_PORT.println("Strip state config EEPROM read failure!"); - #endif - char tmp_strip_size[6], tmp_fxoptions[5], tmp_rgbOrder[5]; //needed tempararily for WiFiManager Settings + (readConfigFS()) ? DBG_OUTPUT_PORT.println("WiFiManager config FS read success!"): DBG_OUTPUT_PORT.println("WiFiManager config FS Read failure!"); + delay(250); + (readStateFS()) ? DBG_OUTPUT_PORT.println("Strip state config FS read Success!") : DBG_OUTPUT_PORT.println("Strip state config FS read failure!"); + char _stripSize[6], _fx_options[5], _rgbOrder[5]; //needed tempararily for WiFiManager Settings WiFiManagerParameter custom_hostname("hostname", "Hostname", HOSTNAME, 64, " maxlength=64"); #if defined(ENABLE_MQTT) - char tmp_mqtt_port[6]; //needed tempararily for WiFiManager Settings + char _mqtt_port[6]; //needed tempararily for WiFiManager Settings WiFiManagerParameter custom_mqtt_host("host", "MQTT hostname", mqtt_host, 64, " maxlength=64"); - sprintf(tmp_mqtt_port, "%d", mqtt_port); - WiFiManagerParameter custom_mqtt_port("port", "MQTT port", tmp_mqtt_port, 5, " maxlength=5 type=\"number\""); + sprintf(_mqtt_port, "%d", mqtt_port); + WiFiManagerParameter custom_mqtt_port("port", "MQTT port", _mqtt_port, 5, " maxlength=5 type=\"number\""); WiFiManagerParameter custom_mqtt_user("user", "MQTT user", mqtt_user, 32, " maxlength=32"); WiFiManagerParameter custom_mqtt_pass("pass", "MQTT pass", mqtt_pass, 32, " maxlength=32 type=\"password\""); #endif - sprintf(tmp_strip_size, "%d", WS2812FXStripSettings.stripSize); - WiFiManagerParameter custom_strip_size("strip_size", "Number of LEDs", tmp_strip_size, 4, " maxlength=4 type=\"number\""); + sprintf(_stripSize, "%d", Config.stripSize); + WiFiManagerParameter custom_strip_size("strip_size", "Number of LEDs", _stripSize, 4, " maxlength=4 type=\"number\""); #if !defined(USE_WS2812FX_DMA) char tmp_led_pin[3]; - sprintf(tmp_led_pin, "%d", WS2812FXStripSettings.pin); + sprintf(tmp_led_pin, "%d", Config.pin); WiFiManagerParameter custom_led_pin("led_pin", "LED GPIO", tmp_led_pin, 2, " maxlength=2 type=\"number\""); #endif - sprintf(tmp_rgbOrder, "%s", WS2812FXStripSettings.RGBOrder); - WiFiManagerParameter custom_rgbOrder("rgbOrder", "RGBOrder", tmp_rgbOrder, 4, " maxlength=4"); - sprintf(tmp_fxoptions, "%d", WS2812FXStripSettings.fxoptions); - WiFiManagerParameter custom_fxoptions("fxoptions", "fxOptions", tmp_fxoptions, 3, " maxlength=3"); + sprintf(_rgbOrder, "%s", Config.RGBOrder); + WiFiManagerParameter custom_rgbOrder("rgbOrder", "RGBOrder", _rgbOrder, 4, " maxlength=4"); + sprintf(_fx_options, "%d", segState.options); + WiFiManagerParameter custom_fxoptions("fxoptions", "fxOptions", _fx_options, 3, " maxlength=3"); #endif @@ -504,7 +398,7 @@ void setup() { wifiManager.setAPCallback(configModeCallback); //set config save notify callback wifiManager.setSaveConfigCallback(saveConfigCallback); - + wifiManager.addParameter(&custom_hostname); #if defined(ENABLE_MQTT) //add all your parameters here @@ -519,16 +413,16 @@ void setup() { #endif wifiManager.addParameter(&custom_rgbOrder); wifiManager.addParameter(&custom_fxoptions); - + WiFi.setSleepMode(WIFI_NONE_SLEEP); - + // Uncomment if you want to restart ESP8266 if it cannot connect to WiFi. // Value in brackets is in seconds that WiFiManger waits until restart #if defined(WIFIMGR_PORTAL_TIMEOUT) wifiManager.setConfigPortalTimeout(WIFIMGR_PORTAL_TIMEOUT); #endif - // Order is: IP, Gateway and Subnet + // Order is: IP, Gateway and Subnet #if defined(WIFIMGR_SET_MANUAL_IP) wifiManager.setSTAStaticIPConfig(IPAddress(_ip[0], _ip[1], _ip[2], _ip[3]), IPAddress(_gw[0], _gw[1], _gw[2], _gw[3]), IPAddress(_sn[0], _sn[1], _sn[2], _sn[3])); #endif @@ -540,10 +434,10 @@ void setup() { 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(); //Will be removed when upgrading to standalone offline McLightingUI version - delay(1000); //Will be removed when upgrading to standalone offline McLightingUI version + //ESP.reset(); //Will be removed when upgrading to standalone offline McLightingUI version + //delay(1000); //Will be removed when upgrading to standalone offline McLightingUI version } - + //save the custom parameters to FS/EEPROM #if defined(ENABLE_STATE_SAVE) strcpy(HOSTNAME, custom_hostname.getValue()); @@ -554,34 +448,19 @@ void setup() { strcpy(mqtt_user, custom_mqtt_user.getValue()); strcpy(mqtt_pass, custom_mqtt_pass.getValue()); #endif - strcpy(tmp_strip_size, custom_strip_size.getValue()); - WS2812FXStripSettings.stripSize = constrain(atoi(custom_strip_size.getValue()), 1, MAXLEDS); + strcpy(_stripSize, custom_strip_size.getValue()); + Config.stripSize = constrain(atoi(custom_strip_size.getValue()), 1, MAXLEDS); #if !defined(USE_WS2812FX_DMA) checkPin(atoi(custom_led_pin.getValue())); #endif - strcpy(tmp_rgbOrder, custom_rgbOrder.getValue()); - checkRGBOrder(tmp_rgbOrder); - WS2812FXStripSettings.fxoptions = atoi(custom_fxoptions.getValue()); + strcpy(_rgbOrder, custom_rgbOrder.getValue()); + checkRGBOrder(_rgbOrder); + segState.options = atoi(custom_fxoptions.getValue()); if (updateConfig) { - #if ENABLE_STATE_SAVE == 1 - (writeConfigFS(updateConfig)) ? DBG_OUTPUT_PORT.println("WiFiManager config FS Save success!"): DBG_OUTPUT_PORT.println("WiFiManager config FS Save failure!"); - #endif - #if ENABLE_STATE_SAVE == 0 - char last_conf[223]; - DBG_OUTPUT_PORT.println("Saving WiFiManager config"); - #if defined(ENABLE_MQTT) - snprintf(last_conf, sizeof(last_conf), "CNF|%64s|%64s|%5d|%32s|%32s|%4d|%2d|%4s|%3d", HOSTNAME, mqtt_host, mqtt_port, mqtt_user, mqtt_pass, WS2812FXStripSettings.stripSize, WS2812FXStripSettings.pin, WS2812FXStripSettings.RGBOrder, WS2812FXStripSettings.fxoptions); - #else - snprintf(last_conf, sizeof(last_conf), "CNF|%64s|%64s|%5d|%32s|%32s|%4d|%2d|%4s|%3d", HOSTNAME, "", "", "", "", WS2812FXStripSettings.stripSize, WS2812FXStripSettings.pin, WS2812FXStripSettings.RGBOrder, WS2812FXStripSettings.fxoptions); - #endif - last_conf[sizeof(last_conf)] = 0x00; - writeEEPROM(0, 222, last_conf); - EEPROM.commit(); - updateConfig = false; - #endif + (writeConfigFS(updateConfig)) ? DBG_OUTPUT_PORT.println("WiFiManager config FS Save success!"): DBG_OUTPUT_PORT.println("WiFiManager config FS Save failure!"); } #endif - + //if you get here you have connected to the WiFi DBG_OUTPUT_PORT.println("connected...yeey :)"); ticker.detach(); @@ -605,11 +484,11 @@ void setup() { // 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"); }); @@ -627,7 +506,7 @@ void setup() { 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 @@ -644,7 +523,7 @@ void setup() { wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect); wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect); #endif - + // *************************************************************************** // Setup: MDNS responder // *************************************************************************** @@ -657,11 +536,11 @@ void setup() { DBG_OUTPUT_PORT.print("Use http://"); DBG_OUTPUT_PORT.print(HOSTNAME); DBG_OUTPUT_PORT.println(".local/ when you have Bonjour installed."); - +#if !defined(USE_HTML_MIN_GZ) DBG_OUTPUT_PORT.print("New users: Open http://"); DBG_OUTPUT_PORT.print(WiFi.localIP()); DBG_OUTPUT_PORT.println("/upload to upload the webpages first."); - +#endif DBG_OUTPUT_PORT.println(""); // *************************************************************************** @@ -679,8 +558,6 @@ void setup() { MDNS.addService("http", "tcp", 80); } - prevmode = mode; - #if defined(ENABLE_BUTTON_GY33) tcs.setConfig(MCU_LED_06, MCU_WHITE_ON); // delay(2000); @@ -688,12 +565,12 @@ void setup() { #endif #if defined(ENABLE_REMOTE) irrecv.enableIRIn(); // Start the receiver - snprintf(last_state, sizeof(last_state), "STA|%2d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d", mode, ws2812fx_mode, ws2812fx_speed, brightness, main_color.red, main_color.green, main_color.blue, main_color.white, back_color.red, back_color.green, back_color.blue, back_color.white, xtra_color.red, xtra_color.green, xtra_color.blue,xtra_color.white); - last_state[sizeof(last_state)]= 0x00; #endif - DBG_OUTPUT_PORT.println("finished Main Setup!"); - + fx_speed = segState.speed[State.segment]; + brightness_trans = State.brightness; initStrip(); + strip->setBrightness(0); + DBG_OUTPUT_PORT.println("finished Main Setup!"); } // *************************************************************************** @@ -706,7 +583,7 @@ void loop() { #if defined(ENABLE_BUTTON_GY33) button_gy33(); - #endif + #endif server.handleClient(); webSocket.loop(); @@ -727,7 +604,7 @@ void loop() { WiFi.disconnect(); WiFi.setSleepMode(WIFI_NONE_SLEEP); WiFi.mode(WIFI_STA); - WiFi.hostname(HOSTNAME); + WiFi.hostname(HOSTNAME); WiFi.begin(); } else { if ((strlen(mqtt_host) != 0) && (mqtt_port != 0) && (mqtt_reconnect_retries < MQTT_MAX_RECONNECT_TRIES)) { @@ -748,148 +625,119 @@ void loop() { if (new_ha_mqtt_msg) sendState(); #endif #endif - + // *************************************************************************** // Simple statemachine that handles the different modes - // *************************************************************************** - if ((mode != OFF) && (mode != TV) && (mode != E131)) { // strip->start() is only needed for modes with WS2812FX functionality - if(!strip->isRunning()) strip->start(); - } + // *************************************************************************** - if ((mode == OFF) || (mode == TV) || (mode == E131)) { + if ((State.mode == OFF) && ((strip->getBrightness() == 0) || !Config.transEffect)) { if(strip->isRunning()) { - strip->strip_off(); // Workaround: to be shure, + strip->strip_off(); // Workaround: to be shure, delay(10); // that strip is really off. Sometimes strip->stop isn't enought - strip->stop(); // should clear memory + strip->stop(); // should clear memory + for (uint8_t i = 0; i < Config.segments; i++) { + autoCount[i] = 0; + autoDelay[i] = 0; + } } else { - if (prevmode != mode) { // Start temporarily to clear strip + if (prevmode != State.mode) { // Start temporarily to clear strip strip->start(); - strip->strip_off(); // Workaround: to be shure, + strip->strip_off(); // Workaround: to be shure, delay(10); // that strip is really off. Sometimes strip->stop isn't enought - strip->stop(); // should clear memory + strip->stop(); // should clear memory + for (uint8_t i = 0; i < Config.segments; i++) { + autoCount[i] = 0; + autoDelay[i] = 0; + } } } } - - if (( mode == AUTO) || (mode == HOLD)) { // strip->service() is only needed for modes with WS2812FX functionality - strip->service(); + + if (State.mode == OFF) { + if (prevmode != State.mode) { + #if defined(ENABLE_MQTT) + snprintf(mqtt_buf, sizeof(mqtt_buf), "OK =off", ""); + #endif + #if defined(POWER_SUPPLY) + digitalWrite(POWER_SUPPLY, !POWER_ON); // power off -> external power supply + #endif + if (Config.transEffect) { + brightness_trans = 0; + } + } } - - if ((prevmode == AUTO) && (mode != AUTO)) { handleAutoStop(); } // stop auto mode - - if (mode == OFF) { - #if defined(ENABLE_MQTT) - if (prevmode != mode) { snprintf(mqtt_buf, sizeof(mqtt_buf), "OK =off", ""); } - #endif - #if defined(POWER_SUPPLY) - if (prevmode != mode) {digitalWrite(POWER_SUPPLY, !POWER_ON); } // power off -> external power supply - #endif - } - #if defined(POWER_SUPPLY) - if (mode != OFF) { - if (prevmode != mode) {digitalWrite(POWER_SUPPLY, POWER_ON); } // power on -> external power supply + if (State.mode != OFF) { + if (prevmode != State.mode) {digitalWrite(POWER_SUPPLY, POWER_ON); } // power on -> external power supply } #endif - if (mode == AUTO) { - if (prevmode != mode) { - handleAutoStart(); + if (State.mode == SET) { + State.mode = HOLD; + // Segment + if (prevsegment != State.segment) { #if defined(ENABLE_MQTT) - snprintf(mqtt_buf, sizeof(mqtt_buf), "OK =auto", ""); + snprintf(mqtt_buf, sizeof(mqtt_buf), "OK Ss%i", State.segment); #endif + //prevsegment = State.segment; } - } - - #if defined(ENABLE_TV) - if (mode == TV) { - handleTV(); + // Mode + if (segState.mode[State.segment] != fx_mode) { + segState.mode[State.segment] = fx_mode; #if defined(ENABLE_MQTT) - if (prevmode != mode) { snprintf(mqtt_buf, sizeof(mqtt_buf), "OK =tv", ""); } + snprintf(mqtt_buf, sizeof(mqtt_buf), "OK /%i", segState.mode[State.segment]); #endif + strip->strip_off(); + autoCount[State.segment] = 0; + autoDelay[State.segment] = 0; + //strip->setSpeed(State.segment, segState.speed[State.segment]); + //strip->setColors(State.segment, segState.colors[State.segment]); + strip->setMode(State.segment, segState.mode[State.segment]); + //strip->trigger; } - #endif - - #if defined(ENABLE_E131) - if (mode == E131) { - handleE131(); + //Color + /*if (memcmp(segmentState.colors[prevsegment)], strip->getColors(prevsegment), sizeof(segmentState.colors[prevsegment)])) != 0) { + convertColors(); + }*/ + // Brightness + if (strip->getBrightness() != State.brightness) { #if defined(ENABLE_MQTT) - if (prevmode != mode) { snprintf(mqtt_buf, sizeof(mqtt_buf), "OK =e131", ""); } + snprintf(mqtt_buf, sizeof(mqtt_buf), "OK %%%i", State.brightness); + #endif + brightness_trans = State.brightness; + } + // Speed + if (fx_speed != segState.speed[State.segment]) { + #if defined(ENABLE_MQTT) + snprintf(mqtt_buf, sizeof(mqtt_buf), "OK ?%i", segState.speed[prevsegment]); #endif } - #endif - - if (mode == INIT_STRIP) { - mode = prevmode; - //ws2812fx_mode = strip->getMode(); - strip->strip_off(); - delay(10); - if(strip->isRunning()) strip->stop(); - initStrip(); - prevmode = INIT_STRIP; + prevmode = SET; } - - if (mode == SET_ALL) { - mode = HOLD; - if ((prevmode == OFF) || (prevmode == AUTO) || (prevmode == TV) || (prevmode == E131)) { setModeByStateString(last_state); } - #if defined(ENABLE_MQTT) - snprintf(mqtt_buf, sizeof(mqtt_buf), "OK /%i", ws2812fx_mode); - #endif - strip->setMode(ws2812fx_mode); - convertColors(); - strip->setColors(0, hex_colors); - strip->setSpeed(convertSpeed(ws2812fx_speed)); - strip->setBrightness(brightness); - prevmode = SET_ALL; - strip->trigger(); - } - - if (mode == SET_MODE) { - mode = HOLD; - #if defined(ENABLE_MQTT) - snprintf(mqtt_buf, sizeof(mqtt_buf), "OK /%i", ws2812fx_mode); - #endif - strip->setMode(ws2812fx_mode); - prevmode = SET_MODE; - strip->trigger(); - } - - if (mode == SET_COLOR) { - convertColors(); - strip->setColors(0, hex_colors); - mode = prevmode; - prevmode = SET_COLOR; - //DBG_OUTPUT_PORT.printf("mode: %d\r\n", mode); - if (mode == HOLD) strip->trigger(); - } - if (mode == SET_SPEED) { - #if defined(ENABLE_MQTT) - snprintf(mqtt_buf, sizeof(mqtt_buf), "OK ?%i", ws2812fx_speed); - #endif - strip->setSpeed(convertSpeed(ws2812fx_speed)); - mode = prevmode; - prevmode = SET_SPEED; - if (mode == HOLD) strip->trigger(); - } - if (mode == SET_BRIGHTNESS) { - #if defined(ENABLE_MQTT) - snprintf(mqtt_buf, sizeof(mqtt_buf), "OK %%%i", brightness); - #endif - strip->setBrightness(brightness); - mode = prevmode; - prevmode = SET_BRIGHTNESS; - //DBG_OUTPUT_PORT.printf("mode: %d\r\n", mode); - if (mode == HOLD) strip->trigger(); - } - - if (prevmode != mode) { - if ((prevmode != AUTO) && (prevmode != INIT_STRIP)) { // do not save if AUTO Mode was set - #if defined(ENABLE_STATE_SAVE) - if(!settings_save_state.active()) settings_save_state.once(3, tickerSaveState); - #endif - snprintf(last_state, sizeof(last_state), "STA|%2d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d|%3d", prevmode, ws2812fx_mode, ws2812fx_speed, brightness, main_color.red, main_color.green, main_color.blue, main_color.white, back_color.red, back_color.green, back_color.blue, back_color.white, xtra_color.red, xtra_color.green, xtra_color.blue, xtra_color.white); - last_state[sizeof(last_state) - 1] = 0x00; + + if ((State.mode == HOLD) || ((State.mode == OFF) && (strip->getBrightness() > 0) && Config.transEffect)) { + if(!strip->isRunning()) strip->start(); + strip->service(); + bool playE131 = false; + for (uint8_t i = 0; i < Config.segments; i++) { + if (segState.mode[i] == FX_MODE_CUSTOM_0) { handleAutoPlay(i); } + if (segState.mode[i] == FX_MODE_CUSTOM_3) { playE131 = true; } } + if (playE131 == true) { handleE131Play(); } + } + + if (prevmode != State.mode) { + if (segState.mode[prevsegment] != FX_MODE_CUSTOM_0) { + convertColors(); + if (memcmp(hexcolors_trans, strip->getColors(prevsegment), sizeof(hexcolors_trans)) != 0) { + DBG_OUTPUT_PORT.println("Color changed!"); + trans_cnt_max = convertColorsFade(prevsegment); + trans_cnt = 1; + memcpy(segState.colors[prevsegment], hexcolors_trans, sizeof(hexcolors_trans)); + } + strip->setSpeed(prevsegment, convertSpeed(fx_speed)); + } + //strip->setBrightness(brightness_actual); #if defined(ENABLE_MQTT) #if ENABLE_MQTT == 0 mqtt_client->publish(mqtt_outtopic, mqtt_buf); @@ -898,44 +746,113 @@ void loop() { mqtt_client->publish(mqtt_outtopic, qospub, false, mqtt_buf); #endif #if defined(ENABLE_HOMEASSISTANT) - if(!ha_send_data.active()) ha_send_data.once(3, tickerSendState); + if(ha_send_data.active()) ha_send_data.detach(); + ha_send_data.once(5, tickerSendState); #endif #endif } + + prevmode = State.mode; + #if defined(ENABLE_STATE_SAVE) if (updateState){ - #if ENABLE_STATE_SAVE == 1 - (writeStateFS(updateState)) ? DBG_OUTPUT_PORT.println(" State FS Save Success!") : DBG_OUTPUT_PORT.println("State FS Save failure!"); - #endif - #if ENABLE_STATE_SAVE == 0 - writeEEPROM(384, 66, last_state); // 384 --> last_state (reserved 66 bytes) - EEPROM.commit(); - updateState = false; - settings_save_state.detach(); - #endif + (writeStateFS(updateState)) ? DBG_OUTPUT_PORT.println("State FS Save Success!") : DBG_OUTPUT_PORT.println("State FS Save failure!"); + } + if (updateSegState) { + (writeSegmentStateFS(updateSegState, prevsegment)) ? DBG_OUTPUT_PORT.println("Segment State FS Save Success!") : DBG_OUTPUT_PORT.println("Segment State FS Save failure!"); } if (updateConfig) { - #if ENABLE_STATE_SAVE == 1 (writeConfigFS(updateConfig)) ? DBG_OUTPUT_PORT.println("Config FS Save success!"): DBG_OUTPUT_PORT.println("Config FS Save failure!"); - #endif - #if ENABLE_STATE_SAVE == 0 - char last_conf[223]; - #if defined(ENABLE_MQTT) - snprintf(last_conf, sizeof(last_conf), "CNF|%64s|%64s|%5d|%32s|%32s|%4d|%2d|%4s|%3d", HOSTNAME, mqtt_host, mqtt_port, mqtt_user, mqtt_pass, WS2812FXStripSettings.stripSize, WS2812FXStripSettings.pin, WS2812FXStripSettings.RGBOrder, WS2812FXStripSettings.fxoptions); - #else - snprintf(last_conf, sizeof(last_conf), "CNF|%64s|%64s|%5d|%32s|%32s|%4d|%2d|%4s|%3d", HOSTNAME, "", "", "", "", WS2812FXStripSettings.stripSize, WS2812FXStripSettings.pin, WS2812FXStripSettings.RGBOrder, WS2812FXStripSettings.fxoptions); - #endif - last_conf[sizeof(last_conf) - 1] = 0x00; - writeEEPROM(0, 222, last_conf); - EEPROM.commit(); - updateConfig = false; - settings_save_conf.detach(); - #endif } #endif - - prevmode = mode; - + + // Async color transition + if ((segState.mode[prevsegment] != FX_MODE_CUSTOM_0) && (memcmp(hexcolors_trans, strip->getColors(prevsegment), sizeof(hexcolors_trans)) != 0)) { + if (Config.transEffect) { + if ((trans_cnt > 0) && (trans_cnt < trans_cnt_max)) { + if (colorFadeDelay <= millis()) { + uint32_t _hexcolors_new[3] = {}; + _hexcolors_new[0] = trans(hexcolors_trans[0], strip->getColors(prevsegment)[0], trans_cnt, trans_cnt_max); + _hexcolors_new[1] = trans(hexcolors_trans[1], strip->getColors(prevsegment)[1], trans_cnt, trans_cnt_max); + _hexcolors_new[2] = trans(hexcolors_trans[2], strip->getColors(prevsegment)[2], trans_cnt, trans_cnt_max); + strip->setColors(prevsegment, _hexcolors_new); + trans_cnt++; + colorFadeDelay = millis() + TRANS_COLOR_DELAY; + if (State.mode == HOLD) strip->trigger(); + } + } else if (trans_cnt >= trans_cnt_max) { + strip->setColors(prevsegment, hexcolors_trans); + if (State.mode == HOLD) strip->trigger(); + DBG_OUTPUT_PORT.println("Color transition finished!"); + trans_cnt = 0; + } + } else { + strip->setColors(prevsegment, hexcolors_trans); + if (State.mode == HOLD) strip->trigger(); + } + } + // Async speed transition + if ((segState.mode[prevsegment] != FX_MODE_CUSTOM_0) && (segState.mode[prevsegment] != FX_MODE_CUSTOM_3) && (fx_speed != segState.speed[prevsegment])) { + if (Config.transEffect) { + //if (true == false) { // disabled for the moment + if (speedFadeDelay <= millis()) { + //DBG_OUTPUT_PORT.print("Speed trans actual: "); + if (fx_speed < segState.speed[prevsegment]) { + fx_speed++; + } + if (fx_speed > segState.speed[prevsegment]) { + fx_speed--; + } + //DBG_OUTPUT_PORT.println(fx_speed); + speedFadeDelay = millis() + TRANS_DELAY; + strip->setSpeed(prevsegment, convertSpeed(fx_speed)); + if (State.mode == HOLD) strip->trigger(); + } + } else { + fx_speed = segState.speed[prevsegment]; + //DBG_OUTPUT_PORT.print("Speed actual: "); + strip->setSpeed(prevsegment, convertSpeed(fx_speed)); + //DBG_OUTPUT_PORT.println(fx_speed); + if (State.mode == HOLD) strip->trigger(); + } + } + + // Async brightness transition + if (strip->getBrightness() != brightness_trans) { + if (Config.transEffect) { + if(brightnessFadeDelay <= millis()) { + if (strip->getBrightness() < brightness_trans) { + strip->increaseBrightness(1); + } + if (strip->getBrightness() > brightness_trans) { + strip->decreaseBrightness(1); + } + brightnessFadeDelay = millis() + TRANS_DELAY; + if (State.mode == HOLD) strip->trigger(); + } + } else { + brightness_trans = State.brightness; + strip->setBrightness(brightness_trans); + } + } + + // Segment change only if color and speed transitions are finished, because they are segment specific + if (prevsegment != State.segment) { + DBG_OUTPUT_PORT.println("Segment not equal"); + //if ((segState.mode[State.segment] == FX_MODE_CUSTOM_0) || (segState.mode[State.segment] == FX_MODE_CUSTOM_2) || (segState.mode[prevsegment] == FX_MODE_CUSTOM_0)) { + if ((segState.mode[State.segment] == FX_MODE_CUSTOM_0) || (segState.mode[prevsegment] == FX_MODE_CUSTOM_0)) { + fx_speed = segState.speed[State.segment]; + DBG_OUTPUT_PORT.printf("Switched segment from: %i to %i", prevsegment, State.segment); + prevsegment = State.segment; + } else if ((memcmp(hexcolors_trans, strip->getColors(prevsegment), sizeof(hexcolors_trans)) == 0) && (fx_speed == segState.speed[prevsegment])) { + memcpy(hexcolors_trans, segState.colors[State.segment], sizeof(hexcolors_trans)); + fx_speed = segState.speed[State.segment]; + DBG_OUTPUT_PORT.printf("Switched segment from: %i to %i\r\n", prevsegment, State.segment); + prevsegment = State.segment; + } + } + + #if defined(ENABLE_REMOTE) handleRemote(); #endif diff --git a/Arduino/McLighting/WS2812FX.cpp b/Arduino/McLighting/WS2812FX.cpp new file mode 100644 index 0000000..dad49ac --- /dev/null +++ b/Arduino/McLighting/WS2812FX.cpp @@ -0,0 +1,1647 @@ +/* + WS2812FX.cpp - Library for WS2812 LED effects. + + Harm Aldick - 2016 + www.aldick.org + + + FEATURES + * A lot of blinken modes and counting + * WS2812FX can be used as drop-in replacement for Adafruit NeoPixel Library + + NOTES + * Uses the Adafruit NeoPixel library. Get it here: + https://github.com/adafruit/Adafruit_NeoPixel + + + + LICENSE + + The MIT License (MIT) + + Copyright (c) 2016 Harm Aldick + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + + CHANGELOG + + 2016-05-28 Initial beta release + 2016-06-03 Code cleanup, minor improvements, new modes + 2016-06-04 2 new fx, fixed setColor (now also resets _mode_color) + 2017-02-02 added external trigger functionality (e.g. for sound-to-light) + 2017-02-02 removed "blackout" on mode, speed or color-change + 2017-09-26 implemented segment and reverse features + 2017-11-16 changed speed calc, reduced memory footprint + 2018-02-24 added hooks for user created custom effects +*/ + +#include "WS2812FX.h" + +void WS2812FX::init() { + resetSegmentRuntimes(); + Adafruit_NeoPixel::begin(); +} + +// void WS2812FX::timer() { +// for (int j=0; j < 1000; j++) { +// uint16_t delay = (this->*_mode[SEGMENT.mode])(); +// } +// } + +void WS2812FX::service() { + if(_running || _triggered) { + unsigned long now = millis(); // Be aware, millis() rolls over every 49 days + bool doShow = false; + for(uint8_t i=0; i < _num_segments; i++) { + _segment_index = i; + CLR_FRAME; + if(now > SEGMENT_RUNTIME.next_time || _triggered) { + SET_FRAME; + doShow = true; + uint16_t delay = (this->*_mode[SEGMENT.mode])(); + SEGMENT_RUNTIME.next_time = now + max(delay, SPEED_MIN); + SEGMENT_RUNTIME.counter_mode_call++; + } + } + if(doShow) { + delay(1); // for ESP32 (see https://forums.adafruit.com/viewtopic.php?f=47&t=117327) + show(); + } + _triggered = false; + } +} + +// overload setPixelColor() functions so we can use gamma correction +// (see https://learn.adafruit.com/led-tricks-gamma-correction/the-issue) +void WS2812FX::setPixelColor(uint16_t n, uint32_t c) { + if(IS_GAMMA) { + uint8_t w = (c >> 24) & 0xFF; + uint8_t r = (c >> 16) & 0xFF; + uint8_t g = (c >> 8) & 0xFF; + uint8_t b = c & 0xFF; + Adafruit_NeoPixel::setPixelColor(n, gamma8(r), gamma8(g), gamma8(b), gamma8(w)); + } else { + Adafruit_NeoPixel::setPixelColor(n, c); + } +} + +void WS2812FX::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b) { + if(IS_GAMMA) { + Adafruit_NeoPixel::setPixelColor(n, gamma8(r), gamma8(g), gamma8(b)); + } else { + Adafruit_NeoPixel::setPixelColor(n, r, g, b); + } +} + +void WS2812FX::setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { + if(IS_GAMMA) { + Adafruit_NeoPixel::setPixelColor(n, gamma8(r), gamma8(g), gamma8(b), gamma8(w)); + } else { + Adafruit_NeoPixel::setPixelColor(n, r, g, b, w); + } +} + +void WS2812FX::copyPixels(uint16_t dest, uint16_t src, uint16_t count) { + uint8_t *pixels = getPixels(); + uint8_t bytesPerPixel = getNumBytesPerPixel(); // 3=RGB, 4=RGBW + + memmove(pixels + (dest * bytesPerPixel), pixels + (src * bytesPerPixel), count * bytesPerPixel); +} + +// overload show() functions so we can use custom show() +void WS2812FX::show(void) { + if(customShow == NULL) { + Adafruit_NeoPixel::show(); + } else { + customShow(); + } +} + +void WS2812FX::start() { + resetSegmentRuntimes(); + _running = true; +} + +void WS2812FX::stop() { + _running = false; + strip_off(); +} + +void WS2812FX::pause() { + _running = false; +} + +void WS2812FX::resume() { + _running = true; +} + +void WS2812FX::trigger() { + _triggered = true; +} + +void WS2812FX::setMode(uint8_t m) { + setMode(0, m); +} + +void WS2812FX::setMode(uint8_t seg, uint8_t m) { + resetSegmentRuntime(seg); + _segments[seg].mode = constrain(m, 0, MODE_COUNT - 1); +} + +void WS2812FX::setOptions(uint8_t seg, uint8_t o) { + _segments[seg].options = o; +} + +void WS2812FX::setSpeed(uint16_t s) { + setSpeed(0, s); +} + +void WS2812FX::setSpeed(uint8_t seg, uint16_t s) { +// resetSegmentRuntime(seg); + _segments[seg].speed = constrain(s, SPEED_MIN, SPEED_MAX); +} + +void WS2812FX::increaseSpeed(uint8_t s) { + uint16_t newSpeed = constrain(SEGMENT.speed + s, SPEED_MIN, SPEED_MAX); + setSpeed(newSpeed); +} + +void WS2812FX::decreaseSpeed(uint8_t s) { + uint16_t newSpeed = constrain(SEGMENT.speed - s, SPEED_MIN, SPEED_MAX); + setSpeed(newSpeed); +} + +void WS2812FX::setColor(uint8_t r, uint8_t g, uint8_t b) { + setColor(((uint32_t)r << 16) | ((uint32_t)g << 8) | b); +} + +void WS2812FX::setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { + setColor((((uint32_t)w << 24)| ((uint32_t)r << 16) | ((uint32_t)g << 8)| ((uint32_t)b))); +} + +void WS2812FX::setColor(uint32_t c) { + setColor(0, c); +} + +void WS2812FX::setColor(uint8_t seg, uint32_t c) { +// resetSegmentRuntime(seg); + _segments[seg].colors[0] = c; +} + +void WS2812FX::setColors(uint8_t seg, uint32_t* c) { +// resetSegmentRuntime(seg); + for(uint8_t i=0; i 1); + + _segments[0].start = 0; + _segments[0].stop = Adafruit_NeoPixel::numLEDs - 1; +} + +void WS2812FX::increaseLength(uint16_t s) { + s = _segments[0].stop - _segments[0].start + 1 + s; + setLength(s); +} + +void WS2812FX::decreaseLength(uint16_t s) { + if (s > _segments[0].stop - _segments[0].start + 1) s = 1; + s = _segments[0].stop - _segments[0].start + 1 - s; + + for(uint16_t i=_segments[0].start + s; i <= (_segments[0].stop - _segments[0].start + 1); i++) { + setPixelColor(i, 0); + } + show(); + + setLength(s); +} + +boolean WS2812FX::isRunning() { + return _running; +} + +boolean WS2812FX::isTriggered() { + return _triggered; +} + +boolean WS2812FX::isFrame() { + return isFrame(0); +} + +boolean WS2812FX::isFrame(uint8_t segIndex) { + return (_segment_runtimes[segIndex].aux_param2 & FRAME); +} + +boolean WS2812FX::isCycle() { + return isCycle(0); +} + +boolean WS2812FX::isCycle(uint8_t segIndex) { + return (_segment_runtimes[segIndex].aux_param2 & CYCLE); +} + +uint8_t WS2812FX::getMode(void) { + return getMode(0); +} + +uint8_t WS2812FX::getMode(uint8_t seg) { + return _segments[seg].mode; +} + +uint16_t WS2812FX::getSpeed(void) { + return getSpeed(0); +} + +uint16_t WS2812FX::getSpeed(uint8_t seg) { + return _segments[seg].speed; +} + + +uint8_t WS2812FX::getOptions(uint8_t seg) { + return _segments[seg].options; +} + +uint16_t WS2812FX::getLength(void) { + return numPixels(); +} + +uint16_t WS2812FX::getNumBytes(void) { + return numBytes; +} + +uint8_t WS2812FX::getNumBytesPerPixel(void) { + return (wOffset == rOffset) ? 3 : 4; // 3=RGB, 4=RGBW +} + +uint8_t WS2812FX::getModeCount(void) { + return MODE_COUNT; +} + +uint8_t WS2812FX::getNumSegments(void) { + return _num_segments; +} + +void WS2812FX::setNumSegments(uint8_t n) { + _num_segments = n; +} + +uint32_t WS2812FX::getColor(void) { + return getColor(0); +} + +uint32_t WS2812FX::getColor(uint8_t seg) { + return _segments[seg].colors[0]; +} + +uint32_t* WS2812FX::getColors(uint8_t seg) { + return _segments[seg].colors; +} + +uint8_t WS2812FX::getSegmentIndex(void) { + return _segment_index; +} + +WS2812FX::Segment* WS2812FX::getSegment(void) { + return &_segments[_segment_index]; +} + +WS2812FX::Segment* WS2812FX::getSegment(uint8_t seg) { + return &_segments[seg]; +} + +WS2812FX::Segment* WS2812FX::getSegments(void) { + return _segments; +} + +WS2812FX::Segment_runtime* WS2812FX::getSegmentRuntime(void) { + return &_segment_runtimes[_segment_index]; +} + +WS2812FX::Segment_runtime* WS2812FX::getSegmentRuntime(uint8_t seg) { + return &_segment_runtimes[seg]; +} + +WS2812FX::Segment_runtime* WS2812FX::getSegmentRuntimes(void) { + return _segment_runtimes; +} + +const __FlashStringHelper* WS2812FX::getModeName(uint8_t m) { + if(m < MODE_COUNT) { + return _names[m]; + } else { + return F(""); + } +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, bool reverse) { + uint32_t colors[] = {color, 0, 0}; + setSegment(n, start, stop, mode, colors, speed, reverse); +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, uint8_t options) { + uint32_t colors[] = {color, 0, 0}; + setSegment(n, start, stop, mode, colors, speed, options); +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, bool reverse) { + setSegment(n, start, stop, mode, colors, speed, (uint8_t)(reverse ? REVERSE : NO_OPTIONS)); +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, uint8_t options) { + if(n < (sizeof(_segments) / sizeof(_segments[0]))) { + if(n + 1 > _num_segments) _num_segments = n + 1; + _segments[n].start = start; + _segments[n].stop = stop; + _segments[n].mode = mode; + _segments[n].speed = speed; + _segments[n].options = options; + + for(uint8_t i=0; i g -> b -> back to r + * Inspired by the Adafruit examples. + */ +uint32_t WS2812FX::color_wheel(uint8_t pos) { + pos = 255 - pos; + if(pos < 85) { + return ((uint32_t)(255 - pos * 3) << 16) | ((uint32_t)(0) << 8) | (pos * 3); + } else if(pos < 170) { + pos -= 85; + return ((uint32_t)(0) << 16) | ((uint32_t)(pos * 3) << 8) | (255 - pos * 3); + } else { + pos -= 170; + return ((uint32_t)(pos * 3) << 16) | ((uint32_t)(255 - pos * 3) << 8) | (0); + } +} + + +/* + * Returns a new, random wheel index with a minimum distance of 42 from pos. + */ +uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) { + uint8_t r = 0; + uint8_t x = 0; + uint8_t y = 0; + uint8_t d = 0; + + while(d < 42) { + r = random8(); + x = abs(pos - r); + y = 255 - x; + d = min(x, y); + } + + return r; +} + +// fast 8-bit random number generator shamelessly borrowed from FastLED +uint8_t WS2812FX::random8() { + _rand16seed = (_rand16seed * 2053) + 13849; + return (uint8_t)((_rand16seed + (_rand16seed >> 8)) & 0xFF); +} + +// note random8(lim) generates numbers in the range 0 to (lim -1) +uint8_t WS2812FX::random8(uint8_t lim) { + uint8_t r = random8(); + r = (r * lim) >> 8; + return r; +} + +uint16_t WS2812FX::random16() { + return (uint16_t)random8() * 256 + random8(); +} + +// note random16(lim) generates numbers in the range 0 to (lim - 1) +uint16_t WS2812FX::random16(uint16_t lim) { + uint16_t r = random16(); + r = ((uint32_t)r * lim) >> 16; + return r; +} + +// Return the sum of all LED intensities (can be used for +// rudimentary power calculations) +uint32_t WS2812FX::intensitySum() { + uint8_t *pixels = getPixels(); + uint32_t sum = 0; + for(uint16_t i=0; i 255) lum = 511 - lum; // lum = 15 -> 255 -> 15 + + uint16_t delay; + if(lum == 15) delay = 970; // 970 pause before each breath + else if(lum <= 25) delay = 38; // 19 + else if(lum <= 50) delay = 36; // 18 + else if(lum <= 75) delay = 28; // 14 + else if(lum <= 100) delay = 20; // 10 + else if(lum <= 125) delay = 14; // 7 + else if(lum <= 150) delay = 11; // 5 + else delay = 10; // 4 + + uint32_t color = SEGMENT.colors[0]; + uint8_t w = (color >> 24 & 0xFF) * lum / 256; + uint8_t r = (color >> 16 & 0xFF) * lum / 256; + uint8_t g = (color >> 8 & 0xFF) * lum / 256; + uint8_t b = (color & 0xFF) * lum / 256; + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + setPixelColor(i, r, g, b, w); + } + + SEGMENT_RUNTIME.counter_mode_step += 2; + if(SEGMENT_RUNTIME.counter_mode_step > (512-15)) SEGMENT_RUNTIME.counter_mode_step = 15; + return delay; +} + + +/* + * Fades the LEDs between two colors + */ +uint16_t WS2812FX::mode_fade(void) { + int lum = SEGMENT_RUNTIME.counter_mode_step; + if(lum > 255) lum = 511 - lum; // lum = 0 -> 255 -> 0 + + uint32_t color = color_blend(SEGMENT.colors[0], SEGMENT.colors[1], lum); + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + setPixelColor(i, color); + } + + SEGMENT_RUNTIME.counter_mode_step += 4; + if(SEGMENT_RUNTIME.counter_mode_step > 511) SEGMENT_RUNTIME.counter_mode_step = 0; + return (SEGMENT.speed / 128); +} + + +/* + * scan function - runs a block of pixels back and forth. + */ +uint16_t WS2812FX::scan(uint32_t color1, uint32_t color2, bool dual) { + int8_t dir = SEGMENT_RUNTIME.aux_param ? -1 : 1; + uint8_t size = 1 << SIZE_OPTION; + + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + setPixelColor(i, color2); + } + + for(uint8_t i = 0; i < size; i++) { + if(IS_REVERSE || dual) { + setPixelColor(SEGMENT.stop - SEGMENT_RUNTIME.counter_mode_step - i, color1); + } + if(!IS_REVERSE || dual) { + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step + i, color1); + } + } + + SEGMENT_RUNTIME.counter_mode_step += dir; + if(SEGMENT_RUNTIME.counter_mode_step == 0) SEGMENT_RUNTIME.aux_param = 0; + if(SEGMENT_RUNTIME.counter_mode_step >= (uint16_t)(SEGMENT_LENGTH - size)) SEGMENT_RUNTIME.aux_param = 1; + + return (SEGMENT.speed / (SEGMENT_LENGTH * 2)); +} + + +/* + * Runs a block of pixels back and forth. + */ +uint16_t WS2812FX::mode_scan(void) { + return scan(SEGMENT.colors[0], SEGMENT.colors[1], false); +} + + +/* + * Runs two blocks of pixels back and forth in opposite directions. + */ +uint16_t WS2812FX::mode_dual_scan(void) { + return scan(SEGMENT.colors[0], SEGMENT.colors[1], true); +} + + +/* + * Cycles all LEDs at once through a rainbow. + */ +uint16_t WS2812FX::mode_rainbow(void) { + uint32_t color = color_wheel(SEGMENT_RUNTIME.counter_mode_step); + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + setPixelColor(i, color); + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) & 0xFF; + return (SEGMENT.speed / 256); +} + + +/* + * Cycles a rainbow over the entire string of LEDs. + */ +uint16_t WS2812FX::mode_rainbow_cycle(void) { + for(uint16_t i=0; i < SEGMENT_LENGTH; i++) { + uint32_t color = color_wheel(((i * 256 / SEGMENT_LENGTH) + SEGMENT_RUNTIME.counter_mode_step) & 0xFF); + setPixelColor(SEGMENT.start + i, color); + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) & 0xFF; + return (SEGMENT.speed / 256); +} + + +/* + * Theatre-style crawling lights. + * Inspired by the Adafruit examples. + */ +uint16_t WS2812FX::mode_theater_chase(void) { + return tricolor_chase(SEGMENT.colors[0], SEGMENT.colors[1], SEGMENT.colors[1]); +} + + +/* + * Theatre-style crawling lights with rainbow effect. + * Inspired by the Adafruit examples. + */ +uint16_t WS2812FX::mode_theater_chase_rainbow(void) { + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) & 0xFF; + uint32_t color = color_wheel(SEGMENT_RUNTIME.counter_mode_step); + return tricolor_chase(color, SEGMENT.colors[1], SEGMENT.colors[1]); +} + + +/* + * Running lights effect with smooth sine transition. + */ +uint16_t WS2812FX::mode_running_lights(void) { + uint8_t w = ((SEGMENT.colors[0] >> 24) & 0xFF); + uint8_t r = ((SEGMENT.colors[0] >> 16) & 0xFF); + uint8_t g = ((SEGMENT.colors[0] >> 8) & 0xFF); + uint8_t b = (SEGMENT.colors[0] & 0xFF); + + uint8_t size = 1 << SIZE_OPTION; + uint8_t sineIncr = max(1, (256 / SEGMENT_LENGTH) * size); + for(uint16_t i=0; i < SEGMENT_LENGTH; i++) { + int lum = (int)sine8(((i + SEGMENT_RUNTIME.counter_mode_step) * sineIncr)); + if(IS_REVERSE) { + setPixelColor(SEGMENT.start + i, (r * lum) / 256, (g * lum) / 256, (b * lum) / 256, (w * lum) / 256); + } else { + setPixelColor(SEGMENT.stop - i, (r * lum) / 256, (g * lum) / 256, (b * lum) / 256, (w * lum) / 256); + } + } + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % 256; + return (SEGMENT.speed / SEGMENT_LENGTH); +} + + +/* + * twinkle function + */ +uint16_t WS2812FX::twinkle(uint32_t color1, uint32_t color2) { + if(SEGMENT_RUNTIME.counter_mode_step == 0) { + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + setPixelColor(i, color2); + } + uint16_t min_leds = max(1, SEGMENT_LENGTH / 5); // make sure, at least one LED is on + uint16_t max_leds = max(1, SEGMENT_LENGTH / 2); // make sure, at least one LED is on + SEGMENT_RUNTIME.counter_mode_step = random(min_leds, max_leds); + } + + setPixelColor(SEGMENT.start + random16(SEGMENT_LENGTH), color1); + + SEGMENT_RUNTIME.counter_mode_step--; + return (SEGMENT.speed / SEGMENT_LENGTH); +} + +/* + * Blink several LEDs on, reset, repeat. + * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ + */ +uint16_t WS2812FX::mode_twinkle(void) { + return twinkle(SEGMENT.colors[0], SEGMENT.colors[1]); +} + +/* + * Blink several LEDs in random colors on, reset, repeat. + * Inspired by www.tweaking4all.com/hardware/arduino/arduino-led-strip-effects/ + */ +uint16_t WS2812FX::mode_twinkle_random(void) { + return twinkle(color_wheel(random8()), SEGMENT.colors[1]); +} + + +/* + * fade out functions + */ +void WS2812FX::fade_out() { + return fade_out(SEGMENT.colors[1]); +} + +void WS2812FX::fade_out(uint32_t targetColor) { + static const uint8_t rateMapH[] = {0, 1, 1, 1, 2, 3, 4, 6}; + static const uint8_t rateMapL[] = {0, 2, 3, 8, 8, 8, 8, 8}; + + uint8_t rate = FADE_RATE; + uint8_t rateH = rateMapH[rate]; + uint8_t rateL = rateMapL[rate]; + + uint32_t color = targetColor; + int w2 = (color >> 24) & 0xff; + int r2 = (color >> 16) & 0xff; + int g2 = (color >> 8) & 0xff; + int b2 = color & 0xff; + + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + color = getPixelColor(i); // current color + if(rate == 0) { // old fade-to-black algorithm + setPixelColor(i, (color >> 1) & 0x7F7F7F7F); + } else { // new fade-to-color algorithm + int w1 = (color >> 24) & 0xff; + int r1 = (color >> 16) & 0xff; + int g1 = (color >> 8) & 0xff; + int b1 = color & 0xff; + + // calculate the color differences between the current and target colors + int wdelta = w2 - w1; + int rdelta = r2 - r1; + int gdelta = g2 - g1; + int bdelta = b2 - b1; + + // if the current and target colors are almost the same, jump right to the target + // color, otherwise calculate an intermediate color. (fixes rounding issues) + wdelta = abs(wdelta) < 3 ? wdelta : (wdelta >> rateH) + (wdelta >> rateL); + rdelta = abs(rdelta) < 3 ? rdelta : (rdelta >> rateH) + (rdelta >> rateL); + gdelta = abs(gdelta) < 3 ? gdelta : (gdelta >> rateH) + (gdelta >> rateL); + bdelta = abs(bdelta) < 3 ? bdelta : (bdelta >> rateH) + (bdelta >> rateL); + + setPixelColor(i, r1 + rdelta, g1 + gdelta, b1 + bdelta, w1 + wdelta); + } + } +} + + +/* + * color blend function + */ +uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint8_t blend) { + if(blend == 0) return color1; + if(blend == 255) return color2; + + uint8_t w1 = (color1 >> 24) & 0xff; + uint8_t r1 = (color1 >> 16) & 0xff; + uint8_t g1 = (color1 >> 8) & 0xff; + uint8_t b1 = color1 & 0xff; + + uint8_t w2 = (color2 >> 24) & 0xff; + uint8_t r2 = (color2 >> 16) & 0xff; + uint8_t g2 = (color2 >> 8) & 0xff; + uint8_t b2 = color2 & 0xff; + + uint32_t w3 = ((w2 * blend) + (w1 * (255U - blend))) / 256U; + uint32_t r3 = ((r2 * blend) + (r1 * (255U - blend))) / 256U; + uint32_t g3 = ((g2 * blend) + (g1 * (255U - blend))) / 256U; + uint32_t b3 = ((b2 * blend) + (b1 * (255U - blend))) / 256U; + + return ((w3 << 24) | (r3 << 16) | (g3 << 8) | (b3)); +} + + +/* + * twinkle_fade function + */ +uint16_t WS2812FX::twinkle_fade(uint32_t color) { + fade_out(); + + if(random8(3) == 0) { + uint8_t size = 1 << SIZE_OPTION; + uint16_t index = SEGMENT.start + random16(SEGMENT_LENGTH - size); + for(uint8_t i=0; i= (uint16_t)((SEGMENT_LENGTH * 2) - 2)) { + SEGMENT_RUNTIME.counter_mode_step = 0; + } + + return (SEGMENT.speed / (SEGMENT_LENGTH * 2)); +} + + +/* + * Firing comets from one end. + */ +uint16_t WS2812FX::mode_comet(void) { + fade_out(); + + if(IS_REVERSE) { + setPixelColor(SEGMENT.stop - SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + } else { + setPixelColor(SEGMENT.start + SEGMENT_RUNTIME.counter_mode_step, SEGMENT.colors[0]); + } + + SEGMENT_RUNTIME.counter_mode_step = (SEGMENT_RUNTIME.counter_mode_step + 1) % SEGMENT_LENGTH; + return (SEGMENT.speed / SEGMENT_LENGTH); +} + + +/* + * Fireworks function. + */ +uint16_t WS2812FX::fireworks(uint32_t color) { + fade_out(); + +// for better performance, manipulate the Adafruit_NeoPixels pixels[] array directly + uint8_t *pixels = getPixels(); + uint8_t bytesPerPixel = getNumBytesPerPixel(); // 3=RGB, 4=RGBW + uint16_t startPixel = SEGMENT.start * bytesPerPixel + bytesPerPixel; + uint16_t stopPixel = SEGMENT.stop * bytesPerPixel ; + for(uint16_t i=startPixel; i > 2) + + pixels[i] + + (pixels[i + bytesPerPixel] >> 2); + pixels[i] = tmpPixel > 255 ? 255 : tmpPixel; + } + + uint8_t size = 2 << SIZE_OPTION; + if(!_triggered) { + for(uint16_t i=0; i> 24) & 0xFF; + byte r = (SEGMENT.colors[0] >> 16) & 0xFF; + byte g = (SEGMENT.colors[0] >> 8) & 0xFF; + byte b = (SEGMENT.colors[0] & 0xFF); + byte lum = max(w, max(r, max(g, b))) / rev_intensity; + for(uint16_t i=SEGMENT.start; i <= SEGMENT.stop; i++) { + int flicker = random8(lum); + setPixelColor(i, max(r - flicker, 0), max(g - flicker, 0), max(b - flicker, 0), max(w - flicker, 0)); + } + return (SEGMENT.speed / SEGMENT_LENGTH); +} + +/* + * Random flickering. + */ +uint16_t WS2812FX::mode_fire_flicker(void) { + return fire_flicker(3); +} + +/* +* Random flickering, less intensity. +*/ +uint16_t WS2812FX::mode_fire_flicker_soft(void) { + return fire_flicker(6); +} + +/* +* Random flickering, more intensity. +*/ +uint16_t WS2812FX::mode_fire_flicker_intense(void) { + return fire_flicker(1.7); +} + + +/* + * Tricolor chase function + */ +uint16_t WS2812FX::tricolor_chase(uint32_t color1, uint32_t color2, uint32_t color3) { + uint8_t sizeCnt = 1 << SIZE_OPTION; + uint16_t index = SEGMENT_RUNTIME.counter_mode_call % (sizeCnt * 3); + for(uint16_t i=0; i < SEGMENT_LENGTH; i++, index++) { + index = index % (sizeCnt * 3); + + uint32_t color = color3; + if(index < sizeCnt) color = color1; + else if(index < (sizeCnt * 2)) color = color2; + + if(IS_REVERSE) { + setPixelColor(SEGMENT.start + i, color); + } else { + setPixelColor(SEGMENT.stop - i, color); + } + } + + return (SEGMENT.speed / SEGMENT_LENGTH); +} + + +/* + * Tricolor chase mode + */ +uint16_t WS2812FX::mode_tricolor_chase(void) { + return tricolor_chase(SEGMENT.colors[0], SEGMENT.colors[1], SEGMENT.colors[2]); +} + + +/* + * Alternating white/red/black pixels running. + */ +uint16_t WS2812FX::mode_circus_combustus(void) { + return tricolor_chase(RED, WHITE, BLACK); +} + +/* + * ICU mode + */ +uint16_t WS2812FX::mode_icu(void) { + uint16_t dest = SEGMENT_RUNTIME.counter_mode_step & 0xFFFF; + + setPixelColor(SEGMENT.start + dest, SEGMENT.colors[0]); + setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, SEGMENT.colors[0]); + + if(SEGMENT_RUNTIME.aux_param3 == dest) { // pause between eye movements + if(random8(6) == 0) { // blink once in a while + setPixelColor(SEGMENT.start + dest, BLACK); + setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, BLACK); + return 200; + } + SEGMENT_RUNTIME.aux_param3 = random16(SEGMENT_LENGTH/2); + return 1000 + random16(2000); + } + + setPixelColor(SEGMENT.start + dest, BLACK); + setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, BLACK); + + if(SEGMENT_RUNTIME.aux_param3 > SEGMENT_RUNTIME.counter_mode_step) { + SEGMENT_RUNTIME.counter_mode_step++; + dest++; + } else if (SEGMENT_RUNTIME.aux_param3 < SEGMENT_RUNTIME.counter_mode_step) { + SEGMENT_RUNTIME.counter_mode_step--; + dest--; + } + + setPixelColor(SEGMENT.start + dest, SEGMENT.colors[0]); + setPixelColor(SEGMENT.start + dest + SEGMENT_LENGTH/2, SEGMENT.colors[0]); + + return (SEGMENT.speed / SEGMENT_LENGTH); +} + +/* + * Custom modes + */ +uint16_t WS2812FX::mode_custom_0() { + return customModes[0](); +} +uint16_t WS2812FX::mode_custom_1() { + return customModes[1](); +} +uint16_t WS2812FX::mode_custom_2() { + return customModes[2](); +} +uint16_t WS2812FX::mode_custom_3() { + return customModes[3](); +} +uint16_t WS2812FX::mode_custom_4() { + return customModes[4](); +} +uint16_t WS2812FX::mode_custom_5() { + return customModes[5](); +} +uint16_t WS2812FX::mode_custom_6() { + return customModes[6](); +} +uint16_t WS2812FX::mode_custom_7() { + return customModes[7](); +} +uint16_t WS2812FX::mode_custom_8() { + return customModes[8](); +} +uint16_t WS2812FX::mode_custom_9() { + return customModes[9](); +} +/* + * Custom mode helpers + */ +void WS2812FX::setCustomMode(uint16_t (*p)()) { + customModes[0] = p; +} + +uint8_t WS2812FX::setCustomMode(const __FlashStringHelper* name, uint16_t (*p)()) { + static uint8_t custom_mode_index = 0; + return setCustomMode(custom_mode_index++, name, p); +} + +uint8_t WS2812FX::setCustomMode(uint8_t index, const __FlashStringHelper* name, uint16_t (*p)()) { + if((uint8_t)(FX_MODE_CUSTOM_0 + index) < MODE_COUNT) { + _names[FX_MODE_CUSTOM_0 + index] = name; // store the custom mode name + customModes[index] = p; // store the custom mode + + return (FX_MODE_CUSTOM_0 + index); + } + return 0; +} + +/* + * Custom show helper + */ +void WS2812FX::setCustomShow(void (*p)()) { + customShow = p; +} diff --git a/Arduino/McLighting/WS2812FX.h b/Arduino/McLighting/WS2812FX.h new file mode 100644 index 0000000..5405457 --- /dev/null +++ b/Arduino/McLighting/WS2812FX.h @@ -0,0 +1,651 @@ +/* + WS2812FX.h - Library for WS2812 LED effects. + + Harm Aldick - 2016 + www.aldick.org + FEATURES + * A lot of blinken modes and counting + * WS2812FX can be used as drop-in replacement for Adafruit NeoPixel Library + NOTES + * Uses the Adafruit NeoPixel library. Get it here: + https://github.com/adafruit/Adafruit_NeoPixel + LICENSE + The MIT License (MIT) + Copyright (c) 2016 Harm Aldick + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + CHANGELOG + 2016-05-28 Initial beta release + 2016-06-03 Code cleanup, minor improvements, new modes + 2016-06-04 2 new fx, fixed setColor (now also resets _mode_color) + 2017-02-02 added external trigger functionality (e.g. for sound-to-light) +*/ + +#ifndef WS2812FX_h +#define WS2812FX_h + +#define FSH(x) (__FlashStringHelper*)(x) + +#include + +#define DEFAULT_BRIGHTNESS (uint8_t)50 +#define DEFAULT_MODE (uint8_t)0 +#define DEFAULT_SPEED (uint16_t)1000 +#define DEFAULT_COLOR (uint32_t)0xFF0000 + +#if defined(ESP8266) || defined(ESP32) + //#pragma message("Compiling for ESP") + #define SPEED_MIN (uint16_t)2 +#else + //#pragma message("Compiling for Arduino") + #define SPEED_MIN (uint16_t)10 +#endif +#define SPEED_MAX (uint16_t)65535 + +#define BRIGHTNESS_MIN (uint8_t)0 +#define BRIGHTNESS_MAX (uint8_t)255 + +/* each segment uses 36 bytes of SRAM memory, so if you're application fails because of + insufficient memory, decreasing MAX_NUM_SEGMENTS may help */ +#define MAX_NUM_SEGMENTS 10 +#define NUM_COLORS 3 /* number of colors per segment */ +#define MAX_CUSTOM_MODES 10 +#define SEGMENT _segments[_segment_index] +#define SEGMENT_RUNTIME _segment_runtimes[_segment_index] +#define SEGMENT_LENGTH (uint16_t)(SEGMENT.stop - SEGMENT.start + 1) + +// some common colors +#define RED (uint32_t)0xFF0000 +#define GREEN (uint32_t)0x00FF00 +#define BLUE (uint32_t)0x0000FF +#define WHITE (uint32_t)0xFFFFFF +#define BLACK (uint32_t)0x000000 +#define YELLOW (uint32_t)0xFFFF00 +#define CYAN (uint32_t)0x00FFFF +#define MAGENTA (uint32_t)0xFF00FF +#define PURPLE (uint32_t)0x400080 +#define ORANGE (uint32_t)0xFF3000 +#define PINK (uint32_t)0xFF1493 +#define ULTRAWHITE (uint32_t)0xFFFFFFFF +#define DARK(c) (uint32_t)((c >> 4) & 0x0f0f0f0f) + +// segment options +// bit 7: reverse animation +// bits 4-6: fade rate (0-7) +// bit 3: gamma correction +// bits 1-2: size +// bits 0: TBD +#define NO_OPTIONS (uint8_t)B00000000 +#define REVERSE (uint8_t)B10000000 +#define IS_REVERSE ((SEGMENT.options & REVERSE) == REVERSE) +#define FADE_XFAST (uint8_t)B00010000 +#define FADE_FAST (uint8_t)B00100000 +#define FADE_MEDIUM (uint8_t)B00110000 +#define FADE_SLOW (uint8_t)B01000000 +#define FADE_XSLOW (uint8_t)B01010000 +#define FADE_XXSLOW (uint8_t)B01100000 +#define FADE_GLACIAL (uint8_t)B01110000 +#define FADE_RATE ((SEGMENT.options >> 4) & 7) +#define GAMMA (uint8_t)B00001000 +#define IS_GAMMA ((SEGMENT.options & GAMMA) == GAMMA) +#define SIZE_SMALL (uint8_t)B00000000 +#define SIZE_MEDIUM (uint8_t)B00000010 +#define SIZE_LARGE (uint8_t)B00000100 +#define SIZE_XLARGE (uint8_t)B00000110 +#define SIZE_OPTION ((SEGMENT.options >> 1) & 3) + +// segment runtime options (aux_param2) +#define FRAME (uint8_t)B10000000 +#define SET_FRAME (SEGMENT_RUNTIME.aux_param2 |= FRAME) +#define CLR_FRAME (SEGMENT_RUNTIME.aux_param2 &= ~FRAME) +#define CYCLE (uint8_t)B01000000 +#define SET_CYCLE (SEGMENT_RUNTIME.aux_param2 |= CYCLE) +#define CLR_CYCLE (SEGMENT_RUNTIME.aux_param2 &= ~CYCLE) + +#define MODE_COUNT (sizeof(_names)/sizeof(_names[0])) + +#define FX_MODE_STATIC 0 +#define FX_MODE_BLINK 1 +#define FX_MODE_BREATH 2 +#define FX_MODE_COLOR_WIPE 3 +#define FX_MODE_COLOR_WIPE_INV 4 +#define FX_MODE_COLOR_WIPE_REV 5 +#define FX_MODE_COLOR_WIPE_REV_INV 6 +#define FX_MODE_COLOR_WIPE_RANDOM 7 +#define FX_MODE_RANDOM_COLOR 8 +#define FX_MODE_SINGLE_DYNAMIC 9 +#define FX_MODE_MULTI_DYNAMIC 10 +#define FX_MODE_RAINBOW 11 +#define FX_MODE_RAINBOW_CYCLE 12 +#define FX_MODE_SCAN 13 +#define FX_MODE_DUAL_SCAN 14 +#define FX_MODE_FADE 15 +#define FX_MODE_THEATER_CHASE 16 +#define FX_MODE_THEATER_CHASE_RAINBOW 17 +#define FX_MODE_RUNNING_LIGHTS 18 +#define FX_MODE_TWINKLE 19 +#define FX_MODE_TWINKLE_RANDOM 20 +#define FX_MODE_TWINKLE_FADE 21 +#define FX_MODE_TWINKLE_FADE_RANDOM 22 +#define FX_MODE_SPARKLE 23 +#define FX_MODE_FLASH_SPARKLE 24 +#define FX_MODE_HYPER_SPARKLE 25 +#define FX_MODE_STROBE 26 +#define FX_MODE_STROBE_RAINBOW 27 +#define FX_MODE_MULTI_STROBE 28 +#define FX_MODE_BLINK_RAINBOW 29 +#define FX_MODE_CHASE_WHITE 30 +#define FX_MODE_CHASE_COLOR 31 +#define FX_MODE_CHASE_RANDOM 32 +#define FX_MODE_CHASE_RAINBOW 33 +#define FX_MODE_CHASE_FLASH 34 +#define FX_MODE_CHASE_FLASH_RANDOM 35 +#define FX_MODE_CHASE_RAINBOW_WHITE 36 +#define FX_MODE_CHASE_BLACKOUT 37 +#define FX_MODE_CHASE_BLACKOUT_RAINBOW 38 +#define FX_MODE_COLOR_SWEEP_RANDOM 39 +#define FX_MODE_RUNNING_COLOR 40 +#define FX_MODE_RUNNING_RED_BLUE 41 +#define FX_MODE_RUNNING_RANDOM 42 +#define FX_MODE_LARSON_SCANNER 43 +#define FX_MODE_COMET 44 +#define FX_MODE_FIREWORKS 45 +#define FX_MODE_FIREWORKS_RANDOM 46 +#define FX_MODE_MERRY_CHRISTMAS 47 +#define FX_MODE_FIRE_FLICKER 48 +#define FX_MODE_FIRE_FLICKER_SOFT 49 +#define FX_MODE_FIRE_FLICKER_INTENSE 50 +#define FX_MODE_CIRCUS_COMBUSTUS 51 +#define FX_MODE_HALLOWEEN 52 +#define FX_MODE_BICOLOR_CHASE 53 +#define FX_MODE_TRICOLOR_CHASE 54 +#define FX_MODE_ICU 55 +#define FX_MODE_CUSTOM 56 // keep this for backward compatiblity +#define FX_MODE_CUSTOM_0 56 // custom modes need to go at the end +#define FX_MODE_CUSTOM_1 57 +#define FX_MODE_CUSTOM_2 58 +#define FX_MODE_CUSTOM_3 59 +#define FX_MODE_CUSTOM_4 60 +#define FX_MODE_CUSTOM_5 61 +#define FX_MODE_CUSTOM_6 62 +#define FX_MODE_CUSTOM_7 63 +#define FX_MODE_CUSTOM_8 64 +#define FX_MODE_CUSTOM_9 65 + +// create GLOBAL names to allow WS2812FX to compile with sketches and other libs that store strings +// in PROGMEM (get rid of the "section type conflict with __c" errors once and for all. Amen.) +const char name_0[] PROGMEM = "Static"; +const char name_1[] PROGMEM = "Blink"; +const char name_2[] PROGMEM = "Breath"; +const char name_3[] PROGMEM = "Color Wipe"; +const char name_4[] PROGMEM = "Color Wipe Inverse"; +const char name_5[] PROGMEM = "Color Wipe Reverse"; +const char name_6[] PROGMEM = "Color Wipe Reverse Inverse"; +const char name_7[] PROGMEM = "Color Wipe Random"; +const char name_8[] PROGMEM = "Random Color"; +const char name_9[] PROGMEM = "Single Dynamic"; +const char name_10[] PROGMEM = "Multi Dynamic"; +const char name_11[] PROGMEM = "Rainbow"; +const char name_12[] PROGMEM = "Rainbow Cycle"; +const char name_13[] PROGMEM = "Scan"; +const char name_14[] PROGMEM = "Dual Scan"; +const char name_15[] PROGMEM = "Fade"; +const char name_16[] PROGMEM = "Theater Chase"; +const char name_17[] PROGMEM = "Theater Chase Rainbow"; +const char name_18[] PROGMEM = "Running Lights"; +const char name_19[] PROGMEM = "Twinkle"; +const char name_20[] PROGMEM = "Twinkle Random"; +const char name_21[] PROGMEM = "Twinkle Fade"; +const char name_22[] PROGMEM = "Twinkle Fade Random"; +const char name_23[] PROGMEM = "Sparkle"; +const char name_24[] PROGMEM = "Flash Sparkle"; +const char name_25[] PROGMEM = "Hyper Sparkle"; +const char name_26[] PROGMEM = "Strobe"; +const char name_27[] PROGMEM = "Strobe Rainbow"; +const char name_28[] PROGMEM = "Multi Strobe"; +const char name_29[] PROGMEM = "Blink Rainbow"; +const char name_30[] PROGMEM = "Chase White"; +const char name_31[] PROGMEM = "Chase Color"; +const char name_32[] PROGMEM = "Chase Random"; +const char name_33[] PROGMEM = "Chase Rainbow"; +const char name_34[] PROGMEM = "Chase Flash"; +const char name_35[] PROGMEM = "Chase Flash Random"; +const char name_36[] PROGMEM = "Chase Rainbow White"; +const char name_37[] PROGMEM = "Chase Blackout"; +const char name_38[] PROGMEM = "Chase Blackout Rainbow"; +const char name_39[] PROGMEM = "Color Sweep Random"; +const char name_40[] PROGMEM = "Running Color"; +const char name_41[] PROGMEM = "Running Red Blue"; +const char name_42[] PROGMEM = "Running Random"; +const char name_43[] PROGMEM = "Larson Scanner"; +const char name_44[] PROGMEM = "Comet"; +const char name_45[] PROGMEM = "Fireworks"; +const char name_46[] PROGMEM = "Fireworks Random"; +const char name_47[] PROGMEM = "Merry Christmas"; +const char name_48[] PROGMEM = "Fire Flicker"; +const char name_49[] PROGMEM = "Fire Flicker (soft)"; +const char name_50[] PROGMEM = "Fire Flicker (intense)"; +const char name_51[] PROGMEM = "Circus Combustus"; +const char name_52[] PROGMEM = "Halloween"; +const char name_53[] PROGMEM = "Bicolor Chase"; +const char name_54[] PROGMEM = "Tricolor Chase"; +const char name_55[] PROGMEM = "ICU"; +const char name_56[] PROGMEM = "Custom 0"; // custom modes need to go at the end +const char name_57[] PROGMEM = "Custom 1"; +const char name_58[] PROGMEM = "Custom 2"; +const char name_59[] PROGMEM = "Custom 3"; +const char name_60[] PROGMEM = "Custom 4"; +const char name_61[] PROGMEM = "Custom 5"; +const char name_62[] PROGMEM = "Custom 6"; +const char name_63[] PROGMEM = "Custom 7"; +const char name_64[] PROGMEM = "Custom 8"; +const char name_65[] PROGMEM = "Custom 9"; + +static const __FlashStringHelper* _names[] = { + FSH(name_0), + FSH(name_1), + FSH(name_2), + FSH(name_3), + FSH(name_4), + FSH(name_5), + FSH(name_6), + FSH(name_7), + FSH(name_8), + FSH(name_9), + FSH(name_10), + FSH(name_11), + FSH(name_12), + FSH(name_13), + FSH(name_14), + FSH(name_15), + FSH(name_16), + FSH(name_17), + FSH(name_18), + FSH(name_19), + FSH(name_20), + FSH(name_21), + FSH(name_22), + FSH(name_23), + FSH(name_24), + FSH(name_25), + FSH(name_26), + FSH(name_27), + FSH(name_28), + FSH(name_29), + FSH(name_30), + FSH(name_31), + FSH(name_32), + FSH(name_33), + FSH(name_34), + FSH(name_35), + FSH(name_36), + FSH(name_37), + FSH(name_38), + FSH(name_39), + FSH(name_40), + FSH(name_41), + FSH(name_42), + FSH(name_43), + FSH(name_44), + FSH(name_45), + FSH(name_46), + FSH(name_47), + FSH(name_48), + FSH(name_49), + FSH(name_50), + FSH(name_51), + FSH(name_52), + FSH(name_53), + FSH(name_54), + FSH(name_55), + FSH(name_56), + FSH(name_57), + FSH(name_58), + FSH(name_59), + FSH(name_60), + FSH(name_61), + FSH(name_62), + FSH(name_63), + FSH(name_64), + FSH(name_65) +}; + +class WS2812FX : public Adafruit_NeoPixel { + + typedef uint16_t (WS2812FX::*mode_ptr)(void); + + // segment parameters + public: + typedef struct Segment { // 20 bytes + uint16_t start; + uint16_t stop; + uint16_t speed; + uint8_t mode; + uint8_t options; + uint32_t colors[NUM_COLORS]; + } segment; + + // segment runtime parameters + typedef struct Segment_runtime { // 16 bytes + unsigned long next_time; + uint32_t counter_mode_step; + uint32_t counter_mode_call; + uint8_t aux_param; // auxilary param (usually stores a color_wheel index) + uint8_t aux_param2; // auxilary param (usually stores bitwise options) + uint16_t aux_param3; // auxilary param (usually stores a segment index) + } segment_runtime; + + WS2812FX(uint16_t n, uint8_t p, neoPixelType t) : Adafruit_NeoPixel(n, p, t) { + _mode[FX_MODE_STATIC] = &WS2812FX::mode_static; + _mode[FX_MODE_BLINK] = &WS2812FX::mode_blink; + _mode[FX_MODE_COLOR_WIPE] = &WS2812FX::mode_color_wipe; + _mode[FX_MODE_COLOR_WIPE_INV] = &WS2812FX::mode_color_wipe_inv; + _mode[FX_MODE_COLOR_WIPE_REV] = &WS2812FX::mode_color_wipe_rev; + _mode[FX_MODE_COLOR_WIPE_REV_INV] = &WS2812FX::mode_color_wipe_rev_inv; + _mode[FX_MODE_COLOR_WIPE_RANDOM] = &WS2812FX::mode_color_wipe_random; + _mode[FX_MODE_RANDOM_COLOR] = &WS2812FX::mode_random_color; + _mode[FX_MODE_SINGLE_DYNAMIC] = &WS2812FX::mode_single_dynamic; + _mode[FX_MODE_MULTI_DYNAMIC] = &WS2812FX::mode_multi_dynamic; + _mode[FX_MODE_RAINBOW] = &WS2812FX::mode_rainbow; + _mode[FX_MODE_RAINBOW_CYCLE] = &WS2812FX::mode_rainbow_cycle; + _mode[FX_MODE_SCAN] = &WS2812FX::mode_scan; + _mode[FX_MODE_DUAL_SCAN] = &WS2812FX::mode_dual_scan; + _mode[FX_MODE_FADE] = &WS2812FX::mode_fade; + _mode[FX_MODE_THEATER_CHASE] = &WS2812FX::mode_theater_chase; + _mode[FX_MODE_THEATER_CHASE_RAINBOW] = &WS2812FX::mode_theater_chase_rainbow; + _mode[FX_MODE_TWINKLE] = &WS2812FX::mode_twinkle; + _mode[FX_MODE_TWINKLE_RANDOM] = &WS2812FX::mode_twinkle_random; + _mode[FX_MODE_TWINKLE_FADE] = &WS2812FX::mode_twinkle_fade; + _mode[FX_MODE_TWINKLE_FADE_RANDOM] = &WS2812FX::mode_twinkle_fade_random; + _mode[FX_MODE_SPARKLE] = &WS2812FX::mode_sparkle; + _mode[FX_MODE_FLASH_SPARKLE] = &WS2812FX::mode_flash_sparkle; + _mode[FX_MODE_HYPER_SPARKLE] = &WS2812FX::mode_hyper_sparkle; + _mode[FX_MODE_STROBE] = &WS2812FX::mode_strobe; + _mode[FX_MODE_STROBE_RAINBOW] = &WS2812FX::mode_strobe_rainbow; + _mode[FX_MODE_MULTI_STROBE] = &WS2812FX::mode_multi_strobe; + _mode[FX_MODE_BLINK_RAINBOW] = &WS2812FX::mode_blink_rainbow; + _mode[FX_MODE_CHASE_WHITE] = &WS2812FX::mode_chase_white; + _mode[FX_MODE_CHASE_COLOR] = &WS2812FX::mode_chase_color; + _mode[FX_MODE_CHASE_RANDOM] = &WS2812FX::mode_chase_random; + _mode[FX_MODE_CHASE_RAINBOW] = &WS2812FX::mode_chase_rainbow; + _mode[FX_MODE_CHASE_FLASH] = &WS2812FX::mode_chase_flash; + _mode[FX_MODE_CHASE_FLASH_RANDOM] = &WS2812FX::mode_chase_flash_random; + _mode[FX_MODE_CHASE_RAINBOW_WHITE] = &WS2812FX::mode_chase_rainbow_white; + _mode[FX_MODE_CHASE_BLACKOUT] = &WS2812FX::mode_chase_blackout; + _mode[FX_MODE_CHASE_BLACKOUT_RAINBOW] = &WS2812FX::mode_chase_blackout_rainbow; + _mode[FX_MODE_COLOR_SWEEP_RANDOM] = &WS2812FX::mode_color_sweep_random; + _mode[FX_MODE_RUNNING_COLOR] = &WS2812FX::mode_running_color; + _mode[FX_MODE_RUNNING_RED_BLUE] = &WS2812FX::mode_running_red_blue; + _mode[FX_MODE_RUNNING_RANDOM] = &WS2812FX::mode_running_random; + _mode[FX_MODE_LARSON_SCANNER] = &WS2812FX::mode_larson_scanner; + _mode[FX_MODE_COMET] = &WS2812FX::mode_comet; + _mode[FX_MODE_FIREWORKS] = &WS2812FX::mode_fireworks; + _mode[FX_MODE_FIREWORKS_RANDOM] = &WS2812FX::mode_fireworks_random; + _mode[FX_MODE_MERRY_CHRISTMAS] = &WS2812FX::mode_merry_christmas; + _mode[FX_MODE_FIRE_FLICKER] = &WS2812FX::mode_fire_flicker; + _mode[FX_MODE_FIRE_FLICKER_SOFT] = &WS2812FX::mode_fire_flicker_soft; + _mode[FX_MODE_FIRE_FLICKER_INTENSE] = &WS2812FX::mode_fire_flicker_intense; + _mode[FX_MODE_CIRCUS_COMBUSTUS] = &WS2812FX::mode_circus_combustus; + _mode[FX_MODE_HALLOWEEN] = &WS2812FX::mode_halloween; + _mode[FX_MODE_BICOLOR_CHASE] = &WS2812FX::mode_bicolor_chase; + _mode[FX_MODE_TRICOLOR_CHASE] = &WS2812FX::mode_tricolor_chase; +// if flash memory is constrained (I'm looking at you Arduino Nano), replace modes +// that use a lot of flash with mode_static (reduces flash footprint by about 2100 bytes) +#ifdef REDUCED_MODES + _mode[FX_MODE_BREATH] = &WS2812FX::mode_static; + _mode[FX_MODE_RUNNING_LIGHTS] = &WS2812FX::mode_static; + _mode[FX_MODE_ICU] = &WS2812FX::mode_static; +#else + _mode[FX_MODE_BREATH] = &WS2812FX::mode_breath; + _mode[FX_MODE_RUNNING_LIGHTS] = &WS2812FX::mode_running_lights; + _mode[FX_MODE_ICU] = &WS2812FX::mode_icu; +#endif + _mode[FX_MODE_CUSTOM_0] = &WS2812FX::mode_custom_0; + _mode[FX_MODE_CUSTOM_1] = &WS2812FX::mode_custom_1; + _mode[FX_MODE_CUSTOM_2] = &WS2812FX::mode_custom_2; + _mode[FX_MODE_CUSTOM_3] = &WS2812FX::mode_custom_3; + _mode[FX_MODE_CUSTOM_4] = &WS2812FX::mode_custom_4; + _mode[FX_MODE_CUSTOM_5] = &WS2812FX::mode_custom_5; + _mode[FX_MODE_CUSTOM_6] = &WS2812FX::mode_custom_6; + _mode[FX_MODE_CUSTOM_7] = &WS2812FX::mode_custom_7; + _mode[FX_MODE_CUSTOM_8] = &WS2812FX::mode_custom_8; + _mode[FX_MODE_CUSTOM_9] = &WS2812FX::mode_custom_9; + + brightness = DEFAULT_BRIGHTNESS + 1; // Adafruit_NeoPixel internally offsets brightness by 1 + _running = false; + _num_segments = 1; + _segments[0].mode = DEFAULT_MODE; + _segments[0].colors[0] = DEFAULT_COLOR; + _segments[0].start = 0; + _segments[0].stop = n - 1; + _segments[0].speed = DEFAULT_SPEED; + resetSegmentRuntimes(); + } + + void +// timer(void), + init(void), + service(void), + start(void), + stop(void), + pause(void), + resume(void), + strip_off(void), + fade_out(void), + fade_out(uint32_t), + setMode(uint8_t m), + setMode(uint8_t seg, uint8_t m), + setOptions(uint8_t seg, uint8_t o), + setCustomMode(uint16_t (*p)()), + setCustomShow(void (*p)()), + setSpeed(uint16_t s), + setSpeed(uint8_t seg, uint16_t s), + increaseSpeed(uint8_t s), + decreaseSpeed(uint8_t s), + setColor(uint8_t r, uint8_t g, uint8_t b), + setColor(uint8_t r, uint8_t g, uint8_t b, uint8_t w), + setColor(uint32_t c), + setColor(uint8_t seg, uint32_t c), + setColors(uint8_t seg, uint32_t* c), + setBrightness(uint8_t b), + increaseBrightness(uint8_t s), + decreaseBrightness(uint8_t s), + setLength(uint16_t b), + increaseLength(uint16_t s), + decreaseLength(uint16_t s), + trigger(void), + setNumSegments(uint8_t n), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, bool reverse), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, uint8_t options), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, bool reverse), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, uint8_t options), + resetSegments(), + resetSegmentRuntimes(), + resetSegmentRuntime(uint8_t), + setPixelColor(uint16_t n, uint32_t c), + setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b), + setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w), + copyPixels(uint16_t d, uint16_t s, uint16_t c), + show(void); + + boolean + isRunning(void), + isTriggered(void), + isFrame(void), + isFrame(uint8_t), + isCycle(void), + isCycle(uint8_t); + + uint8_t + random8(void), + random8(uint8_t), + getMode(void), + getMode(uint8_t), + getModeCount(void), + setCustomMode(const __FlashStringHelper* name, uint16_t (*p)()), + setCustomMode(uint8_t i, const __FlashStringHelper* name, uint16_t (*p)()), + getNumSegments(void), + get_random_wheel_index(uint8_t), + getOptions(uint8_t), + getNumBytesPerPixel(void), + getSegmentIndex(void); + + uint16_t + random16(void), + random16(uint16_t), + getSpeed(void), + getSpeed(uint8_t), + getLength(void), + getNumBytes(void); + + uint32_t + color_wheel(uint8_t), + getColor(void), + getColor(uint8_t), + intensitySum(void); + + uint32_t* getColors(uint8_t); + uint32_t* intensitySums(void); + + const __FlashStringHelper* getModeName(uint8_t m); + + WS2812FX::Segment* getSegment(void); + + WS2812FX::Segment* getSegment(uint8_t); + + WS2812FX::Segment* getSegments(void); + + WS2812FX::Segment_runtime* getSegmentRuntime(void); + + WS2812FX::Segment_runtime* getSegmentRuntime(uint8_t); + + WS2812FX::Segment_runtime* getSegmentRuntimes(void); + + // mode helper functions + uint16_t + blink(uint32_t, uint32_t, bool strobe), + color_wipe(uint32_t, uint32_t, bool), + twinkle(uint32_t, uint32_t), + twinkle_fade(uint32_t), + chase(uint32_t, uint32_t, uint32_t), + running(uint32_t, uint32_t), + fireworks(uint32_t), + fire_flicker(int), + tricolor_chase(uint32_t, uint32_t, uint32_t), + scan(uint32_t, uint32_t, bool); + uint32_t + color_blend(uint32_t, uint32_t, uint8_t); + + // builtin modes + uint16_t + mode_static(void), + mode_blink(void), + mode_blink_rainbow(void), + mode_strobe(void), + mode_strobe_rainbow(void), + mode_color_wipe(void), + mode_color_wipe_inv(void), + mode_color_wipe_rev(void), + mode_color_wipe_rev_inv(void), + mode_color_wipe_random(void), + mode_color_sweep_random(void), + mode_random_color(void), + mode_single_dynamic(void), + mode_multi_dynamic(void), + mode_breath(void), + mode_fade(void), + mode_scan(void), + mode_dual_scan(void), + mode_theater_chase(void), + mode_theater_chase_rainbow(void), + mode_rainbow(void), + mode_rainbow_cycle(void), + mode_running_lights(void), + mode_twinkle(void), + mode_twinkle_random(void), + mode_twinkle_fade(void), + mode_twinkle_fade_random(void), + mode_sparkle(void), + mode_flash_sparkle(void), + mode_hyper_sparkle(void), + mode_multi_strobe(void), + mode_chase_white(void), + mode_chase_color(void), + mode_chase_random(void), + mode_chase_rainbow(void), + mode_chase_flash(void), + mode_chase_flash_random(void), + mode_chase_rainbow_white(void), + mode_chase_blackout(void), + mode_chase_blackout_rainbow(void), + mode_running_color(void), + mode_running_red_blue(void), + mode_running_random(void), + mode_larson_scanner(void), + mode_comet(void), + mode_fireworks(void), + mode_fireworks_random(void), + mode_merry_christmas(void), + mode_halloween(void), + mode_fire_flicker(void), + mode_fire_flicker_soft(void), + mode_fire_flicker_intense(void), + mode_circus_combustus(void), + mode_bicolor_chase(void), + mode_tricolor_chase(void), + mode_icu(void), + mode_custom_0(void), + mode_custom_1(void), + mode_custom_2(void), + mode_custom_3(void), + mode_custom_4(void), + mode_custom_5(void), + mode_custom_6(void), + mode_custom_7(void), + mode_custom_8(void), + mode_custom_9(void); + private: + uint16_t _rand16seed; + uint16_t (*customModes[MAX_CUSTOM_MODES])(void) { + []{ return (uint16_t)1000; }, + []{ return (uint16_t)1000; }, + []{ return (uint16_t)1000; }, + []{ return (uint16_t)1000; }, + []{ return (uint16_t)1000; }, + []{ return (uint16_t)1000; }, + []{ return (uint16_t)1000; }, + []{ return (uint16_t)1000; }, + []{ return (uint16_t)1000; }, + []{ return (uint16_t)1000; } + }; + void (*customShow)(void) = NULL; + + boolean + _running, + _triggered; + + mode_ptr _mode[MODE_COUNT]; // SRAM footprint: 4 bytes per element + + uint8_t _segment_index = 0; + uint8_t _num_segments = 1; + segment _segments[MAX_NUM_SEGMENTS] = { // SRAM footprint: 20 bytes per element + // start, stop, speed, mode, options, color[] + { 0, 7, DEFAULT_SPEED, FX_MODE_STATIC, NO_OPTIONS, {DEFAULT_COLOR}} + }; + segment_runtime _segment_runtimes[MAX_NUM_SEGMENTS]; // SRAM footprint: 16 bytes per element +}; + +#endif diff --git a/Arduino/McLighting/data/edit.htm b/Arduino/McLighting/data/edit.htm index c8e8727..728b705 100644 --- a/Arduino/McLighting/data/edit.htm +++ b/Arduino/McLighting/data/edit.htm @@ -182,7 +182,9 @@ var mkfile = document.createElement("button"); mkfile.innerHTML = 'MkFile'; document.getElementById(element).appendChild(mkfile); - + var para = document.createElement("text"); + para.innerHTML = " Press Ctrl-S for save, Ctrl-Z for undo, Ctrl-Shift-Z for redo"; + document.getElementById(element).appendChild(para); function httpPostProcessRequest(){ if (xmlHttp.readyState == 4){ if(xmlHttp.status != 200) alert("ERROR["+xmlHttp.status+"]: "+xmlHttp.responseText); diff --git a/Arduino/McLighting/data/edit.htm.gz b/Arduino/McLighting/data/edit.htm.gz index 69ce414..03cc700 100644 Binary files a/Arduino/McLighting/data/edit.htm.gz and b/Arduino/McLighting/data/edit.htm.gz differ diff --git a/Arduino/McLighting/data/favicon.ico.gz b/Arduino/McLighting/data/favicon.ico.gz new file mode 100644 index 0000000..9e0ba66 Binary files /dev/null and b/Arduino/McLighting/data/favicon.ico.gz differ diff --git a/Arduino/McLighting/data/index.htm b/Arduino/McLighting/data/index.htm index 52eda48..ca9a9a6 100644 --- a/Arduino/McLighting/data/index.htm +++ b/Arduino/McLighting/data/index.htm @@ -5,10 +5,40 @@ - - - + + + + + + + + + + + +
+
+
+ + + + diff --git a/clients/web/edit.htm.gz b/clients/web/edit.htm.gz new file mode 100644 index 0000000..03cc700 Binary files /dev/null and b/clients/web/edit.htm.gz differ diff --git a/clients/web/favicon.ico b/clients/web/favicon.ico new file mode 100644 index 0000000..1c55bea Binary files /dev/null and b/clients/web/favicon.ico differ diff --git a/clients/web/favicon.ico.gz b/clients/web/favicon.ico.gz new file mode 100644 index 0000000..9e0ba66 Binary files /dev/null and b/clients/web/favicon.ico.gz differ diff --git a/clients/web/graphs.js.gz b/clients/web/graphs.js.gz new file mode 100644 index 0000000..7243544 Binary files /dev/null and b/clients/web/graphs.js.gz differ diff --git a/clients/web/index.htm b/clients/web/index.htm new file mode 100644 index 0000000..b56bd4c --- /dev/null +++ b/clients/web/index.htm @@ -0,0 +1,2696 @@ + + + + McLighting + + + + + + + + + + + + + + + +
+ + + + + diff --git a/clients/web/index.htm.gz b/clients/web/index.htm.gz new file mode 100644 index 0000000..c85a898 Binary files /dev/null and b/clients/web/index.htm.gz differ diff --git a/clients/web/material.woff2 b/clients/web/material.woff2 new file mode 100644 index 0000000..d9c6038 Binary files /dev/null and b/clients/web/material.woff2 differ diff --git a/platformio.ini b/platformio.ini index 8d2cfec..e986027 100644 --- a/platformio.ini +++ b/platformio.ini @@ -46,7 +46,7 @@ arduino_core_stage = https://github.com/platformio/platform-espressif8266.git#fe framework = arduino ;platform = ${common.arduino_core_2_4_2} -platform = ${common.arduino_core_2_5_2} +platform = ${common.arduino_core_2_6_3} build_flags = -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -DMQTT_MAX_PACKET_SIZE=512 ; PubSubClient Specific flags