From 46b66c76ef5ce36e7dc2d2f2dbafa8036ddf3d4c Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 2 Oct 2021 10:48:48 +0200 Subject: [PATCH 01/13] Merge pbolduc/WLED/feature/upd-ddp-send into network-bus --- wled00/udp.cpp | 111 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/wled00/udp.cpp b/wled00/udp.cpp index d0b22ec5..43e7e7c9 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -1,4 +1,5 @@ #include "wled.h" +#include "src/dependencies/json/ArduinoJson-v6.h" /* * UDP sync notifier / Realtime / Hyperion / TPM2.NET @@ -89,7 +90,6 @@ void notify(byte callMode, bool followUp) notificationTwoRequired = (followUp)? false:notifyTwice; } - void realtimeLock(uint32_t timeoutMs, byte md) { if (!realtimeMode && !realtimeOverride){ @@ -101,6 +101,10 @@ void realtimeLock(uint32_t timeoutMs, byte md) realtimeTimeout = millis() + timeoutMs; if (timeoutMs == 255001 || timeoutMs == 65000) realtimeTimeout = UINT32_MAX; + // if strip is off (bri==0) and not already in RTM + if (bri == 0 && !realtimeMode) { + strip.setBrightness(scaledBri(briLast)); + } realtimeMode = md; if (arlsForceMaxBri && !realtimeOverride) strip.setBrightness(scaledBri(255)); @@ -514,3 +518,108 @@ void sendSysInfoUDP() notifier2Udp.write(data, sizeof(data)); notifier2Udp.endPacket(); } + + +/*********************************************************************************************\ + * Art-Net, DDP, E131 output - work in progress +\*********************************************************************************************/ + +#define DDP_HEADER_LEN 10 +#define DDP_SYNCPACKET_LEN 10 + +#define DDP_FLAGS1_VER 0xc0 // version mask +#define DDP_FLAGS1_VER1 0x40 // version=1 +#define DDP_FLAGS1_PUSH 0x01 +#define DDP_FLAGS1_QUERY 0x02 +#define DDP_FLAGS1_REPLY 0x04 +#define DDP_FLAGS1_STORAGE 0x08 +#define DDP_FLAGS1_TIME 0x10 + +#define DDP_ID_DISPLAY 1 +#define DDP_ID_CONFIG 250 +#define DDP_ID_STATUS 251 + +// 1440 channels per packet +#define DDP_CHANNELS_PER_PACKET 1440 // 480 leds + +// +// Send real time DDP UDP updates to the specified client +// +// client - the IP address to send to +// length - the number of pixels +// buffer - a buffer of at least length*4 bytes long +// isRGBW - true if the buffer contains 4 components per pixel + +uint8_t sequenceNumber = 0; // this needs to be shared across all outputs + +uint8_t realtimeBroadcast(IPAddress client, uint16_t length, uint8_t *buffer, bool isRGBW) { + WiFiUDP ddpUdp; + + // calclate the number of UDP packets we need to send + uint16_t channelCount = length * 3; // 1 channel for every R,G,B value + uint16_t packetCount = channelCount / DDP_CHANNELS_PER_PACKET; + if (channelCount % DDP_CHANNELS_PER_PACKET) { + packetCount++; + } + + // there are 3 channels per RGB pixel + uint16_t channel = 0; // TODO: allow specifying the start channel + // the current position in the buffer + uint16_t bufferOffset = 0; + + for (uint16_t currentPacket = 0; currentPacket < packetCount; currentPacket++) { + if (sequenceNumber > 15) sequenceNumber = 0; + + int rc = ddpUdp.beginPacket(client, DDP_PORT); + if (rc == 0) { + //DEBUG_PRINTLN("WiFiUDP.beginPacket returned an error"); + return 1; // problem + } + + // the amount of data is AFTER the header in the current packet + uint16_t packetSize = DDP_CHANNELS_PER_PACKET; + + uint8_t flags = DDP_FLAGS1_VER1; + if (currentPacket == (packetCount - 1)) { + // last packet, set the push flag + // TODO: determine if we want to send an empty push packet to each destination after sending the pixel data + flags = DDP_FLAGS1_VER1 | DDP_FLAGS1_PUSH; + if (channelCount % DDP_CHANNELS_PER_PACKET) { + packetSize = channelCount % DDP_CHANNELS_PER_PACKET; + } + } + + // write the header + /*0*/ddpUdp.write(flags); + /*1*/ddpUdp.write(sequenceNumber++ & 0xF); + /*2*/ddpUdp.write(0); + /*3*/ddpUdp.write(DDP_ID_DISPLAY); + // data offset in bytes, 32-bit number, MSB first + /*4*/ddpUdp.write((channel & 0xFF000000) >> 24); + /*5*/ddpUdp.write((channel & 0x00FF0000) >> 16); + /*6*/ddpUdp.write((channel & 0x0000FF00) >> 8); + /*7*/ddpUdp.write((channel & 0x000000FF)); + // data length in bytes, 16-bit number, MSB first + /*8*/ddpUdp.write((packetSize & 0xFF00) >> 8); + /*9*/ddpUdp.write(packetSize & 0xFF); + + // write the colors, the write write(const uint8_t *buffer, size_t size) + // function is just a loop internally too + for (uint16_t i = 0; i < packetSize; i += 3) { + ddpUdp.write(buffer[bufferOffset++]); // R + ddpUdp.write(buffer[bufferOffset++]); // G + ddpUdp.write(buffer[bufferOffset++]); // B + if (isRGBW) bufferOffset++; + } + + rc = ddpUdp.endPacket(); + if (rc == 0) { + //DEBUG_PRINTLN("WiFiUDP.endPacket returned an error"); + return 1; // problem + } + + channel += packetSize; + } + + return 0; +} From c1b08779567476636959ab633c054170bed9b9a7 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 2 Oct 2021 15:07:02 +0200 Subject: [PATCH 02/13] Bus implementation. Added separate DDP listener. LED settings overhaul. Minor fixes: - reduced LED memory - boot brightness fix - reduced debug frequency - added usermod time spent debug - mDNS glitch fix --- wled00/bus_manager.h | 166 +++- wled00/const.h | 8 +- wled00/data/settings_leds.htm | 205 +++-- wled00/data/settings_sync.htm | 2 +- wled00/fcn_declare.h | 4 +- wled00/html_settings.h | 76 +- wled00/html_ui.h | 1551 ++++++++++++++++----------------- wled00/udp.cpp | 82 +- wled00/wled.cpp | 78 +- wled00/wled.h | 19 +- wled00/xml.cpp | 4 +- 11 files changed, 1218 insertions(+), 977 deletions(-) diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 55d929e0..ec77b889 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -10,6 +10,20 @@ #include "bus_wrapper.h" #include +// enable additional debug output +#ifdef WLED_DEBUG + #ifndef ESP8266 + #include + #endif + #define DEBUG_PRINT(x) Serial.print(x) + #define DEBUG_PRINTLN(x) Serial.println(x) + #define DEBUG_PRINTF(x...) Serial.printf(x) +#else + #define DEBUG_PRINT(x) + #define DEBUG_PRINTLN(x) + #define DEBUG_PRINTF(x...) +#endif + //temporary struct for passing bus configuration to bus struct BusConfig { uint8_t type = TYPE_WS2812_RGB; @@ -23,7 +37,8 @@ struct BusConfig { type = busType; count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip; uint8_t nPins = 1; - if (type > 47) nPins = 2; + if (type >= 10 && type <= 15) nPins = 4; + else if (type > 47) nPins = 2; else if (type > 40 && type < 46) nPins = NUM_PWM_PINS(type); for (uint8_t i = 0; i < nPins; i++) pins[i] = ppins[i]; } @@ -74,7 +89,7 @@ class Bus { } virtual uint16_t getLength() { - return 1; + return 1; // is this ok? shouldn't it be 0 in virtual function? } virtual void setColorOrder() {} @@ -135,7 +150,7 @@ class BusDigital : public Bus { _busPtr = PolyBus::create(_iType, _pins, _len, nr); _valid = (_busPtr != nullptr); _colorOrder = bc.colorOrder; - //Serial.printf("Successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n",nr, len, type, pins[0],pins[1],_iType); + DEBUG_PRINTF("Successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u)\n",nr, len, type, pins[0],pins[1],_iType); }; inline void show() { @@ -201,7 +216,7 @@ class BusDigital : public Bus { } void cleanup() { - //Serial.println("Digital Cleanup"); + DEBUG_PRINTLN("Digital Cleanup"); PolyBus::cleanup(_busPtr, _iType); _iType = I_NONE; _valid = false; @@ -227,6 +242,7 @@ class BusDigital : public Bus { class BusPwm : public Bus { public: BusPwm(BusConfig &bc) : Bus(bc.type, bc.start) { + _valid = false; if (!IS_PWM(bc.type)) return; uint8_t numPins = NUM_PWM_PINS(bc.type); @@ -280,10 +296,12 @@ class BusPwm : public Bus { //does no index check uint32_t getPixelColor(uint16_t pix) { + if (!_valid) return 0; return ((_data[3] << 24) | (_data[0] << 16) | (_data[1] << 8) | (_data[2])); } void show() { + if (!_valid) return; uint8_t numPins = NUM_PWM_PINS(_type); for (uint8_t i = 0; i < numPins; i++) { uint8_t scaled = (_data[i] * _bri) / 255; @@ -328,13 +346,13 @@ class BusPwm : public Bus { void deallocatePins() { uint8_t numPins = NUM_PWM_PINS(_type); for (uint8_t i = 0; i < numPins; i++) { + pinManager.deallocatePin(_pins[i], PinOwner::BusPwm); if (!pinManager.isPinOk(_pins[i])) continue; #ifdef ESP8266 digitalWrite(_pins[i], LOW); //turn off PWM interrupt #else if (_ledcStart < 16) ledcDetachPin(_pins[i]); #endif - pinManager.deallocatePin(_pins[i], PinOwner::BusPwm); } #ifdef ARDUINO_ARCH_ESP32 pinManager.deallocateLedc(_ledcStart, numPins); @@ -342,6 +360,133 @@ class BusPwm : public Bus { } }; + +class BusNetwork : public Bus { + public: + BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start) { + _valid = false; +// switch (bc.type) { +// case TYPE_NET_ARTNET_RGB: +// _rgbw = false; +// _UDPtype = 2; +// break; +// case TYPE_NET_E131_RGB: +// _rgbw = false; +// _UDPtype = 1; +// break; +// case TYPE_NET_DDP_RGB: +// _rgbw = false; +// _UDPtype = 0; +// break; +// default: + _rgbw = false; + _UDPtype = bc.type - TYPE_NET_DDP_RGB; +// break; +// } + _UDPchannels = _rgbw ? 4 : 3; + //_rgbw |= bc.rgbwOverride; // RGBW override in bit 7 or can have a special type + _data = (byte *)malloc(bc.count * _UDPchannels); + if (_data == nullptr) return; + memset(_data, 0, bc.count * _UDPchannels); + _len = bc.count; + _colorOrder = bc.colorOrder; + _client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]); + _broadcastLock = false; + _valid = true; + _data2 = (byte *)malloc(_len * _UDPchannels); + }; + + void setPixelColor(uint16_t pix, uint32_t c) { + if (!_valid || pix >= _len) return; + uint16_t offset = pix * _UDPchannels; + _data[offset] = 0xFF & (c >> 16); + _data[offset+1] = 0xFF & (c >> 8); + _data[offset+2] = 0xFF & (c ); + if (_rgbw) _data[offset+3] = 0xFF & (c >> 24); + } + + uint32_t getPixelColor(uint16_t pix) { + if (!_valid || pix >= _len) return 0; + uint16_t offset = pix * _UDPchannels; + // behave as NeoPixelBus + return ( + (_rgbw ? (scale8(_data[offset+3], _bri) << 24) : 0) + | (scale8(_data[offset] , _bri) << 16) + | (scale8(_data[offset+1], _bri) << 8) + | (scale8(_data[offset+2], _bri) ) + ); + } + + void show() { + if (!_valid || !canShow()) return; + _broadcastLock = true; + // apply brightness to second buffer + if (_data2 == nullptr) { + // but display original buffer if memory allocation failed + realtimeBroadcast(_UDPtype, _client, _len, _data, _rgbw); + } else { + for (uint16_t pix=0; pix<_len; pix++) { + uint16_t offset = pix * _UDPchannels; + _data2[offset ] = scale8(_data[offset ], _bri); + _data2[offset+1] = scale8(_data[offset+1], _bri); + _data2[offset+2] = scale8(_data[offset+2], _bri); + if (_rgbw) _data2[offset+3] = scale8(_data[offset+3], _bri); + } + realtimeBroadcast(_UDPtype, _client, _len, _data2, _rgbw); + } + _broadcastLock = false; + } + + inline bool canShow() { + // this should be a return value from UDP routine if it is still sending data out + return !_broadcastLock; + } + + inline void setBrightness(uint8_t b) { + _bri = b; + } + + uint8_t getPins(uint8_t* pinArray) { + for (uint8_t i = 0; i < 4; i++) { + pinArray[i] = _client[i]; + } + return 4; + } + + inline bool isRgbw() { + return _rgbw; + } + + inline uint16_t getLength() { + return _len; + } + + void cleanup() { + _type = I_NONE; + _valid = false; + if (_data != nullptr) free(_data); + _data = nullptr; + if (_data2 != nullptr) free(_data2); + _data2 = nullptr; + } + + ~BusNetwork() { + cleanup(); + } + + private: + IPAddress _client; + uint16_t _len = 0; + uint8_t _colorOrder; + uint8_t _bri = 255; + uint8_t _UDPtype; + uint8_t _UDPchannels; + bool _rgbw; + bool _broadcastLock; + byte *_data, *_data2; +}; + + class BusManager { public: BusManager() { @@ -352,7 +497,7 @@ class BusManager { static uint32_t memUsage(BusConfig &bc) { uint8_t type = bc.type; uint16_t len = bc.count; - if (type < 32) { + if (type > 15 && type < 32) { #ifdef ESP8266 if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem if (type > 29) return len*20; //RGBW @@ -365,15 +510,17 @@ class BusManager { return len*6; #endif } - - if (type > 31 && type < 48) return 5; + if (type >= 10 && type <= 15) return len*6; // double buffer for network + if (type > 31 && type < 48) return 5; if (type == 44 || type == 45) return len*4; //RGBW return len*3; } int add(BusConfig &bc) { if (numBusses >= WLED_MAX_BUSSES) return -1; - if (IS_DIGITAL(bc.type)) { + if (bc.type>=10 && bc.type<=15) { + busses[numBusses] = new BusNetwork(bc); + } else if (IS_DIGITAL(bc.type)) { busses[numBusses] = new BusDigital(bc, numBusses); } else { busses[numBusses] = new BusPwm(bc); @@ -444,6 +591,7 @@ class BusManager { return len; } + // a workaround static inline bool isRgbw(uint8_t type) { return Bus::isRgbw(type); } diff --git a/wled00/const.h b/wled00/const.h index e5061627..ef9f085a 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -122,6 +122,10 @@ #define TYPE_NONE 0 //light is not configured #define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light +//network types (master broadcast) (10-15) +#define TYPE_NET_DDP_RGB 10 //network DDP RGB bus (master broadcast bus) +#define TYPE_NET_E131_RGB 11 //network E131 RGB bus (master broadcast bus) +#define TYPE_NET_ARTNET_RGB 12 //network ArtNet RGB bus (master broadcast bus) //Digital types (data pin only) (16-31) #define TYPE_WS2812_1CH 20 //white-only chips #define TYPE_WS2812_WWA 21 //amber + warm + cold white @@ -241,7 +245,7 @@ #ifndef MAX_LED_MEMORY #ifdef ESP8266 -#define MAX_LED_MEMORY 5000 +#define MAX_LED_MEMORY 4000 #else #define MAX_LED_MEMORY 64000 #endif @@ -282,7 +286,7 @@ // Maximum size of node map (list of other WLED instances) #ifdef ESP8266 - #define WLED_MAX_NODES 15 + #define WLED_MAX_NODES 24 #else #define WLED_MAX_NODES 150 #endif diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 469c72e1..063f59d9 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -35,22 +35,36 @@ var LCs = d.getElementsByTagName("input"); for (i=0; ie==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(d.um_p)} can't be used.`);LCs[i].value="";LCs[i].focus();return false;} else if (LCs[i].value > 5 && LCs[i].value < 12) {alert("Sorry, pins 6-11 can not be used.");LCs[i].value="";LCs[i].focus();return false;} + else if (!(nm == "IR" || nm=="BT") && LCs[i].value > 33) {alert("Sorry, pins >33 are input only.");LCs[i].value="";LCs[i].focus();return false;} for (j=i+1; j 100) {var msg = "Too many LEDs for me to handle!"; if (maxM < 10000) msg += "\n\rConsider using an ESP32."; alert(msg);} @@ -89,21 +103,22 @@ UI(); } //returns mem usage - function getMem(type, len, p0) { - if (type < 32) { + function getMem(t, len, p0) { + if (t >= 10 && t <= 12) return len*6; // double buffer for network UDP bus + if (t > 15 && t < 32) { if (maxM < 10000 && p0==3) { //8266 DMA uses 5x the mem - if (type > 29) return len*20; //RGBW + if (t > 29) return len*20; //RGBW return len*15; } else if (maxM >= 10000) //ESP32 RMT uses double buffer? { - if (type > 29) return len*8; //RGBW + if (t > 29) return len*8; //RGBW return len*6; } - if (type > 29) return len*4; //RGBW + if (t > 29) return len*4; //RGBW return len*3; } - if (type > 31 && type < 48) return 5; - if (type == 44 || type == 45) return len*4; //RGBW + if (t > 31 && t < 48) return 5; + if (t == 44 || t == 45) return len*4; //RGBW return len*3; } function UI(change=false) @@ -115,86 +130,121 @@ if (d.Sf.LA.value == 255) laprev = 12; else if (d.Sf.LA.value > 0) laprev = d.Sf.LA.value; + // enable/disable LED fields var s = d.getElementsByTagName("select"); for (i=0; i 49) ? "Data:" : (type >41) ? "Pins:" : "Pin:"; - gId("p1d"+n).innerHTML = (type > 49) ? "Clk:" : ""; - var LK = d.getElementsByName("L1"+n)[0]; + var n = s[i].name.substring(2); + var t = parseInt(s[i].value,10); + gId("p0d"+n).innerHTML = (t>=10 && t<=15) ? "IP address:" : (t > 49) ? "Data GPIO:" : (t >41) ? "GPIOs:" : "GPIO:"; + gId("p1d"+n).innerHTML = (t > 49) ? "Clk GPIO:" : ""; + var LK = d.getElementsByName("L1"+n)[0]; // clock pin - memu += getMem(type, d.getElementsByName("LC"+n)[0].value, d.getElementsByName("L0"+n)[0].value); + memu += getMem(t, d.getElementsByName("LC"+n)[0].value, d.getElementsByName("L0"+n)[0].value); // calc memory + // enumerate pins for (p=1; p<5; p++) { - var LK = d.getElementsByName("L"+p+n)[0]; + var LK = d.getElementsByName("L"+p+n)[0]; // secondary pins if (!LK) continue; - if ((type>49 && p==1) || (type>41 && type < 50 && (p+40 < type))) // TYPE_xxxx values from const.h + if (((t>=10 && t<=15) && p<4) || (t>49 && p==1) || (t>41 && t < 50 && (p+40 < t))) // TYPE_xxxx values from const.h { + // display pin field LK.style.display = "inline"; LK.required = true; } else { + // hide pin field LK.style.display = "none"; LK.required = false; LK.value=""; } } - if (type == 30 || type == 31 || (type > 40 && type < 46 && type != 43)) isRGBW = true; - gId("dig"+n+"c").style.display = (type > 40 && type < 48) ? "none":"inline"; // hide count for analog - gId("dig"+n+"s").style.display = (type > 40 && type < 48) ? "none":"inline"; // hide skip 1st for virtual & analog - gId("rev"+n).innerHTML = (type > 40 && type < 48) ? "Inverted":"Reverse (rotated 180°)"; // change reverse text for analog - gId("psd"+n).innerHTML = (type > 31 && type < 48) ? "Index:":"Start:"; + if (change) { + gId("ls"+n).value = n+1; // set LED start + if (t > 31 && t < 48) d.getElementsByName("LC"+n)[0].value = 1; // for sanity change analog count just to 1 LED + } + isRGBW |= (t == 30 || t == 31 || (t > 40 && t < 46 && t != 43)); // RGBW checkbox, TYPE_xxxx values from const.h + gId("co"+n).style.display = (t<16 || t == 41 || t == 42) ? "none":"inline"; // hide color order for PWM W & WW/CW + gId("dig"+n+"c").style.display = (t > 40 && t < 48) ? "none":"inline"; // hide count for analog + gId("dig"+n+"r").style.display = (t<16) ? "none":"inline"; // hide reversed for virtual + gId("dig"+n+"s").style.display = (t<16 || (t > 40 && t < 48)) ? "none":"inline"; // hide skip 1st for virtual & analog + gId("rev"+n).innerHTML = (t > 40 && t < 48) ? "Inverted":"Reverse (rotated 180°)"; // change reverse text for analog + gId("psd"+n).innerHTML = (t > 40 && t < 48) ? "Index:":"Start:"; // change analog start description } } - + // display white channel calculation method var myC = d.querySelectorAll('.wc'), l = myC.length; for (i = 0; i < l; i++) { myC[i].style.display = (isRGBW) ? 'inline':'none'; } - + // check for pin conflicts + var LCs = d.getElementsByTagName("input"); + var sLC = 0, maxLC = 0; + for (i=0; imaxLC)maxLC=c;} // increase led count + continue; + } + // do we have led pins for digital leds + if (nm=="L0" || nm=="L1") { + var lc=d.getElementsByName("LC"+n)[0]; + lc.max=maxPB; // update max led count value + } + // ignore IP address + if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3") { + var t = parseInt(d.getElementsByName("LT"+n)[0].value, 10); // LED type SELECT + if (t<16) { + LCs[i].max = 255; + LCs[i].min = 0; + continue; // do not check conflicts + } else { + LCs[i].max = 33; + LCs[i].min = -1; + } + } + // check for pin conflicts + if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR") + if (LCs[i].value!="" && LCs[i].value!="-1") { + var p = []; // used pin array + if (d.um_p && Array.isArray(d.um_p)) for (k=0;ke==parseInt(LCs[i].value,10))) LCs[i].style.color="red"; else LCs[i].style.color=parseInt(LCs[i].value,10)>33?"orange":"#fff"; + } + } + // update total led count + if (gId("LC").readOnly) d.getElementsByName("LC")[0].value = sLC; + // if we are changing total led count update led count for 1st strip if (d.activeElement == d.getElementsByName("LC")[0]) { var o = d.getElementsByClassName("iST"); var i = o.length; if (i == 1) d.getElementsByName("LC0")[0].value = d.getElementsByName("LC")[0].value; } - - var LCs = d.getElementsByTagName("input"); - var sLC = 0, maxLC = 0; - for (i=0; imaxLC)maxLC=c;} - continue; - } - if (nm=="L0" || nm=="L1") { - var lc=d.getElementsByName("LC"+LCs[i].name.substring(2))[0]; - lc.max=maxPB; - } - if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR") - if (LCs[i].value!="" && LCs[i].value!="-1") { - var p = []; - if (d.um_p && Array.isArray(d.um_p)) for (k=0;ke==parseInt(LCs[i].value,10))) LCs[i].style.color="red"; else LCs[i].style.color="#fff"; - } - } - + // memory usage and warnings 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('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (WARNING: Using over ${maxM}B!)` : "") : "800 LEDs per pin"; - + gId('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (WARNING: Using over ${maxM}B!)` : "") : "800 LEDs per GPIO"; + // calculate power var val = Math.ceil((100 + sLC * laprev)/500)/2; val = (val > 5) ? Math.ceil(val) : val; var s = ""; @@ -221,8 +271,8 @@ function lastEnd(i) { if (i<1) return 0; v = parseInt(d.getElementsByName("LS"+(i-1))[0].value) + parseInt(d.getElementsByName("LC"+(i-1))[0].value); - var type = parseInt(d.getElementsByName("LT"+(i-1))[0].value); - if (type > 31 && type < 48) v = 1; //PWM busses + var t = parseInt(d.getElementsByName("LT"+(i-1))[0].value); + if (t > 31 && t < 48) v = 1; //PWM busses if (isNaN(v)) return 0; return v; } @@ -239,10 +289,10 @@ if (n==1) { // npm run build has trouble minimizing spaces inside string var cn = `
-${i>0?'
':''} +
${i+1}: - + @@ -255,8 +305,11 @@ ${i+1}: + + +   -Color Order: +
Color Order:
-Pin: -Clock: - - - +

-Start:   +GPIO: + + + + +
+Start:  
Count:

-
Reverse:
  +
Reversed:
 
Skip 1st LED:
+
`; f.insertAdjacentHTML("beforeend", cn); } @@ -292,7 +347,7 @@ Color Order: var c = gId("btns").innerHTML; var bt = "BT" + i; var be = "BE" + i; - c += `Button ${i} pin:  `; + c += `Button ${i} GPIO: `; c += `
+ Total LED count:
Recommended power supply for brightest white:
?

@@ -363,6 +418,7 @@ Color Order:

Hardware setup

LED outputs:
+

LED Memory Usage: 0 / ? B
@@ -371,11 +427,12 @@ Color Order: ⚠ You might run into stability or lag issues.
Use less than 800 LEDs per pin for the best experience!
+
Make a segment for each output:

Touch threshold:
- IR pin:  
IR info
- Relay pin: Invert  ×
+ Relay GPIO: invert  ×

Defaults

Turn LEDs on after power up/reset:
diff --git a/wled00/data/settings_sync.htm b/wled00/data/settings_sync.htm index 2ae500a8..2d513979 100644 --- a/wled00/data/settings_sync.htm +++ b/wled00/data/settings_sync.htm @@ -99,7 +99,7 @@ Type:
Port:
diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 3c458a15..4621f4a3 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -195,6 +195,7 @@ bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte //udp.cpp void notify(byte callMode, bool followUp=false); +uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte *buffer, bool isRGBW=false); void realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC); void handleNotifications(); void setRealtimePixel(uint16_t i, byte r, byte g, byte b, byte w); @@ -224,14 +225,11 @@ class UsermodManager { public: void loop(); - void setup(); void connected(); - void addToJsonState(JsonObject& obj); void addToJsonInfo(JsonObject& obj); void readFromJsonState(JsonObject& obj); - void addToConfig(JsonObject& obj); bool readFromConfig(JsonObject& obj); void onMqttConnect(bool sessionPresent); diff --git a/wled00/html_settings.h b/wled00/html_settings.h index e15f04fd..ac271896 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -77,18 +77,19 @@ onclick="B()">Back // Autogenerated from wled00/data/settings_leds.htm, do not edit!! const char PAGE_settings_leds[] PROGMEM = R"=====(LED Settings )====="; +const char PAGE_settingsCss[] PROGMEM = R"=====()====="; // Autogenerated from wled00/data/settings.htm, do not edit!! @@ -77,7 +77,7 @@ onclick="B()">Back // Autogenerated from wled00/data/settings_leds.htm, do not edit!! const char PAGE_settings_leds[] PROGMEM = R"=====(LED Settings