Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Art-Net Improvements v2 #179

Merged
merged 16 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 73 additions & 21 deletions wled00/bus_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ uint8_t BusOnOff::getPins(uint8_t* pinArray) const {
}


BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
BusNetwork::BusNetwork(BusConfig &bc, const ColorOrderMap &com) : Bus(bc.type, bc.start, bc.autoWhite), _colorOrderMap(com) {
_valid = false;
USER_PRINT("[");
switch (bc.type) {
Expand All @@ -457,6 +457,11 @@ BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
_UDPtype = 2;
USER_PRINT("NET_ARTNET_RGB");
break;
case TYPE_NET_ARTNET_RGBW:
_rgbw = true;
_UDPtype = 2;
USER_PRINT("NET_ARTNET_RGBW");
break;
case TYPE_NET_E131_RGB:
_rgbw = false;
_UDPtype = 1;
Expand All @@ -469,37 +474,84 @@ BusNetwork::BusNetwork(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite) {
break;
}
_UDPchannels = _rgbw ? 4 : 3;
_data = (byte *)malloc(bc.count * _UDPchannels);
#ifdef ESP32
_data = (byte*) heap_caps_calloc_prefer((bc.count * _UDPchannels)+15, sizeof(byte), 3, MALLOC_CAP_DEFAULT, MALLOC_CAP_SPIRAM);
#else
_data = (byte*) calloc((bc.count * _UDPchannels)+15, sizeof(byte));
#endif
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;
USER_PRINTF(" %u.%u.%u.%u] \n", bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
_artnet_outputs = bc.artnet_outputs;
_artnet_leds_per_output = bc.artnet_leds_per_output;
_artnet_fps_limit = bc.artnet_fps_limit;
troyhacks marked this conversation as resolved.
Show resolved Hide resolved
USER_PRINTF(" %u.%u.%u.%u]\n", bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
}

void BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid || pix >= _len) return;
if (hasWhite()) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); //color correction from CCT
uint16_t offset = pix * _UDPchannels;
_data[offset] = R(c);
_data[offset+1] = G(c);
_data[offset+2] = B(c);
if (_rgbw) _data[offset+3] = W(c);
void IRAM_ATTR_YN BusNetwork::setPixelColor(uint16_t pix, uint32_t c) {
if (!_valid || pix >= _len) return;
if (_rgbw) c = autoWhiteCalc(c);
if (_cct >= 1900) c = colorBalanceFromKelvin(_cct, c); // color correction from CCT

uint16_t offset = pix * _UDPchannels;
uint8_t co = _colorOrderMap.getPixelColorOrder(pix + _start, _colorOrder);

if (_colorOrder != co || _colorOrder != COL_ORDER_RGB) {
switch (co) {
case COL_ORDER_GRB:
_data[offset] = G(c); _data[offset+1] = R(c); _data[offset+2] = B(c);
break;
case COL_ORDER_RGB:
_data[offset] = R(c); _data[offset+1] = G(c); _data[offset+2] = B(c);
break;
case COL_ORDER_BRG:
_data[offset] = B(c); _data[offset+1] = R(c); _data[offset+2] = G(c);
break;
case COL_ORDER_RBG:
_data[offset] = R(c); _data[offset+1] = B(c); _data[offset+2] = G(c);
break;
case COL_ORDER_GBR:
_data[offset] = G(c); _data[offset+1] = B(c); _data[offset+2] = R(c);
break;
case COL_ORDER_BGR:
_data[offset] = B(c); _data[offset+1] = G(c); _data[offset+2] = R(c);
break;
}
if (_rgbw) _data[offset+3] = W(c);
} else {
_data[offset] = R(c); _data[offset+1] = G(c); _data[offset+2] = B(c);
if (_rgbw) _data[offset+3] = W(c);
}
}

uint32_t BusNetwork::getPixelColor(uint16_t pix) const {
if (!_valid || pix >= _len) return 0;
uint16_t offset = pix * _UDPchannels;
return RGBW32(_data[offset], _data[offset+1], _data[offset+2], _rgbw ? (_data[offset+3] << 24) : 0);
uint32_t IRAM_ATTR_YN BusNetwork::getPixelColor(uint16_t pix) const {
if (!_valid || pix >= _len) return 0;
uint16_t offset = pix * _UDPchannels;
uint8_t co = _colorOrderMap.getPixelColorOrder(pix + _start, _colorOrder);

uint8_t r = _data[offset + 0];
uint8_t g = _data[offset + 1];
uint8_t b = _data[offset + 2];
uint8_t w = _rgbw ? _data[offset + 3] : 0;

switch (co) {
case COL_ORDER_GRB: return RGBW32(g, r, b, w);
case COL_ORDER_RGB: return RGBW32(r, g, b, w);
case COL_ORDER_BRG: return RGBW32(b, r, g, w);
case COL_ORDER_RBG: return RGBW32(r, b, g, w);
case COL_ORDER_GBR: return RGBW32(g, b, r, w);
case COL_ORDER_BGR: return RGBW32(b, g, r, w);
default: return RGBW32(r, g, b, w); // default to RGB order
}
}

void BusNetwork::show() {
if (!_valid || !canShow()) return;
_broadcastLock = true;
realtimeBroadcast(_UDPtype, _client, _len, _data, _bri, _rgbw);
realtimeBroadcast(_UDPtype, _client, _len, _data, _bri, _rgbw, _artnet_outputs, _artnet_leds_per_output, _artnet_fps_limit);
_broadcastLock = false;
}

Expand All @@ -513,8 +565,8 @@ uint8_t BusNetwork::getPins(uint8_t* pinArray) const {
void BusNetwork::cleanup() {
_type = I_NONE;
_valid = false;
if (_data != nullptr) free(_data);
_data = nullptr;
// if (_data != nullptr) free(_data);
// _data = nullptr;
troyhacks marked this conversation as resolved.
Show resolved Hide resolved
}

// ***************************************************************************
Expand Down Expand Up @@ -1178,7 +1230,7 @@ int BusManager::add(BusConfig &bc) {
if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
DEBUG_PRINTF("BusManager::add(bc.type=%u)\n", bc.type);
if (bc.type >= TYPE_NET_DDP_RGB && bc.type < 96) {
busses[numBusses] = new BusNetwork(bc);
busses[numBusses] = new BusNetwork(bc, colorOrderMap);
} else if (bc.type >= TYPE_HUB75MATRIX && bc.type <= (TYPE_HUB75MATRIX + 10)) {
#ifdef WLED_ENABLE_HUB75MATRIX
DEBUG_PRINTLN("BusManager::add - Adding BusHub75Matrix");
Expand Down
50 changes: 40 additions & 10 deletions wled00/bus_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,16 @@ struct BusConfig {
uint8_t skipAmount;
bool refreshReq;
uint8_t autoWhite;
uint8_t artnet_outputs, artnet_fps_limit;
uint16_t artnet_leds_per_output;

uint8_t pins[5] = {LEDPIN, 255, 255, 255, 255}; // WLEDMM warning: this means that BusConfig cannot handle nore than 5 pins per bus!
uint16_t frequency;
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U) {
BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, uint8_t art_o=1, uint16_t art_l=1, uint8_t art_f=30) {
refreshReq = (bool) GET_BIT(busType,7);
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
count = len; start = pstart; colorOrder = pcolorOrder; reversed = rev; skipAmount = skip; autoWhite = aw; frequency = clock_kHz;
artnet_outputs = art_o; artnet_leds_per_output = art_l; artnet_fps_limit = art_f;
uint8_t nPins = 1; // default = only one pin (clockless LEDs like WS281x)
if ((type >= TYPE_NET_DDP_RGB) && (type < (TYPE_NET_DDP_RGB + 16))) nPins = 4; // virtual network bus. 4 "pins" store IP address
else if ((type > 47) && (type < 63)) nPins = 2; // (data + clock / SPI) busses - two pins
Expand Down Expand Up @@ -144,6 +148,9 @@ class Bus {
virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; }
virtual uint8_t skippedLeds() const { return 0; }
virtual uint16_t getFrequency() const { return 0U; }
virtual uint8_t get_artnet_fps_limit() const { return 0; }
virtual uint8_t get_artnet_outputs() const { return 0; }
virtual uint16_t get_artnet_leds_per_output() const { return 0; }
inline uint16_t getStart() const { return _start; }
inline void setStart(uint16_t start) { _start = start; }
inline uint8_t getType() const { return _type; }
Expand Down Expand Up @@ -330,7 +337,7 @@ class BusOnOff : public Bus {

class BusNetwork : public Bus {
public:
BusNetwork(BusConfig &bc);
BusNetwork(BusConfig &bc, const ColorOrderMap &com);

uint16_t getMaxPixels() const override { return 4096; };
bool hasRGB() const { return true; }
Expand All @@ -348,25 +355,48 @@ class BusNetwork : public Bus {
return !_broadcastLock;
}

uint8_t getPins(uint8_t* pinArray) const;
uint8_t getPins(uint8_t* pinArray) const;
troyhacks marked this conversation as resolved.
Show resolved Hide resolved

uint16_t getLength() const override {
uint16_t getLength() const override {
return _len;
}

uint8_t get_artnet_fps_limit() const override {
return _artnet_fps_limit;
}

uint8_t get_artnet_outputs() const override {
return _artnet_outputs;
}

uint16_t get_artnet_leds_per_output() const override {
return _artnet_leds_per_output;
}

void setColorOrder(uint8_t colorOrder);

uint8_t getColorOrder() const {
troyhacks marked this conversation as resolved.
Show resolved Hide resolved
return _colorOrder;
}

void cleanup();

~BusNetwork() {
cleanup();
}

private:
IPAddress _client;
uint8_t _UDPtype;
uint8_t _UDPchannels;
bool _rgbw;
bool _broadcastLock;
byte *_data;
IPAddress _client;
uint8_t _UDPtype;
uint8_t _UDPchannels;
bool _rgbw;
bool _broadcastLock;
byte *_data;
uint8_t _colorOrder = COL_ORDER_RGB;
uint8_t _artnet_fps_limit;
uint8_t _artnet_outputs;
uint16_t _artnet_leds_per_output;
const ColorOrderMap &_colorOrderMap;
};

#ifdef WLED_ENABLE_HUB75MATRIX
Expand Down
10 changes: 8 additions & 2 deletions wled00/cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,16 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM (not yet implemented fully)
ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh
uint8_t AWmode = elm[F("rgbwm")] | RGBW_MODE_MANUAL_ONLY;
uint8_t artnet_outputs = elm["artnet_outputs"] | 1; // sanity check
uint16_t artnet_leds_per_output = elm["artnet_leds_per_output"] | length; // sanity check
uint8_t artnet_fps_limit = elm["artnet_fps_limit"] | 24; // sanity check
if (fromFS) {
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz);
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, artnet_outputs, artnet_leds_per_output, artnet_fps_limit);
mem += BusManager::memUsage(bc);
if (mem <= MAX_LED_MEMORY) if (busses.add(bc) == -1) break; // finalization will be done in WLED::beginStrip()
} else {
if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode);
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, artnet_outputs, artnet_leds_per_output, artnet_fps_limit);
busesChanged = true;
}
s++;
Expand Down Expand Up @@ -829,6 +832,9 @@ void serializeConfig() {
ins["ref"] = bus->isOffRefreshRequired();
ins[F("rgbwm")] = bus->getAutoWhiteMode();
ins[F("freq")] = bus->getFrequency();
ins["artnet_outputs"] = bus->get_artnet_outputs();
ins["artnet_fps_limit"] = bus->get_artnet_fps_limit();
ins["artnet_leds_per_output"] = bus->get_artnet_leds_per_output();
}

JsonArray hw_com = hw.createNestedArray(F("com"));
Expand Down
3 changes: 2 additions & 1 deletion wled00/const.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@
//Network types (master broadcast) (80-95)
#define TYPE_NET_DDP_RGB 80 //network DDP RGB bus (master broadcast bus)
#define TYPE_NET_E131_RGB 81 //network E131 RGB bus (master broadcast bus, unused)
#define TYPE_NET_ARTNET_RGB 82 //network ArtNet RGB bus (master broadcast bus, unused)
#define TYPE_NET_ARTNET_RGB 82 //network ArtNet RGB bus (master broadcast bus)
#define TYPE_NET_ARTNET_RGBW 83 //network ArtNet RGB bus (master broadcast bus)
#define TYPE_NET_DDP_RGBW 88 //network DDP RGBW bus (master broadcast bus)

#define IS_DIGITAL(t) (((t) & 0x10) || ((t)==TYPE_HUB75MATRIX)) //digital are 16-31 and 48-63 // WLEDMM added HUB75
Expand Down
29 changes: 29 additions & 0 deletions wled00/data/settings_leds.htm
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,30 @@
gRGBW |= isRGBW = ((t > 17 && t < 22) || (t > 28 && t < 32) || (t > 40 && t < 46 && t != 43) || t == 88); // RGBW checkbox, TYPE_xxxx values from const.h
gId("co"+n).style.display = ((t >= 80 && t < 96) || (t >= 40 && t < 48)||(t >= 100 && t < 110)) ? "none":"inline"; // hide color order for PWM
gId("dig"+n+"w").style.display = (t > 28 && t < 32) ? "inline":"none"; // show swap channels dropdown
gId("dig"+n+"O").style.display = (t == 82 || t == 83) ? "inline":"none"; // show Art-Net output number
softhack007 marked this conversation as resolved.
Show resolved Hide resolved
gId("dig"+n+"L").style.display = (t == 82 || t == 83) ? "inline":"none"; // show Art-Net LEDs per output
gId("dig"+n+"F").style.display = (t == 82 || t == 83) ? "inline":"none"; // show Art-Net FPS limiter
softhack007 marked this conversation as resolved.
Show resolved Hide resolved
if (gId("dig"+n+"F").style.display == "inline") {
total_leds = d.getElementsByName("LC"+n)[0].value;
outputs = d.getElementsByName("AO"+n)[0].value;
leds_per_output = d.getElementsByName("AL"+n)[0].value;
fps_limit = d.getElementsByName("AF"+n)[0].value;
last_octet = d.getElementsByName("L3"+n)[0].value;
if (outputs > 1) {
netmindz marked this conversation as resolved.
Show resolved Hide resolved
if (t == 82) gId("dig"+n+"W").innerHTML = "<br />Set your Art-Net Hardware to "+Math.ceil(leds_per_output/170)+" universes per output.";
if (t == 83) gId("dig"+n+"W").innerHTML = "<br />Set your Art-Net Hardware to "+Math.ceil(leds_per_output/128)+" universes per output.";
} else if (outputs == 1) {
gId("dig"+n+"W").innerHTML = "<br />WLED-style Art-Net output enabled.";
} else {
gId("dig"+n+"W").innerHTML = "<br />You need at least 1 output!";
}
if (outputs > 1 && fps_limit > 33333/leds_per_output) gId("dig"+n+"W").innerHTML += "<br />FPS limit may be too high for WS281x pixels.";
if (outputs*leds_per_output != total_leds) gId("dig"+n+"W").innerHTML += "<br />Total LEDs doesn't match outputs * LEDs per output.";
if (last_octet == 255) {
if (total_leds <= 1024) gId("dig"+n+"W").innerHTML += "<br />Art-Net is in broadcast mode.";
if (total_leds > 1024) gId("dig"+n+"W").innerHTML += "<br />You are sending a lot of broadcast data. Be cautious.";
}
}
if (!(t > 28 && t < 32)) d.getElementsByName("WO"+n)[0].value = 0; // reset swapping
gId("dig"+n+"c").style.display = ((t >= 40 && t < 48)||(t >= 100 && t < 110)) ? "none":"inline"; // hide count for analog and HUB75
gId("dig"+n+"r").style.display = (t >= 80) && (t < 100) ? "none":"inline"; // hide reversed for virtual, except for HUB75
Expand Down Expand Up @@ -314,6 +338,7 @@
gId("lc").textContent = sLC;
gId("pc").textContent = (sLC == sPC) ? "":"(" + sPC + " physical)";


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

additional blank line could be removed

// memory usage and warnings
gId('m0').innerHTML = memu;
bquot = memu / maxM * 100;
Expand Down Expand Up @@ -420,6 +445,10 @@
<span id="p2d${i}"></span><input type="number" name="L2${i}" class="s" onchange="UI()"/>
<span id="p3d${i}"></span><input type="number" name="L3${i}" class="s" onchange="UI()"/>
<span id="p4d${i}"></span><input type="number" name="L4${i}" class="s" onchange="UI()"/>
<div id="dig${i}O" style="display:inline"><br>Number of Outputs: <input type="number" name="AO${i}" min="1" max="255" value="1" oninput="UI()"></div>
<div id="dig${i}L" style="display:inline"><br>LEDs Per Output: <input type="number" name="AL${i}" min="1" max="65535" value="1" oninput="UI()"></div>
<div id="dig${i}F" style="display:inline"><br>FPS Limit: <input type="number" name="AF${i}" min="1" max="255" value="30" oninput="UI()"></div>
<div id="dig${i}W" style="display:inline"><br>Art-Net Warnings Go Here.</div>
<div id="dig${i}r" style="display:inline"><br><span id="rev${i}">Reversed</span>: <input type="checkbox" name="CV${i}"></div>
<div id="dig${i}s" style="display:inline"><br>Skip first LEDs: <input type="number" name="SL${i}" min="0" max="255" value="0" oninput="UI()"></div>
<div id="dig${i}f" style="display:inline"><br><span id="ref${i}">Off Refresh</span>: <input id="rf${i}" type="checkbox" name="RF${i}"></div>
Expand Down
2 changes: 1 addition & 1 deletion wled00/fcn_declare.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply=tru

//udp.cpp
void notify(byte callMode, bool followUp=false);
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8_t *buffer, uint8_t bri=255, bool isRGBW=false);
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, uint8_t *buffer, uint8_t bri=255, bool isRGBW=false, uint8_t artnet_outouts=1, uint16_t artnet_leds_per_output=1, uint8_t artnet_fps_limit=1);
void realtimeLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC);
void exitRealtime();
void handleNotifications();
Expand Down
31 changes: 31 additions & 0 deletions wled00/p4_mul16x16.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#if defined(ARDUINO_ARCH_ESP32P4)
.text
.align 4
.global p4_mul16x16
.type p4_mul16x16,@function
# ESP32-P4 needs -march rv32imafc_zicsr_zifencei_xesppie -mabi ilp32f
# a0 = out_packet, a1 = brightness, a2 = num_loops, a3 = pixelbuffer
p4_mul16x16:
esp.movx.r.cfg t6 # Enable aligned data access
or t6, t6, 2 # Enable aligned data access
esp.movx.w.cfg t6 # Enable aligned data access
li t6, 8 # put 8 (eventually for vmul bitshift) in temp register 6
esp.movx.w.sar t6 # set the numbers of bits to right-shift from t6
li t5, 255 # load 255 into t5 for a comparison
esp.vldbc.8.ip q1, a1, 0 # load the "B" value into q1 from a1, broadcasting the same value to all 16 values of q1
li t1, 0 # start our loop_num counter t1 at 0
loop: # "loop" label
beq t1, a2, exit # branch to "exit" if loop_num == num_loops
esp.vld.128.ip q0, a3, 16 # load 16 "A" values into q0 from a3, then move the pointer by 16 to get a new batch
beq a1, t5, skip # If brightness (a1) == 255, jump to "skip"
esp.vmul.u8 q2, q0, q1 # C = A*B (q2 = q0 * q1) then >> by esp.movx.w.sar which we set to 8
esp.vst.128.ip q2, a0, 16 # store the 16 "C" values into a0, then move the pointer by 16
j end_skip # jump to "end_skip"
skip: # "skip" label
esp.vst.128.ip q0, a0, 16 # just store brightness (q0 from a3) to packet (a0)
end_skip: # "end_skip" label
addi t1, t1, 1 # increment loop_num counter t1
j loop # jump to "loop"
exit: # "exit" label
ret # return
#endif
Loading