diff --git a/CHANGELOG.md b/CHANGELOG.md index 48406c74..9acec991 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,29 @@ ### Development versions after the 0.10.2 release +#### Build 2010020 + +- Fixed interaction of `T` and `NL` HTTP API commands (#1214) +- Fixed an issue where Sunrise mode nightlight does not activate if toggled on simultaneously + +#### Build 2009291 + +- Fixed MQTT bootloop (no F() macro, #1199) + +#### Build 2009290 + +- Added basic DDP protocol support +- Added Washing Machine effect (PR #1208) + +#### Build 2009260 + +- Added Loxone parser (PR #1185) +- Added support for kelvin input via `K=` HTTP and `"col":[[val]]` JSON API calls +- Added supplementary UDP socket (#1205) +- TMP2.net receivable by default +- UDP sockets accept HTTP and JSON API commands +- Fixed missing timezones (#1201) + #### Build 2009202 - Fixed LPD8806 compilation diff --git a/platformio.ini b/platformio.ini index 55045408..7b53b2cd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -377,3 +377,43 @@ build_flags = ${common.build_flags_esp8266} ${common.debug_flags} ${common.build extends = env:esp32dev ; build_type = debug build_flags = ${common.build_flags_esp32} ${common.debug_flags} ${common.build_flags_all_features} + +# ------------------------------------------------------------------------------ +# codm pixel controller board configurations +# ------------------------------------------------------------------------------ + +[env:codm-controller-0.4] +board = esp_wroom_02 +platform = ${common.platform_wled_default} +board_build.ldscript = ${common.ldscript_2m1m} +build_flags = ${common.build_flags_esp8266} -D LEDPIN=3 + +[env:codm-controller-0.4-WS2801] +board = esp_wroom_02 +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_2m1m} +build_flags = ${common.build_flags_esp8266} -D USE_WS2801 -D CLKPIN=13 -D DATAPIN=3 + +[env:codm-controller-0.4-APA102] +board = esp_wroom_02 +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_2m1m} +build_flags = ${common.build_flags_esp8266} -D USE_APA102 -D CLKPIN=13 -D DATAPIN=3 + +[env:codm-controller-0.5] +board = esp_wroom_02 +platform = ${common.platform_wled_default} +board_build.ldscript = ${common.ldscript_2m1m} +build_flags = ${common.build_flags_esp8266} + +[env:codm-controller-0.5-WS2801] +board = esp_wroom_02 +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_2m1m} +build_flags = ${common.build_flags_esp8266} -D USE_WS2801 #-D CLKPIN=0 -D DATAPIN=2 + +[env:codm-controller-0.5-APA102] +board = esp_wroom_02 +platform = ${common.platform_latest} +board_build.ldscript = ${common.ldscript_2m1m} +build_flags = ${common.build_flags_esp8266} -D USE_APA102 #-D CLKPIN=0 -D DATAPIN=2 diff --git a/readme.md b/readme.md index 2080fd45..ce83eb75 100644 --- a/readme.md +++ b/readme.md @@ -1,10 +1,12 @@

+ +

# Welcome to my project WLED! ✨ diff --git a/wled00/FX.cpp b/wled00/FX.cpp index d8265c29..b3888488 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -3712,3 +3712,22 @@ uint16_t WS2812FX::mode_dancing_shadows(void) return FRAMETIME; } + +/* + Imitates a washing machine, rotating same waves forward, then pause, then backward. + By Stefan Seegel +*/ +uint16_t WS2812FX::mode_washing_machine(void) { + float speed = tristate_square8(now >> 7, 90, 15); + float quot = 32.0f - ((float)SEGMENT.speed / 16.0f); + speed /= quot; + + SEGENV.step += (speed * 128.0f); + + for (int i=0; i> 7)); + setPixelColor(i, color_from_palette(col, false, PALETTE_SOLID_WRAP, 3)); + } + + return FRAMETIME; +} diff --git a/wled00/FX.h b/wled00/FX.h index 3a1a99e4..e3c170f7 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -107,7 +107,7 @@ #define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE ) #define IS_SELECTED ((SEGMENT.options & SELECTED ) == SELECTED ) -#define MODE_COUNT 113 +#define MODE_COUNT 114 #define FX_MODE_STATIC 0 #define FX_MODE_BLINK 1 @@ -222,6 +222,7 @@ #define FX_MODE_FLOW 110 #define FX_MODE_CHUNCHUN 111 #define FX_MODE_DANCING_SHADOWS 112 +#define FX_MODE_WASHING_MACHINE 113 class WS2812FX { typedef uint16_t (WS2812FX::*mode_ptr)(void); @@ -426,6 +427,7 @@ class WS2812FX { _mode[FX_MODE_FLOW] = &WS2812FX::mode_flow; _mode[FX_MODE_CHUNCHUN] = &WS2812FX::mode_chunchun; _mode[FX_MODE_DANCING_SHADOWS] = &WS2812FX::mode_dancing_shadows; + _mode[FX_MODE_WASHING_MACHINE] = &WS2812FX::mode_washing_machine; _brightness = DEFAULT_BRIGHTNESS; currentPalette = CRGBPalette16(CRGB::Black); @@ -485,6 +487,9 @@ class WS2812FX { gamma8(uint8_t), get_random_wheel_index(uint8_t); + int8_t + tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec); + uint16_t ablMilliampsMax, currentMilliamps, @@ -625,7 +630,8 @@ class WS2812FX { mode_phased_noise(void), mode_flow(void), mode_chunchun(void), - mode_dancing_shadows(void); + mode_dancing_shadows(void), + mode_washing_machine(void); private: NeoPixelWrapper *bus; @@ -712,7 +718,7 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Twinklefox","Twinklecat","Halloween Eyes","Solid Pattern","Solid Pattern Tri","Spots","Spots Fade","Glitter","Candle","Fireworks Starburst", "Fireworks 1D","Bouncing Balls","Sinelon","Sinelon Dual","Sinelon Rainbow","Popcorn","Drip","Plasma","Percent","Ripple Rainbow", "Heartbeat","Pacifica","Candle Multi", "Solid Glitter","Sunrise","Phased","Twinkleup","Noise Pal", "Sine","Phased Noise", -"Flow","Chunchun","Dancing Shadows" +"Flow","Chunchun","Dancing Shadows","Washing Machine" ])====="; diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 20bed951..56fdb98a 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -699,6 +699,32 @@ uint16_t WS2812FX::triwave16(uint16_t in) return 0xFFFF - (in - 0x8000)*2; } +/* + * Generates a tristate square wave w/ attac & decay + * @param x input value 0-255 + * @param pulsewidth 0-127 + * @param attdec attac & decay, max. pulsewidth / 2 + * @returns signed waveform value + */ +int8_t WS2812FX::tristate_square8(uint8_t x, uint8_t pulsewidth, uint8_t attdec) { + int8_t a = 127; + if (x > 127) { + a = -127; + x -= 127; + } + + if (x < attdec) { //inc to max + return (int16_t) x * a / attdec; + } + else if (x < pulsewidth - attdec) { //max + return a; + } + else if (x < pulsewidth) { //dec to 0 + return (int16_t) (pulsewidth - x) * a / attdec; + } + return 0; +} + /* * Put a value 0 to 255 in to get a color value. * The colours are a transition r -> g -> b -> back to r @@ -844,7 +870,7 @@ void WS2812FX::handle_palette(void) load_gradient_palette(paletteIndex -13); } - if (singleSegmentMode && paletteFade) //only blend if just one segment uses FastLED mode + if (singleSegmentMode && paletteFade && SEGENV.call > 0) //only blend if just one segment uses FastLED mode { nblendPaletteTowardPalette(currentPalette, targetPalette, 48); } else diff --git a/wled00/NpbWrapper.h b/wled00/NpbWrapper.h index dbb867b6..d867ef86 100644 --- a/wled00/NpbWrapper.h +++ b/wled00/NpbWrapper.h @@ -88,11 +88,20 @@ #define W2PIN 5 //W2 pin for analog LED strip #undef IR_PIN #else + //Enable override of Pins by using the platformio_override.ini file //PWM pins - PINs 5,12,13,15 are used with Magic Home LED Controller - #define RPIN 5 //R pin for analog LED strip - #define GPIN 12 //G pin for analog LED strip - #define BPIN 15 //B pin for analog LED strip - #define WPIN 13 //W pin for analog LED strip + #ifndef RPIN + #define RPIN 5 //R pin for analog LED strip + #endif + #ifndef GPIN + #define GPIN 12 //G pin for analog LED strip + #endif + #ifndef BPIN + #define BPIN 15 //B pin for analog LED strip + #endif + #ifndef WPIN + #define WPIN 13 //W pin for analog LED strip + #endif #endif #undef RLYPIN #define RLYPIN -1 //disable as pin 12 is used by analog LEDs diff --git a/wled00/colors.cpp b/wled00/colors.cpp index f76499a0..361e3e06 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -62,7 +62,31 @@ void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb if (useRGBW && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col); } -void colorCTtoRGB(uint16_t mired, byte* rgb) //white spectrum to rgb +void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc +{ + float r = 0, g = 0, b = 0; + float temp = kelvin / 100; + if (temp <= 66) { + r = 255; + g = round(99.4708025861 * log(temp) - 161.1195681661); + if (temp <= 19) { + b = 0; + } else { + b = round(138.5177312231 * log((temp - 10)) - 305.0447927307); + } + } else { + r = round(329.698727446 * pow((temp - 60), -0.1332047592)); + g = round(288.1221695283 * pow((temp - 60), -0.0755148492)); + b = 255; + } + g += 15; //mod by Aircoookie, a bit less accurate but visibly less pinkish + rgb[0] = (uint8_t) constrain(r, 0, 255); + rgb[1] = (uint8_t) constrain(g, 0, 255); + rgb[2] = (uint8_t) constrain(b, 0, 255); + rgb[3] = 0; +} + +void colorCTtoRGB(uint16_t mired, byte* rgb) //white spectrum to rgb, bins { //this is only an approximation using WS2812B with gamma correction enabled if (mired > 475) { diff --git a/wled00/const.h b/wled00/const.h index 73de807e..88ad4974 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -57,6 +57,7 @@ #define REALTIME_MODE_ADALIGHT 5 #define REALTIME_MODE_ARTNET 6 #define REALTIME_MODE_TPM2NET 7 +#define REALTIME_MODE_DDP 8 //realtime override modes #define REALTIME_OVERRIDE_NONE 0 diff --git a/wled00/data/settings_sync.htm b/wled00/data/settings_sync.htm index 7c13650e..89030c3c 100644 --- a/wled00/data/settings_sync.htm +++ b/wled00/data/settings_sync.htm @@ -5,7 +5,7 @@ function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#sync- function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.DA.value == 1) d.Sf.DA.value = 0; if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;} else if (d.Sf.DI.value == 5568) {if (d.Sf.DA.value == 0) d.Sf.DA.value = 1; if (d.Sf.EU.value == 0) d.Sf.EU.value = 1;} } function SP(){var p = d.Sf.DI.value; d.getElementById("xp").style.display = (p > 0)?"none":"block"; if (p > 0) d.Sf.EP.value = p;} -function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568: d.Sf.DI.value = 5568; break; case 6454: d.Sf.DI.value = 6454; break; }; SP();} +function SetVal(){switch(parseInt(d.Sf.EP.value)){case 5568: d.Sf.DI.value = 5568; break; case 6454: d.Sf.DI.value = 6454; break; case 4048: d.Sf.DI.value = 4048; break; }; SP();} function S(){GetV();SetVal();} function GetV(){var d=document;} @@ -31,13 +31,15 @@ Infrared remote: IR info

WLED Broadcast

UDP Port:
+2nd Port:
Receive Brightness, Color, and Effects
Send notifications on direct change:
Send notifications on button press:
Send Alexa notifications:
Send Philips Hue change notifications:
Send Macro notifications:
-Send notifications twice: +Send notifications twice:
+Reboot required to apply changes.

Realtime

Receive UDP realtime:

Network DMX input
@@ -45,6 +47,7 @@ Type:
Port:
diff --git a/wled00/data/settings_time.htm b/wled00/data/settings_time.htm index c0231277..33b80af2 100644 --- a/wled00/data/settings_time.htm +++ b/wled00/data/settings_time.htm @@ -103,7 +103,11 @@ - + + + + +
UTC offset: seconds (max. 18 hours)
Current local time is unknown. diff --git a/wled00/e131.cpp b/wled00/e131.cpp index 2656f9e2..12e2ba85 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -7,25 +7,66 @@ * E1.31 handler */ -void handleE131Packet(e131_packet_t* p, IPAddress clientIP, bool isArtnet){ - //E1.31 protocol support +//DDP protocol support, called by handleE131Packet +//handles RGB data only +void handleDDPPacket(e131_packet_t* p) { + int lastPushSeq = e131LastSequenceNumber[0]; + + //reject late packets belonging to previous frame (assuming 4 packets max. before push) + if (e131SkipOutOfSequence && lastPushSeq) { + int sn = p->sequenceNum & 0xF; + if (sn) { + if (lastPushSeq > 5) { + if (sn > (lastPushSeq -5) && sn < lastPushSeq) return; + } else { + if (sn > (10 + lastPushSeq) || sn < lastPushSeq) return; + } + } + } + + uint32_t offsetLeds = htonl(p->channelOffset) /3; + uint16_t packetLeds = htons(p->dataLen) /3; + uint8_t* data = p->data; + uint16_t c = 0; + if (p->flags & DDP_TIMECODE_FLAG) c = 4; //packet has timecode flag, we do not support it, but data starts 4 bytes later + + realtimeLock(realtimeTimeoutMs, REALTIME_MODE_DDP); + + for (uint16_t i = offsetLeds; i < offsetLeds + packetLeds; i++) { + setRealtimePixel(i, data[c++], data[c++], data[c++], 0); + } + + bool push = p->flags & DDP_PUSH_FLAG; + if (push) { + e131NewData = true; + byte sn = p->sequenceNum & 0xF; + if (sn) e131LastSequenceNumber[0] = sn; + } +} + +//E1.31 and Art-Net protocol support +void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){ uint16_t uni = 0, dmxChannels = 0; uint8_t* e131_data = nullptr; uint8_t seq = 0, mde = REALTIME_MODE_E131; - if (isArtnet) + if (protocol == P_ARTNET) { uni = p->art_universe; dmxChannels = htons(p->art_length); e131_data = p->art_data; seq = p->art_sequence_number; mde = REALTIME_MODE_ARTNET; - } else { + } else if (protocol == P_E131) { uni = htons(p->universe); dmxChannels = htons(p->property_value_count) -1; e131_data = p->property_values; seq = p->sequence_number; + } else { //DDP + realtimeIP = clientIP; + handleDDPPacket(p); + return; } #ifdef WLED_ENABLE_DMX @@ -133,7 +174,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, bool isArtnet){ } } else { // All subsequent universes start at the first channel. - dmxOffset = isArtnet ? 0 : 1; + dmxOffset = (protocol == P_ARTNET) ? 0 : 1; uint16_t ledsInFirstUniverse = (MAX_CHANNELS_PER_UNIVERSE - DMXAddress) / 3; previousLeds = ledsInFirstUniverse + (previousUniverses - 1) * MAX_LEDS_PER_UNIVERSE; } diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index eb8ec876..1d8f32ef 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -30,6 +30,7 @@ void colorFromUint32(uint32_t in, bool secondary = false); void colorFromUint24(uint32_t in, bool secondary = false); void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0); void colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb +void colorKtoRGB(uint16_t kelvin, byte* rgb); void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb void colorXYtoRGB(float x, float y, byte* rgb); // only defined if huesync disabled TODO @@ -43,7 +44,7 @@ void initDMX(); void handleDMX(); //e131.cpp -void handleE131Packet(e131_packet_t* p, IPAddress clientIP, bool isArtnet); +void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol); //file.cpp bool handleFileRead(AsyncWebServerRequest*, String path); @@ -106,6 +107,10 @@ void updateInterfaces(uint8_t callMode); void handleTransitions(); void handleNightlight(); +//lx_parser.cpp +bool parseLx(int lxValue, byte* rgbw); +void parseLxJson(int lxValue, byte segId, bool secondary); + //mqtt.cpp bool initMqtt(); void publishMqtt(); diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 22ed90ca..98e0c922 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -233,23 +233,23 @@ var t = typeof s[i]; if (gId(fk)) { //already exists if(t === 'boolean') { - gId(fk).checked = s[i]; +gId(fk).checked = s[i]; } else { - gId(fk).value = s[i]; +gId(fk).value = s[i]; } if (gId(fk).previousElementSibling.matches('.l')) { - gId(fk).previousElementSibling.innerHTML = lb; +gId(fk).previousElementSibling.innerHTML = lb; } } else { if(t === 'boolean') { - str += `${lb}:
`; +str += `${lb}:
`; } else if (t === 'number') { - str += `${lb}:
`; +str += `${lb}:
`; } else if (t === 'string') { - str += `${lb}:

`; +str += `${lb}:

`; } } } @@ -357,7 +357,7 @@ onclick="Save()">Save)====="; // Autogenerated from wled00/data/settings_sync.htm, do not edit!! const char PAGE_settings_sync[] PROGMEM = R"=====(Sync Settings