Squashed commit of the white-balance branch.

Updated simple UI.
Minor change in ST7789 display.
This commit is contained in:
Blaz Kristan 2021-10-16 15:13:30 +02:00
parent 5a658b7080
commit c2e6d1c6bf
22 changed files with 4445 additions and 4206 deletions

View File

@ -92,7 +92,7 @@ class St7789DisplayUsermod : public Usermod {
updateLocalTime();
byte minuteCurrent = minute(localTime);
byte hourCurrent = hour(localTime);
byte secondCurrent = second(localTime);
//byte secondCurrent = second(localTime);
knownMinute = minuteCurrent;
knownHour = hourCurrent;
@ -140,7 +140,7 @@ class St7789DisplayUsermod : public Usermod {
void setup()
{
PinManagerPinType pins[] = { { TFT_MOSI, true }, { TFT_MISO, false}, { TFT_SCLK, true }, { TFT_CS, true}, { TFT_DC, true}, { TFT_RST, true }, { TFT_BL, true } };
if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_FourLineDisplay)) { return; }
if (!pinManager.allocateMultiplePins(pins, 7, PinOwner::UM_FourLineDisplay)) { return; }
tft.init();
tft.setRotation(0); //Rotation here is set up for the text to be readable with the port on the left. Use 1 to flip.
@ -364,19 +364,14 @@ class St7789DisplayUsermod : public Usermod {
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
/*
void addToJsonInfo(JsonObject& root)
{
int reading = 20;
//this code adds "u":{"Light":[20," lux"]} to the info object
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray lightArr = user.createNestedArray("Light"); //name
lightArr.add(reading); //value
lightArr.add(" lux"); //unit
JsonArray lightArr = user.createNestedArray("ST7789"); //name
lightArr.add(F("installed")); //unit
}
*/
/*

View File

@ -166,7 +166,7 @@
#define FX_MODE_GRADIENT 46
#define FX_MODE_LOADING 47
#define FX_MODE_POLICE 48
#define FX_MODE_POLICE_ALL 49
#define FX_MODE_POLICE_ALL 49 // candidate for removal
#define FX_MODE_TWO_DOTS 50
#define FX_MODE_TWO_AREAS 51
#define FX_MODE_RUNNING_DUAL 52
@ -247,7 +247,7 @@ class WS2812FX {
// segment parameters
public:
typedef struct Segment { // 29 (32 in memory?) bytes
typedef struct Segment { // 30 (33 in memory?) bytes
uint16_t start;
uint16_t stop; //segment invalid if stop == 0
uint16_t offset;
@ -259,6 +259,7 @@ class WS2812FX {
uint8_t grouping, spacing;
uint8_t opacity;
uint32_t colors[NUM_COLORS];
uint8_t cct; //0==2000K, 255==10160K
char *name;
bool setColor(uint8_t slot, uint32_t c, uint8_t segn) { //returns true if changed
if (slot >= NUM_COLORS || segn >= MAX_NUM_SEGMENTS) return false;

View File

@ -220,6 +220,17 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
uint16_t realIndex = realPixelIndex(i);
uint16_t len = SEGMENT.length();
// determine if we can do white balance
int16_t cct = -1;
for (uint8_t b = 0; b < busses.getNumBusses(); b++) {
Bus *bus = busses.getBus(b);
if (bus == nullptr || !bus->containsPixel(realIndex)) continue;
if (allowCCT || bus->getType() == TYPE_ANALOG_2CH || bus->getType() == TYPE_ANALOG_5CH) {
cct = SEGMENT.cct;
break;
}
}
for (uint16_t j = 0; j < SEGMENT.grouping; j++) {
uint16_t indexSet = realIndex + (IS_REVERSE ? -j : j);
if (indexSet >= SEGMENT.start && indexSet < SEGMENT.stop) {
@ -230,14 +241,14 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
if (indexMir >= SEGMENT.stop) indexMir -= len;
if (indexMir < customMappingSize) indexMir = customMappingTable[indexMir];
busses.setPixelColor(indexMir, col);
busses.setPixelColor(indexMir, col, cct);
}
/* offset/phase */
indexSet += SEGMENT.offset;
if (indexSet >= SEGMENT.stop) indexSet -= len;
if (indexSet < customMappingSize) indexSet = customMappingTable[indexSet];
busses.setPixelColor(indexSet, col);
busses.setPixelColor(indexSet, col, cct);
}
}
} else { //live data, etc.
@ -624,6 +635,7 @@ void WS2812FX::resetSegments() {
_segments[0].setOption(SEG_OPTION_SELECTED, 1);
_segments[0].setOption(SEG_OPTION_ON, 1);
_segments[0].opacity = 255;
_segments[0].cct = 128;
for (uint16_t i = 1; i < MAX_NUM_SEGMENTS; i++)
{
@ -631,6 +643,7 @@ void WS2812FX::resetSegments() {
_segments[i].grouping = 1;
_segments[i].setOption(SEG_OPTION_ON, 1);
_segments[i].opacity = 255;
_segments[i].cct = 128;
_segments[i].speed = DEFAULT_SPEED;
_segments[i].intensity = DEFAULT_INTENSITY;
_segment_runtimes[i].reset();

View File

@ -10,6 +10,9 @@
#include "bus_wrapper.h"
#include <Arduino.h>
//color.cpp
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
// enable additional debug output
#ifdef WLED_DEBUG
#ifndef ESP8266
@ -70,66 +73,35 @@ class Bus {
_start = start;
};
virtual ~Bus() {} //throw the bus under the bus
virtual void show() {}
virtual bool canShow() { return true; }
virtual void setPixelColor(uint16_t pix, uint32_t c) {};
virtual void setBrightness(uint8_t b) {};
virtual void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) {};
virtual uint32_t getPixelColor(uint16_t pix) { return 0; };
virtual void setBrightness(uint8_t b) {};
virtual void cleanup() {};
virtual ~Bus() { //throw the bus under the bus
}
virtual uint8_t getPins(uint8_t* pinArray) { return 0; }
inline uint16_t getStart() {
return _start;
}
inline void setStart(uint16_t start) {
_start = start;
}
virtual uint16_t getLength() {
return 1; // is this ok? shouldn't it be 0 in virtual function?
}
virtual uint16_t getLength() { return 1; }
virtual void setColorOrder() {}
virtual uint8_t getColorOrder() { return COL_ORDER_RGB; }
virtual uint8_t skippedLeds() { return 0; }
virtual uint8_t getColorOrder() {
return COL_ORDER_RGB;
}
virtual bool isRgbw() {
return false;
}
virtual uint8_t skippedLeds() {
return 0;
}
inline uint8_t getType() {
return _type;
}
inline bool isOk() {
return _valid;
}
inline uint16_t getStart() { return _start; }
inline void setStart(uint16_t start) { _start = start; }
inline uint8_t getType() { return _type; }
inline bool isOk() { return _valid; }
inline bool isOffRefreshRequired() { return _needsRefresh; }
inline bool containsPixel(uint16_t pix) { return pix >= _start; }
virtual bool isRgbw() { return false; }
static bool isRgbw(uint8_t type) {
if (type == TYPE_SK6812_RGBW || type == TYPE_TM1814) return true;
if (type > TYPE_ONOFF && type <= TYPE_ANALOG_5CH && type != TYPE_ANALOG_3CH) return true;
return false;
}
inline bool isOffRefreshRequired() {
return _needsRefresh;
}
bool reversed = false;
protected:
@ -190,6 +162,11 @@ class BusDigital : public Bus {
PolyBus::setPixelColor(_busPtr, _iType, pix, c, _colorOrder);
}
void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) {
c = colorBalanceFromKelvin(2000+(cct<<5), c); // color correction from CCT
setPixelColor(pix, c);
}
uint32_t getPixelColor(uint16_t pix) {
if (reversed) pix = _len - pix -1;
else pix += _skip;
@ -285,6 +262,34 @@ class BusPwm : public Bus {
_valid = true;
};
void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) {
if (pix != 0 || !_valid) return; //only react to first pixel
c = colorBalanceFromKelvin(2000+(cct<<5), c); // color correction from CCT (w remains unchanged)
uint8_t r = c >> 16;
uint8_t g = c >> 8;
uint8_t b = c ;
uint8_t w = c >> 24;
switch (_type) {
case TYPE_ANALOG_1CH: //one channel (white), use highest RGBW value
_data[0] = max(r, max(g, max(b, w)));
break;
case TYPE_ANALOG_2CH: //warm white + cold white
// perhaps a non-linear adjustment would be in order. need to test
_data[1] = (w * cct) / 255;
_data[0] = 255 - _data[1]; // or (w * (255-cct)) / 255;
break;
case TYPE_ANALOG_5CH: //RGB + warm white + cold white
// perhaps a non-linear adjustment would be in order. need to test
_data[4] = (w * cct) / 255; w = 255 - w; // or (w * (255-cct)) / 255;
case TYPE_ANALOG_4CH: //RGBW
_data[3] = w;
case TYPE_ANALOG_3CH: //standard dumb RGB
_data[0] = r; _data[1] = g; _data[2] = b;
break;
}
}
void setPixelColor(uint16_t pix, uint32_t c) {
if (pix != 0 || !_valid) return; //only react to first pixel
uint8_t r = c >> 16;
@ -295,14 +300,11 @@ class BusPwm : public Bus {
switch (_type) {
case TYPE_ANALOG_1CH: //one channel (white), use highest RGBW value
_data[0] = max(r, max(g, max(b, w))); break;
case TYPE_ANALOG_2CH: //warm white + cold white, we'll need some nice handling here, for now just R+G channels
case TYPE_ANALOG_2CH: //warm white + cold white
case TYPE_ANALOG_3CH: //standard dumb RGB
case TYPE_ANALOG_4CH: //RGBW
case TYPE_ANALOG_4CH: //standard dumb RGBW
case TYPE_ANALOG_5CH: //we'll want the white handling from 2CH here + RGB
_data[0] = r; _data[1] = g; _data[2] = b; _data[3] = w; _data[4] = 0; break;
default: return;
}
}
@ -419,6 +421,11 @@ class BusNetwork : public Bus {
if (_rgbw) _data[offset+3] = 0xFF & (c >> 24);
}
void setPixelColor(uint16_t pix, uint32_t c, uint8_t cct) {
c = colorBalanceFromKelvin(2000+(cct<<5), c); // color correction from CCT
setPixelColor(pix, c);
}
uint32_t getPixelColor(uint16_t pix) {
if (!_valid || pix >= _len) return 0;
uint16_t offset = pix * _UDPchannels;
@ -540,12 +547,13 @@ class BusManager {
}
}
void setPixelColor(uint16_t pix, uint32_t c) {
void setPixelColor(uint16_t pix, uint32_t c, int16_t cct=-1) {
for (uint8_t i = 0; i < numBusses; i++) {
Bus* b = busses[i];
uint16_t bstart = b->getStart();
if (pix < bstart || pix >= bstart + b->getLength()) continue;
busses[i]->setPixelColor(pix - bstart, c);
if (cct<0) busses[i]->setPixelColor(pix - bstart, c); // no white balance
else busses[i]->setPixelColor(pix - bstart, c, cct); // do white balance
}
}

View File

@ -86,6 +86,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
CJSON(strip.rgbwMode, hw_led[F("rgbwm")]);
CJSON(allowCCT, hw_led["cct"]);
JsonArray ins = hw_led["ins"];
@ -534,6 +535,7 @@ void serializeConfig() {
hw_led[F("maxpwr")] = strip.ablMilliampsMax;
hw_led[F("ledma")] = strip.milliampsPerLed;
hw_led[F("rgbwm")] = strip.rgbwMode;
hw_led["cct"] = allowCCT;
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
@ -550,7 +552,7 @@ void serializeConfig() {
ins[F("order")] = bus->getColorOrder();
ins["rev"] = bus->reversed;
ins[F("skip")] = bus->skippedLeds();
ins["type"] = bus->getType() & 0x7F;;
ins["type"] = bus->getType() & 0x7F;
ins["ref"] = bus->isOffRefreshRequired();
ins[F("rgbw")] = bus->isRgbw();
}

View File

@ -67,6 +67,7 @@ void colorHStoRGB(uint16_t hue, byte sat, byte* rgb) //hue, sat to rgb
if (strip.isRgbw && strip.rgbwMode == RGBW_MODE_LEGACY) colorRGBtoRGBW(col);
}
//get RGB values from color temperature in K (https://tannerhelland.com/2012/09/18/convert-temperature-rgb-algorithm-code.html)
void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc
{
float r = 0, g = 0, b = 0;
@ -84,7 +85,7 @@ void colorKtoRGB(uint16_t kelvin, byte* rgb) //white spectrum to rgb, calc
g = round(288.1221695283 * pow((temp - 60), -0.0755148492));
b = 255;
}
g += 15; //mod by Aircoookie, a bit less accurate but visibly less pinkish
//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);
@ -244,3 +245,24 @@ void colorRGBtoRGBW(byte* rgb) //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_M
float sat = 100.0f * ((high - low) / high);; // maximum saturation is 100 (corrected from 255)
rgb[3] = (byte)((255.0f - sat) / 255.0f * (rgb[0] + rgb[1] + rgb[2]) / 3);
}
// adjust RGB values based on color temperature in K (range [2800-10200]) (https://en.wikipedia.org/wiki/Color_balance)
void colorBalanceFromKelvin(uint16_t kelvin, byte *rgb)
{
byte rgbw[4] = {0,0,0,0};
colorKtoRGB(kelvin, rgbw); // convert Kelvin to RGB
rgb[0] = ((uint16_t) rgbw[0] * rgb[0]) / 255; // correct R
rgb[1] = ((uint16_t) rgbw[1] * rgb[1]) / 255; // correct G
rgb[2] = ((uint16_t) rgbw[2] * rgb[2]) / 255; // correct B
}
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb)
{
byte rgbw[4] = {0,0,0,0};
colorKtoRGB(kelvin, rgbw); // convert Kelvin to RGB
rgbw[0] = ((uint16_t) rgbw[0] * ((rgb>>16) & 0xFF)) / 255; // correct R
rgbw[1] = ((uint16_t) rgbw[1] * ((rgb>> 8) & 0xFF)) / 255; // correct G
rgbw[2] = ((uint16_t) rgbw[2] * ((rgb ) & 0xFF)) / 255; // correct B
rgbw[3] = ((rgb>>24) & 0xFF);
return colorFromRgbw(rgbw);
}

View File

@ -456,17 +456,23 @@ button {
#info table, #nodes table {
table-layout: fixed;
width: 490px;
margin: auto;
width: 100%;
}
#info td, #nodes td {
padding-bottom: 8px;
}
#info .btn, #nodes .btn {
#info .btn {
margin: 5px;
}
#info table .btn, #nodes table .btn {
margin: 0;
}
#info div, #nodes div {
width: 490px;
margin: 0 auto;
}
#lv {
max-width: 600px;
@ -498,13 +504,28 @@ img {
.sliderdisplay {
content:'';
position: absolute;
top: 13px; bottom: 13px;
left: 10px; right: 10px;
top: 13px; left: 8px; right: 8px;
height: 4px;
background: var(--c-4);
border-radius: 17px;
border-radius: 16px;
pointer-events: none;
z-index: -1;
}
#rwrap .sliderdisplay,
#gwrap .sliderdisplay,
#bwrap .sliderdisplay,
#wwrap .sliderdisplay,
#wbal .sliderdisplay {
height: 28px;
top: 0; bottom: 0;
left: 0; right: 0;
/*border: 1px solid var(--c-b);*/
}
#rwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #f00); }
#gwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #0f0); }
#bwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #00f); }
#wwrap .sliderdisplay { background: linear-gradient(90deg, #000 0%, #fff); }
#wbal .sliderdisplay { background: linear-gradient(90deg, #ff8f1f 0%, #fff 50%, #d4e0ff); }
.sliderbubble {
width: 24px;
@ -515,7 +536,7 @@ img {
color: var(--c-f);
padding: 4px 4px 2px;
font-size: 14px;
right: 5px;
right: 3px;
transition: visibility 0.25s ease, opacity 0.25s ease;
opacity: 0;
visibility: hidden;
@ -532,12 +553,20 @@ output.sliderbubbleshow {
input[type=range] {
-webkit-appearance: none;
width: 220px;
padding: 0px;
margin: 0px 10px 0px 10px;
width: 100%;
padding: 0;
margin: 0;
background-color: transparent;
cursor: pointer;
}
#rwrap input[type=range],
#gwrap input[type=range],
#bwrap input[type=range],
#wwrap input[type=range],
#wbal input[type=range] {
width: 252px;
margin: 0;
}
input[type=range]:focus {
outline: none;
}
@ -550,7 +579,7 @@ input[type=range]::-webkit-slider-runnable-track {
input[type=range]::-webkit-slider-thumb {
height: 16px;
width: 16px;
border-radius: 17px;
border-radius: 50%;
background: var(--c-f);
cursor: pointer;
-webkit-appearance: none;
@ -565,19 +594,44 @@ input[type=range]::-moz-range-thumb {
border: 0px solid rgba(0, 0, 0, 0);
height: 16px;
width: 16px;
border-radius: 17px;
border-radius: 50%;
background: var(--c-f);
transform: translateY(7px);
transform: translateY(5px);
}
#wwrap {
display: none;
#rwrap input[type=range]::-webkit-slider-thumb,
#gwrap input[type=range]::-webkit-slider-thumb,
#bwrap input[type=range]::-webkit-slider-thumb,
#wwrap input[type=range]::-webkit-slider-thumb,
#wbal input[type=range]::-webkit-slider-thumb {
height: 18px;
width: 18px;
border: 2px solid #000;
margin-top: 5px;
}
#rwrap input[type=range]::-moz-range-thumb,
#gwrap input[type=range]::-moz-range-thumb,
#bwrap input[type=range]::-moz-range-thumb,
#wwrap input[type=range]::-moz-range-thumb,
#wbal input[type=range]::-moz-range-thumb {
border: 2px solid var(--c-1);
}
#wwrap, #wbal {
display: block;
}
.sliderwrap {
height: 30px;
width: 240px;
width: 230px;
position: relative;
}
#rwrap .sliderwrap,
#gwrap .sliderwrap,
#bwrap .sliderwrap,
#wwrap .sliderwrap,
#wbal .sliderwrap {
width: 260px;
margin: 10px 0 0;
}
.hd {
display: var(--bhd);
@ -589,7 +643,7 @@ input[type=range]::-moz-range-thumb {
}
#picker {
margin: 10px auto;
margin: 10px auto 0;
width: 260px;
}
@ -1141,9 +1195,9 @@ input[type="text"].fnd:hover {
@media all and (max-width: 550px) and (min-width: 374px) {
#info .btn, #nodes .btn {
width: 155px;
width: 150px;
}
#info table, #nodes table {
#info div, #nodes div {
width: 320px;
}
}

View File

@ -49,33 +49,41 @@
<div id="rgbwrap">
<div id="rwrap" class="il">
<div class="sliderwrap il">
<input id="sliderR" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,1)" max="255" min="0" type="range" value="128" />
<input id="sliderR" class="noslide" onchange="fromRgb()" xoninput="updateTrail(this,1)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
<!--output class="sliderbubble"></output-->
</div><br>
<div id="gwrap" class="il">
<div class="sliderwrap il">
<input id="sliderG" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,2)" max="255" min="0" type="range" value="128" />
<input id="sliderG" class="noslide" onchange="fromRgb()" xoninput="updateTrail(this,2)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
<!--output class="sliderbubble"></output-->
</div><br>
<div id="bwrap" class="il">
<div class="sliderwrap il">
<input id="sliderB" class="noslide" onchange="fromRgb()" oninput="updateTrail(this,3)" max="255" min="0" type="range" value="128" />
<input id="sliderB" class="noslide" onchange="fromRgb()" xoninput="updateTrail(this,3)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
<!--output class="sliderbubble"></output-->
</div><br>
</div>
<div id="wwrap">
<p class="labels">White channel</p>
<div class="sliderwrap il">
<input id="sliderW" class="noslide" onchange="setColor(0)" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<p class="labels hd">White channel</p>
<div id="whibri" class="sliderwrap il">
<input id="sliderW" class="noslide" onchange="setColor(0)" xoninput="updateTrail(this,4)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
<output class="sliderbubble"></output>
</div>
<!--output class="sliderbubble"></output-->
</div>
<div id="wbal">
<p class="labels hd">White balance</p>
<div class="sliderwrap il">
<input id="sliderA" class="noslide" onchange="setBalance(this.value)" xoninput="updateTrail(this,5)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<!--output class="sliderbubble"></output-->
</div>
<div id="qcs-w">
<div class="qcs" onclick="pC('#ff0000');" title="Red" style="background-color:#ff0000;"></div>
@ -201,28 +209,32 @@
<img class="wi" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAFCAYAAAC5Fuf5AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABbSURBVChTlY9bDoAwDMNW7n9nwCipytQN4Z8tbrTHmDmF4oPzyldwRqp1SSdnV/NuZuzqerAByxXznBw3igkeFEfXyUuhK/yFM0CxJfyqXZEOc6/Sr9/bf7uIC5Nwd7orMvAPAAAAAElFTkSuQmCC" />
</div><br>
<div id="kv">Loading...</div><br>
<table>
<div>
<!--table>
<tr>
<td class="keytd"><button class="btn" onclick="loadInfo()">Refresh</button></td>
<td class="valtd"><button class="btn" onclick="toggleInfo()">Close Info</button></td>
<td class="keytd"--><button class="btn" onclick="loadInfo()">Refresh</button><!--/td>
<td class="valtd"--><button class="btn" onclick="toggleInfo()">Close Info</button><!--/td>
</tr>
<tr>
<td class="keytd"><button class="btn" onclick="toggleNodes()">Instance List</button></td>
<td class="valtd"><button class="btn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button></td>
<td class="keytd"--><button class="btn" onclick="toggleNodes()">Instance List</button><!--/td>
<td class="valtd"--><button class="btn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button><!--/td>
</tr>
</table><br>
</table-->
</div><br>
<span class="h">Made with <span id="heart">&#10084;&#xFE0E;</span> by Aircoookie and the WLED community</span>
</div>
<div id="nodes" class="modal">
<div id="ndlt">WLED instances</div>
<div id="kn">Loading...</div><br>
<table>
<div>
<!--table>
<tr>
<td class="keytd"><button class="btn infobtn" onclick="loadNodes()">Refresh</button></td>
<td class="valtd"><button class="btn infobtn" onclick="toggleNodes()">Close list</button></td>
<td class="keytd"--><button class="btn infobtn" onclick="loadNodes()">Refresh</button><!--/td>
<td class="valtd"--><button class="btn infobtn" onclick="toggleNodes()">Close list</button><!--/td>
</tr>
</table><br>
</table-->
</div><br>
</div>
<div id="rover" class="modal">

View File

@ -49,15 +49,15 @@ var cpick = new iro.ColorPicker("#picker", {
{
component: iro.ui.Slider,
options: { sliderType: 'value' }
},
}/*,
{
component: iro.ui.Slider,
options: {
sliderType: 'kelvin',
minTemperature: 2100,
maxTemperature: 10000
}
minTemperature: 2000,
maxTemperature: 10160
}
}*/
]
});
@ -253,7 +253,6 @@ function onLoad()
});
});
resetUtil();
updateUI(true);
d.addEventListener("visibilitychange", handleVisibilityChange, false);
size();
@ -392,7 +391,7 @@ function presetError(empty)
if (bckstr.length > 10) hasBackup = true;
} catch (e) {}
var cn = `<div class="seg c">`;
var cn = `<div class="seg c" style="padding:8px;">`;
if (empty)
cn += `You have no presets yet!`;
else
@ -593,6 +592,7 @@ function loadInfo(callback=null)
parseInfo();
showNodes();
if (isInfo) populateInfo(json);
updateUI();
reqsLegal = true;
if (!ws && lastinfo.ws > -1) setTimeout(makeWS,500);
})
@ -949,7 +949,10 @@ function updateTrail(e, slidercol)
case 3: scol = "#00f"; break;
default: scol = "var(--c-f)";
}
var val = `linear-gradient(90deg, ${scol} ${perc}%, var(--c-4) ${perc}%)`;
var g = `${scol} ${perc}%, var(--c-4) ${perc}%`;
if (slidercol==4) g = `#000 0%, #fff`;
if (slidercol==5) g = `#ff8f1f 0%, #fff 50%, #d4e0ff`;
var val = `linear-gradient(90deg, ${g})`;
e.parentNode.getElementsByClassName('sliderdisplay')[0].style.background = val;
var bubble = e.parentNode.parentNode.getElementsByTagName('output')[0];
if (bubble) bubble.innerHTML = e.value;
@ -1013,28 +1016,30 @@ function updatePA(scrollto=false)
}
}
function updateUI(scrollto=false)
function updateUI()
{
gId('buttonPower').className = (isOn) ? "active":"";
gId('buttonNl').className = (nlA) ? "active":"";
gId('buttonSync').className = (syncSend) ? "active":"";
showNodes();
updateSelectedPalette(scrollto);
updateSelectedFx(scrollto);
updateSelectedPalette();
updateSelectedFx();
updateTrail(gId('sliderBri'));
updateTrail(gId('sliderSpeed'));
updateTrail(gId('sliderIntensity'));
updateTrail(gId('sliderW'));
//updateTrail(gId('sliderW'),4);
//updateTrail(gId('sliderA'),5);
if (isRgbw) gId('wwrap').style.display = "block";
gId("wbal").style.display = (lastinfo.leds.cct) ? "block":"none";
updatePA(scrollto);
updatePA(true);
updateHex();
updateRgb();
}
function updateSelectedPalette(scrollto=false)
function updateSelectedPalette()
{
var parent = gId('pallist');
var selPaletteInput = parent.querySelector(`input[name="palette"][value="${selectedPal}"]`);
@ -1047,7 +1052,7 @@ function updateSelectedPalette(scrollto=false)
if (selectedPalette) parent.querySelector(`.lstI[data-id="${selectedPal}"]`).classList.add('selected');
}
function updateSelectedFx(scrollto=false)
function updateSelectedFx()
{
var parent = gId('fxlist');
var selEffectInput = parent.querySelector(`input[name="fx"][value="${selectedFx}"]`);
@ -1160,6 +1165,7 @@ function readState(s,command=false)
}
selectSlot(csel);
gId('sliderW').value = whites[csel];
if (i.cct && i.cct>=0) gId("sliderA").value = i.cct;
gId('sliderSpeed').value = i.sx;
gId('sliderIntensity').value = i.ix;
@ -1189,7 +1195,7 @@ function readState(s,command=false)
selectedPal = i.pal;
selectedFx = i.fx;
redrawPalPrev(); // if any color changed (random palette did at least)
updateUI(true);
updateUI();
}
var jsonTimeout;
@ -1804,7 +1810,7 @@ function selectSlot(b)
cd[csel].classList.add('xxs-w');
cpick.color.set(cd[csel].style.backgroundColor);
gId('sliderW').value = whites[csel];
updateTrail(gId('sliderW'));
//updateTrail(gId('sliderW'),4);
updateHex();
updateRgb();
}
@ -1826,13 +1832,10 @@ function pC(col)
function updateRgb()
{
var col = cpick.color.rgb;
var s = gId('sliderR');
s.value = col.r; updateTrail(s,1);
s = gId('sliderG');
s.value = col.g; updateTrail(s,2);
s = gId('sliderB');
s.value = col.b; updateTrail(s,3);
var s,col = cpick.color.rgb;
s = gId('sliderR').value = col.r; //updateTrail(s,1);
s = gId('sliderG').value = col.g; //updateTrail(s,2);
s = gId('sliderB').value = col.b; //updateTrail(s,3);
}
function updateHex()
@ -1889,6 +1892,12 @@ function setColor(sr)
requestJson(obj);
}
function setBalance(b)
{
var obj = {"seg": {"cct": parseInt(b)}};
requestJson(obj);
}
var hc = 0;
setInterval(function(){if (!isInfo) return; hc+=18; if (hc>300) hc=0; if (hc>200)hc=306; if (hc==144) hc+=36; if (hc==108) hc+=18;
gId('heart').style.color = `hsl(${hc}, 100%, 50%)`;}, 910);

View File

@ -188,13 +188,13 @@
var n = LCs[i].name.substring(2); // bus number
// do we have a led count field
if (nm=="LC") {
var c=parseInt(LCs[i].value,10);
if (!customStarts || !startsDirty[n]) gId("ls"+n).value=sLC;
gId("ls"+n).disabled = !customStarts;
var c=parseInt(LCs[i].value,10); //get LED gount
if (!customStarts || !startsDirty[n]) gId("ls"+n).value=sLC; //update start value
gId("ls"+n).disabled = !customStarts; //enable/disable field editing
if(c){
var s = parseInt(gId("ls"+n).value);
if (s+c > sLC) sLC = s+c;
if(c>maxLC)maxLC=c;
var s = parseInt(gId("ls"+n).value); //start value
if (s+c > sLC) sLC = s+c; //update total count
if(c>maxLC)maxLC=c; //max per output
var t = parseInt(d.getElementsByName("LT"+n)[0].value); // LED type SELECT
if (t<80) sPC+=c; //virtual out busses do not count towards physical LEDs
} // increase led count
@ -247,8 +247,8 @@
gId('m0').innerHTML = memu;
bquot = memu / maxM * 100;
gId('dbar').style.background = `linear-gradient(90deg, ${bquot > 60 ? (bquot > 90 ? "red":"orange"):"#ccc"} 0 ${bquot}%%, #444 ${bquot}%% 100%%)`;
gId('ledwarning').style.display = (sLC > maxPB || maxLC > 800 || bquot > 80) ? 'inline':'none';
gId('ledwarning').style.color = (sLC > maxPB || maxLC > maxPB || bquot > 100) ? 'red':'orange';
gId('ledwarning').style.display = (maxLC > Math.min(maxPB,800) || bquot > 80) ? 'inline':'none';
gId('ledwarning').style.color = (maxLC > Math.max(maxPB,800) || bquot > 100) ? 'red':'orange';
gId('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (<b>ERROR: Using over ${maxM}B!</b>)` : "") : "800 LEDs per output";
// calculate power
var val = Math.ceil((100 + sPC * laprev)/500)/2;
@ -448,6 +448,7 @@ ${i+1}:
<hr style="width:260px">
Make a segment for each output: <input type="checkbox" name="MS"> <br>
Custom bus start indices: <input type="checkbox" onchange="tglSi(this.checked)" id="si"> <br>
Allow WB correction: <input type="checkbox" name="CCT"> <br>
<hr style="width:260px">
<div id="btns"></div>
Touch threshold: <input type="number" class="s" min="0" max="100" name="TT" required><br>

File diff suppressed because one or more lines are too long

View File

@ -42,8 +42,6 @@
<div class ="container">
<div class="tabcontent">
<div id="picker" class="center"></div>
<div id="qcs-w" class="center">
<div class="qcs" onclick="pC('#ff0000');" title="Red" style="background-color:#ff0000;"></div>
<div class="qcs" onclick="pC('#ffa000');" title="Orange" style="background-color:#ffa000;"></div>
@ -63,14 +61,48 @@
<button class="xxs btn" onclick="selectSlot(2);">3</button>
</div>
<div id="wwrap" class="center">
<p class="label h">White channel</p>
<i class="icons slider-icon" id="wht" title="White channel">&#xe333;</i>
<div id="picker" class="center"></div>
<div id="rgbwrap" class="center">
<div id="rwrap" class="il">
<div class="sliderwrap il">
<input id="sliderW" onchange="setColor(0)" oninput="updateTrail(this)" max="255" min="0" type="range" value="128" />
<input id="sliderR" onchange="fromRgb()" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<output class="sliderbubble"></output>
<!--output class="sliderbubble"></output-->
</div><br>
<div id="gwrap" class="il">
<div class="sliderwrap il">
<input id="sliderG" onchange="fromRgb()" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<!--output class="sliderbubble"></output-->
</div><br>
<div id="bwrap" class="il">
<div class="sliderwrap il">
<input id="sliderB" onchange="fromRgb()" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<!--output class="sliderbubble"></output-->
</div><br>
</div>
<div id="wwrap" class="center">
<p class="label hd">White channel</p>
<!--i class="icons slider-icon" id="wht" title="White channel">&#xe333;</i-->
<div class="sliderwrap il">
<input id="sliderW" onchange="setColor(0)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<!--output class="sliderbubble"></output-->
</div>
<div id="wbal">
<p class="labels hd">White balance</p>
<div class="sliderwrap il">
<input id="sliderA" onchange="setBalance(this.value)" max="255" min="0" type="range" value="128" />
<div class="sliderdisplay"></div>
</div>
<!--output class="sliderbubble"></output-->
</div>
<div id="Segments" class="center">

View File

@ -45,7 +45,7 @@ var cpick = new iro.ColorPicker("#picker", {
{
component: iro.ui.Slider,
options: { sliderType: 'value' }
},
}/*,
{
component: iro.ui.Slider,
options: {
@ -53,7 +53,7 @@ var cpick = new iro.ColorPicker("#picker", {
minTemperature: 2100,
maxTemperature: 10000
}
}
}*/
]
});
@ -70,7 +70,7 @@ function applyCfg()
if (bg) sCol('--c-1', bg);
var ccfg = cfg.comp.colors;
//gId('picker').style.display = "none"; // ccfg.picker ? "block":"none";
//gId('rgbwrap').style.display = ccfg.rgb ? "block":"none";
gId('rgbwrap').style.display = ccfg.rgb ? "block":"none";
gId('qcs-w').style.display = ccfg.quick ? "block":"none";
var l = cfg.comp.labels; //l = false;
var e = d.querySelectorAll('.tab-label');
@ -239,7 +239,7 @@ async function onLoad()
});
});
});
updateUI(true);
//updateUI(true);
d.addEventListener("visibilitychange", handleVisibilityChange, false);
size();
@ -458,7 +458,7 @@ function populatePresets()
pNum++;
}
gId('pcont').innerHTML = cn;
updatePA(true);
updatePA();
populateQL();
}
@ -768,22 +768,14 @@ function generateListItemHtml(id, name, clickAction, extraHtml = '')
return `<div class="lstI c" data-id="${id}" onClick="${clickAction}(${id})"><span class="lstIname">${name}</span>${extraHtml}</div>`;
}
function updateTrail(e, slidercol)
function updateTrail(e)
{
if (e==null) return;
var max = e.hasAttribute('max') ? e.attributes.max.value : 255;
var perc = e.value * 100 / max;
perc = parseInt(perc);
if (perc < 50) perc += 2;
var scol;
switch (slidercol) {
case 1: scol = "#f00"; break;
case 2: scol = "#0f0"; break;
case 3: scol = "#00f"; break;
default: scol = "var(--c-f)";
}
var val = `linear-gradient(90deg, ${scol} ${perc}%, var(--c-4) ${perc}%)`;
e.parentNode.getElementsByClassName('sliderdisplay')[0].style.background = val;
e.parentNode.getElementsByClassName('sliderdisplay')[0].style.background = `linear-gradient(90deg, var(--c-f) ${perc}%, var(--c-4) ${perc}%)`;
var bubble = e.parentNode.parentNode.getElementsByTagName('output')[0];
if (bubble) bubble.innerHTML = e.value;
}
@ -794,7 +786,7 @@ function toggleBubble(e)
bubble.classList.toggle('sliderbubbleshow');
}
function updatePA(scrollto=false)
function updatePA()
{
var ps = gEBCN("pres");
for (let i = 0; i < ps.length; i++) {
@ -812,7 +804,7 @@ function updatePA(scrollto=false)
}
}
function updateUI(scrollto=false)
function updateUI()
{
gId('buttonPower').className = (isOn) ? "active":"";
@ -830,11 +822,12 @@ function updateUI(scrollto=false)
updateTrail(gId('sliderBri'));
updateTrail(gId('sliderSpeed'));
updateTrail(gId('sliderIntensity'));
updateTrail(gId('sliderW'));
if (isRgbw) gId('wwrap').style.display = "block";
gId("wbal").style.display = (lastinfo.leds.cct) ? "block":"none";
updatePA(scrollto);
updatePA(true);
redrawPalPrev();
updateRgb();
var l = cfg.comp.labels; //l = false;
var e = d.querySelectorAll('.label');
@ -942,6 +935,7 @@ function readState(s,command=false)
selectSlot(csel);
}
gId('sliderW').value = whites[csel];
if (i.cct && i.cct>=0) gId("sliderA").value = i.cct;
gId('sliderSpeed').value = i.sx;
gId('sliderIntensity').value = i.ix;
@ -970,7 +964,7 @@ function readState(s,command=false)
selectedPal = i.pal;
selectedFx = i.fx;
updateUI(true);
updateUI();
}
var jsonTimeout;
@ -1060,10 +1054,10 @@ function tglBri(b=null)
function tglCP()
{
// var p = gId(`picker`).style.display === "block";
var p = gId('buttonCP').className === "active";
gId('buttonCP').className = !p ? "active":"";
gId('picker').style.display = !p ? "block":"none";
gId('rgbwrap').style.display = !p ? "block":"none";
var csl = gId(`csl`).style.display === "block";
gId('csl').style.display = !csl ? "block":"none";
var ps = gId(`Presets`).style.display === "block";
@ -1176,7 +1170,6 @@ function selectSlot(b)
cd[csel].classList.add('xxs-w');
cpick.color.set(cd[csel].style.backgroundColor);
gId('sliderW').value = whites[csel];
updateTrail(gId('sliderW'));
redrawPalPrev();
}
@ -1195,6 +1188,23 @@ function pC(col)
setColor(0);
}
function updateRgb()
{
var s,col = cpick.color.rgb;
s = gId('sliderR').value = col.r;
s = gId('sliderG').value = col.g;
s = gId('sliderB').value = col.b;
}
function fromRgb()
{
var r = gId('sliderR').value;
var g = gId('sliderG').value;
var b = gId('sliderB').value;
cpick.color.set(`rgb(${r},${g},${b})`);
setColor(0);
}
function setColor(sr)
{
var cd = gId('csl').children;
@ -1212,6 +1222,12 @@ function setColor(sr)
requestJson(obj);
}
function setBalance(b)
{
var obj = {"seg": {"cct": parseInt(b)}};
requestJson(obj);
}
var hc = 0;
setInterval(function(){if (!isInfo) return; hc+=18; if (hc>300) hc=0; if (hc>200)hc=306; if (hc==144) hc+=36; if (hc==108) hc+=18;
gId('heart').style.color = `hsl(${hc}, 100%, 50%)`;}, 910);

View File

@ -71,6 +71,9 @@ void colorFromDecOrHexString(byte* rgb, char* in);
bool colorFromHexString(byte* rgb, const char* in);
void colorRGBtoRGBW(byte* rgb); //rgb to rgbw (http://codewelt.com/rgbw). (RGBW_MODE_LEGACY)
void colorBalanceFromKelvin(uint16_t kelvin, byte *rgb);
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
//dmx.cpp
void initDMX();
void handleDMX();

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -74,6 +74,8 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
if (elem["on"].is<const char*>() && elem["on"].as<const char*>()[0] == 't') on = !on;
seg.setOption(SEG_OPTION_ON, on, id);
seg.cct = elem["cct"] | seg.cct;
JsonArray colarr = elem["col"];
if (!colarr.isNull())
{
@ -96,6 +98,7 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
const char* hexCol = colarr[i];
if (hexCol == nullptr) { //Kelvin color temperature (or invalid), e.g 2400
int kelvin = colarr[i] | -1;
//kelvin = map(seg.cct, 0, 255, 2800, 10200)
if (kelvin < 0) continue;
if (kelvin == 0) seg.setColor(i, 0, id);
if (kelvin > 0) colorKtoRGB(kelvin, brgbw);
@ -370,6 +373,7 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
root["on"] = seg.getOption(SEG_OPTION_ON);
byte segbri = seg.opacity;
root["bri"] = (segbri) ? segbri : 255;
root["cct"] = seg.cct;
if (segmentBounds && seg.name != nullptr) root["n"] = reinterpret_cast<const char *>(seg.name);
@ -466,6 +470,17 @@ void serializeInfo(JsonObject root)
leds[F("count")] = ledCount;
leds[F("rgbw")] = strip.isRgbw;
leds[F("wv")] = strip.isRgbw && (strip.rgbwMode == RGBW_MODE_MANUAL_ONLY || strip.rgbwMode == RGBW_MODE_DUAL); //should a white channel slider be displayed?
leds["cct"] = allowCCT;
for (uint8_t s = 0; s < busses.getNumBusses(); s++) {
Bus *bus = busses.getBus(s);
if (!bus || bus->getLength()==0) break;
switch (bus->getType() & 0x7F) {
case TYPE_ANALOG_5CH:
case TYPE_ANALOG_2CH:
leds["cct"] = true;
break;
}
}
JsonArray leds_pin = leds.createNestedArray("pin");
for (uint8_t s=0; s<busses.getNumBusses(); s++) {

View File

@ -72,6 +72,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
uint8_t pins[5] = {255, 255, 255, 255, 255};
autoSegments = request->hasArg(F("MS"));
allowCCT = request->hasArg(F("CCT"));
for (uint8_t s = 0; s < WLED_MAX_BUSSES; s++) {
char lp[4] = "L0"; lp[2] = 48+s; lp[3] = 0; //ascii 0-9 //strip data pin

View File

@ -36,6 +36,9 @@ void WLED::loop()
{
#ifdef WLED_DEBUG
static unsigned long maxUsermodMillis = 0;
static uint16_t avgUsermodMillis = 0;
static unsigned long maxStripMillis = 0;
static uint16_t avgStripMillis = 0;
#endif
handleTime();
@ -55,6 +58,7 @@ void WLED::loop()
usermods.loop();
#ifdef WLED_DEBUG
usermodMillis = millis() - usermodMillis;
avgUsermodMillis += usermodMillis;
if (usermodMillis > maxUsermodMillis) maxUsermodMillis = usermodMillis;
#endif
@ -91,12 +95,20 @@ void WLED::loop()
yield();
#ifdef WLED_DEBUG
unsigned long stripMillis = millis();
#endif
if (!offMode || strip.isOffRefreshRequred)
strip.service();
#ifdef ESP8266
else if (!noWifiSleep)
delay(1); //required to make sure ESP enters modem sleep (see #1184)
#endif
#ifdef WLED_DEBUG
stripMillis = millis() - stripMillis;
avgStripMillis += stripMillis;
if (stripMillis > maxStripMillis) maxStripMillis = stripMillis;
#endif
}
yield();
#ifdef ESP8266
@ -171,9 +183,13 @@ void WLED::loop()
DEBUG_PRINT(F("NTP last sync: ")); DEBUG_PRINTLN(ntpLastSyncTime);
DEBUG_PRINT(F("Client IP: ")); DEBUG_PRINTLN(Network.localIP());
DEBUG_PRINT(F("Loops/sec: ")); DEBUG_PRINTLN(loops / 30);
DEBUG_PRINT(F("Max UM time[ms]: ")); DEBUG_PRINTLN(maxUsermodMillis);
DEBUG_PRINT(F("UM time[ms]: ")); DEBUG_PRINT(avgUsermodMillis/loops); DEBUG_PRINT("/");DEBUG_PRINTLN(maxUsermodMillis);
DEBUG_PRINT(F("Strip time[ms]: ")); DEBUG_PRINT(avgStripMillis/loops); DEBUG_PRINT("/"); DEBUG_PRINTLN(maxStripMillis);
loops = 0;
maxUsermodMillis = 0;
maxStripMillis = 0;
avgUsermodMillis = 0;
avgStripMillis = 0;
debugTime = millis();
}
loops++;

View File

@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2110111
#define VERSION 2110151
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@ -273,6 +273,7 @@ WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load after p
//if false, only one segment spanning the total LEDs is created,
//but not on LED settings save if there is more than one segment currently
WLED_GLOBAL bool autoSegments _INIT(false);
WLED_GLOBAL bool allowCCT _INIT(false); //CCT color correction
WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color.
WLED_GLOBAL byte colSec[] _INIT_N(({ 0, 0, 0, 0 })); // current RGB(W) secondary color

View File

@ -322,6 +322,7 @@ void getSettingsJS(byte subPage, char* dest)
oappend(SET_F(");"));
sappend('c',SET_F("MS"),autoSegments);
sappend('c',SET_F("CCT"),allowCCT);
for (uint8_t s=0; s < busses.getNumBusses(); s++) {
Bus* bus = busses.getBus(s);