From 2ecc53ba56b6538af7a34bbf7745697fb843f62d Mon Sep 17 00:00:00 2001 From: Ahmed Shehata <4982099+ahmed-shehata@users.noreply.github.com> Date: Thu, 19 Aug 2021 17:24:41 +0100 Subject: [PATCH] UDP Signal color correction (#1902) * added ui changes for saturation in sync * added setters/getters for hsv settings * added color correction logic * faster algorithm for color conversion * added save/load config to fs * adjusted value scale * move color functions to colors.cpp * remove unchecked file * Various small changes Moved settings location in sync settings Changed wording from hyperion to live Moved code into setRealtimePixel(), reducing duplication and enabling the functionality for DMX streams Co-authored-by: Christian Schwinne --- wled00/cfg.cpp | 8 ++++ wled00/colors.cpp | 71 +++++++++++++++++++++++++++++++++++ wled00/data/settings_sync.htm | 7 +++- wled00/fcn_declare.h | 3 ++ wled00/html_settings.h | 13 ++++--- wled00/set.cpp | 4 ++ wled00/udp.cpp | 10 +++-- wled00/wled.h | 3 ++ wled00/xml.cpp | 5 +++ 9 files changed, 114 insertions(+), 10 deletions(-) diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 6d86b6ea3..bdceb3a4c 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -271,6 +271,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(arlsDisableGammaCorrection, if_live[F("no-gc")]); // false CJSON(arlsOffset, if_live[F("offset")]); // 0 + CJSON(liveHSVCorrection, if_live[F("corr")]); + CJSON(liveHSVSaturation, if_live[F("hsvsat")]); + CJSON(liveHSVValue, if_live[F("hsvval")]); + CJSON(alexaEnabled, interfaces["va"][F("alexa")]); // false CJSON(macroAlexaOn, interfaces["va"]["macros"][0]); @@ -608,10 +612,14 @@ void serializeConfig() { if_live_dmx[F("seqskip")] = e131SkipOutOfSequence; if_live_dmx[F("addr")] = DMXAddress; if_live_dmx[F("mode")] = DMXMode; + if_live[F("timeout")] = realtimeTimeoutMs / 100; if_live[F("maxbri")] = arlsForceMaxBri; if_live[F("no-gc")] = arlsDisableGammaCorrection; if_live[F("offset")] = arlsOffset; + if_live[F("corr")] = liveHSVCorrection; + if_live[F("hsvsat")] = liveHSVSaturation; + if_live[F("hsvval")] = liveHSVValue; JsonObject if_va = interfaces.createNestedObject("va"); if_va[F("alexa")] = alexaEnabled; diff --git a/wled00/colors.cpp b/wled00/colors.cpp index dfdd53e07..5db873267 100644 --- a/wled00/colors.cpp +++ b/wled00/colors.cpp @@ -47,6 +47,77 @@ void relativeChangeWhite(int8_t amount, byte lowerBoundary) col[3] = new_val; } +void colorHSVtoRGB(float hue, float saturation, float value, byte& red, byte& green, byte& blue) +{ + float r, g, b; + + auto i = static_cast(hue * 6); + auto f = hue * 6 - i; + auto p = value * (1 - saturation); + auto q = value * (1 - f * saturation); + auto t = value * (1 - (1 - f) * saturation); + + switch (i % 6) + { + case 0: r = value, g = t, b = p; + break; + case 1: r = q, g = value, b = p; + break; + case 2: r = p, g = value, b = t; + break; + case 3: r = p, g = q, b = value; + break; + case 4: r = t, g = p, b = value; + break; + case 5: r = value, g = p, b = q; + break; + } + + red = static_cast(r * 255); + green = static_cast(g * 255); + blue = static_cast(b * 255); +} + +void colorRGBtoHSV(byte red, byte green, byte blue, float& hue, float& saturation, float& value) +{ + auto rd = static_cast(red) / 255; + auto gd = static_cast(green) / 255; + auto bd = static_cast(blue) / 255; + auto max = std::max({ rd, gd, bd }), min = std::min({ rd, gd, bd }); + + value = max; + + auto d = max - min; + saturation = max == 0 ? 0 : d / max; + + hue = 0; + if (max != min) + { + if (max == rd) hue = (gd - bd) / d + (gd < bd ? 6 : 0); + else if (max == gd) hue = (bd - rd) / d + 2; + else if (max == bd) hue = (rd - gd) / d + 4; + hue /= 6; + } +} + +#define SATURATION_THRESHOLD 0.1 +#define MAX_HSV_VALUE 1 +#define MAX_HSV_SATURATION 1 + +//corrects the realtime colors. 10 is the unchanged saturation/value +//this feature might cause slowdowns with large LED counts +void correctColors(byte r, byte g, byte b, byte* rgb) { + float hsv[3] = { 0,0,0 }; + colorRGBtoHSV(r, g,b , hsv[0], hsv[1], hsv[2]); + float saturated = hsv[1] > SATURATION_THRESHOLD ? + hsv[1] * ((float)liveHSVSaturation / 10) : hsv[1]; + float saturation = saturated < MAX_HSV_SATURATION ? saturated : MAX_HSV_SATURATION; + + float valued = hsv[2] * ((float)liveHSVValue/10); + float value = valued < MAX_HSV_VALUE ? valued : MAX_HSV_VALUE; + colorHSVtoRGB(hsv[0], saturation, value, rgb[0], rgb[1], rgb[2]); +} + void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb { float h = ((float)hue)/65535.0; diff --git a/wled00/data/settings_sync.htm b/wled00/data/settings_sync.htm index 23cddc28a..3e42e35fa 100644 --- a/wled00/data/settings_sync.htm +++ b/wled00/data/settings_sync.htm @@ -28,7 +28,7 @@ Send notifications twice:
Reboot required to apply changes.

Instance List

Enable instance list:
-Make this instance discoverable:
+Make this instance discoverable:

Realtime

Receive UDP realtime:

Network DMX input
@@ -59,7 +59,10 @@ DMX mode: Timeout: ms
Force max brightness:
Disable realtime gamma correction:
-Realtime LED offset: +Realtime LED offset:

+Realtime HSV color correction:
+Saturation (1-30):
+Value (1-60):

Alexa Voice Assistant

Emulate Alexa device:
Alexa invocation name: diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 3c458a155..0e3dc1d81 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -58,6 +58,9 @@ void colorFromUint32(uint32_t in, bool secondary = false); void colorFromUint24(uint32_t in, bool secondary = false); uint32_t colorFromRgbw(byte* rgbw); void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0); +void colorHSVtoRGB(float hue, float saturation, float value, byte& red, byte& green, byte& blue); +void colorRGBtoHSV(byte red, byte green, byte blue, float& hue, float& saturation, float& value); +void correctColors(byte r, byte g, byte b, byte* rgb); 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 diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 51e331d9d..4d02814f1 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -254,8 +254,8 @@ Send Macro notifications:
Send notifications twice:
Reboot required to apply changes.

Instance List

Enable instance list:
-Make this instance discoverable:

-Realtime

Receive UDP realtime:

+Make this instance discoverable:

Realtime +

Receive UDP realtime:

Network DMX input
Type: ms
Force max brightness:
Disable realtime gamma correction:
Realtime LED offset:

Alexa Voice Assistant

Emulate Alexa device:
Alexa invocation name:

Blynk

+required>

Realtime HSV color correction:
Saturation (1-30):
Value (1-60):

Alexa Voice Assistant

+Emulate Alexa device:
+Alexa invocation name:

Blynk

Blynk, MQTT and Hue sync all connect to external hosts!
This may impact the responsiveness of the ESP8266.

For best results, only use one of these services at a time.
diff --git a/wled00/set.cpp b/wled00/set.cpp index 37004dd05..358f97985 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -214,6 +214,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) notifyMacro = request->hasArg(F("SM")); notifyTwice = request->hasArg(F("S2")); + liveHSVCorrection = request->hasArg(F("HX")); + liveHSVSaturation = request->arg(F("HS")).toInt(); + liveHSVValue = request->arg(F("HV")).toInt(); + nodeListEnabled = request->hasArg(F("NL")); if (!nodeListEnabled) Nodes.clear(); nodeBroadcastEnabled = request->hasArg(F("NB")); diff --git a/wled00/udp.cpp b/wled00/udp.cpp index df30cde20..590d25407 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -161,7 +161,6 @@ void handleNotifications() for (uint16_t i = 0; i < packetSize -2; i += 3) { setRealtimePixel(id, lbuf[i], lbuf[i+1], lbuf[i+2], 0); - id++; if (id >= ledCount) break; } strip.show(); @@ -385,7 +384,7 @@ void handleNotifications() uint16_t id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00); for (uint16_t i = 4; i < packetSize -2; i += 3) { - if (id >= ledCount) break; + if (id >= ledCount) break; setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0); id++; } @@ -394,7 +393,7 @@ void handleNotifications() uint16_t id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00); for (uint16_t i = 4; i < packetSize -2; i += 4) { - if (id >= ledCount) break; + if (id >= ledCount) break; setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]); id++; } @@ -424,6 +423,11 @@ void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w) uint16_t pix = i + arlsOffset; if (pix < ledCount) { + if (liveHSVCorrection) { + byte correctedColors[3] = {0,0,0}; + correctColors(r, g, b, correctedColors); + r = correctedColors[0]; g = correctedColors[1]; b = correctedColors[2]; + } if (!arlsDisableGammaCorrection && strip.gammaCorrectCol) { strip.setPixelColor(pix, strip.gamma8(r), strip.gamma8(g), strip.gamma8(b), strip.gamma8(w)); diff --git a/wled00/wled.h b/wled00/wled.h index df83c66fd..a92c4f09e 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -294,6 +294,9 @@ WLED_GLOBAL byte irEnabled _INIT(0); // Infrared receiver WLED_GLOBAL uint16_t udpPort _INIT(21324); // WLED notifier default port WLED_GLOBAL uint16_t udpPort2 _INIT(65506); // WLED notifier supplemental port WLED_GLOBAL uint16_t udpRgbPort _INIT(19446); // Hyperion port +WLED_GLOBAL bool liveHSVCorrection _INIT(false); +WLED_GLOBAL uint16_t liveHSVSaturation _INIT(13); +WLED_GLOBAL uint16_t liveHSVValue _INIT(10); WLED_GLOBAL bool receiveNotificationBrightness _INIT(true); // apply brightness from incoming notifications WLED_GLOBAL bool receiveNotificationColor _INIT(true); // apply color diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 453206067..f87bf35db 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -396,6 +396,11 @@ void getSettingsJS(byte subPage, char* dest) { sappend('v',SET_F("UP"),udpPort); sappend('v',SET_F("U2"),udpPort2); + + sappend('c',SET_F("HX"),liveHSVCorrection); + sappend('v',SET_F("HS"),liveHSVSaturation); + sappend('v',SET_F("HV"),liveHSVValue); + sappend('c',SET_F("RB"),receiveNotificationBrightness); sappend('c',SET_F("RC"),receiveNotificationColor); sappend('c',SET_F("RX"),receiveNotificationEffects);