kopia lustrzana https://github.com/Aircoookie/WLED
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 <dev.aircoookie@gmail.com>pull/1911/head
rodzic
3eb1fe0eb2
commit
2ecc53ba56
|
@ -271,6 +271,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||||
CJSON(arlsDisableGammaCorrection, if_live[F("no-gc")]); // false
|
CJSON(arlsDisableGammaCorrection, if_live[F("no-gc")]); // false
|
||||||
CJSON(arlsOffset, if_live[F("offset")]); // 0
|
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(alexaEnabled, interfaces["va"][F("alexa")]); // false
|
||||||
|
|
||||||
CJSON(macroAlexaOn, interfaces["va"]["macros"][0]);
|
CJSON(macroAlexaOn, interfaces["va"]["macros"][0]);
|
||||||
|
@ -608,10 +612,14 @@ void serializeConfig() {
|
||||||
if_live_dmx[F("seqskip")] = e131SkipOutOfSequence;
|
if_live_dmx[F("seqskip")] = e131SkipOutOfSequence;
|
||||||
if_live_dmx[F("addr")] = DMXAddress;
|
if_live_dmx[F("addr")] = DMXAddress;
|
||||||
if_live_dmx[F("mode")] = DMXMode;
|
if_live_dmx[F("mode")] = DMXMode;
|
||||||
|
|
||||||
if_live[F("timeout")] = realtimeTimeoutMs / 100;
|
if_live[F("timeout")] = realtimeTimeoutMs / 100;
|
||||||
if_live[F("maxbri")] = arlsForceMaxBri;
|
if_live[F("maxbri")] = arlsForceMaxBri;
|
||||||
if_live[F("no-gc")] = arlsDisableGammaCorrection;
|
if_live[F("no-gc")] = arlsDisableGammaCorrection;
|
||||||
if_live[F("offset")] = arlsOffset;
|
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");
|
JsonObject if_va = interfaces.createNestedObject("va");
|
||||||
if_va[F("alexa")] = alexaEnabled;
|
if_va[F("alexa")] = alexaEnabled;
|
||||||
|
|
|
@ -47,6 +47,77 @@ void relativeChangeWhite(int8_t amount, byte lowerBoundary)
|
||||||
col[3] = new_val;
|
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<int>(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<uint8_t>(r * 255);
|
||||||
|
green = static_cast<uint8_t>(g * 255);
|
||||||
|
blue = static_cast<uint8_t>(b * 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
void colorRGBtoHSV(byte red, byte green, byte blue, float& hue, float& saturation, float& value)
|
||||||
|
{
|
||||||
|
auto rd = static_cast<float>(red) / 255;
|
||||||
|
auto gd = static_cast<float>(green) / 255;
|
||||||
|
auto bd = static_cast<float>(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
|
void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb
|
||||||
{
|
{
|
||||||
float h = ((float)hue)/65535.0;
|
float h = ((float)hue)/65535.0;
|
||||||
|
|
|
@ -28,7 +28,7 @@ Send notifications twice: <input type="checkbox" name="S2"><br>
|
||||||
<i>Reboot required to apply changes. </i>
|
<i>Reboot required to apply changes. </i>
|
||||||
<h3>Instance List</h3>
|
<h3>Instance List</h3>
|
||||||
Enable instance list: <input type="checkbox" name="NL"><br>
|
Enable instance list: <input type="checkbox" name="NL"><br>
|
||||||
Make this instance discoverable: <input type="checkbox" name="NB"><br>
|
Make this instance discoverable: <input type="checkbox" name="NB">
|
||||||
<h3>Realtime</h3>
|
<h3>Realtime</h3>
|
||||||
Receive UDP realtime: <input type="checkbox" name="RD"><br><br>
|
Receive UDP realtime: <input type="checkbox" name="RD"><br><br>
|
||||||
<i>Network DMX input</i><br>
|
<i>Network DMX input</i><br>
|
||||||
|
@ -59,7 +59,10 @@ DMX mode:
|
||||||
Timeout: <input name="ET" type="number" min="1" max="65000" required> ms<br>
|
Timeout: <input name="ET" type="number" min="1" max="65000" required> ms<br>
|
||||||
Force max brightness: <input type="checkbox" name="FB"><br>
|
Force max brightness: <input type="checkbox" name="FB"><br>
|
||||||
Disable realtime gamma correction: <input type="checkbox" name="RG"><br>
|
Disable realtime gamma correction: <input type="checkbox" name="RG"><br>
|
||||||
Realtime LED offset: <input name="WO" type="number" min="-255" max="255" required>
|
Realtime LED offset: <input name="WO" type="number" min="-255" max="255" required><br><br>
|
||||||
|
Realtime HSV color correction: <input type="checkbox" name="HX"><br>
|
||||||
|
Saturation (1-30): <input name="HS" type="number" min="1" max="30" value="1" step="1"><br>
|
||||||
|
Value (1-60): <input name="HV" type="number" min="1" max="60" value="10" step="1">
|
||||||
<h3>Alexa Voice Assistant</h3>
|
<h3>Alexa Voice Assistant</h3>
|
||||||
Emulate Alexa device: <input type="checkbox" name="AL"><br>
|
Emulate Alexa device: <input type="checkbox" name="AL"><br>
|
||||||
Alexa invocation name: <input name="AI" maxlength="32">
|
Alexa invocation name: <input name="AI" maxlength="32">
|
||||||
|
|
|
@ -58,6 +58,9 @@ void colorFromUint32(uint32_t in, bool secondary = false);
|
||||||
void colorFromUint24(uint32_t in, bool secondary = false);
|
void colorFromUint24(uint32_t in, bool secondary = false);
|
||||||
uint32_t colorFromRgbw(byte* rgbw);
|
uint32_t colorFromRgbw(byte* rgbw);
|
||||||
void relativeChangeWhite(int8_t amount, byte lowerBoundary = 0);
|
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 colorHStoRGB(uint16_t hue, byte sat, byte* rgb); //hue, sat to rgb
|
||||||
void colorKtoRGB(uint16_t kelvin, byte* rgb);
|
void colorKtoRGB(uint16_t kelvin, byte* rgb);
|
||||||
void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb
|
void colorCTtoRGB(uint16_t mired, byte* rgb); //white spectrum to rgb
|
||||||
|
|
|
@ -254,8 +254,8 @@ Send Macro notifications: <input type="checkbox" name="SM"><br>
|
||||||
Send notifications twice: <input type="checkbox" name="S2"><br><i>
|
Send notifications twice: <input type="checkbox" name="S2"><br><i>
|
||||||
Reboot required to apply changes.</i><h3>Instance List</h3>
|
Reboot required to apply changes.</i><h3>Instance List</h3>
|
||||||
Enable instance list: <input type="checkbox" name="NL"><br>
|
Enable instance list: <input type="checkbox" name="NL"><br>
|
||||||
Make this instance discoverable: <input type="checkbox" name="NB"><br><h3>
|
Make this instance discoverable: <input type="checkbox" name="NB"><h3>Realtime
|
||||||
Realtime</h3>Receive UDP realtime: <input type="checkbox" name="RD"><br><br><i>
|
</h3>Receive UDP realtime: <input type="checkbox" name="RD"><br><br><i>
|
||||||
Network DMX input</i><br>Type: <select name="DI" onchange="SP(),adj()"><option
|
Network DMX input</i><br>Type: <select name="DI" onchange="SP(),adj()"><option
|
||||||
value="5568">E1.31 (sACN)</option><option value="6454">Art-Net</option><option
|
value="5568">E1.31 (sACN)</option><option value="6454">Art-Net</option><option
|
||||||
value="4048">DDP</option><option value="0" selected="selected">Custom port
|
value="4048">DDP</option><option value="0" selected="selected">Custom port
|
||||||
|
@ -275,9 +275,12 @@ E1.31 info</a><br>Timeout: <input name="ET" type="number" min="1" max="65000"
|
||||||
required> ms<br>Force max brightness: <input type="checkbox" name="FB"><br>
|
required> ms<br>Force max brightness: <input type="checkbox" name="FB"><br>
|
||||||
Disable realtime gamma correction: <input type="checkbox" name="RG"><br>
|
Disable realtime gamma correction: <input type="checkbox" name="RG"><br>
|
||||||
Realtime LED offset: <input name="WO" type="number" min="-255" max="255"
|
Realtime LED offset: <input name="WO" type="number" min="-255" max="255"
|
||||||
required><h3>Alexa Voice Assistant</h3>Emulate Alexa device: <input
|
required><br><br>Realtime HSV color correction: <input type="checkbox"
|
||||||
type="checkbox" name="AL"><br>Alexa invocation name: <input name="AI"
|
name="HX"><br>Saturation (1-30): <input name="HS" type="number" min="1"
|
||||||
maxlength="32"><h3>Blynk</h3><b>
|
max="30" value="1" step="1"><br>Value (1-60): <input name="HV" type="number"
|
||||||
|
min="1" max="60" value="10" step="1"><h3>Alexa Voice Assistant</h3>
|
||||||
|
Emulate Alexa device: <input type="checkbox" name="AL"><br>
|
||||||
|
Alexa invocation name: <input name="AI" maxlength="32"><h3>Blynk</h3><b>
|
||||||
Blynk, MQTT and Hue sync all connect to external hosts!<br>
|
Blynk, MQTT and Hue sync all connect to external hosts!<br>
|
||||||
This may impact the responsiveness of the ESP8266.</b><br>
|
This may impact the responsiveness of the ESP8266.</b><br>
|
||||||
For best results, only use one of these services at a time.<br>
|
For best results, only use one of these services at a time.<br>
|
||||||
|
|
|
@ -214,6 +214,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||||
notifyMacro = request->hasArg(F("SM"));
|
notifyMacro = request->hasArg(F("SM"));
|
||||||
notifyTwice = request->hasArg(F("S2"));
|
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"));
|
nodeListEnabled = request->hasArg(F("NL"));
|
||||||
if (!nodeListEnabled) Nodes.clear();
|
if (!nodeListEnabled) Nodes.clear();
|
||||||
nodeBroadcastEnabled = request->hasArg(F("NB"));
|
nodeBroadcastEnabled = request->hasArg(F("NB"));
|
||||||
|
|
|
@ -161,7 +161,6 @@ void handleNotifications()
|
||||||
for (uint16_t i = 0; i < packetSize -2; i += 3)
|
for (uint16_t i = 0; i < packetSize -2; i += 3)
|
||||||
{
|
{
|
||||||
setRealtimePixel(id, lbuf[i], lbuf[i+1], lbuf[i+2], 0);
|
setRealtimePixel(id, lbuf[i], lbuf[i+1], lbuf[i+2], 0);
|
||||||
|
|
||||||
id++; if (id >= ledCount) break;
|
id++; if (id >= ledCount) break;
|
||||||
}
|
}
|
||||||
strip.show();
|
strip.show();
|
||||||
|
@ -385,7 +384,7 @@ void handleNotifications()
|
||||||
uint16_t id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00);
|
uint16_t id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00);
|
||||||
for (uint16_t i = 4; i < packetSize -2; i += 3)
|
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);
|
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], 0);
|
||||||
id++;
|
id++;
|
||||||
}
|
}
|
||||||
|
@ -394,7 +393,7 @@ void handleNotifications()
|
||||||
uint16_t id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00);
|
uint16_t id = ((udpIn[3] << 0) & 0xFF) + ((udpIn[2] << 8) & 0xFF00);
|
||||||
for (uint16_t i = 4; i < packetSize -2; i += 4)
|
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]);
|
setRealtimePixel(id, udpIn[i], udpIn[i+1], udpIn[i+2], udpIn[i+3]);
|
||||||
id++;
|
id++;
|
||||||
}
|
}
|
||||||
|
@ -424,6 +423,11 @@ void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w)
|
||||||
uint16_t pix = i + arlsOffset;
|
uint16_t pix = i + arlsOffset;
|
||||||
if (pix < ledCount)
|
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)
|
if (!arlsDisableGammaCorrection && strip.gammaCorrectCol)
|
||||||
{
|
{
|
||||||
strip.setPixelColor(pix, strip.gamma8(r), strip.gamma8(g), strip.gamma8(b), strip.gamma8(w));
|
strip.setPixelColor(pix, strip.gamma8(r), strip.gamma8(g), strip.gamma8(b), strip.gamma8(w));
|
||||||
|
|
|
@ -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 udpPort _INIT(21324); // WLED notifier default port
|
||||||
WLED_GLOBAL uint16_t udpPort2 _INIT(65506); // WLED notifier supplemental port
|
WLED_GLOBAL uint16_t udpPort2 _INIT(65506); // WLED notifier supplemental port
|
||||||
WLED_GLOBAL uint16_t udpRgbPort _INIT(19446); // Hyperion 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 receiveNotificationBrightness _INIT(true); // apply brightness from incoming notifications
|
||||||
WLED_GLOBAL bool receiveNotificationColor _INIT(true); // apply color
|
WLED_GLOBAL bool receiveNotificationColor _INIT(true); // apply color
|
||||||
|
|
|
@ -396,6 +396,11 @@ void getSettingsJS(byte subPage, char* dest)
|
||||||
{
|
{
|
||||||
sappend('v',SET_F("UP"),udpPort);
|
sappend('v',SET_F("UP"),udpPort);
|
||||||
sappend('v',SET_F("U2"),udpPort2);
|
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("RB"),receiveNotificationBrightness);
|
||||||
sappend('c',SET_F("RC"),receiveNotificationColor);
|
sappend('c',SET_F("RC"),receiveNotificationColor);
|
||||||
sappend('c',SET_F("RX"),receiveNotificationEffects);
|
sappend('c',SET_F("RX"),receiveNotificationEffects);
|
||||||
|
|
Ładowanie…
Reference in New Issue