diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index f17f8b418..a547642e5 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -404,6 +404,8 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { CJSON(e131SkipOutOfSequence, if_live_dmx[F("seqskip")]); CJSON(DMXAddress, if_live_dmx[F("addr")]); if (!DMXAddress || DMXAddress > 510) DMXAddress = 1; + CJSON(DMXSegmentSpacing, if_live_dmx[F("dss")]); + if (DMXSegmentSpacing > 150) DMXSegmentSpacing = 0; CJSON(DMXMode, if_live_dmx["mode"]); tdd = if_live[F("timeout")] | -1; @@ -864,6 +866,7 @@ void serializeConfig() { if_live_dmx[F("uni")] = e131Universe; if_live_dmx[F("seqskip")] = e131SkipOutOfSequence; if_live_dmx[F("addr")] = DMXAddress; + if_live_dmx[F("dss")] = DMXSegmentSpacing; if_live_dmx["mode"] = DMXMode; if_live[F("timeout")] = realtimeTimeoutMs / 100; diff --git a/wled00/const.h b/wled00/const.h index c34891d46..cb2353f20 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -169,10 +169,14 @@ #define DMX_MODE_DISABLED 0 //not used #define DMX_MODE_SINGLE_RGB 1 //all LEDs same RGB color (3 channels) #define DMX_MODE_SINGLE_DRGB 2 //all LEDs same RGB color and master dimmer (4 channels) -#define DMX_MODE_EFFECT 3 //trigger standalone effects of WLED (11 channels) +#define DMX_MODE_EFFECT 3 //trigger standalone effects of WLED (15 channels) +#define DMX_MODE_EFFECT_W 7 //trigger standalone effects of WLED (18 channels) #define DMX_MODE_MULTIPLE_RGB 4 //every LED is addressed with its own RGB (ledCount * 3 channels) #define DMX_MODE_MULTIPLE_DRGB 5 //every LED is addressed with its own RGB and share a master dimmer (ledCount * 3 + 1 channels) #define DMX_MODE_MULTIPLE_RGBW 6 //every LED is addressed with its own RGBW (ledCount * 4 channels) +#define DMX_MODE_EFFECT_SEGMENT 8 //trigger standalone effects of WLED (15 channels per segement) +#define DMX_MODE_EFFECT_SEGMENT_W 9 //trigger standalone effects of WLED (18 channels per segement) +#define DMX_MODE_PRESET 10 //apply presets (1 channel) //Light capability byte (unused) 0bRCCCTTTT //bits 0/1/2/3: specifies a type of LED driver. A single "driver" may have different chip models but must have the same protocol/behavior diff --git a/wled00/data/settings_sync.htm b/wled00/data/settings_sync.htm index 1e50cab2a..30d794797 100644 --- a/wled00/data/settings_sync.htm +++ b/wled00/data/settings_sync.htm @@ -148,15 +148,20 @@ Start universe:
Reboot required. Check out LedFx!
Skip out-of-sequence packets:
DMX start address:
+DMX segment spacing:
DMX mode:
E1.31 info
Timeout: ms
diff --git a/wled00/e131.cpp b/wled00/e131.cpp index fd68759a9..e4c3fabbc 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -132,7 +132,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){ return; // nothing to do break; - case DMX_MODE_SINGLE_RGB: // RGB only + case DMX_MODE_SINGLE_RGB: // 3 channel: [R,G,B] if (uni != e131Universe) return; if (availDMXLen < 3) return; @@ -145,7 +145,7 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){ setRealtimePixel(i, e131_data[dataOffset+0], e131_data[dataOffset+1], e131_data[dataOffset+2], wChannel); break; - case DMX_MODE_SINGLE_DRGB: // Dimmer + RGB + case DMX_MODE_SINGLE_DRGB: // 4 channel: [Dimmer,R,G,B] if (uni != e131Universe) return; if (availDMXLen < 4) return; @@ -162,38 +162,77 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){ setRealtimePixel(i, e131_data[dataOffset+1], e131_data[dataOffset+2], e131_data[dataOffset+3], wChannel); break; - case DMX_MODE_EFFECT: // Length 1: Apply Preset ID, length 11-13: apply effect config - if (uni != e131Universe) return; - if (availDMXLen < 11) { - if (availDMXLen > 1) return; - applyPreset(e131_data[dataOffset+0], CALL_MODE_NOTIFICATION); - return; + case DMX_MODE_PRESET: // 2 channel: [Dimmer,Preset] + if (uni != e131Universe || availDMXLen < 2) return; + applyPreset(e131_data[dataOffset+1], CALL_MODE_NOTIFICATION); + if (bri != e131_data[dataOffset]) { + bri = e131_data[dataOffset]; + strip.setBrightness(bri, true); } - - if (bri != e131_data[dataOffset+0]) { - bri = e131_data[dataOffset+0]; - } - if (e131_data[dataOffset+1] < strip.getModeCount()) - effectCurrent = e131_data[dataOffset+ 1]; - effectSpeed = e131_data[dataOffset+ 2]; // flickers - effectIntensity = e131_data[dataOffset+ 3]; - effectPalette = e131_data[dataOffset+ 4]; - col[0] = e131_data[dataOffset+ 5]; - col[1] = e131_data[dataOffset+ 6]; - col[2] = e131_data[dataOffset+ 7]; - colSec[0] = e131_data[dataOffset+ 8]; - colSec[1] = e131_data[dataOffset+ 9]; - colSec[2] = e131_data[dataOffset+10]; - if (availDMXLen > 11) - { - col[3] = e131_data[dataOffset+11]; //white - colSec[3] = e131_data[dataOffset+12]; - } - transitionDelayTemp = 0; // act fast - colorUpdated(CALL_MODE_NOTIFICATION); // don't send UDP - return; // don't activate realtime live mode + return; break; + case DMX_MODE_EFFECT: // 15 channels [bri,effectCurrent,effectSpeed,effectIntensity,effectPalette,effectOption,R,G,B,R2,G2,B2,R3,G3,B3] + case DMX_MODE_EFFECT_W: // 18 channels, same as above but with extra +3 white channels [..,W,W2,W3] + case DMX_MODE_EFFECT_SEGMENT: // 15 channels per segment; + case DMX_MODE_EFFECT_SEGMENT_W: // 18 Channels per segment; + { + if (uni != e131Universe) return; + bool isSegmentMode = DMXMode == DMX_MODE_EFFECT_SEGMENT || DMXMode == DMX_MODE_EFFECT_SEGMENT_W; + uint8_t dmxEffectChannels = (DMXMode == DMX_MODE_EFFECT || DMXMode == DMX_MODE_EFFECT_SEGMENT) ? 15 : 18; + for (uint8_t id = 0; id < strip.getSegmentsNum(); id++) { + Segment& seg = strip.getSegment(id); + if (isSegmentMode) + dataOffset = DMXAddress + id * (dmxEffectChannels + DMXSegmentSpacing); + else + dataOffset = DMXAddress; + // Modify address for Art-Net data + if (protocol == P_ARTNET && dataOffset > 0) + dataOffset--; + // Skip out of universe addresses + if (dataOffset > dmxChannels - dmxEffectChannels + 1) + return; + + if (e131_data[dataOffset+1] < strip.getModeCount()) + if (e131_data[dataOffset+1] != seg.mode) seg.setMode( e131_data[dataOffset+1]); + if (e131_data[dataOffset+2] != seg.speed) seg.speed = e131_data[dataOffset+2]; + if (e131_data[dataOffset+3] != seg.intensity) seg.intensity = e131_data[dataOffset+3]; + if (e131_data[dataOffset+4] != seg.palette) seg.setPalette(e131_data[dataOffset+4]); + + uint8_t segOption = (uint8_t)floor(e131_data[dataOffset+5]/64.0); + if (segOption == 0 && (seg.mirror || seg.reverse )) {seg.setOption(SEG_OPTION_MIRROR, false); seg.setOption(SEG_OPTION_REVERSED, false);} + if (segOption == 1 && (seg.mirror || !seg.reverse)) {seg.setOption(SEG_OPTION_MIRROR, false); seg.setOption(SEG_OPTION_REVERSED, true);} + if (segOption == 2 && (!seg.mirror || seg.reverse )) {seg.setOption(SEG_OPTION_MIRROR, true); seg.setOption(SEG_OPTION_REVERSED, false);} + if (segOption == 3 && (!seg.mirror || !seg.reverse)) {seg.setOption(SEG_OPTION_MIRROR, true); seg.setOption(SEG_OPTION_REVERSED, true);} + + uint32_t colors[3]; + byte whites[3] = {0,0,0}; + if (dmxEffectChannels == 18) { + whites[0] = e131_data[dataOffset+15]; + whites[1] = e131_data[dataOffset+16]; + whites[2] = e131_data[dataOffset+17]; + } + colors[0] = RGBW32(e131_data[dataOffset+ 6], e131_data[dataOffset+ 7], e131_data[dataOffset+ 8], whites[0]); + colors[1] = RGBW32(e131_data[dataOffset+ 9], e131_data[dataOffset+10], e131_data[dataOffset+11], whites[1]); + colors[2] = RGBW32(e131_data[dataOffset+12], e131_data[dataOffset+13], e131_data[dataOffset+14], whites[2]); + if (colors[0] != seg.colors[0]) seg.setColor(0, colors[0]); + if (colors[1] != seg.colors[1]) seg.setColor(1, colors[1]); + if (colors[2] != seg.colors[2]) seg.setColor(2, colors[2]); + + // Set segment opacity or global brightness + if (isSegmentMode) { + if (e131_data[dataOffset] != seg.opacity) seg.setOpacity(e131_data[dataOffset]); + } else if ( id == strip.getSegmentsNum()-1 ) { + if (bri != e131_data[dataOffset]) { + bri = e131_data[dataOffset]; + strip.setBrightness(bri, true); + } + } + } + return; + break; + } + case DMX_MODE_MULTIPLE_DRGB: case DMX_MODE_MULTIPLE_RGB: case DMX_MODE_MULTIPLE_RGBW: @@ -279,7 +318,11 @@ void handleArtnetPollReply(IPAddress ipAddress) { case DMX_MODE_SINGLE_RGB: case DMX_MODE_SINGLE_DRGB: + case DMX_MODE_PRESET: case DMX_MODE_EFFECT: + case DMX_MODE_EFFECT_W: + case DMX_MODE_EFFECT_SEGMENT: + case DMX_MODE_EFFECT_SEGMENT_W: break; // 1 universe is enough case DMX_MODE_MULTIPLE_DRGB: diff --git a/wled00/set.cpp b/wled00/set.cpp index ad189f241..fa433faeb 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -284,8 +284,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) if (t >= 0 && t <= 63999) e131Universe = t; t = request->arg(F("DA")).toInt(); if (t >= 0 && t <= 510) DMXAddress = t; + t = request->arg(F("XX")).toInt(); + if (t >= 0 && t <= 150) DMXSegmentSpacing = t; t = request->arg(F("DM")).toInt(); - if (t >= DMX_MODE_DISABLED && t <= DMX_MODE_MULTIPLE_RGBW) DMXMode = t; + if (t >= DMX_MODE_DISABLED && t <= DMX_MODE_PRESET) DMXMode = t; t = request->arg(F("ET")).toInt(); if (t > 99 && t <= 65000) realtimeTimeoutMs = t; arlsForceMaxBri = request->hasArg(F("FB")); diff --git a/wled00/wled.h b/wled00/wled.h index 43643bb04..1a50a3ed5 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -405,6 +405,7 @@ WLED_GLOBAL uint16_t e131Universe _INIT(1); // settings fo WLED_GLOBAL uint16_t e131Port _INIT(5568); // DMX in port. E1.31 default is 5568, Art-Net is 6454 WLED_GLOBAL byte DMXMode _INIT(DMX_MODE_MULTIPLE_RGB); // DMX mode (s.a.) WLED_GLOBAL uint16_t DMXAddress _INIT(1); // DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol] +WLED_GLOBAL uint16_t DMXSegmentSpacing _INIT(0); // Number of void/unused channels between each segments DMX channels WLED_GLOBAL byte e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; // to detect packet loss WLED_GLOBAL bool e131Multicast _INIT(false); // multicast or unicast WLED_GLOBAL bool e131SkipOutOfSequence _INIT(false); // freeze instead of flickering diff --git a/wled00/xml.cpp b/wled00/xml.cpp index c250340aa..c82999e73 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -508,6 +508,7 @@ void getSettingsJS(byte subPage, char* dest) sappend('c',SET_F("EM"),e131Multicast); sappend('v',SET_F("EU"),e131Universe); sappend('v',SET_F("DA"),DMXAddress); + sappend('v',SET_F("XX"),DMXSegmentSpacing); sappend('v',SET_F("DM"),DMXMode); sappend('v',SET_F("ET"),realtimeTimeoutMs); sappend('c',SET_F("FB"),arlsForceMaxBri);