diff --git a/README.md b/README.md index 564012b81..a96cbcd18 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## Sonoff-Tasmota Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE. -Current version is **5.7.1g** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. +Current version is **5.7.1h** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/development/sonoff/_releasenotes.ino) for change information. ### ATTENTION All versions diff --git a/sonoff/_releasenotes.ino b/sonoff/_releasenotes.ino index 9fd82ac3f..643b76097 100644 --- a/sonoff/_releasenotes.ino +++ b/sonoff/_releasenotes.ino @@ -1,4 +1,9 @@ -/* 5.7.1g +/* 5.7.1h + * Consolidate WS2812 (xdrv_ws2812) into Sonoff Led (xdrv_snfled) + * Invert WS2812 fade speed to align with Sonoff led (Speed 1 = fast, Speed 8 = slow) + * Remove upper case MQTT receive buffer + * + * 5.7.1g * Add option WIFI_WAIT (5) to command WifiConfig to allow connection retry to same AP without restart or update flash (#772, #869) * * 5.7.1f diff --git a/sonoff/settings.ino b/sonoff/settings.ino index effd79812..bc866bc18 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -500,6 +500,9 @@ void CFG_DefaultSet2() // 5.4.1 memcpy_P(sysCfg.sfb_code[0], sfb_codeDefault, 9); + // 5.7.1g + sysCfg.led_pixels = WS2812_LEDS; + } /********************************************************************************************/ @@ -533,7 +536,7 @@ void CFG_DefaultSet_3_9_3() sysCfg.my_module.gp.io[i] = 0; } - sysCfg.led_pixels = 0; + sysCfg.led_pixels = WS2812_LEDS; for (byte i = 0; i < 5; i++) { sysCfg.led_color[i] = 255; } @@ -542,9 +545,9 @@ void CFG_DefaultSet_3_9_3() sysCfg.led_dimmer[i] = 10; } sysCfg.led_fade = 0; - sysCfg.led_speed = 0; + sysCfg.led_speed = 1; sysCfg.led_scheme = 0; - sysCfg.led_width = 0; + sysCfg.led_width = 1; sysCfg.led_wakeup = 0; } @@ -708,6 +711,30 @@ void CFG_Delta() } memcpy_P(sysCfg.sfb_code[0], sfb_codeDefault, 9); } + if (sysCfg.version < 0x05070108) { + uint8_t cfg_wsflg = 0; + for (byte i = 0; i < MAX_GPIO_PIN; i++) { + if (GPIO_WS2812 == sysCfg.my_module.gp.io[i]) { + cfg_wsflg = 1; + } + } + if (!sysCfg.led_pixels && cfg_wsflg) { + sysCfg.led_pixels = sysCfg.ws_pixels; + sysCfg.led_color[0] = sysCfg.ws_red; + sysCfg.led_color[1] = sysCfg.ws_green; + sysCfg.led_color[2] = sysCfg.ws_blue; + sysCfg.led_dimmer[0] = sysCfg.ws_dimmer; + sysCfg.led_table = sysCfg.ws_ledtable; + sysCfg.led_fade = sysCfg.ws_fade; + sysCfg.led_speed = sysCfg.ws_speed; + sysCfg.led_scheme = sysCfg.ws_scheme; + sysCfg.led_width = sysCfg.ws_width; + sysCfg.led_wakeup = sysCfg.ws_wakeup; + } else { + sysCfg.led_pixels = WS2812_LEDS; + sysCfg.led_width = 1; + } + } sysCfg.version = VERSION; CFG_Save(1); diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index ace4d09b7..da6ef36df 100644 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -25,7 +25,7 @@ - Select IDE Tools - Flash Size: "1M (no SPIFFS)" ====================================================*/ -#define VERSION 0x05070107 // 5.7.1g +#define VERSION 0x05070108 // 5.7.1h enum log_t {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE, LOG_LEVEL_ALL}; enum week_t {Last, First, Second, Third, Fourth}; @@ -419,7 +419,8 @@ void setRelay(uint8_t rpower) Serial.flush(); } else if (sfl_flg) { - sl_setPower(rpower &1); +// sl_setPower(rpower &1); + sl_setPower(rpower); } else if (EXS_RELAY == sysCfg.module) { setLatchingRelay(rpower, 1); @@ -875,7 +876,6 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) char topicBuf[TOPSZ]; char dataBuf[data_len+1]; - char dataBufUc[128]; char stemp1[TOPSZ]; char *p; char *mtopic = NULL; @@ -930,12 +930,8 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) type[i] = '\0'; } - for (i = 0; i <= sizeof(dataBufUc); i++) { - dataBufUc[i] = toupper(dataBuf[i]); - } - - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_RESULT D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " %s, " D_DATA " %s (%s)"), - grpflg, index, type, dataBuf, dataBufUc); + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_RESULT D_GROUP " %d, " D_INDEX " %d, " D_COMMAND " %s, " D_DATA " %s"), + grpflg, index, type, dataBuf); addLog(LOG_LEVEL_DEBUG); if (type != NULL) { @@ -944,7 +940,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) blinks++; } - if (!strcmp(dataBufUc,"?")) { + if (!strcmp(dataBuf,"?")) { data_len = 0; } int16_t payload = -99; // No payload @@ -1060,7 +1056,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) } snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_BLINKCOUNT "\":%d}"), sysCfg.blinkcount); } - else if (sfl_flg && sl_command(type, index, dataBufUc, data_len, payload)) { + else if (sfl_flg && sl_command(type, index, dataBuf, data_len, payload)) { // Serviced } else if (!strcasecmp_P(type, PSTR(D_CMND_SAVEDATA))) { @@ -1552,13 +1548,8 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len) i2c_scan(mqtt_data, sizeof(mqtt_data)); } #endif // USE_I2C -#ifdef USE_WS2812 - else if ((pin[GPIO_WS2812] < 99) && ws2812_command(type, index, dataBuf, data_len, payload)) { - // Serviced - } -#endif // USE_WS2812 #ifdef USE_IR_REMOTE - else if ((pin[GPIO_IRSEND] < 99) && ir_send_command(type, index, dataBufUc, data_len, payload)) { + else if ((pin[GPIO_IRSEND] < 99) && ir_send_command(type, index, dataBuf, data_len, payload)) { // Serviced } #endif // USE_IR_REMOTE @@ -2330,12 +2321,6 @@ void stateloop() sl_animate(); } -#ifdef USE_WS2812 - if (pin[GPIO_WS2812] < 99) { - ws2812_animate(); - } -#endif // USE_WS2812 - /*-------------------------------------------------------------------------------------------*\ * Every 0.2 second \*-------------------------------------------------------------------------------------------*/ @@ -2536,6 +2521,10 @@ void serial() } } +/*-------------------------------------------------------------------------------------------*\ + * Sonoff SC 19200 baud serial interface +\*-------------------------------------------------------------------------------------------*/ + if (SerialInByte == '\x1B') { // Sonoff SC status from ATMEGA328P serialInBuf[SerialInByteCounter] = 0; // serial data completed sc_rcvstat(serialInBuf); @@ -2543,6 +2532,9 @@ void serial() Serial.flush(); return; } + +/*-------------------------------------------------------------------------------------------*/ + else if (SerialInByte == '\n') { serialInBuf[SerialInByteCounter] = 0; // serial data completed seriallog_level = (sysCfg.seriallog_level < LOG_LEVEL_INFO) ? LOG_LEVEL_INFO : sysCfg.seriallog_level; @@ -2674,8 +2666,14 @@ void GPIO_init() } } - if (sfl_flg) { // Sonoff B1, AiLight, Sonoff Led or BN-SZ01 - if (sfl_flg < 4) { +#ifdef USE_WS2812 + if (!sfl_flg && (pin[GPIO_WS2812] < 99)) { + Maxdevice++; + sfl_flg = 3; + } +#endif // USE_WS2812 + if (sfl_flg) { // Sonoff B1, AiLight, Sonoff Led or BN-SZ01, WS2812 + if (sfl_flg < 3) { pwm_idxoffset = sfl_flg; // 1 for BN-SZ01, 2 for Sonoff Led } sl_init(); @@ -2694,13 +2692,6 @@ void GPIO_init() } setLed(sysCfg.ledstate &8); -#ifdef USE_WS2812 - if (pin[GPIO_WS2812] < 99) { - Maxdevice++; - ws2812_init(Maxdevice); - } -#endif // USE_WS2812 - #ifdef USE_IR_REMOTE if (pin[GPIO_IRSEND] < 99) { ir_send_init(); diff --git a/sonoff/user_config.h b/sonoff/user_config.h index 56beac40d..d614e7593 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -169,7 +169,7 @@ #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+3k code, 0.3k mem) // #define USE_IR_HVAC // Support for HVAC system using IR (+2k code) -#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+11k code, +1k mem) - Disable by // +#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem) - Disable by // #define USE_WS2812_CTYPE 1 // WS2812 Color type (0 - RGB, 1 - GRB) // #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem) // When USE_WS2812_DMA is enabled expect Exceptions on Pow diff --git a/sonoff/xdrv_ir_send.ino b/sonoff/xdrv_ir_send.ino index b221c55af..ff89de9d9 100644 --- a/sonoff/xdrv_ir_send.ino +++ b/sonoff/xdrv_ir_send.ino @@ -68,7 +68,7 @@ void ir_send_init(void) { "Vendor": "", "Power": <0|1>, "Mode": "", "FanSpeed": "<1|2|3|4|5|Auto|Silence>", "Temp": <17..30> } */ -boolean ir_send_command(char *type, uint16_t index, char *dataBufUc, uint16_t data_len, int16_t payload) +boolean ir_send_command(char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload) { boolean serviced = true; boolean error = false; @@ -85,7 +85,7 @@ boolean ir_send_command(char *type, uint16_t index, char *dataBufUc, uint16_t da if (!strcasecmp_P(type, PSTR(D_CMND_IRSEND))) { if (data_len) { StaticJsonBuffer<128> jsonBuf; - JsonObject &ir_json = jsonBuf.parseObject(dataBufUc); + JsonObject &ir_json = jsonBuf.parseObject(dataBuf); if (!ir_json.success()) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_IRSEND "\":\"" D_INVALID_JSON "\"}")); // JSON decode failed } else { @@ -94,13 +94,13 @@ boolean ir_send_command(char *type, uint16_t index, char *dataBufUc, uint16_t da bits = ir_json[D_IRSEND_BITS]; data = ir_json[D_IRSEND_DATA]; if (protocol && bits && data) { - if (!strcmp_P(protocol,PSTR("NEC"))) irsend->sendNEC(data, bits); - else if (!strcmp_P(protocol,PSTR("SONY"))) irsend->sendSony(data, bits); - else if (!strcmp_P(protocol,PSTR("RC5"))) irsend->sendRC5(data, bits); - else if (!strcmp_P(protocol,PSTR("RC6"))) irsend->sendRC6(data, bits); - else if (!strcmp_P(protocol,PSTR("DISH"))) irsend->sendDISH(data, bits); - else if (!strcmp_P(protocol,PSTR("JVC"))) irsend->sendJVC(data, bits, 1); - else if (!strcmp_P(protocol,PSTR("SAMSUNG"))) irsend->sendSAMSUNG(data, bits); + if (!strcasecmp_P(protocol, PSTR("NEC"))) irsend->sendNEC(data, bits); + else if (!strcasecmp_P(protocol, PSTR("SONY"))) irsend->sendSony(data, bits); + else if (!strcasecmp_P(protocol, PSTR("RC5"))) irsend->sendRC5(data, bits); + else if (!strcasecmp_P(protocol, PSTR("RC6"))) irsend->sendRC6(data, bits); + else if (!strcasecmp_P(protocol, PSTR("DISH"))) irsend->sendDISH(data, bits); + else if (!strcasecmp_P(protocol, PSTR("JVC"))) irsend->sendJVC(data, bits, 1); + else if (!strcasecmp_P(protocol, PSTR("SAMSUNG"))) irsend->sendSAMSUNG(data, bits); else { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_IRSEND "\":\"" D_PROTOCOL_NOT_SUPPORTED "\"}")); } @@ -115,7 +115,7 @@ boolean ir_send_command(char *type, uint16_t index, char *dataBufUc, uint16_t da else if (!strcasecmp_P(type, PSTR(D_CMND_IRHVAC))) { if (data_len) { StaticJsonBuffer<164> jsonBufer; - JsonObject &root = jsonBufer.parseObject(dataBufUc); + JsonObject &root = jsonBufer.parseObject(dataBuf); if (!root.success()) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_IRHVAC "\":\"" D_INVALID_JSON "\"}")); // JSON decode failed } else { @@ -130,10 +130,10 @@ boolean ir_send_command(char *type, uint16_t index, char *dataBufUc, uint16_t da // HVAC_Vendor, HVAC_Power, HVAC_Mode, HVAC_FanMode, HVAC_Temp); // addLog(LOG_LEVEL_DEBUG); - if (HVAC_Vendor == NULL || !strcmp_P(HVAC_Vendor,PSTR("TOSHIBA"))) { + if (HVAC_Vendor == NULL || !strcasecmp_P(HVAC_Vendor, PSTR("TOSHIBA"))) { error = ir_hvac_toshiba(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); } - else if (!strcmp_P(HVAC_Vendor,PSTR("MITSUBISHI"))) { + else if (!strcasecmp_P(HVAC_Vendor, PSTR("MITSUBISHI"))) { error = ir_hvac_mitsubishi(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); } else error = true; @@ -163,7 +163,7 @@ boolean ir_hvac_toshiba(const char *HVAC_Mode, const char *HVAC_FanMode, boolean if (HVAC_Mode == NULL) { p = (char*)HVACMODE; // default HVAC_HOT } else { - p = strchr(HVACMODE, HVAC_Mode[0]); + p = strchr(HVACMODE, toupper(HVAC_Mode[0])); } if (!p) { return true; @@ -177,7 +177,7 @@ boolean ir_hvac_toshiba(const char *HVAC_Mode, const char *HVAC_FanMode, boolean if (HVAC_FanMode == NULL) { p = (char*)FANSPEED; // default FAN_SPEED_AUTO } else { - p = strchr(FANSPEED, HVAC_FanMode[0]); + p = strchr(FANSPEED, toupper(HVAC_FanMode[0])); } if (!p) { return true; @@ -250,7 +250,7 @@ boolean ir_hvac_mitsubishi(const char *HVAC_Mode,const char *HVAC_FanMode, boole if (HVAC_Mode == NULL) { p = (char*)HVACMODE; // default HVAC_HOT } else { - p = strchr(HVACMODE, HVAC_Mode[0]); + p = strchr(HVACMODE, toupper(HVAC_Mode[0])); } if (!p) { return true; @@ -263,7 +263,7 @@ boolean ir_hvac_mitsubishi(const char *HVAC_Mode,const char *HVAC_FanMode, boole if (HVAC_FanMode == NULL) { p = (char*)FANSPEED; // default FAN_SPEED_AUTO } else { - p = strchr(FANSPEED, HVAC_FanMode[0]); + p = strchr(FANSPEED, toupper(HVAC_FanMode[0])); } if (!p) { return true; diff --git a/sonoff/xdrv_snfled.ino b/sonoff/xdrv_snfled.ino index a7db3934e..70486a589 100644 --- a/sonoff/xdrv_snfled.ino +++ b/sonoff/xdrv_snfled.ino @@ -1,5 +1,5 @@ /* - xdrv_snfled.ino - sonoff led support for Sonoff-Tasmota + xdrv_snfled.ino - WS2812 and sonoff led support for Sonoff-Tasmota Copyright (C) 2017 Theo Arends @@ -18,44 +18,346 @@ */ /*********************************************************************************************\ - * Sonoff B1, AiLight, Sonoff Led and BN-SZ01 + * WS2812, Sonoff B1, AiLight, Sonoff Led and BN-SZ01 * * sfl_flg Module Color ColorTemp * 1 Sonoff BN-SZ W no * 2 Sonoff Led CW yes - * 3 not used + * 3 +WS2812 RGB no * 4 AiLight RGBW no * 5 Sonoff B1 RGBCW yes + * + * led_scheme WS2812 Others Effect + * 0 yes yes Color On/Off + * 1 yes yes Wakeup light + * 2 yes no Clock + * 3 yes no Incandescent + * 4 yes no RGB + * 5 yes no Christmas + * 6 yes no Hanukkah + * 7 yes no Kwanzaa + * 8 yes no Rainbow + * 9 yes no Fire + * \*********************************************************************************************/ uint8_t ledTable[] = { - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, - 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, - 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, - 14, 15, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, - 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, - 33, 33, 34, 35, 36, 36, 37, 38, 39, 40, 40, 41, 42, 43, 44, 45, - 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, - 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, - 80, 81, 82, 83, 85, 86, 87, 89, 90, 91, 93, 94, 95, 97, 98, 99, - 101,102,104,105,107,108,110,111,113,114,116,117,119,121,122,124, - 125,127,129,130,132,134,135,137,139,141,142,144,146,148,150,151, - 153,155,157,159,161,163,165,166,168,170,172,174,176,178,180,182, - 184,186,189,191,193,195,197,199,201,204,206,208,210,212,215,217, - 219,221,224,226,228,231,233,235,238,240,243,245,248,250,253,255 }; + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, + 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, + 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, + 14, 15, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 22, + 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, + 33, 33, 34, 35, 36, 36, 37, 38, 39, 40, 40, 41, 42, 43, 44, 45, + 46, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, + 80, 81, 82, 83, 85, 86, 87, 89, 90, 91, 93, 94, 95, 97, 98, 99, +101,102,104,105,107,108,110,111,113,114,116,117,119,121,122,124, +125,127,129,130,132,134,135,137,139,141,142,144,146,148,150,151, +153,155,157,159,161,163,165,166,168,170,172,174,176,178,180,182, +184,186,189,191,193,195,197,199,201,204,206,208,210,212,215,217, +219,221,224,226,228,231,233,235,238,240,243,245,248,250,253,255 }; uint8_t sl_dcolor[5]; uint8_t sl_tcolor[5]; uint8_t sl_lcolor[5]; -uint8_t sl_power; -uint8_t sl_any; +uint8_t sl_any = 0; uint8_t sl_wakeupActive = 0; uint8_t sl_wakeupDimmer = 0; uint16_t sl_wakeupCntr = 0; +unsigned long stripTimerCntr = 0; // Bars and Gradient + +#ifdef USE_WS2812 +/*********************************************************************************************\ + * WS2812 Leds using NeopixelBus library +\*********************************************************************************************/ + +#ifdef USE_WS2812_DMA +#if (USE_WS2812_CTYPE == 1) + NeoPixelBus *strip = NULL; +#else // USE_WS2812_CTYPE + NeoPixelBus *strip = NULL; +#endif // USE_WS2812_CTYPE +#else // USE_WS2812_DMA +#if (USE_WS2812_CTYPE == 1) + NeoPixelBus *strip = NULL; +#else // USE_WS2812_CTYPE + NeoPixelBus *strip = NULL; +#endif // USE_WS2812_CTYPE +#endif // USE_WS2812_DMA + +struct wsColor { + uint8_t red, green, blue; +}; + +struct ColorScheme { + wsColor* colors; + uint8_t count; +}; + +wsColor incandescent[2] = { 255, 140, 20, 0, 0, 0 }; +wsColor rgb[3] = { 255, 0, 0, 0, 255, 0, 0, 0, 255 }; +wsColor christmas[2] = { 255, 0, 0, 0, 255, 0 }; +wsColor hanukkah[2] = { 0, 0, 255, 255, 255, 255 }; +wsColor kwanzaa[3] = { 255, 0, 0, 0, 0, 0, 0, 255, 0 }; +wsColor rainbow[7] = { 255, 0, 0, 255, 128, 0, 255, 255, 0, 0, 255, 0, 0, 0, 255, 128, 0, 255, 255, 0, 255 }; +wsColor fire[3] = { 255, 0, 0, 255, 102, 0, 255, 192, 0 }; +ColorScheme schemes[7] = { + incandescent, 2, + rgb, 3, + christmas, 2, + hanukkah, 2, + kwanzaa, 3, + rainbow, 7, + fire, 3 }; + +uint8_t widthValues[5] = { + 1, // Small + 2, // Medium + 4, // Large + 8, // Largest + 255 }; // All +uint8_t repeatValues[5] = { + 8, // Small + 6, // Medium + 4, // Large + 2, // Largest + 1 }; // All + +uint8_t speedValues[9] = { + 0, // None + 1 * (STATES / 10), // Fastest + 3 * (STATES / 10), + 5 * (STATES / 10), // Fast + 7 * (STATES / 10), + 9 * (STATES / 10), + 11 * (STATES / 10), // Slow + 13 * (STATES / 10), + 15 * (STATES / 10) }; // Slowest + +uint8_t sl_ledcolor[3]; + +/********************************************************************************************/ + +void ws2812_pixels() +{ + strip->ClearTo(0); + strip->Show(); + sl_any = 1; +} + +void ws2812_init() +{ +#ifdef USE_WS2812_DMA +#if (USE_WS2812_CTYPE == 1) + strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. +#else // USE_WS2812_CTYPE + strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. +#endif // USE_WS2812_CTYPE +#else // USE_WS2812_DMA +#if (USE_WS2812_CTYPE == 1) + strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); +#else // USE_WS2812_CTYPE + strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); +#endif // USE_WS2812_CTYPE +#endif // USE_WS2812_DMA + strip->Begin(); + ws2812_pixels(); +} + +void ws2812_setLedColor(uint16_t led) +{ + RgbColor lcolor; + lcolor.R = sl_ledcolor[0]; + lcolor.G = sl_ledcolor[1]; + lcolor.B = sl_ledcolor[2]; + strip->SetPixelColor(led -1, lcolor); // Led 1 is strip Led 0 -> substract offset 1 + strip->Show(); +} + +char* ws2812_getLedColor(uint16_t led, char* scolor) +{ + RgbColor lcolor = strip->GetPixelColor(led -1); + sl_ledcolor[0] = lcolor.R; + sl_ledcolor[1] = lcolor.G; + sl_ledcolor[2] = lcolor.B; + scolor[0] = '\0'; + for (byte i = 0; i < sfl_flg; i++) { + snprintf_P(scolor, 11, PSTR("%s%02X"), scolor, sl_ledcolor[i]); + } + return scolor; +} + +void ws2812_stripShow() +{ + RgbColor c; + + if (sysCfg.led_table) { + for (uint16_t i = 0; i < sysCfg.led_pixels; i++) { + c = strip->GetPixelColor(i); + strip->SetPixelColor(i, RgbColor(ledTable[c.R], ledTable[c.G], ledTable[c.B])); + } + } + strip->Show(); +} + +int mod(int a, int b) +{ + int ret = a % b; + if (ret < 0) { + ret += b; + } + return ret; +} + +void ws2812_clock() +{ + RgbColor c; + + strip->ClearTo(0); // Reset strip + float newDim = 100 / (float)sysCfg.led_dimmer[0]; + float f1 = 255 / newDim; + uint8_t i1 = (uint8_t)f1; + float f2 = 127 / newDim; + uint8_t i2 = (uint8_t)f2; + float f3 = 63 / newDim; + uint8_t i3 = (uint8_t)f3; + + int j = sysCfg.led_pixels; + int clksize = 600 / j; + int i = (rtcTime.Second * 10) / clksize; + + c = strip->GetPixelColor(mod(i, j)); c.B = i1; strip->SetPixelColor(mod(i, j), c); + i = (rtcTime.Minute * 10) / clksize; + c = strip->GetPixelColor(mod(i -1, j)); c.G = i3; strip->SetPixelColor(mod(i -1, j), c); + c = strip->GetPixelColor(mod(i, j)); c.G = i1; strip->SetPixelColor(mod(i, j), c); + c = strip->GetPixelColor(mod(i +1, j)); c.G = i3; strip->SetPixelColor(mod(i +1, j), c); + i = (rtcTime.Hour % 12) * (50 / clksize); + c = strip->GetPixelColor(mod(i -2, j)); c.R = i3; strip->SetPixelColor(mod(i -2, j), c); + c = strip->GetPixelColor(mod(i -1, j)); c.R = i2; strip->SetPixelColor(mod(i -1, j), c); + c = strip->GetPixelColor(mod(i, j)); c.R = i1; strip->SetPixelColor(mod(i, j), c); + c = strip->GetPixelColor(mod(i +1, j)); c.R = i2; strip->SetPixelColor(mod(i +1, j), c); + c = strip->GetPixelColor(mod(i +2, j)); c.R = i3; strip->SetPixelColor(mod(i +2, j), c); + ws2812_stripShow(); +} + +void ws2812_gradientColor(struct wsColor* mColor, uint16_t range, uint16_t gradRange, uint16_t i) +{ +/* + * Compute the color of a pixel at position i using a gradient of the color scheme. + * This function is used internally by the gradient function. + */ + ColorScheme scheme = schemes[sysCfg.led_scheme -3]; + uint16_t curRange = i / range; + uint16_t rangeIndex = i % range; + uint16_t colorIndex = rangeIndex / gradRange; + uint16_t start = colorIndex; + uint16_t end = colorIndex +1; + if (curRange % 2 != 0) { + start = (scheme.count -1) - start; + end = (scheme.count -1) - end; + } + float newDim = 100 / (float)sysCfg.led_dimmer[0]; + float fmyRed = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].red, scheme.colors[end].red) / newDim; + float fmyGrn = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].green, scheme.colors[end].green) / newDim; + float fmyBlu = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].blue, scheme.colors[end].blue) / newDim; + mColor->red = (uint8_t)fmyRed; + mColor->green = (uint8_t)fmyGrn; + mColor->blue = (uint8_t)fmyBlu; +} + +void ws2812_gradient() +{ +/* + * This routine courtesy Tony DiCola (Adafruit) + * Display a gradient of colors for the current color scheme. + * Repeat is the number of repetitions of the gradient (pick a multiple of 2 for smooth looping of the gradient). + */ + RgbColor c; + + ColorScheme scheme = schemes[sysCfg.led_scheme -3]; + if (scheme.count < 2) { + return; + } + + uint8_t repeat = repeatValues[sysCfg.led_width]; // number of scheme.count per ledcount + uint16_t range = (uint16_t)ceil((float)sysCfg.led_pixels / (float)repeat); + uint16_t gradRange = (uint16_t)ceil((float)range / (float)(scheme.count - 1)); + uint16_t offset = speedValues[sysCfg.led_speed] > 0 ? stripTimerCntr / speedValues[sysCfg.led_speed] : 0; + + wsColor oldColor, currentColor; + ws2812_gradientColor(&oldColor, range, gradRange, offset); + currentColor = oldColor; + for (uint16_t i = 0; i < sysCfg.led_pixels; i++) { + if (repeatValues[sysCfg.led_width] > 1) { + ws2812_gradientColor(¤tColor, range, gradRange, i +offset); + } + if (sysCfg.led_speed > 0) { + // Blend old and current color based on time for smooth movement. + c.R = map(stripTimerCntr % speedValues[sysCfg.led_speed], 0, speedValues[sysCfg.led_speed], oldColor.red, currentColor.red); + c.G = map(stripTimerCntr % speedValues[sysCfg.led_speed], 0, speedValues[sysCfg.led_speed], oldColor.green, currentColor.green); + c.B = map(stripTimerCntr % speedValues[sysCfg.led_speed], 0, speedValues[sysCfg.led_speed], oldColor.blue, currentColor.blue); + } + else { + // No animation, just use the current color. + c.R = currentColor.red; + c.G = currentColor.green; + c.B = currentColor.blue; + } + strip->SetPixelColor(i, c); + oldColor = currentColor; + } + ws2812_stripShow(); +} + +void ws2812_bars() +{ +/* + * This routine courtesy Tony DiCola (Adafruit) + * Display solid bars of color for the current color scheme. + * Width is the width of each bar in pixels/lights. + */ + RgbColor c; + uint16_t i; + + ColorScheme scheme = schemes[sysCfg.led_scheme -3]; + + uint16_t maxSize = sysCfg.led_pixels / scheme.count; + if (widthValues[sysCfg.led_width] > maxSize) { + maxSize = 0; + } + + uint8_t offset = speedValues[sysCfg.led_speed] > 0 ? stripTimerCntr / speedValues[sysCfg.led_speed] : 0; + + wsColor mcolor[scheme.count]; + memcpy(mcolor, scheme.colors, sizeof(mcolor)); + float newDim = 100 / (float)sysCfg.led_dimmer[0]; + for (i = 0; i < scheme.count; i++) { + float fmyRed = (float)mcolor[i].red / newDim; + float fmyGrn = (float)mcolor[i].green / newDim; + float fmyBlu = (float)mcolor[i].blue / newDim; + mcolor[i].red = (uint8_t)fmyRed; + mcolor[i].green = (uint8_t)fmyGrn; + mcolor[i].blue = (uint8_t)fmyBlu; + } + uint8_t colorIndex = offset % scheme.count; + for (i = 0; i < sysCfg.led_pixels; i++) { + if (maxSize) { + colorIndex = ((i + offset) % (scheme.count * widthValues[sysCfg.led_width])) / widthValues[sysCfg.led_width]; + } + c.R = mcolor[colorIndex].red; + c.G = mcolor[colorIndex].green; + c.B = mcolor[colorIndex].blue; + strip->SetPixelColor(i, c); + } + ws2812_stripShow(); +} + +#endif // USE_WS2812 + /*********************************************************************************************\ * Sonoff B1 and AiLight inspired by OpenLight https://github.com/icamgo/noduino-sdk \*********************************************************************************************/ @@ -139,8 +441,7 @@ void sl_my92x1_duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t duty void sl_init(void) { - pin[GPIO_WS2812] = 99; // I do not allow both Sonoff Led AND WS2812 led - if (sfl_flg < 4) { + if (sfl_flg < 3) { if (!my_module.gp.io[4]) { pinMode(4, OUTPUT); // Stop floating outputs digitalWrite(4, LOW); @@ -157,7 +458,17 @@ void sl_init(void) if (2 == sfl_flg) { sysCfg.pwmvalue[1] = 0; // We use led_color } - } else { + sysCfg.led_scheme = 0; + } +#ifdef USE_WS2812 // ************************************************************************ + else if (3 == sfl_flg) { + ws2812_init(); + if (1 == sysCfg.led_scheme) { + sysCfg.led_scheme = 0; + } + } +#endif // USE_WS2812 ************************************************************************ + else { sl_pdi = pin[GPIO_DI]; sl_pdcki = pin[GPIO_DCKI]; @@ -167,9 +478,9 @@ void sl_init(void) digitalWrite(sl_pdcki, LOW); sl_my92x1_init(); + sysCfg.led_scheme = 0; } - sl_power = 0; sl_any = 0; sl_wakeupActive = 0; } @@ -257,33 +568,37 @@ char* sl_getColor(char* scolor) return scolor; } +boolean sl_power() +{ + return ((power & (0x01 << (Maxdevice -1))) != 0); +} + void sl_prepPower() { char scolor[11]; // do_cmnd_power(index, (sysCfg.led_dimmer[0]>0)); - if (sysCfg.led_dimmer[0] && !(power&1)) { - do_cmnd_power(1, 7); // No publishPowerState + if (sysCfg.led_dimmer[0] && !(sl_power())) { + do_cmnd_power(Maxdevice, 7); // No publishPowerState } - else if (!sysCfg.led_dimmer[0] && (power&1)) { - do_cmnd_power(1, 6); // No publishPowerState + else if (!sysCfg.led_dimmer[0] && sl_power()) { + do_cmnd_power(Maxdevice, 6); // No publishPowerState } #ifdef USE_DOMOTICZ // mqtt_publishDomoticzPowerState(1); - domoticz_updatePowerState(1); + domoticz_updatePowerState(Maxdevice); #endif // USE_DOMOTICZ if (sfl_flg > 1) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_RSLT_POWER "\":\"%s\", \"" D_CMND_DIMMER "\":%d, \"" D_CMND_COLOR "\":\"%s\"}"), - getStateText(power &1), sysCfg.led_dimmer[0], sl_getColor(scolor)); + getStateText(sl_power()), sysCfg.led_dimmer[0], sl_getColor(scolor)); } else { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_RSLT_POWER "\":\"%s\", \"" D_CMND_DIMMER "\":%d}"), - getStateText(power &1), sysCfg.led_dimmer[0]); + getStateText(sl_power()), sysCfg.led_dimmer[0]); } } -void sl_setPower(uint8_t power) +void sl_setPower(uint8_t mpower) { - sl_power = power &1; if (sl_wakeupActive) { sl_wakeupActive--; } @@ -296,74 +611,113 @@ void sl_animate() uint8_t fadeValue; uint8_t cur_col[5]; - if (0 == sl_power) { // Power Off + stripTimerCntr++; + if (!sl_power()) { // Power Off + sleep = sysCfg.sleep; + stripTimerCntr = 0; for (byte i = 0; i < sfl_flg; i++) { sl_tcolor[i] = 0; } } else { - if (!sl_wakeupActive) { // Power On - sl_setDim(sysCfg.led_dimmer[0]); - if (0 == sysCfg.led_fade) { - for (byte i = 0; i < sfl_flg; i++) { - sl_tcolor[i] = sl_dcolor[i]; - } - } else { - for (byte i = 0; i < sfl_flg; i++) { - if (sl_tcolor[i] != sl_dcolor[i]) { - if (sl_tcolor[i] < sl_dcolor[i]) { - sl_tcolor[i] += ((sl_dcolor[i] - sl_tcolor[i]) >> sysCfg.led_speed) +1; - } - if (sl_tcolor[i] > sl_dcolor[i]) { - sl_tcolor[i] -= ((sl_tcolor[i] - sl_dcolor[i]) >> sysCfg.led_speed) +1; - } - } - } - } - } else { // Power On using wake up duration - if (2 == sl_wakeupActive) { - sl_wakeupActive = 1; - for (byte i = 0; i < sfl_flg; i++) { - sl_tcolor[i] = 0; - } - sl_wakeupCntr = 0; - sl_wakeupDimmer = 0; - } - sl_wakeupCntr++; - if (sl_wakeupCntr > ((sysCfg.led_wakeup * STATES) / sysCfg.led_dimmer[0])) { - sl_wakeupCntr = 0; - sl_wakeupDimmer++; - if (sl_wakeupDimmer <= sysCfg.led_dimmer[0]) { - sl_setDim(sl_wakeupDimmer); + sleep = 0; + switch (sysCfg.led_scheme) { + case 0: // Power On + sl_setDim(sysCfg.led_dimmer[0]); // Power On + if (0 == sysCfg.led_fade) { for (byte i = 0; i < sfl_flg; i++) { sl_tcolor[i] = sl_dcolor[i]; } } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_WAKEUP "\":\"" D_DONE "\"}")); - mqtt_publish_topic_P(2, PSTR(D_CMND_WAKEUP)); - sl_wakeupActive = 0; + for (byte i = 0; i < sfl_flg; i++) { + if (sl_tcolor[i] != sl_dcolor[i]) { + if (sl_tcolor[i] < sl_dcolor[i]) { + sl_tcolor[i] += ((sl_dcolor[i] - sl_tcolor[i]) >> sysCfg.led_speed) +1; + } + if (sl_tcolor[i] > sl_dcolor[i]) { + sl_tcolor[i] -= ((sl_tcolor[i] - sl_dcolor[i]) >> sysCfg.led_speed) +1; + } + } + } } - } + break; + case 1: // Power On using wake up duration + if (2 == sl_wakeupActive) { + sl_wakeupActive = 1; + for (byte i = 0; i < sfl_flg; i++) { + sl_tcolor[i] = 0; + } + sl_wakeupCntr = 0; + sl_wakeupDimmer = 0; + } + sl_wakeupCntr++; + if (sl_wakeupCntr > ((sysCfg.led_wakeup * STATES) / sysCfg.led_dimmer[0])) { + sl_wakeupCntr = 0; + sl_wakeupDimmer++; + if (sl_wakeupDimmer <= sysCfg.led_dimmer[0]) { + sl_setDim(sl_wakeupDimmer); + for (byte i = 0; i < sfl_flg; i++) { + sl_tcolor[i] = sl_dcolor[i]; + } + } else { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_WAKEUP "\":\"" D_DONE "\"}")); + mqtt_publish_topic_P(2, PSTR(D_CMND_WAKEUP)); + sl_wakeupActive = 0; + sysCfg.led_scheme = 0; + } + } + break; +#ifdef USE_WS2812 // ************************************************************************ + case 2: // Clock + if (((STATES/10)*2 == state) || (sl_any != 2)) { + ws2812_clock(); + } + sl_any = 2; + break; + default: + if (1 == sysCfg.led_fade) { + ws2812_gradient(); + } else { + ws2812_bars(); + } + sl_any = 1; + break; +#endif // USE_WS2812 ************************************************************************ } } - for (byte i = 0; i < sfl_flg; i++) { - if (sl_lcolor[i] != sl_tcolor[i]) { - sl_any = 1; - } - } - if (sl_any) { - sl_any = 0; + + if ((sysCfg.led_scheme < 2) || !sl_power()) { for (byte i = 0; i < sfl_flg; i++) { - sl_lcolor[i] = sl_tcolor[i]; - cur_col[i] = (sysCfg.led_table) ? ledTable[sl_lcolor[i]] : sl_lcolor[i]; - if (sfl_flg < 4) { - if (pin[GPIO_PWM1 +i] < 99) { - analogWrite(pin[GPIO_PWM1 +i], cur_col[i] * (PWM_RANGE / 255)); - } + if (sl_lcolor[i] != sl_tcolor[i]) { + sl_any = 1; } } - if (sfl_flg > 3) { - sl_my92x1_duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); + if (sl_any) { + sl_any = 0; + for (byte i = 0; i < sfl_flg; i++) { + sl_lcolor[i] = sl_tcolor[i]; + cur_col[i] = (sysCfg.led_table) ? ledTable[sl_lcolor[i]] : sl_lcolor[i]; + if (sfl_flg < 3) { + if (pin[GPIO_PWM1 +i] < 99) { + analogWrite(pin[GPIO_PWM1 +i], cur_col[i] * (PWM_RANGE / 255)); + } + } + } +#ifdef USE_WS2812 // ************************************************************************ + if (3 == sfl_flg) { + RgbColor lcolor; + lcolor.R = cur_col[0]; + lcolor.G = cur_col[1]; + lcolor.B = cur_col[2]; + for (uint16_t i = 0; i < sysCfg.led_pixels; i++) { + strip->SetPixelColor(i, lcolor); + } + strip->Show(); + } +#endif // USE_ES2812 ************************************************************************ + if (sfl_flg > 3) { + sl_my92x1_duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); + } } } } @@ -469,7 +823,7 @@ void sl_setHSB(float hue, float sat, float bri, uint16_t ct) * Commands \*********************************************************************************************/ -boolean sl_command(char *type, uint16_t index, char *dataBufUc, uint16_t data_len, int16_t payload) +boolean sl_command(char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload) { boolean serviced = true; boolean coldim = false; @@ -477,13 +831,13 @@ boolean sl_command(char *type, uint16_t index, char *dataBufUc, uint16_t data_le char *p; if ((sfl_flg > 1) && !strcasecmp_P(type, PSTR(D_CMND_COLOR))) { - if (dataBufUc[0] == '#') { - dataBufUc++; + if (dataBuf[0] == '#') { + dataBuf++; data_len--; } if ((2 * sfl_flg) == data_len) { for (byte i = 0; i < sfl_flg; i++) { - strlcpy(scolor, dataBufUc + (i *2), 3); + strlcpy(scolor, dataBuf + (i *2), 3); sl_dcolor[i] = (uint8_t)strtol(scolor, &p, 16); } sl_setColor(); @@ -492,6 +846,46 @@ boolean sl_command(char *type, uint16_t index, char *dataBufUc, uint16_t data_le snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_COLOR "\":\"%s\"}"), sl_getColor(scolor)); } } +#ifdef USE_WS2812 // *********************************************************************** + else if ((3 == sfl_flg) && !strcasecmp_P(type, PSTR(D_CMND_LED)) && (index > 0) && (index <= sysCfg.led_pixels)) { + if (dataBuf[0] == '#') { + dataBuf++; + data_len--; + } + if ((2 * sfl_flg) == data_len) { + for (byte i = 0; i < sfl_flg; i++) { + strlcpy(scolor, dataBuf + (i *2), 3); + sl_ledcolor[i] = (uint8_t)strtol(scolor, &p, 16); + } + ws2812_setLedColor(index); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_LED "%d\":\"%s\"}"), index, ws2812_getLedColor(index, scolor)); + } + else if ((3 == sfl_flg) && !strcasecmp_P(type, PSTR(D_CMND_PIXELS))) { + if ((payload > 0) && (payload <= WS2812_MAX_LEDS)) { + sysCfg.led_pixels = payload; + ws2812_pixels(); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_PIXELS "\":%d}"), sysCfg.led_pixels); + } + else if ((3 == sfl_flg) && !strcasecmp_P(type, PSTR(D_CMND_WIDTH))) { + if ((payload >= 0) && (payload <= 4)) { + sysCfg.led_width = payload; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_WIDTH "\":%d}"), sysCfg.led_width); + } + else if ((3 == sfl_flg) && !strcasecmp_P(type, PSTR(D_CMND_SCHEME))) { + if ((payload >= 0) && (payload <= 9)) { + sysCfg.led_scheme = payload; + if (1 == sysCfg.led_scheme) { + sl_wakeupActive = 3; + } + do_cmnd_power(Maxdevice, 1); + stripTimerCntr = 0; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_SCHEME "\":%d}"), sysCfg.led_scheme); + } +#endif // USE_WS2812 ************************************************************************ else if (!strcasecmp_P(type, PSTR(D_CMND_COLORTEMPERATURE)) && ((2 == sfl_flg) || (5 == sfl_flg))) { // ColorTemp if ((payload >= 153) && (payload <= 500)) { // https://developers.meethue.com/documentation/core-concepts sl_setColorTemp(payload); @@ -553,14 +947,15 @@ boolean sl_command(char *type, uint16_t index, char *dataBufUc, uint16_t data_le sysCfg.led_dimmer[0] = payload; } sl_wakeupActive = 3; - do_cmnd_power(1, 1); + sysCfg.led_scheme = 1; + do_cmnd_power(Maxdevice, 1); snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_WAKEUP "\":\"" D_STARTED "\"}")); } else if (!strcasecmp_P(type, PSTR("UNDOCA"))) { // Theos legacy status sl_getColor(scolor); scolor[6] = '\0'; // RGB only - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s, %d, %d, 1, %d, 1"), - scolor, sysCfg.led_fade, sysCfg.led_table, sysCfg.led_speed); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s, %d, %d, %d, %d, %d"), + scolor, sysCfg.led_fade, sysCfg.led_table, sysCfg.led_scheme, sysCfg.led_speed, sysCfg.led_width); mqtt_publish_topic_P(1, type); mqtt_data[0] = '\0'; } diff --git a/sonoff/xdrv_ws2812.ino b/sonoff/xdrv_ws2812.ino deleted file mode 100644 index ce17db2ee..000000000 --- a/sonoff/xdrv_ws2812.ino +++ /dev/null @@ -1,629 +0,0 @@ -/* - xdrv_ws2812.ino - ws2812 led string support for Sonoff-Tasmota - - Copyright (C) 2017 Heiko Krupp and Theo Arends - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifdef USE_WS2812 -/*********************************************************************************************\ - * WS2812 Leds using NeopixelBus library -\*********************************************************************************************/ - -//#include // Global defined as also used by Sonoff Led - -#ifdef USE_WS2812_DMA -#if (USE_WS2812_CTYPE == 1) - NeoPixelBus *strip = NULL; -#else // USE_WS2812_CTYPE - NeoPixelBus *strip = NULL; -#endif // USE_WS2812_CTYPE -#else // USE_WS2812_DMA -#if (USE_WS2812_CTYPE == 1) - NeoPixelBus *strip = NULL; -#else // USE_WS2812_CTYPE - NeoPixelBus *strip = NULL; -#endif // USE_WS2812_CTYPE -#endif // USE_WS2812_DMA - -struct wsColor { - uint8_t red, green, blue; -}; - -struct ColorScheme { - wsColor* colors; - uint8_t count; -}; - -wsColor incandescent[2] = { 255, 140, 20, 0, 0, 0 }; -wsColor rgb[3] = { 255, 0, 0, 0, 255, 0, 0, 0, 255 }; -wsColor christmas[2] = { 255, 0, 0, 0, 255, 0 }; -wsColor hanukkah[2] = { 0, 0, 255, 255, 255, 255 }; -wsColor kwanzaa[3] = { 255, 0, 0, 0, 0, 0, 0, 255, 0 }; -wsColor rainbow[7] = { 255, 0, 0, 255, 128, 0, 255, 255, 0, 0, 255, 0, 0, 0, 255, 128, 0, 255, 255, 0, 255 }; -wsColor fire[3] = { 255, 0, 0, 255, 102, 0, 255, 192, 0 }; -ColorScheme schemes[7] = { - incandescent, 2, - rgb, 3, - christmas, 2, - hanukkah, 2, - kwanzaa, 3, - rainbow, 7, - fire, 3 }; - -uint8_t widthValues[5] = { - 1, // Small - 2, // Medium - 4, // Large - 8, // Largest - 255 }; // All -uint8_t repeatValues[5] = { - 8, // Small - 6, // Medium - 4, // Large - 2, // Largest - 1 }; // All -uint8_t speedValues[6] = { - 0, // None - 9 * (STATES / 10), // Slowest - 7 * (STATES / 10), // Slower - 5 * (STATES / 10), // Slow - 3 * (STATES / 10), // Fast - 1 * (STATES / 10) }; // Fastest - -uint8_t lany = 0; -RgbColor dcolor; -RgbColor tcolor; -RgbColor lcolor; - -uint8_t wakeupDimmer = 0; -uint8_t ws_bit = 0; -uint16_t wakeupCntr = 0; -unsigned long stripTimerCntr = 0; // Bars and Gradient - -void ws2812_setDim(uint8_t myDimmer) -{ - float newDim = 100 / (float)myDimmer; - float fmyRed = (float)sysCfg.ws_red / newDim; - float fmyGrn = (float)sysCfg.ws_green / newDim; - float fmyBlu = (float)sysCfg.ws_blue / newDim; - dcolor.R = (uint8_t)fmyRed; - dcolor.G = (uint8_t)fmyGrn; - dcolor.B = (uint8_t)fmyBlu; -} - -void ws2812_setColor(uint16_t led, char* colstr) -{ - HtmlColor hcolor; - char lcolstr[8]; - - snprintf_P(lcolstr, sizeof(lcolstr), PSTR("#%s"), colstr); - uint8_t result = hcolor.Parse((char *)lcolstr, 7); - if (result) { - if (led) { - strip->SetPixelColor(led -1, RgbColor(hcolor)); // Led 1 is strip Led 0 -> substract offset 1 - strip->Show(); - } else { - dcolor = RgbColor(hcolor); - -// snprintf_P(log_data, sizeof(log_data), PSTR("DBG: Red %02X, Green %02X, Blue %02X"), dcolor.R, dcolor.G, dcolor.B); -// addLog(LOG_LEVEL_DEBUG); - - uint16_t temp = dcolor.R; - if (temp < dcolor.G) { - temp = dcolor.G; - } - if (temp < dcolor.B) { - temp = dcolor.B; - } - float mDim = (float)temp / 2.55; - sysCfg.ws_dimmer = (uint8_t)mDim; - - float newDim = 100 / mDim; - float fmyRed = (float)dcolor.R * newDim; - float fmyGrn = (float)dcolor.G * newDim; - float fmyBlu = (float)dcolor.B * newDim; - sysCfg.ws_red = (uint8_t)fmyRed; - sysCfg.ws_green = (uint8_t)fmyGrn; - sysCfg.ws_blue = (uint8_t)fmyBlu; - - lany = 1; - } - } -} - -void ws2812_getColor(uint16_t led) -{ - RgbColor mcolor; - char stemp[20]; - - if (led) { - mcolor = strip->GetPixelColor(led -1); - snprintf_P(stemp, sizeof(stemp), PSTR(D_CMND_LED "%d"), led); - } else { - ws2812_setDim(sysCfg.ws_dimmer); - mcolor = dcolor; - snprintf_P(stemp, sizeof(stemp), PSTR(D_CMND_COLOR)); - } - uint32_t color = (uint32_t)mcolor.R << 16; - color += (uint32_t)mcolor.G << 8; - color += (uint32_t)mcolor.B; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s\":\"%06X\"}"), stemp, color); -} - -void ws2812_stripShow() -{ - RgbColor c; - - if (sysCfg.ws_ledtable) { - for (uint16_t i = 0; i < sysCfg.ws_pixels; i++) { - c = strip->GetPixelColor(i); - strip->SetPixelColor(i, RgbColor(ledTable[c.R], ledTable[c.G], ledTable[c.B])); - } - } - strip->Show(); -} - -void ws2812_resetWakupState() -{ - wakeupDimmer = 0; - wakeupCntr = 0; -} - -void ws2812_resetStripTimer() -{ - stripTimerCntr = 0; -} - -int mod(int a, int b) -{ - int ret = a % b; - if (ret < 0) { - ret += b; - } - return ret; -} - -void ws2812_clock() -{ - RgbColor c; - - strip->ClearTo(0); // Reset strip - float newDim = 100 / (float)sysCfg.ws_dimmer; - float f1 = 255 / newDim; - uint8_t i1 = (uint8_t)f1; - float f2 = 127 / newDim; - uint8_t i2 = (uint8_t)f2; - float f3 = 63 / newDim; - uint8_t i3 = (uint8_t)f3; - - int j = sysCfg.ws_pixels; - int clksize = 600 / j; - int i = (rtcTime.Second * 10) / clksize; - - c = strip->GetPixelColor(mod(i, j)); c.B = i1; strip->SetPixelColor(mod(i, j), c); - i = (rtcTime.Minute * 10) / clksize; - c = strip->GetPixelColor(mod(i -1, j)); c.G = i3; strip->SetPixelColor(mod(i -1, j), c); - c = strip->GetPixelColor(mod(i, j)); c.G = i1; strip->SetPixelColor(mod(i, j), c); - c = strip->GetPixelColor(mod(i +1, j)); c.G = i3; strip->SetPixelColor(mod(i +1, j), c); - i = (rtcTime.Hour % 12) * (50 / clksize); - c = strip->GetPixelColor(mod(i -2, j)); c.R = i3; strip->SetPixelColor(mod(i -2, j), c); - c = strip->GetPixelColor(mod(i -1, j)); c.R = i2; strip->SetPixelColor(mod(i -1, j), c); - c = strip->GetPixelColor(mod(i, j)); c.R = i1; strip->SetPixelColor(mod(i, j), c); - c = strip->GetPixelColor(mod(i +1, j)); c.R = i2; strip->SetPixelColor(mod(i +1, j), c); - c = strip->GetPixelColor(mod(i +2, j)); c.R = i3; strip->SetPixelColor(mod(i +2, j), c); - ws2812_stripShow(); -} - -void ws2812_gradientColor(struct wsColor* mColor, uint16_t range, uint16_t gradRange, uint16_t i) -{ -/* - * Compute the color of a pixel at position i using a gradient of the color scheme. - * This function is used internally by the gradient function. - */ - ColorScheme scheme = schemes[sysCfg.ws_scheme -3]; - uint16_t curRange = i / range; - uint16_t rangeIndex = i % range; - uint16_t colorIndex = rangeIndex / gradRange; - uint16_t start = colorIndex; - uint16_t end = colorIndex +1; - if (curRange % 2 != 0) { - start = (scheme.count -1) - start; - end = (scheme.count -1) - end; - } - float newDim = 100 / (float)sysCfg.ws_dimmer; - float fmyRed = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].red, scheme.colors[end].red) / newDim; - float fmyGrn = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].green, scheme.colors[end].green) / newDim; - float fmyBlu = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].blue, scheme.colors[end].blue) / newDim; - mColor->red = (uint8_t)fmyRed; - mColor->green = (uint8_t)fmyGrn; - mColor->blue = (uint8_t)fmyBlu; -} - -void ws2812_gradient() -{ -/* - * This routine courtesy Tony DiCola (Adafruit) - * Display a gradient of colors for the current color scheme. - * Repeat is the number of repetitions of the gradient (pick a multiple of 2 for smooth looping of the gradient). - */ - RgbColor c; - - ColorScheme scheme = schemes[sysCfg.ws_scheme -3]; - if (scheme.count < 2) { - return; - } - - uint8_t repeat = repeatValues[sysCfg.ws_width]; // number of scheme.count per ledcount - uint16_t range = (uint16_t)ceil((float)sysCfg.ws_pixels / (float)repeat); - uint16_t gradRange = (uint16_t)ceil((float)range / (float)(scheme.count - 1)); - uint16_t offset = speedValues[sysCfg.ws_speed] > 0 ? stripTimerCntr / speedValues[sysCfg.ws_speed] : 0; - - wsColor oldColor, currentColor; - ws2812_gradientColor(&oldColor, range, gradRange, offset); - currentColor = oldColor; - for (uint16_t i = 0; i < sysCfg.ws_pixels; i++) { - if (repeatValues[sysCfg.ws_width] > 1) { - ws2812_gradientColor(¤tColor, range, gradRange, i +offset); - } - if (sysCfg.ws_speed > 0) { - // Blend old and current color based on time for smooth movement. - c.R = map(stripTimerCntr % speedValues[sysCfg.ws_speed], 0, speedValues[sysCfg.ws_speed], oldColor.red, currentColor.red); - c.G = map(stripTimerCntr % speedValues[sysCfg.ws_speed], 0, speedValues[sysCfg.ws_speed], oldColor.green, currentColor.green); - c.B = map(stripTimerCntr % speedValues[sysCfg.ws_speed], 0, speedValues[sysCfg.ws_speed], oldColor.blue, currentColor.blue); - } - else { - // No animation, just use the current color. - c.R = currentColor.red; - c.G = currentColor.green; - c.B = currentColor.blue; - } - strip->SetPixelColor(i, c); - oldColor = currentColor; - } - ws2812_stripShow(); -} - -void ws2812_bars() -{ -/* - * This routine courtesy Tony DiCola (Adafruit) - * Display solid bars of color for the current color scheme. - * Width is the width of each bar in pixels/lights. - */ - RgbColor c; - uint16_t i; - - ColorScheme scheme = schemes[sysCfg.ws_scheme -3]; - - uint16_t maxSize = sysCfg.ws_pixels / scheme.count; - if (widthValues[sysCfg.ws_width] > maxSize) { - maxSize = 0; - } - - uint8_t offset = speedValues[sysCfg.ws_speed] > 0 ? stripTimerCntr / speedValues[sysCfg.ws_speed] : 0; - - wsColor mcolor[scheme.count]; - memcpy(mcolor, scheme.colors, sizeof(mcolor)); - float newDim = 100 / (float)sysCfg.ws_dimmer; - for (i = 0; i < scheme.count; i++) { - float fmyRed = (float)mcolor[i].red / newDim; - float fmyGrn = (float)mcolor[i].green / newDim; - float fmyBlu = (float)mcolor[i].blue / newDim; - mcolor[i].red = (uint8_t)fmyRed; - mcolor[i].green = (uint8_t)fmyGrn; - mcolor[i].blue = (uint8_t)fmyBlu; - } - uint8_t colorIndex = offset % scheme.count; - for (i = 0; i < sysCfg.ws_pixels; i++) { - if (maxSize) { - colorIndex = ((i + offset) % (scheme.count * widthValues[sysCfg.ws_width])) / widthValues[sysCfg.ws_width]; - } - c.R = mcolor[colorIndex].red; - c.G = mcolor[colorIndex].green; - c.B = mcolor[colorIndex].blue; - strip->SetPixelColor(i, c); - } - ws2812_stripShow(); -} - -void ws2812_animate() -{ - uint8_t fadeValue; - - stripTimerCntr++; - if (0 == bitRead(power, ws_bit)) { // Power Off - sleep = sysCfg.sleep; - stripTimerCntr = 0; - tcolor = 0; - } - else { - sleep = 0; - switch (sysCfg.ws_scheme) { - case 0: // Power On - ws2812_setDim(sysCfg.ws_dimmer); - if (0 == sysCfg.ws_fade) { - tcolor = dcolor; - } else { - if (tcolor != dcolor) { - uint8_t ws_speed = speedValues[sysCfg.ws_speed]; - if (tcolor.R < dcolor.R) { - tcolor.R += ((dcolor.R - tcolor.R) / ws_speed) +1; - } - if (tcolor.G < dcolor.G) { - tcolor.G += ((dcolor.G - tcolor.G) / ws_speed) +1; - } - if (tcolor.B < dcolor.B) { - tcolor.B += ((dcolor.B - tcolor.B) / ws_speed) +1; - } - if (tcolor.R > dcolor.R) { - tcolor.R -= ((tcolor.R - dcolor.R) / ws_speed) +1; - } - if (tcolor.G > dcolor.G) { - tcolor.G -= ((tcolor.G - dcolor.G) / ws_speed) +1; - } - if (tcolor.B > dcolor.B) { - tcolor.B -= ((tcolor.B - dcolor.B) / ws_speed) +1; - } - } - } - break; - case 1: // Wake up light - wakeupCntr++; - if (0 == wakeupDimmer) { - tcolor = 0; - wakeupDimmer++; - } - else { - if (wakeupCntr > ((sysCfg.ws_wakeup * STATES) / sysCfg.ws_dimmer)) { - wakeupCntr = 0; - wakeupDimmer++; - if (wakeupDimmer <= sysCfg.ws_dimmer) { - ws2812_setDim(wakeupDimmer); - tcolor = dcolor; - } else { - sysCfg.ws_scheme = 0; - } - } - } - break; - case 2: // Clock - if (((STATES/10)*2 == state) || (lany != 2)) { - ws2812_clock(); - } - lany = 2; - break; - default: - if (1 == sysCfg.ws_fade) { - ws2812_gradient(); - } else { - ws2812_bars(); - } - lany = 1; - break; - } - } - - if ((sysCfg.ws_scheme <= 1) || (0 == bitRead(power, ws_bit))) { - if ((lcolor != tcolor) || lany) { - lany = 0; - lcolor = tcolor; - -// snprintf_P(log_data, sizeof(log_data), PSTR("DBG: StripPixels %d, CfgPixels %d, Red %02X, Green %02X, Blue %02X"), strip->PixelCount(), sysCfg.ws_pixels, lcolor.R, lcolor.G, lcolor.B); -// addLog(LOG_LEVEL_DEBUG); - - if (sysCfg.ws_ledtable) { - for (uint16_t i = 0; i < sysCfg.ws_pixels; i++) { - strip->SetPixelColor(i, RgbColor(ledTable[lcolor.R],ledTable[lcolor.G],ledTable[lcolor.B])); - } - } else { - for (uint16_t i = 0; i < sysCfg.ws_pixels; i++) { - strip->SetPixelColor(i, lcolor); - } - } - strip->Show(); - } - } -} - -void ws2812_update() -{ - lany = 1; -} - -void ws2812_pixels() -{ - strip->ClearTo(0); - strip->Show(); - tcolor = 0; - lany = 1; -} - -void ws2812_init(uint8_t powerbit) -{ - ws_bit = powerbit -1; -#ifdef USE_WS2812_DMA -#if (USE_WS2812_CTYPE == 1) - strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. -#else // USE_WS2812_CTYPE - strip = new NeoPixelBus(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use. -#endif // USE_WS2812_CTYPE -#else // USE_WS2812_DMA -#if (USE_WS2812_CTYPE == 1) - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); -#else // USE_WS2812_CTYPE - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); -#endif // USE_WS2812_CTYPE -#endif // USE_WS2812_DMA - strip->Begin(); - ws2812_pixels(); -} - -/*********************************************************************************************\ - * Hue support -\*********************************************************************************************/ - -void ws2812_replaceHSB(String *response) -{ - ws2812_setDim(sysCfg.ws_dimmer); - HsbColor hsb = HsbColor(dcolor); - response->replace("{h}", String((uint16_t)(65535.0f * hsb.H))); - response->replace("{s}", String((uint8_t)(254.0f * hsb.S))); - response->replace("{b}", String((uint8_t)(254.0f * hsb.B))); -} - -void ws2812_getHSB(float *hue, float *sat, float *bri) -{ - ws2812_setDim(sysCfg.ws_dimmer); - HsbColor hsb = HsbColor(dcolor); - *hue = hsb.H; - *sat = hsb.S; - *bri = hsb.B; -} - -void ws2812_setHSB(float hue, float sat, float bri) -{ - char rgb[7]; - - HsbColor hsb; - hsb.H = hue; - hsb.S = sat; - hsb.B = bri; - RgbColor tmp = RgbColor(hsb); - sprintf(rgb,"%02X%02X%02X", tmp.R, tmp.G, tmp.B); - ws2812_setColor(0,rgb); -} - -/*********************************************************************************************\ - * Commands -\*********************************************************************************************/ - -boolean ws2812_command(char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload) -{ - boolean serviced = true; - - if (!strcasecmp_P(type, PSTR(D_CMND_PIXELS))) { - if ((payload > 0) && (payload <= WS2812_MAX_LEDS)) { - sysCfg.ws_pixels = payload; - ws2812_pixels(); - } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_PIXELS "\":%d}"), sysCfg.ws_pixels); - } - else if (!strcasecmp_P(type, PSTR(D_CMND_LED)) && (index > 0) && (index <= sysCfg.ws_pixels)) { - if (6 == data_len) { - ws2812_setColor(index, dataBuf); - } - ws2812_getColor(index); - } - else if (!strcasecmp_P(type, PSTR(D_CMND_COLOR))) { - if (dataBuf[0] == '#') { - dataBuf++; - data_len--; - } - if (6 == data_len) { - ws2812_setColor(0, dataBuf); - bitSet(power, ws_bit); - } - ws2812_getColor(0); - } - else if (!strcasecmp_P(type, PSTR(D_CMND_DIMMER))) { - if ((payload >= 0) && (payload <= 100)) { - sysCfg.ws_dimmer = payload; - bitSet(power, ws_bit); -#ifdef USE_DOMOTICZ -// mqtt_publishDomoticzPowerState(index); -// mqtt_publishDomoticzPowerState(ws_bit +1); - domoticz_updatePowerState(ws_bit +1); -#endif // USE_DOMOTICZ - } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_DIMMER "\":%d}"), sysCfg.ws_dimmer); - } - else if (!strcasecmp_P(type, PSTR(D_CMND_LEDTABLE))) { - if ((payload >= 0) && (payload <= 2)) { - switch (payload) { - case 0: // Off - case 1: // On - sysCfg.ws_ledtable = payload; - break; - case 2: // Toggle - sysCfg.ws_ledtable ^= 1; - break; - } - ws2812_update(); - } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_LEDTABLE "\":\"%s\"}"), getStateText(sysCfg.ws_ledtable)); - } - else if (!strcasecmp_P(type, PSTR(D_CMND_FADE))) { - switch (payload) { - case 0: // Off - case 1: // On - sysCfg.ws_fade = payload; - break; - case 2: // Toggle - sysCfg.ws_fade ^= 1; - break; - } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_FADE "\":\"%s\"}"), getStateText(sysCfg.ws_fade)); - } - else if (!strcasecmp_P(type, PSTR(D_CMND_SPEED))) { // 1 - fast, 5 - slow - if ((payload > 0) && (payload <= 5)) { - sysCfg.ws_speed = payload; - } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_SPEED "\":%d}"), sysCfg.ws_speed); - } - else if (!strcasecmp_P(type, PSTR(D_CMND_WIDTH))) { - if ((payload >= 0) && (payload <= 4)) { - sysCfg.ws_width = payload; - } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_WIDTH "\":%d}"), sysCfg.ws_width); - } - else if (!strcasecmp_P(type, PSTR(D_CMND_WAKEUP))) { - if ((payload > 0) && (payload < 3001)) { - sysCfg.ws_wakeup = payload; - if (1 == sysCfg.ws_scheme) { - sysCfg.ws_scheme = 0; - } - } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_WAKEUP "\":%d}"), sysCfg.ws_wakeup); - } - else if (!strcasecmp_P(type, PSTR(D_CMND_SCHEME))) { - if ((payload >= 0) && (payload <= 9)) { - sysCfg.ws_scheme = payload; - if (1 == sysCfg.ws_scheme) { - ws2812_resetWakupState(); - } - bitSet(power, ws_bit); - ws2812_resetStripTimer(); - } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_SCHEME "\":%d}"), sysCfg.ws_scheme); - } - else if (!strcasecmp_P(type, PSTR("UNDOCA"))) { // Theos legacy status - RgbColor mcolor; - ws2812_setDim(sysCfg.ws_dimmer); - mcolor = dcolor; - uint32_t color = (uint32_t)mcolor.R << 16; - color += (uint32_t)mcolor.G << 8; - color += (uint32_t)mcolor.B; - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%06X, %d, %d, %d, %d, %d"), - color, sysCfg.ws_fade, sysCfg.ws_ledtable, sysCfg.ws_scheme, sysCfg.ws_speed, sysCfg.ws_width); - mqtt_publish_topic_P(1, type); - mqtt_data[0] = '\0'; - } - else { - serviced = false; // Unknown command - } - return serviced; -} -#endif // USE_WS2812