Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
13 changes: 8 additions & 5 deletions wled00/FX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11772,18 +11772,21 @@ static const char _data_RESERVED[] PROGMEM = "RSVD";
// add (or replace reserved) effect mode and data into vector
// use id==255 to find unallocated gaps (with "Reserved" data string)
// if vector size() is smaller than id (single) data is appended at the end (regardless of id)
void WS2812FX::addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name) {
if (id == 255) { // find empty slot
for (size_t i=1; i<_mode.size(); i++) if (_modeData[i] == _data_RESERVED) { id = i; break; }
void WS2812FX::addEffect(uint16_t id, mode_ptr mode_fn, const char *mode_name) {
if ((id == MODE_AUTO) || (id == MODE_AUTO_LEGACY)) { // find empty slot // WLEDMM need to make sure that slot 255 is always skipped
for (size_t i=1; i<_mode.size(); i++) {
if ((_modeData[i] == _data_RESERVED) && (i != MODE_AUTO) && (i != MODE_AUTO_LEGACY)) {
id = i; break; // style hint: break is a goto in disguise
} }
}
if (id < _mode.size()) {
if ((id < _mode.size()) && (id != MODE_AUTO) && (id != MODE_AUTO_LEGACY)) { // do not overwrite legacy "auto" slot 255
if (_modeData[id] != _data_RESERVED) return; // do not overwrite alerady added effect
_mode[id] = mode_fn;
_modeData[id] = mode_name;
} else {
_mode.push_back(mode_fn);
_modeData.push_back(mode_name);
if (_modeCount < _mode.size()) _modeCount++;
if (_modeCount < _mode.size()) _modeCount++; // toDo: check if this works when _modeCount goes from 254 (max 8bit) to 256 (first 16bit)
}
}

Expand Down
25 changes: 13 additions & 12 deletions wled00/FX.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,8 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented
#define FX_MODE_PS1DSPRINGY 227

#define MODE_COUNT 228
#define MODE_AUTO 65000 // magic value to add/remove effects at runtime
#define MODE_AUTO_LEGACY 255 // magic value #2, for legacy code still using addEffect(255, ....

typedef enum mapping1D2D {
M12_Pixels = 0,
Expand All @@ -392,7 +394,7 @@ typedef struct Segment {
uint8_t speed;
uint8_t intensity;
uint8_t palette;
uint8_t mode;
uint16_t mode;
union {
uint16_t options; //bit pattern: msb first: [transposed mirrorY reverseY] transitional (tbd) paused needspixelstate mirrored on reverse selected
struct {
Expand Down Expand Up @@ -481,7 +483,7 @@ typedef struct Segment {
uint8_t _cctT; // temporary CCT
CRGBPalette16 _palT; // temporary palette
uint8_t _prevPaletteBlends; // number of previous palette blends (there are max 255 blends possible)
uint8_t _modeP; // previous mode/effect
uint16_t _modeP; // previous mode/effect
//uint16_t _aux0, _aux1; // previous mode/effect runtime data
//uint32_t _step, _call; // previous mode/effect runtime data
//byte *_data; // previous mode/effect runtime data
Expand Down Expand Up @@ -616,7 +618,7 @@ typedef struct Segment {
void setCCT(uint16_t k);
void setOpacity(uint8_t o);
void setOption(uint8_t n, bool val);
void setMode(uint8_t fx, bool loadDefaults = false, bool sliderDefaultsOnly = false);
void setMode(uint16_t fx, bool loadDefaults = false, bool sliderDefaultsOnly = false);
void setPalette(uint8_t pal);
uint8_t differs(Segment& b) const;
void refreshLightCapabilities(void);
Expand Down Expand Up @@ -659,7 +661,7 @@ typedef struct Segment {
}
}

uint8_t currentMode(uint8_t modeNew);
uint16_t currentMode(uint16_t modeNew);
uint32_t currentColor(uint8_t slot, uint32_t colorNew);
CRGBPalette16 &loadPalette(CRGBPalette16 &tgt, uint8_t pal) const;
void setCurrentPalette(void);
Expand Down Expand Up @@ -862,10 +864,10 @@ class WS2812FX { // 96 bytes
typedef uint16_t (*mode_ptr)(void); // pointer to mode function
typedef void (*show_callback)(void); // pre show callback
typedef struct ModeData {
uint8_t _id; // mode (effect) id
uint16_t _id; // mode (effect) id
mode_ptr _fcn; // mode (effect) function
const char *_data; // mode (effect) name and its UI control data
ModeData(uint8_t id, uint16_t (*fcn)(void), const char *data) : _id(id), _fcn(fcn), _data(data) {}
ModeData(uint16_t id, uint16_t (*fcn)(void), const char *data) : _id(id), _fcn(fcn), _data(data) {}
} mode_data_t;

static WS2812FX* instance;
Expand Down Expand Up @@ -944,7 +946,7 @@ class WS2812FX { // 96 bytes
finalizeInit(),
waitUntilIdle(void), // WLEDMM
service(void),
setMode(uint8_t segid, uint8_t m),
setMode(uint8_t segid, uint16_t m),
setColor(uint8_t slot, uint32_t c),
setCCT(uint16_t k),
setBrightness(uint8_t b, bool direct = false),
Expand All @@ -964,7 +966,7 @@ class WS2812FX { // 96 bytes

void setColor(uint8_t slot, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0) { setColor(slot, RGBW32(r,g,b,w)); }
void fill(uint32_t c) { for (int i = 0; i < getLengthTotal(); i++) setPixelColor(i, c); } // fill whole strip with color (inline)
void addEffect(uint8_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp
void addEffect(uint16_t id, mode_ptr mode_fn, const char *mode_name); // add effect to the list; defined in FX.cpp
void setupEffectData(void); // add default effects to the list; defined in FX.cpp

// outsmart the compiler :) by correctly overloading
Expand Down Expand Up @@ -1004,7 +1006,7 @@ class WS2812FX { // 96 bytes
inline uint8_t getCurrSegmentId(void) const { return _segment_index; }
inline uint8_t getMainSegmentId(void) const { return _mainSegment; }
inline uint8_t getTargetFps() const { return _targetFps; }
inline uint8_t getModeCount() const { return _modeCount; }
inline uint16_t getModeCount() const { return _modeCount; }
inline static constexpr uint8_t getMaxSegments(void) { return MAX_NUM_SEGMENTS; } // returns maximum number of supported segments (fixed value)
inline static constexpr uint8_t getPaletteCount() { return 13 + GRADIENT_PALETTE_COUNT; } // will only return built-in palette count

Expand All @@ -1031,8 +1033,7 @@ class WS2812FX { // 96 bytes
inline uint32_t segColor(uint8_t i) const { return _colors_t[i]; }

const char *
getModeData(uint8_t id = 0) const { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); }

getModeData(uint16_t id = 0) const { return (id && id<_modeCount) ? _modeData[id] : PSTR("Solid"); }
const char **
getModeDataSrc(void) { return &(_modeData[0]); } // vectors use arrays for underlying data

Expand Down Expand Up @@ -1144,7 +1145,7 @@ class WS2812FX { // 96 bytes
bool _triggered : 1;
};

uint8_t _modeCount;
uint16_t _modeCount;
std::vector<mode_ptr> _mode; // SRAM footprint: 4 bytes per element
std::vector<const char*> _modeData; // mode (effect) name and its slider control data array

Expand Down
8 changes: 4 additions & 4 deletions wled00/FX_fcn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ void Segment::startTransition(uint16_t dur) {
uint8_t _briT = currentBri(on ? opacity : 0);
uint8_t _cctT = currentBri(cct, true);
CRGBPalette16 _palT = CRGBPalette16(DEFAULT_COLOR); loadPalette(_palT, palette);
uint8_t _modeP = mode;
auto _modeP = mode;
uint32_t _colorT[NUM_COLORS];
for (size_t i=0; i<NUM_COLORS; i++) _colorT[i] = currentColor(i, colors[i]);

Expand Down Expand Up @@ -478,7 +478,7 @@ uint8_t IRAM_ATTR_YN Segment::currentBri(uint8_t briNew, bool useCct) const {
}
#endif

uint8_t Segment::currentMode(uint8_t newMode) {
uint16_t Segment::currentMode(uint16_t newMode) {
return (progress()>32767U) ? newMode : (_t ? _t->_modeP : newMode); // change effect in the middle of transition
}

Expand Down Expand Up @@ -590,7 +590,7 @@ void Segment::setOption(uint8_t n, bool val) {
if (!(n == SEG_OPTION_SELECTED || n == SEG_OPTION_RESET || n == SEG_OPTION_TRANSITIONAL)) stateChanged = true; // send UDP/WS broadcast
}

void Segment::setMode(uint8_t fx, bool loadDefaults, bool sliderDefaultsOnly) {
void Segment::setMode(uint16_t fx, bool loadDefaults, bool sliderDefaultsOnly) {
//WLEDMM: return to old setting if not explicitly set
static int16_t oldMap = -1;
static int16_t oldSim = -1;
Expand Down Expand Up @@ -2144,7 +2144,7 @@ void WS2812FX::setTargetFps(uint8_t fps) {
if (fps >= FPS_UNLIMITED) _frametime = 2; // WLEDMM unlimited mode
}

void WS2812FX::setMode(uint8_t segid, uint8_t m) {
void WS2812FX::setMode(uint8_t segid, uint16_t m) {
if (segid >= _segments.size()) return;

if (m >= getModeCount()) m = getModeCount() - 1;
Expand Down
2 changes: 1 addition & 1 deletion wled00/e131.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ void handleDMXData(uint16_t uni, uint16_t dmxChannels, uint8_t* e131_data, uint8
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+1] != seg.mode) seg.setMode( e131_data[dataOffset+1]); // WLEDMM Only one byte for mode, so DMX cannot control effects with id>255
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]);
Expand Down
11 changes: 8 additions & 3 deletions wled00/fcn_declare.h
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,11 @@ int getNumVal(const String* req, uint16_t pos);
void parseNumber(const char* str, byte* val, byte minv=0, byte maxv=255);
bool getVal(JsonVariant elem, byte* val, byte minv=0, byte maxv=255);
bool updateVal(const char* req, const char* key, byte* val, byte minv=0, byte maxv=255);

void parseNumber16(const char* str, uint16_t* val, uint16_t minv=0, uint16_t maxv=255); // the real thing in 16bit
bool getVal16(JsonVariant elem, uint16_t* val, uint16_t vmin, uint16_t vmax=255); // same as above, with 2byte output buffer
bool updateVal16(const char* req, const char* key, uint16_t* val, uint16_t minv=0, uint16_t maxv=255);

void oappendUseDeflate(bool OnOff); // enable / disable string squeezing
bool oappend(const char* txt); // append new c string to temp buffer efficiently
bool oappendi(int i); // append new number to temp buffer efficiently
Expand All @@ -395,9 +400,9 @@ void prepareHostname(char* hostname);
bool isAsterisksOnly(const char* str, byte maxLen) __attribute__((pure));
bool requestJSONBufferLock(uint8_t module=255);
void releaseJSONBufferLock();
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen);
uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen, uint8_t *var = nullptr);
int16_t extractModeDefaults(uint8_t mode, const char *segVar);
uint16_t extractModeName(uint16_t mode, const char *src, char *dest, uint16_t maxLen);
uint16_t extractModeSlider(uint16_t mode, uint8_t slider, char *dest, uint16_t maxLen, uint8_t *var = nullptr);
int16_t extractModeDefaults(uint16_t mode, const char *segVar);
void checkSettingsPIN(const char *pin);
uint16_t __attribute__((pure)) crc16(const unsigned char* data_p, size_t length); // WLEDMM: added attribute pure

Expand Down
6 changes: 3 additions & 3 deletions wled00/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,15 +292,15 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
if (seg.is2D() && (seg.map1D2D == M12_pArc || seg.map1D2D == M12_sCircle) && (reverse != seg.reverse || reverse_y != seg.reverse_y || mirror != seg.mirror || mirror_y != seg.mirror_y)) seg.markForBlank(); // clear entire segment (in case of Arc 1D to 2D expansion) WLEDMM: also Circle
#endif

byte fx = seg.mode;
byte last = strip.getModeCount();
auto fx = seg.mode;
auto last = strip.getModeCount();
// partial fix for #3605
if (!elem["fx"].isNull() && elem["fx"].is<const char*>()) {
const char *tmp = elem["fx"].as<const char *>();
if (strlen(tmp) > 3 && (strchr(tmp,'r') || strchr(tmp,'~') != strrchr(tmp,'~'))) last = 0; // we have "X~Y(r|[w]~[-])" form
}
// end fix
if (getVal(elem["fx"], &fx, 0, last)) { //load effect ('r' random, '~' inc/dec, 0-255 exact value, 5~10r pick random between 5 & 10)
if (getVal16(elem["fx"], &fx, 0, last)) { //load effect ('r' random, '~' inc/dec, 0-255 exact value, 5~10r pick random between 5 & 10)
if (!presetId && currentPlaylist>=0) unloadPlaylist();
if (fx != seg.mode) seg.setMode(fx, elem[F("fxdef")], elem[F("fxdef2")]); // WLEDMM fxdef2 added
}
Expand Down
4 changes: 2 additions & 2 deletions wled00/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
uint32_t col1 = selseg.colors[1];
byte colIn[4] = {R(col0), G(col0), B(col0), W(col0)};
byte colInSec[4] = {R(col1), G(col1), B(col1), W(col1)};
byte effectIn = selseg.mode;
auto effectIn = selseg.mode;
byte speedIn = selseg.speed;
byte intensityIn = selseg.intensity;
byte paletteIn = selseg.palette;
Expand Down Expand Up @@ -1049,7 +1049,7 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
bool fxModeChanged = false, speedChanged = false, intensityChanged = false, paletteChanged = false;
bool custom1Changed = false, custom2Changed = false, custom3Changed = false, check1Changed = false, check2Changed = false, check3Changed = false;
// set effect parameters
if (updateVal(req.c_str(), "FX=", &effectIn, 0, strip.getModeCount()-1)) {
if (updateVal16(req.c_str(), "FX=", &effectIn, 0, strip.getModeCount()-1)) {
if (request != nullptr) unloadPlaylist(); // unload playlist if changing FX using web request
fxModeChanged = true;
}
Expand Down
4 changes: 2 additions & 2 deletions wled00/udp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ void notify(byte callMode, bool followUp)
udpOut[5] = B(col);
udpOut[6] = nightlightActive;
udpOut[7] = nightlightDelayMins;
udpOut[8] = mainseg.mode;
udpOut[8] = byte(mainseg.mode); // WLEDMM toDo: need workaround for 16bit mode numbers
udpOut[9] = mainseg.speed;
Copy link

@coderabbitai coderabbitai bot Oct 21, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

16-bit sync regression in UDP payload

With mainseg.mode now holding values above 255, sticking only the low byte into udpOut[8] means we silently truncate any effect ID ≥256. Peers receiving the notification will run a different effect once we start using the new IDs, so cross-device sync breaks. Please extend the notifier format (and its decoder) to ship both bytes—or version-gate the message—before landing the 16-bit change.

🤖 Prompt for AI Agents
In wled00/udp.cpp around lines 40-41, the code writes only the low byte of
mainseg.mode into udpOut[8], which truncates 16-bit mode IDs ≥256; update the
notifier to emit both low and high bytes (e.g., little-endian: udpOut[8]=low,
udpOut[9]=high) and increment or set a protocol/version byte so receivers can
detect the extended format, and simultaneously update the UDP decoder/receiver
code to read two bytes into a 16-bit mode value when the version indicates
extension (fall back to single-byte behavior for older versions).

Copy link
Collaborator Author

@softhack007 softhack007 Oct 22, 2025

Choose a reason for hiding this comment

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

the (very clumsy) workaround for now is to clamp effect ID at 255 or udp. For a real solution, there should be a new version of the UDP dataset.

Copy link

Choose a reason for hiding this comment

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

@softhack007 Agreed. Here’s a minimal, backward-compatible “UDP notifier v13” that carries 16-bit effect IDs via the per-segment payload, while keeping the header’s mode field clamped to 8-bit for legacy peers. This avoids changing the fixed 41-byte header and uses the already variable per-segment block size.

Key points

  • Header (byte 8) remains 8-bit for compatibility; new peers should consume the per-segment mode.
  • Bump compatibilityVersionByte to 13.
  • Increase per-segment block size from 36 to 38 bytes and append:
    • [36+ofs]: mode MSB
  • Receiver: if version >= 13 and per-segment size >= 38, read 16-bit mode = LSB ([11+ofs]) | (MSB << 8) ([36+ofs]).

Proposed patch (wled00/udp.cpp)

diff --git a/wled00/udp.cpp b/wled00/udp.cpp
index 5f6c9ab..b0c2e33 100644
--- a/wled00/udp.cpp
+++ b/wled00/udp.cpp
@@ -6,7 +6,8 @@
  * UDP sync notifier / Realtime / Hyperion / TPM2.NET
  */

-#define UDP_SEG_SIZE 36
+// v13: extend per-segment payload by 2 bytes (mode MSB + reserved)
+#define UDP_SEG_SIZE 38
 #define SEG_OFFSET (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE))
 #define WLEDPACKETSIZE (41+(MAX_NUM_SEGMENTS*UDP_SEG_SIZE)+0)
 #define UDP_IN_MAXSIZE 1472
@@ -33,10 +34,10 @@ void notify(byte callMode, bool followUp)
   udpOut[4] = G(col);
   udpOut[5] = B(col);
   udpOut[6] = nightlightActive;
   udpOut[7] = nightlightDelayMins;
-  udpOut[8] = min(mainseg.mode, uint16_t(255));  // WLEDMM toDo: need workaround for 16bit mode numbers - critical as this affects webUI
+  udpOut[8] = min(mainseg.mode, uint16_t(255));  // v13: header remains 8-bit for legacy peers; 16-bit lives in per-segment payload
   udpOut[9] = mainseg.speed;
   udpOut[10] = W(col);
   //compatibilityVersionByte:
   //0: old 1: supports white 2: supports secondary color
   //3: supports FX intensity, 24 byte packet 4: supports transitionDelay 5: sup palette
   //6: supports timebase syncing, 29 byte packet 7: supports tertiary color 8: supports sys time sync, 36 byte packet
   //9: supports sync groups, 37 byte packet 10: supports CCT, 39 byte packet 11: per segment options, variable packet length (40+MAX_NUM_SEGMENTS*3)
-  //12: enhanced effect sliders, 2D & mapping options
-  udpOut[11] = 12;
+  //12: enhanced effect sliders, 2D & mapping options 13: 16-bit effect ID in per-segment payload
+  udpOut[11] = 13;
   col = mainseg.colors[1];
   udpOut[12] = R(col);
   udpOut[13] = G(col);
@@ -86,6 +87,8 @@ void notify(byte callMode, bool followUp)
     udpOut[10+ofs] = selseg.opacity;
-    udpOut[11+ofs] = min(selseg.mode, uint16_t(255));  // WLEDMM toDo: need workaround for 16bit mode numbers
+    // Per-segment effect: keep LSB at legacy location for old receivers, add MSB at the tail for v13+
+    udpOut[11+ofs] = min(selseg.mode, uint16_t(255));
     udpOut[12+ofs] = selseg.speed;
     udpOut[13+ofs] = selseg.intensity;
     udpOut[14+ofs] = selseg.palette;
@@ -109,6 +112,10 @@ void notify(byte callMode, bool followUp)
     udpOut[34+ofs] = selseg.stopY >> 8;
     udpOut[35+ofs] = selseg.stopY & 0xFF;
+    // v13 additions for 16-bit effect IDs
+    udpOut[36+ofs] = (selseg.mode >> 8) & 0xFF; // mode MSB
+    udpOut[37+ofs] = 0;                         // reserved for future use
     ++s;
   }
@@ -282,12 +289,21 @@ void handleNotifications()
           selseg.setOpacity(udpIn[10+ofs]);
           if (applyEffects) {
-            strip.setMode(id,  udpIn[11+ofs]);
+            // Reconstruct 16-bit mode when sender is v13+ and per-segment size >= 38
+            uint16_t rxMode = udpIn[11+ofs];
+            if (version >= 13 && udpIn[40] >= 38) {
+              rxMode |= uint16_t(udpIn[36+ofs]) << 8;
+            }
+            strip.setMode(id, rxMode);
             selseg.speed     = udpIn[12+ofs];
             selseg.intensity = udpIn[13+ofs];
             selseg.palette   = udpIn[14+ofs];
           }
           if (receiveNotificationColor || !someSel) {
             selseg.setColor(0, RGBW32(udpIn[15+ofs],udpIn[16+ofs],udpIn[17+ofs],udpIn[18+ofs]));
             selseg.setColor(1, RGBW32(udpIn[19+ofs],udpIn[20+ofs],udpIn[21+ofs],udpIn[22+ofs]));
             selseg.setColor(2, RGBW32(udpIn[23+ofs],udpIn[24+ofs],udpIn[25+ofs],udpIn[26+ofs]));
             selseg.setCCT(udpIn[27+ofs]);
           }

Behavior and compatibility

  • Sender
    • Keeps header mode truncated to 8-bit (byte 8).
    • Advertises version 13 and sets per-segment size to 38 via byte 40.
    • Adds mode MSB at [36+ofs].
  • Receiver
    • If version >= 13 and per-segment size >= 38, reconstructs 16-bit mode from [11+ofs] and [36+ofs].
    • Otherwise, falls back to 8-bit behavior unchanged.
  • Legacy nodes keep working; they’ll ignore the extra bytes and use the 8-bit header and 8-bit per-segment mode.

Limitations

  • “Simple effect sync” path (when receiveSegmentOptions is disabled or sender < 11) remains 8-bit by design since the 41-byte header is fixed. 16-bit sync is guaranteed when segment options are received.

Test plan

  • v13 sender → v13 receiver: select an effect with ID > 255; verify matching effect on peer via per-segment path.
  • v13 sender → legacy receiver: peer should continue to operate but will display the truncated 8-bit effect.
  • Legacy sender → v13 receiver: unchanged behavior.

Would you like me to open a follow-up issue titled “UDP notifier v13: 16-bit effect IDs in segment payload” and reference this PR/comment so we can track the wider rollout (UI docs, config hint to enable receiveSegmentOptions, etc.)?

udpOut[10] = W(col);
//compatibilityVersionByte:
Expand Down Expand Up @@ -107,7 +107,7 @@ void notify(byte callMode, bool followUp)
udpOut[8 +ofs] = selseg.offset & 0xFF;
udpOut[9 +ofs] = selseg.options & 0x8F; //only take into account selected, mirrored, on, reversed, reverse_y (for 2D); ignore freeze, reset, transitional
udpOut[10+ofs] = selseg.opacity;
udpOut[11+ofs] = selseg.mode;
udpOut[11+ofs] = byte(selseg.mode); // WLEDMM toDo: need workaround for 16bit mode numbers
udpOut[12+ofs] = selseg.speed;
udpOut[13+ofs] = selseg.intensity;
udpOut[14+ofs] = selseg.palette;
Expand Down
53 changes: 43 additions & 10 deletions wled00/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,19 @@ int getNumVal(const String* req, uint16_t pos)
}


// wrapper for parseNumber16 to suppport byte target buffer
void parseNumber(const char* str, byte* val, byte minv, byte maxv) { // wrapper for 8bit buffer
uint16_t temp = *val;
parseNumber16(str, &temp, (uint16_t)minv, (uint16_t)maxv);
*val = constrain(temp, 0, 255);
if ((temp > 255) && (minv == 0) && (maxv == 0)) *val = temp & 0x00FF; // 8bit support for "r" with min=max=0
}

//helper to get int value with in/decrementing support via ~ syntax
void parseNumber(const char* str, byte* val, byte minv, byte maxv)
void parseNumber16(const char* str, uint16_t* val, uint16_t minv, uint16_t maxv) // the real thing in 16bit
{
if (str == nullptr || str[0] == '\0') return;
if (str[0] == 'r') {*val = random8(minv,maxv?maxv:255); return;} // maxv for random cannot be 0
if (str[0] == 'r') {*val = uint16_t(hw_random(minv,maxv?maxv:65535)); return;} // maxv for random cannot be 0, use full range
bool wrap = false;
if (str[0] == 'w' && strlen(str) > 1) {str++; wrap = true;}
if (str[0] == '~') {
Expand All @@ -38,18 +46,18 @@ void parseNumber(const char* str, byte* val, byte minv, byte maxv)
}
return;
} else if (minv == maxv && minv == 0) { // limits "unset" i.e. both 0
byte p1 = atoi(str);
uint16_t p1 = atoi(str);
const char* str2 = strchr(str,'~'); // min/max range (for preset cycle, e.g. "1~5~")
if (str2) {
byte p2 = atoi(++str2); // skip ~
uint16_t p2 = atoi(++str2); // skip ~
if (p2 > 0) {
while (isdigit(*(++str2))); // skip digits
parseNumber(str2, val, p1, p2);
parseNumber16(str2, val, p1, p2);
return;
}
}
}
*val = atoi(str);
*val = uint16_t(atoi(str));
}


Expand All @@ -68,6 +76,21 @@ bool getVal(JsonVariant elem, byte* val, byte vmin, byte vmax) {
return false; //key does not exist
}

bool getVal16(JsonVariant elem, uint16_t* val, uint16_t vmin, uint16_t vmax) { // same as above, with 2byte output buffer
if (elem.is<int>()) {
if (elem < 0) return false; //ignore e.g. {"ps":-1}
*val = elem;
return true;
} else if (elem.is<const char*>()) {
const char* str = elem;
size_t len = strnlen(str, 12);
if (len == 0 || len > 10) return false;
parseNumber16(str, val, vmin, vmax);
return true;
}
return false; //key does not exist
}


bool updateVal(const char* req, const char* key, byte* val, byte minv, byte maxv)
{
Expand All @@ -78,6 +101,15 @@ bool updateVal(const char* req, const char* key, byte* val, byte minv, byte maxv
return true;
}

bool updateVal16(const char* req, const char* key, uint16_t* val, uint16_t minv, uint16_t maxv)
{
const char *v = strstr(req, key);
if (v) v += strlen(key);
else return false;
parseNumber16(v, val, minv, maxv);
return true;
}


//append a numeric setting to string buffer
void sappend(char stype, const char* key, int val)
Expand Down Expand Up @@ -248,7 +280,7 @@ void releaseJSONBufferLock()

// extracts effect mode (or palette) name from names serialized string
// caller must provide large enough buffer for name (including SR extensions)!
uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen)
uint16_t extractModeName(uint16_t mode, const char *src, char *dest, uint16_t maxLen)
{
if (src == JSON_mode_names || src == nullptr) {
if (mode < strip.getModeCount()) {
Expand All @@ -268,7 +300,8 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
}

if (src == JSON_palette_names && mode > (GRADIENT_PALETTE_COUNT + 13)) {
snprintf_P(dest, maxLen, PSTR("~ Custom %d ~"), 255-mode);
if (mode <= 255) snprintf_P(dest, maxLen, PSTR("~ Custom %d ~"), 255-mode); // hmmm ... this function is abused to generate palette names
else snprintf_P(dest, maxLen, PSTR("~ Custom +%u ~"), mode - 255); // fallback for mode > 255 ... this should not happen for palettes, better safe than sorry
dest[maxLen-1] = '\0';
return strlen(dest);
}
Expand Down Expand Up @@ -305,7 +338,7 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe


// extracts effect slider data (1st group after @)
uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxLen, uint8_t *var)
uint16_t extractModeSlider(uint16_t mode, uint8_t slider, char *dest, uint16_t maxLen, uint8_t *var)
{
dest[0] = '\0'; // start by clearing buffer

Expand Down Expand Up @@ -380,7 +413,7 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL


// extracts mode parameter defaults from last section of mode data (e.g. "Juggle@!,Trail;!,!,;!;sx=16,ix=240,1d")
int16_t extractModeDefaults(uint8_t mode, const char *segVar)
int16_t extractModeDefaults(uint16_t mode, const char *segVar)
{
if (mode < strip.getModeCount()) {
char lineBuffer[256] = { '\0' };
Expand Down
2 changes: 1 addition & 1 deletion wled00/wled.h
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,7 @@ WLED_GLOBAL byte notificationSentCallMode _INIT(CALL_MODE_INIT);
WLED_GLOBAL uint8_t notificationCount _INIT(0);

// effects
WLED_GLOBAL byte effectCurrent _INIT(0);
WLED_GLOBAL uint16_t effectCurrent _INIT(0);
WLED_GLOBAL byte effectSpeed _INIT(128);
WLED_GLOBAL byte effectIntensity _INIT(128);
WLED_GLOBAL byte effectPalette _INIT(0);
Expand Down