From 077b4d5c89ef574337eae2dcbce44df703566a04 Mon Sep 17 00:00:00 2001 From: Phil Bolduc Date: Sun, 19 Sep 2021 12:08:05 -0700 Subject: [PATCH 01/21] Add initial DDP UDP output --- wled00/DDP.cpp | 123 +++++++++++++++++++++++++++++++++++++++++++++++++ wled00/DDP.h | 4 ++ 2 files changed, 127 insertions(+) create mode 100644 wled00/DDP.cpp create mode 100644 wled00/DDP.h diff --git a/wled00/DDP.cpp b/wled00/DDP.cpp new file mode 100644 index 000000000..9c3818d93 --- /dev/null +++ b/wled00/DDP.cpp @@ -0,0 +1,123 @@ + +/* +** Expected interface: +** +** realtimeBrodacast(IPAddress client, uint16_t busLength, byte rgbwData[busLength][4]); +** +*/ + +#include +#include +#include "DDP.h" + +#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 + + +// +// copies a 4 byte rgbw buffer to a 3 byte rgb buffer (skipping the w channel) +// +// destination - the buffer to write to +// source - the buffer to read from +// length - the number of 4 byte channels in the source buffer +// +void copyRgbwToRgb(byte *destination, byte *source, uint16_t length) { + + uint16_t destinationOffset = 0; + uint16_t sourceOffset = 0; + + for (uint16_t offset = 0; offset < length; offset++) + { + destination[destinationOffset+0] = source[sourceOffset+0]; + destination[destinationOffset+1] = source[sourceOffset+1]; + destination[destinationOffset+2] = source[sourceOffset+2]; + + destinationOffset += 3; + sourceOffset += 4; + } +} + +// +// Send real time DDP UDP updates to the specified client +// +// client - the IP address to send to +// busLength - the number of pixels +// rgbwData - a buffer of at least busLength*4 bytes long +// +uint8_t realtimeBrodacast(IPAddress client, uint16_t busLength, byte *rgbwData) { + + WiFiUDP ddpUdp; + + // calclate the number of UDP packets we need to send + uint16_t channelCount = busLength * 3; + uint16_t packetCount = channelCount / DDP_CHANNELS_PER_PACKET; + if (channelCount % DDP_CHANNELS_PER_PACKET) { + packetCount++; + } + + // allocate a buffer for the UDP packet + size_t bufferSize = DDP_HEADER_LEN + DDP_CHANNELS_PER_PACKET ; + byte* buffer = (byte*)malloc(bufferSize); + if (!buffer) { + return 1; + } + + memset(buffer, 0, bufferSize); + + // set common header values + buffer[0] = DDP_FLAGS1_VER1; + buffer[2] = 1; + buffer[3] = DDP_ID_DISPLAY; + + // there are 3 channels per RGB pixel + int channel = 0; // TODO: allow specifying the start channel + + for (int packetIndex = 0; packetIndex < packetCount; packetIndex++) { + + // how much data is after the header + uint16_t packetSize = DDP_CHANNELS_PER_PACKET; + + if (packetIndex == (packetCount -1)) { + // last packet, set the push flag + buffer[0] = DDP_FLAGS1_VER1 | DDP_FLAGS1_PUSH; + + if (channelCount % DDP_CHANNELS_PER_PACKET) { + packetSize = channelCount % DDP_CHANNELS_PER_PACKET; + } + } + + //offset + buffer[4] = (channel & 0xFF000000) >> 24; + buffer[5] = (channel & 0xFF0000) >> 16; + buffer[6] = (channel & 0xFF00) >> 8; + buffer[7] = (channel & 0xFF); + + //size + buffer[8] = (packetSize & 0xFF00) >> 8; + buffer[9] = packetSize & 0xFF; + + // copy the data into our buffer + copyRgbwToRgb(&buffer[DDP_HEADER_LEN], rgbwData, busLength); + + ddpUdp.beginPacket(client, DDP_PORT); + ddpUdp.write(buffer, packetSize); + ddpUdp.endPacket(); + + channel += packetSize; + } +} diff --git a/wled00/DDP.h b/wled00/DDP.h new file mode 100644 index 000000000..b591ec32b --- /dev/null +++ b/wled00/DDP.h @@ -0,0 +1,4 @@ +#define DDP_PORT 4048 + +#define DDP_PUSH_FLAG 0x01 +#define DDP_TIMECODE_FLAG 0x10 From 95c87919a8707c0f9cd5c23e492002d3281acc5e Mon Sep 17 00:00:00 2001 From: Phil Bolduc Date: Sun, 19 Sep 2021 12:11:57 -0700 Subject: [PATCH 02/21] return ok status code and free buffer --- wled00/DDP.cpp | 5 ++++- wled00/DDP.h | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/wled00/DDP.cpp b/wled00/DDP.cpp index 9c3818d93..c682232d1 100644 --- a/wled00/DDP.cpp +++ b/wled00/DDP.cpp @@ -62,7 +62,7 @@ void copyRgbwToRgb(byte *destination, byte *source, uint16_t length) { uint8_t realtimeBrodacast(IPAddress client, uint16_t busLength, byte *rgbwData) { WiFiUDP ddpUdp; - + // calclate the number of UDP packets we need to send uint16_t channelCount = busLength * 3; uint16_t packetCount = channelCount / DDP_CHANNELS_PER_PACKET; @@ -120,4 +120,7 @@ uint8_t realtimeBrodacast(IPAddress client, uint16_t busLength, byte *rgbwData) channel += packetSize; } + + free(buffer); + return 0; } diff --git a/wled00/DDP.h b/wled00/DDP.h index b591ec32b..82dd0cdc4 100644 --- a/wled00/DDP.h +++ b/wled00/DDP.h @@ -2,3 +2,15 @@ #define DDP_PUSH_FLAG 0x01 #define DDP_TIMECODE_FLAG 0x10 + +// +// Send real time DDP UDP updates to the specified client +// +// client - the IP address to send to +// busLength - the number of pixels +// rgbwData - a buffer of at least busLength*4 bytes long +// +// Returns +// 0 - Ok +// 1 - could not allocate buffer +uint8_t realtimeBrodacast(IPAddress client, uint16_t busLength, byte *rgbwData); From 6472d35d913005216102c4faa76841e7a50f92ba Mon Sep 17 00:00:00 2001 From: Phil Bolduc Date: Sun, 19 Sep 2021 15:20:06 -0700 Subject: [PATCH 03/21] optimze copyRgbwToRgb, do not copy too much data into buffer --- wled00/DDP.cpp | 55 ++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/wled00/DDP.cpp b/wled00/DDP.cpp index c682232d1..2cc85c131 100644 --- a/wled00/DDP.cpp +++ b/wled00/DDP.cpp @@ -4,6 +4,7 @@ ** ** realtimeBrodacast(IPAddress client, uint16_t busLength, byte rgbwData[busLength][4]); ** +** http://www.3waylabs.com/ddp/ */ #include @@ -32,24 +33,24 @@ // // copies a 4 byte rgbw buffer to a 3 byte rgb buffer (skipping the w channel) // -// destination - the buffer to write to -// source - the buffer to read from -// length - the number of 4 byte channels in the source buffer -// -void copyRgbwToRgb(byte *destination, byte *source, uint16_t length) { - - uint16_t destinationOffset = 0; - uint16_t sourceOffset = 0; +// Parameters: +// destination - the buffer to write to must be able to hold length*3 bytes +// source - the buffer to read from +// length - the number of 4 byte channels in the source buffer +// Returns: +// the pointer in the source where we have copied up to +// +uint8_t* copyRgbwToRgb(uint8_t *destination, uint8_t *source, uint16_t length) { - for (uint16_t offset = 0; offset < length; offset++) + while (length--) { - destination[destinationOffset+0] = source[sourceOffset+0]; - destination[destinationOffset+1] = source[sourceOffset+1]; - destination[destinationOffset+2] = source[sourceOffset+2]; - - destinationOffset += 3; - sourceOffset += 4; + *(destination++) = *(source++); // R + *(destination++) = *(source++); // G + *(destination++) = *(source++); // B + source++; // W } + + return source; } // @@ -59,7 +60,7 @@ void copyRgbwToRgb(byte *destination, byte *source, uint16_t length) { // busLength - the number of pixels // rgbwData - a buffer of at least busLength*4 bytes long // -uint8_t realtimeBrodacast(IPAddress client, uint16_t busLength, byte *rgbwData) { +uint8_t realtimeBrodacast(IPAddress client, uint16_t busLength, uint8_t *rgbwData) { WiFiUDP ddpUdp; @@ -71,8 +72,8 @@ uint8_t realtimeBrodacast(IPAddress client, uint16_t busLength, byte *rgbwData) } // allocate a buffer for the UDP packet - size_t bufferSize = DDP_HEADER_LEN + DDP_CHANNELS_PER_PACKET ; - byte* buffer = (byte*)malloc(bufferSize); + size_t bufferSize = (DDP_HEADER_LEN + DDP_CHANNELS_PER_PACKET) * sizeof(uint8_t); + uint8_t* buffer = (byte*)malloc(bufferSize); if (!buffer) { return 1; } @@ -87,13 +88,19 @@ uint8_t realtimeBrodacast(IPAddress client, uint16_t busLength, byte *rgbwData) // there are 3 channels per RGB pixel int channel = 0; // TODO: allow specifying the start channel - for (int packetIndex = 0; packetIndex < packetCount; packetIndex++) { + // if we need to split + // + // + + + for (uint16_t currentPacket = 0; currentPacket < packetCount; currentPacket++) { // how much data is after the header uint16_t packetSize = DDP_CHANNELS_PER_PACKET; - if (packetIndex == (packetCount -1)) { + 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 buffer[0] = DDP_FLAGS1_VER1 | DDP_FLAGS1_PUSH; if (channelCount % DDP_CHANNELS_PER_PACKET) { @@ -101,18 +108,18 @@ uint8_t realtimeBrodacast(IPAddress client, uint16_t busLength, byte *rgbwData) } } - //offset + // data offset in bytes, 32-bit number, MSB first buffer[4] = (channel & 0xFF000000) >> 24; buffer[5] = (channel & 0xFF0000) >> 16; buffer[6] = (channel & 0xFF00) >> 8; buffer[7] = (channel & 0xFF); - //size + // data length in bytes, 16-bit number, MSB first buffer[8] = (packetSize & 0xFF00) >> 8; buffer[9] = packetSize & 0xFF; - // copy the data into our buffer - copyRgbwToRgb(&buffer[DDP_HEADER_LEN], rgbwData, busLength); + // copy the data from the source buffer into our pack + rgbwData = copyRgbwToRgb(&buffer[DDP_HEADER_LEN], rgbwData, packetSize); ddpUdp.beginPacket(client, DDP_PORT); ddpUdp.write(buffer, packetSize); From 7dc07f6d2106395c551a53b780f897000e551622 Mon Sep 17 00:00:00 2001 From: Phil Bolduc Date: Sun, 19 Sep 2021 15:30:17 -0700 Subject: [PATCH 04/21] Change parameter order for better stack alignment --- wled00/DDP.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/wled00/DDP.cpp b/wled00/DDP.cpp index 2cc85c131..dfbb8fcb4 100644 --- a/wled00/DDP.cpp +++ b/wled00/DDP.cpp @@ -57,15 +57,15 @@ uint8_t* copyRgbwToRgb(uint8_t *destination, uint8_t *source, uint16_t length) { // Send real time DDP UDP updates to the specified client // // client - the IP address to send to -// busLength - the number of pixels -// rgbwData - a buffer of at least busLength*4 bytes long +// length - the number of pixels +// rgbwData - a buffer of at least length*4 bytes long // -uint8_t realtimeBrodacast(IPAddress client, uint16_t busLength, uint8_t *rgbwData) { +uint8_t realtimeBrodacast(IPAddress client, uint8_t *rgbwData, uint16_t length) { WiFiUDP ddpUdp; // calclate the number of UDP packets we need to send - uint16_t channelCount = busLength * 3; + 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++; @@ -73,7 +73,7 @@ uint8_t realtimeBrodacast(IPAddress client, uint16_t busLength, uint8_t *rgbwDat // allocate a buffer for the UDP packet size_t bufferSize = (DDP_HEADER_LEN + DDP_CHANNELS_PER_PACKET) * sizeof(uint8_t); - uint8_t* buffer = (byte*)malloc(bufferSize); + uint8_t* buffer = (uint8_t*)malloc(bufferSize); if (!buffer) { return 1; } @@ -86,12 +86,7 @@ uint8_t realtimeBrodacast(IPAddress client, uint16_t busLength, uint8_t *rgbwDat buffer[3] = DDP_ID_DISPLAY; // there are 3 channels per RGB pixel - int channel = 0; // TODO: allow specifying the start channel - - // if we need to split - // - // - + uint16_t channel = 0; // TODO: allow specifying the start channel for (uint16_t currentPacket = 0; currentPacket < packetCount; currentPacket++) { From d95ba43fd1196ba0a6dbe82b44efcb108e16992d Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Mon, 20 Sep 2021 22:24:58 +0200 Subject: [PATCH 05/21] Virtual bus implementation. Base for virtual WLED set-up (multiple instances acting as one). UDP broadcast not yet implemented. --- wled00/bus_manager.h | 88 ++- wled00/const.h | 2 + wled00/data/settings_leds.htm | 937 ++++++++++++++--------------- wled00/fcn_declare.h | 1 + wled00/html_settings.h | 55 +- wled00/set.cpp | 50 +- wled00/udp.cpp | 1040 +++++++++++++++++---------------- 7 files changed, 1146 insertions(+), 1027 deletions(-) diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index f607a0015..0bec1e3f7 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -353,6 +353,86 @@ class BusPwm : public Bus { } }; + +class BusVirtual : public Bus { + public: + BusVirtual(BusConfig &bc) : Bus(bc.type, bc.start) { + _valid = false; + _data = (byte *)malloc(bc.count * (_rgbw ? 4 : 3)); + if (_data == nullptr) return; + memset(_data, 0, bc.count * (_rgbw ? 4 : 3)); + _len = bc.count; + //_rgbw = bc.rgbwOverride; // RGBW override in bit 7 or can have a special type + _rgbw = _type == TYPE_VIRTUAL_RGBW; + _colorOrder = bc.colorOrder; + _client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]); + _broadcastLock = false; + _valid = true; + }; + + void setPixelColor(uint16_t pix, uint32_t c) { + if (!_valid || pix >= _len) return; + _data[pix] = 0xFF & (c >> 16); + _data[pix+1] = 0xFF & (c >> 8); + _data[pix+2] = 0xFF & (c ); + if (_rgbw) _data[pix+3] = 0xFF & (c >> 24); + } + + uint32_t getPixelColor(uint16_t pix) { + if (!_valid || pix >= _len) return 0; + return ((_rgbw?(_data[pix+3] << 24):0) | (_data[pix] << 16) | (_data[pix+1] << 8) | (_data[pix+2])); + } + + void show() { + if (!_valid || _broadcastLock) return; + _broadcastLock = true; + realtimeBoroadcast(_client, _len, _data, _rgbw); + _broadcastLock = false; + } + + inline bool canShow() { + return !_broadcastLock; + } + + inline void setBrightness(uint8_t b) { + // not sure if this is correctly implemented + for (uint16_t pix=0; pix<_len; pix++) { + _data[pix ] = scale8(_data[pix ], b); + _data[pix+1] = scale8(_data[pix+1], b); + _data[pix+2] = scale8(_data[pix+2], b); + if (_rgbw) _data[pix+3] = scale8(_data[pix+3], b); + } + } + + 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; + } + + ~BusVirtual() { + cleanup(); + } + + private: + IPAddress _client; + uint16_t _len = 0; + uint8_t _colorOrder; + bool _rgbw; + bool _broadcastLock; + byte* _data; +}; + + class BusManager { public: BusManager() { @@ -363,7 +443,9 @@ class BusManager { static uint32_t memUsage(BusConfig &bc) { uint8_t type = bc.type; uint16_t len = bc.count; - if (type < 32) { + if (type < 4) { + return len * (type+1); + } else if (type < 32) { #ifdef ESP8266 if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem if (type > 29) return len*20; //RGBW @@ -384,7 +466,9 @@ class BusManager { int add(BusConfig &bc) { if (numBusses >= WLED_MAX_BUSSES) return -1; - if (IS_DIGITAL(bc.type)) { + if (bc.type>1 && bc.type<4) { + busses[numBusses] = new BusVirtual(bc); + } else if (IS_DIGITAL(bc.type)) { busses[numBusses] = new BusDigital(bc, numBusses); } else { busses[numBusses] = new BusPwm(bc); diff --git a/wled00/const.h b/wled00/const.h index db63b7c4a..27eff968a 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -122,6 +122,8 @@ #define TYPE_NONE 0 //light is not configured #define TYPE_RESERVED 1 //unused. Might indicate a "virtual" light +#define TYPE_VIRTUAL_RGB 2 //virtual RGB bus (master broadcast bus) +#define TYPE_VIRTUAL_RGBW 3 //virtual RGBW 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 diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 110fed77b..50c44d075 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -1,459 +1,478 @@ - - - - - - LED Settings - - - - -
-
-
-
-
-

LED & Hardware setup

- Total LED count:
- Recommended power supply for brightest white:
- ?
-
-
- Enable automatic brightness limiter:
-
- Maximum Current: mA
- - Automatically limits brightness to stay close to the limit.
- Keep at <1A if powering LEDs directly from the ESP 5V pin!
- If you are using an external power supply, enter its rating.
- (Current estimated usage: unknown)


- LED voltage (Max. current for a single LED):
-
- - Keep at default if you are unsure about your type of LEDs.
-
-

Hardware setup

-
LED outputs:
- -
- LED Memory Usage: 0 / ? B
-

- - Make a segment for each output:
-
-
- Touch threshold:
- IR GPIO:  ×
- -
- IR info
- Relay GPIO: invert  ×
-
-

Defaults

- Turn LEDs on after power up/reset:
- Default brightness: (0-255)

- Apply preset at boot (0 uses defaults) -

- Use Gamma correction for color: (strongly recommended)
- Use Gamma correction for brightness: (not recommended)

- Brightness factor: % -

Transitions

- Crossfade:
- Transition Time: ms
- Enable Palette transitions: -

Timed light

- Default Duration: min
- Default Target brightness:
- Mode: - -

Advanced

- Palette blending: -
- - Auto-calculate white channel from RGB:
- -

- -
- - + + + + + + LED Settings + + + + +
+
+
+
+
+

LED & Hardware setup

+ Total LED count:
+ Recommended power supply for brightest white:
+ ?
+
+
+ Enable automatic brightness limiter:
+
+ Maximum Current: mA
+ + Automatically limits brightness to stay close to the limit.
+ Keep at <1A if powering LEDs directly from the ESP 5V pin!
+ If you are using an external power supply, enter its rating.
+ (Current estimated usage: unknown)


+ LED voltage (Max. current for a single LED):
+
+ + Keep at default if you are unsure about your type of LEDs.
+
+

Hardware setup

+
LED outputs:
+
+ +
+ LED Memory Usage: 0 / ? B
+

+ +
+ Create a segment for each output:
+
+
+ Touch threshold:
+ IR GPIO:  ×
+ +
+ IR info
+ Relay GPIO: invert  ×
+
+

Defaults

+ Turn LEDs on after power up/reset:
+ Default brightness: (0-255)

+ Apply preset at boot (0 uses defaults) +

+ Use Gamma correction for color: (strongly recommended)
+ Use Gamma correction for brightness: (not recommended)

+ Brightness factor: % +

Transitions

+ Crossfade:
+ Transition Time: ms
+ Enable Palette transitions: +

Timed light

+ Default Duration: min
+ Default Target brightness:
+ Mode: + +

Advanced

+ Palette blending: +
+ + Auto-calculate white channel from RGB:
+ +

+ +
+ + diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 06f80cfc8..3556bbd24 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -197,6 +197,7 @@ bool updateVal(const String* req, const char* key, byte* val, byte minv=0, byte //udp.cpp void notify(byte callMode, bool followUp=false); +void realtimeBoroadcast(IPAddress client, uint16_t length, byte *buffer, bool isRGBW); 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); diff --git a/wled00/html_settings.h b/wled00/html_settings.h index 1b267892b..3da5145b0 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -77,15 +77,15 @@ onclick="B()">Back // Autogenerated from wled00/data/settings_leds.htm, do not edit!! const char PAGE_settings_leds[] PROGMEM = R"=====(LED Settings