+ DMX start address:
+ DMX mode: disabled
+ Single RGB (3 Channels for all LEDs: Red Green Blue)
+ Single DRGB (4 Channels for all LEDs: Dimmer Red Green Blue)
+ Effect (11 Channels parametrizing Effects: Dimmer Effect Speed Intensity Palette PriRed PriGreen PriBlue SecRed SecGreen SecBlue)
+ Multiple RGB (3 Channels for each LED: Red Green Blue)
+ Multiple DRGB (1+3 Channels for each LED: Dimmer Red1 Green1 Blue1 Red2 Green2 Blue2...)
+ Reboot required. Check out LedFx!
Timeout: ms
Force max brightness:
Disable realtime gamma correction:
diff --git a/wled00/html_settings.h b/wled00/html_settings.h
index 86b2cfbff..253342548 100644
--- a/wled00/html_settings.h
+++ b/wled00/html_settings.h
@@ -205,7 +205,14 @@ Receive UDP realtime:
E1.31 (sACN)
Use E1.31 multicast:
E1.31 start universe:
-Reboot required. Check out LedFx!
+Reboot required. Check out LedFx!
+DMX start address:
+DMX mode: disabled
+ Single RGB (3 Channels for all LEDs: Red Green Blue)
+ Single DRGB (4 Channels for all LEDs: Dimmer Red Green Blue)
+ Effect (11 Channels for properties: Dimmer FX Speed Intensity Palette PriR PriG PriB SecR SecG SecB)
+ Multiple RGB (3 Channels for each LED: Red Green Blue)
+ Multiple DRGB (1+3 Channels for each LED: Dimmer R1 G1 B1 R2 G2 B2...)
Timeout: ms
Force max brightness:
Disable realtime gamma correction:
diff --git a/wled00/wled00.ino b/wled00/wled00.ino
index 5b17a6dcc..f186f5e84 100644
--- a/wled00/wled00.ino
+++ b/wled00/wled00.ino
@@ -177,8 +177,18 @@ bool receiveDirect = true; //receive UDP realtime
bool arlsDisableGammaCorrection = true; //activate if gamma correction is handled by the source
bool arlsForceMaxBri = false; //enable to force max brightness if source has very dark colors that would be black
-uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol
-bool e131Multicast = false;
+uint16_t e131Universe = 1; //settings for E1.31 (sACN) protocol (only DMX_MODE_MULTIPLE_* can span over consequtive universes)
+#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_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)
+uint8_t DMXMode; //DMX mode (s.a.)
+uint16_t DMXAddress; //DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol]
+uint8_t DMXOldDimmer = 0; //only update brightness on change
+uint8_t e131LastSequenceNumber = 0; //to detect packet loss
+bool e131Multicast = false; //multicast or unicast
bool mqttEnabled = false;
char mqttDeviceTopic[33] = ""; //main MQTT topic (individual per device, default is wled/mac)
@@ -352,10 +362,17 @@ bool presetApplyBri = false, presetApplyCol = true, presetApplyFx = true;
bool saveCurrPresetCycConf = false;
//realtime
-bool realtimeActive = false;
+#define REALTIME_MODE_INACTIVE 0
+#define REALTIME_MODE_GENERIC 1
+#define REALTIME_MODE_UDP 2
+#define REALTIME_MODE_HYPERION 3
+#define REALTIME_MODE_E131 4
+#define REALTIME_MODE_ADALIGHT 5
+byte realtimeMode = 0;
IPAddress realtimeIP = (0,0,0,0);
unsigned long realtimeTimeout = 0;
+
//mqtt
long lastMqttReconnectAttempt = 0;
long lastInterfaceUpdate = 0;
@@ -417,6 +434,7 @@ AsyncMqttClient* mqtt = NULL;
void colorFromUint32(uint32_t,bool=false);
void serveMessage(AsyncWebServerRequest*,uint16_t,String,String,byte);
void handleE131Packet(e131_packet_t*, IPAddress);
+void arlsLock(uint32_t,byte);
void handleOverlayDraw();
#define E131_MAX_UNIVERSE_COUNT 9
@@ -517,7 +535,7 @@ void loop() {
yield();
if (doReboot) reset();
- if (!realtimeActive) //block stuff if WARLS/Adalight is enabled
+ if (!realtimeMode) //block stuff if WARLS/Adalight is enabled
{
if (apActive) dnsServer.processNextRequest();
#ifndef WLED_DISABLE_OTA
diff --git a/wled00/wled01_eeprom.ino b/wled00/wled01_eeprom.ino
index 790ed1a46..ec4ea2a3a 100644
--- a/wled00/wled01_eeprom.ino
+++ b/wled00/wled01_eeprom.ino
@@ -6,7 +6,7 @@
#define EEPSIZE 2560
//eeprom Version code, enables default settings instead of 0 init on update
-#define EEPVER 14
+#define EEPVER 15
//0 -> old version, default
//1 -> 0.4p 1711272 and up
//2 -> 0.4p 1711302 and up
@@ -22,6 +22,7 @@
//12-> 0.8.7-dev
//13-> 0.9.0-dev
//14-> 0.9.0-b1
+//15-> 0.9.0-b3
void commit()
{
@@ -211,6 +212,9 @@ void saveSettingsToEEPROM()
EEPROM.write(2194, (realtimeTimeoutMs >> 8) & 0xFF);
EEPROM.write(2195, arlsForceMaxBri);
EEPROM.write(2196, arlsDisableGammaCorrection);
+ EEPROM.write(2197, DMXAddress & 0xFF);
+ EEPROM.write(2198, (DMXAddress >> 8) & 0xFF);
+ EEPROM.write(2199, DMXMode);
EEPROM.write(2200, !receiveDirect);
EEPROM.write(2201, notifyMacro); //was enableRealtime
@@ -471,6 +475,16 @@ void loadSettingsFromEEPROM(bool first)
syncToggleReceive = false;
}
+ if (lastEEPROMversion > 14)
+ {
+ DMXAddress = EEPROM.read(2197) + ((EEPROM.read(2198) << 8) & 0xFF00);
+ DMXMode = EEPROM.read(2199);
+ } else {
+ DMXAddress = 1;
+ DMXMode = DMX_MODE_MULTIPLE_RGB;
+ }
+
+
receiveDirect = !EEPROM.read(2200);
notifyMacro = EEPROM.read(2201);
diff --git a/wled00/wled02_xml.ino b/wled00/wled02_xml.ino
index 9940c1a53..aac4a9be0 100644
--- a/wled00/wled02_xml.ino
+++ b/wled00/wled02_xml.ino
@@ -58,13 +58,25 @@ char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr)
oappend("");
oappendi(presetCyclingEnabled);
oappend("");
- if (realtimeActive)
+ if (realtimeMode)
{
String mesg = "Live ";
- if (realtimeIP[0] == 0)
+ if (realtimeMode == REALTIME_MODE_E131)
{
- mesg += "E1.31 mode";
- } else {
+ mesg += "E1.31 mode ";
+ mesg += DMXMode;
+ mesg += " at DMX Address ";
+ mesg += DMXAddress;
+ mesg += " from ";
+ mesg += realtimeIP[0];
+ for (int i = 1; i < 4; i++)
+ {
+ mesg += ".";
+ mesg += realtimeIP[i];
+ }
+ mesg += " seq=";
+ mesg += e131LastSequenceNumber;
+ } else if (realtimeMode == REALTIME_MODE_UDP || realtimeMode == REALTIME_MODE_HYPERION) {
mesg += "UDP from ";
mesg += realtimeIP[0];
for (int i = 1; i < 4; i++)
@@ -72,6 +84,10 @@ char* XML_response(AsyncWebServerRequest *request, char* dest = nullptr)
mesg += ".";
mesg += realtimeIP[i];
}
+ } else if (realtimeMode == REALTIME_MODE_ADALIGHT) {
+ mesg += "USB Adalight";
+ } else { //generic
+ mesg += "data";
}
oappend((char*)mesg.c_str());
} else {
@@ -263,6 +279,8 @@ void getSettingsJS(byte subPage, char* dest)
sappend('c',"RD",receiveDirect);
sappend('c',"EM",e131Multicast);
sappend('v',"EU",e131Universe);
+ sappend('v',"DA",DMXAddress);
+ sappend('v',"DM",DMXMode);
sappend('v',"ET",realtimeTimeoutMs);
sappend('c',"FB",arlsForceMaxBri);
sappend('c',"RG",arlsDisableGammaCorrection);
diff --git a/wled00/wled03_set.ino b/wled00/wled03_set.ino
index a1ad3c6df..c05844677 100644
--- a/wled00/wled03_set.ino
+++ b/wled00/wled03_set.ino
@@ -138,6 +138,10 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
e131Multicast = request->hasArg("EM");
t = request->arg("EU").toInt();
if (t > 0 && t <= 63999) e131Universe = t;
+ t = request->arg("DA").toInt();
+ if (t > 0 && t <= 510) DMXAddress = t;
+ t = request->arg("DM").toInt();
+ if (t >= DMX_MODE_DISABLED && t <= DMX_MODE_MULTIPLE_DRGB) DMXMode = t;
t = request->arg("ET").toInt();
if (t > 99 && t <= 65000) realtimeTimeoutMs = t;
arlsForceMaxBri = request->hasArg("FB");
diff --git a/wled00/wled04_file.ino b/wled00/wled04_file.ino
index 155337ba0..de7523281 100644
--- a/wled00/wled04_file.ino
+++ b/wled00/wled04_file.ino
@@ -67,8 +67,8 @@ void handleSerial()
setRealtimePixel(pixel++, red, green, blue, 0);
if (--count > 0) state = AdaState::Data_Red;
else {
- if (!realtimeActive && bri == 0) strip.setBrightness(briLast);
- arlsLock(realtimeTimeoutMs);
+ if (!realtimeMode && bri == 0) strip.setBrightness(briLast);
+ arlsLock(realtimeTimeoutMs, REALTIME_MODE_ADALIGHT);
strip.show();
state = AdaState::Header_A;
diff --git a/wled00/wled07_notify.ino b/wled00/wled07_notify.ino
index 8bb739e05..ab55df28d 100644
--- a/wled00/wled07_notify.ino
+++ b/wled00/wled07_notify.ino
@@ -72,14 +72,14 @@ void notify(byte callMode, bool followUp=false)
}
-void arlsLock(uint32_t timeoutMs)
+void arlsLock(uint32_t timeoutMs, byte md = REALTIME_MODE_GENERIC)
{
- if (!realtimeActive){
+ if (!realtimeMode){
for (uint16_t i = 0; i < ledCount; i++)
{
strip.setPixelColor(i,0,0,0,0);
}
- realtimeActive = true;
+ realtimeMode = md;
}
realtimeTimeout = millis() + timeoutMs;
if (timeoutMs == 255001 || timeoutMs == 65000) realtimeTimeout = UINT32_MAX;
@@ -89,23 +89,133 @@ void arlsLock(uint32_t timeoutMs)
void handleE131Packet(e131_packet_t* p, IPAddress clientIP){
//E1.31 protocol support
- uint16_t uni = htons(p->universe);
- if (uni < e131Universe || uni >= e131Universe + E131_MAX_UNIVERSE_COUNT) return;
- uint16_t len = htons(p->property_value_count) -1;
- len /= 3; //one LED is 3 DMX channels
-
- uint16_t multipacketOffset = (uni - e131Universe)*170; //if more than 170 LEDs (510 channels), client will send in next higher universe
- if (ledCount <= multipacketOffset) return;
+ // skip out-of-sequence packets
+ if (p->sequence_number < e131LastSequenceNumber && p->sequence_number - e131LastSequenceNumber > -20){
+ DEBUG_PRINT("skipping E1.31 frame (last seq=");
+ DEBUG_PRINT(e131LastSequenceNumber);
+ DEBUG_PRINT(", current seq=");
+ DEBUG_PRINT(p->sequence_number);
+ DEBUG_PRINTLN(")");
+ e131LastSequenceNumber = p->sequence_number;
+ return;
+ }
+ e131LastSequenceNumber = p->sequence_number;
- arlsLock(realtimeTimeoutMs);
- if (len + multipacketOffset > ledCount) len = ledCount - multipacketOffset;
+ // update status info
+ realtimeIP = clientIP;
- for (uint16_t i = 0; i < len; i++) {
- int j = i * 3 +1;
- setRealtimePixel(i + multipacketOffset, p->property_values[j], p->property_values[j+1], p->property_values[j+2], 0);
+ uint16_t uni = htons(p->universe);
+ uint8_t previousUniverses = uni - e131Universe;
+ uint16_t possibleLEDsInCurrentUniverse;
+ uint16_t dmxChannels = htons(p->property_value_count) -1;
+
+ switch (DMXMode) {
+ case DMX_MODE_DISABLED:
+ return; // nothing to do
+ break;
+
+ case DMX_MODE_SINGLE_RGB:
+ if (uni != e131Universe) return;
+ if (dmxChannels-DMXAddress+1 < 3) return;
+ for (uint16_t i = 0; i < ledCount; i++)
+ setRealtimePixel(i, p->property_values[DMXAddress+0], p->property_values[DMXAddress+1], p->property_values[DMXAddress+2], 0);
+ break;
+
+ case DMX_MODE_SINGLE_DRGB:
+ if (uni != e131Universe) return;
+ if (dmxChannels-DMXAddress+1 < 4) return;
+ if (DMXOldDimmer != p->property_values[DMXAddress+0]) {
+ DMXOldDimmer = p->property_values[DMXAddress+0];
+ bri = p->property_values[DMXAddress+0];
+ strip.setBrightness(bri);
+ }
+ for (uint16_t i = 0; i < ledCount; i++)
+ setRealtimePixel(i, p->property_values[DMXAddress+1], p->property_values[DMXAddress+2], p->property_values[DMXAddress+3], 0);
+ break;
+
+ case DMX_MODE_EFFECT:
+ if (uni != e131Universe) return;
+ if (dmxChannels-DMXAddress+1 < 11) return;
+ if (DMXOldDimmer != p->property_values[DMXAddress+0]) {
+ DMXOldDimmer = p->property_values[DMXAddress+0];
+ bri = p->property_values[DMXAddress+0];
+ }
+ if (p->property_values[DMXAddress+1] < MODE_COUNT)
+ effectCurrent = p->property_values[DMXAddress+ 1];
+ effectSpeed = p->property_values[DMXAddress+ 2]; // flickers
+ effectIntensity = p->property_values[DMXAddress+ 3];
+ effectPalette = p->property_values[DMXAddress+ 4];
+ col[0] = p->property_values[DMXAddress+ 5];
+ col[1] = p->property_values[DMXAddress+ 6];
+ col[2] = p->property_values[DMXAddress+ 7];
+ colSec[0] = p->property_values[DMXAddress+ 8];
+ colSec[1] = p->property_values[DMXAddress+ 9];
+ colSec[2] = p->property_values[DMXAddress+10];
+ if (dmxChannels-DMXAddress+1 > 11)
+ {
+ col[3] = p->property_values[DMXAddress+11]; //white
+ colSec[3] = p->property_values[DMXAddress+12];
+ }
+ transitionDelayTemp = 0; // act fast
+ colorUpdated(3); // don't send UDP
+ return; // don't activate realtime live mode
+ break;
+
+ case DMX_MODE_MULTIPLE_RGB:
+ if (previousUniverses == 0) {
+ // first universe of this fixture
+ possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress + 1) / 3;
+ for (uint16_t i = 0; i < ledCount; i++) {
+ if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s)
+ setRealtimePixel(i, p->property_values[DMXAddress+i*3+0], p->property_values[DMXAddress+i*3+1], p->property_values[DMXAddress+i*3+2], 0);
+ }
+ } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) {
+ // additional universe(s) of this fixture
+ uint16_t numberOfLEDsInPreviousUniverses = ((512 - DMXAddress + 1) / 3); // first universe
+ if (previousUniverses > 1) numberOfLEDsInPreviousUniverses += (512 / 3) * (previousUniverses - 1); // extended universe(s) before current
+ possibleLEDsInCurrentUniverse = dmxChannels / 3;
+ for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) {
+ uint8_t j = i - numberOfLEDsInPreviousUniverses;
+ if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s)
+ setRealtimePixel(i, p->property_values[j*3+1], p->property_values[j*3+2], p->property_values[j*3+3], 0);
+ }
+ }
+ break;
+
+ case DMX_MODE_MULTIPLE_DRGB:
+ if (previousUniverses == 0) {
+ // first universe of this fixture
+ if (DMXOldDimmer != p->property_values[DMXAddress+0]) {
+ DMXOldDimmer = p->property_values[DMXAddress+0];
+ bri = p->property_values[DMXAddress+0];
+ strip.setBrightness(bri);
+ }
+ possibleLEDsInCurrentUniverse = (dmxChannels - DMXAddress) / 3;
+ for (uint16_t i = 0; i < ledCount; i++) {
+ if (i >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s)
+ setRealtimePixel(i, p->property_values[DMXAddress+i*3+1], p->property_values[DMXAddress+i*3+2], p->property_values[DMXAddress+i*3+3], 0);
+ }
+ } else if (previousUniverses > 0 && uni < (e131Universe + E131_MAX_UNIVERSE_COUNT)) {
+ // additional universe(s) of this fixture
+ uint16_t numberOfLEDsInPreviousUniverses = ((512 - DMXAddress + 1) / 3); // first universe
+ if (previousUniverses > 1) numberOfLEDsInPreviousUniverses += (512 / 3) * (previousUniverses - 1); // extended universe(s) before current
+ possibleLEDsInCurrentUniverse = dmxChannels / 3;
+ for (uint16_t i = numberOfLEDsInPreviousUniverses; i < ledCount; i++) {
+ uint8_t j = i - numberOfLEDsInPreviousUniverses;
+ if (j >= possibleLEDsInCurrentUniverse) break; // more LEDs will follow in next universe(s)
+ setRealtimePixel(i, p->property_values[j*3+1], p->property_values[j*3+2], p->property_values[j*3+3], 0);
+ }
+ }
+ break;
+
+ default:
+ DEBUG_PRINTLN("unknown E1.31 DMX mode");
+ return; // nothing to do
+ break;
}
+ arlsLock(realtimeTimeoutMs, REALTIME_MODE_E131);
e131NewData = true;
}
@@ -124,12 +234,10 @@ void handleNotifications()
}
//unlock strip when realtime UDP times out
- if (realtimeActive && millis() > realtimeTimeout)
+ if (realtimeMode && millis() > realtimeTimeout)
{
strip.setBrightness(bri);
- realtimeActive = false;
- //strip.setMode(effectCurrent);
- realtimeIP[0] = 0;
+ realtimeMode = REALTIME_MODE_INACTIVE;
}
//receive UDP notifications
@@ -146,7 +254,7 @@ void handleNotifications()
DEBUG_PRINTLN(rgbUdp.remoteIP());
uint8_t lbuf[packetSize];
rgbUdp.read(lbuf, packetSize);
- arlsLock(realtimeTimeoutMs);
+ arlsLock(realtimeTimeoutMs, REALTIME_MODE_HYPERION);
uint16_t id = 0;
for (uint16_t i = 0; i < packetSize -2; i += 3)
{
@@ -166,7 +274,7 @@ void handleNotifications()
notifierUdp.read(udpIn, packetSize);
//wled notifier, block if realtime packets active
- if (udpIn[0] == 0 && !realtimeActive && receiveNotifications)
+ if (udpIn[0] == 0 && !realtimeMode && receiveNotifications)
{
//ignore notification if received within a second after sending a notification ourselves
if (millis() - notificationSentTime < 1000) return;
@@ -233,7 +341,7 @@ void handleNotifications()
realtimeTimeout = 0;
return;
} else {
- arlsLock(udpIn[1]*1000 +1);
+ arlsLock(udpIn[1]*1000 +1, REALTIME_MODE_UDP);
}
if (udpIn[0] == 1) //warls
{
diff --git a/wled00/wled08_led.ino b/wled00/wled08_led.ino
index 480b97411..883baa06a 100644
--- a/wled00/wled08_led.ino
+++ b/wled00/wled08_led.ino
@@ -33,7 +33,7 @@ void toggleOnOff()
void setAllLeds() {
- if (!realtimeActive || !arlsForceMaxBri)
+ if (!realtimeMode || !arlsForceMaxBri)
{
double d = briT*briMultiplier;
int val = d/100;
diff --git a/wled00/wled19_json.ino b/wled00/wled19_json.ino
index 5f36c58b4..0a9328bad 100644
--- a/wled00/wled19_json.ino
+++ b/wled00/wled19_json.ino
@@ -255,7 +255,7 @@ void serializeInfo(JsonObject root)
root["name"] = serverDescription;
root["udpport"] = udpPort;
- root["live"] = realtimeActive;
+ root["live"] = (bool)realtimeMode;
root["fxcount"] = strip.getModeCount();
root["palcount"] = strip.getPaletteCount();