Skip to content
Open
Show file tree
Hide file tree
Changes from 13 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
2 changes: 1 addition & 1 deletion usermods/ST7789_display/ST7789_display.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class St7789DisplayUsermod : public Usermod {
String knownSsid = "";
IPAddress knownIp;
uint8_t knownBrightness = 0;
uint8_t knownMode = 0;
uint16_t knownMode = 0;
uint8_t knownPalette = 0;
uint8_t knownEffectSpeed = 0;
uint8_t knownEffectIntensity = 0;
Expand Down
4 changes: 2 additions & 2 deletions usermods/usermod_v2_auto_save/usermod_v2_auto_save.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class AutoSaveUsermod : public Usermod {
uint8_t knownBrightness = 0;
uint8_t knownEffectSpeed = 0;
uint8_t knownEffectIntensity = 0;
uint8_t knownMode = 0;
uint16_t knownMode = 0;
uint8_t knownPalette = 0;

#ifdef USERMOD_FOUR_LINE_DISPLAY
Expand Down Expand Up @@ -133,7 +133,7 @@ class AutoSaveUsermod : public Usermod {
if (!autoSaveAfterSec || !enabled || strip.isUpdating() || currentPreset>0) return; // setting 0 as autosave seconds disables autosave

unsigned long now = millis();
uint8_t currentMode = strip.getMainSegment().mode;
uint16_t currentMode = strip.getMainSegment().mode;
uint8_t currentPalette = strip.getMainSegment().palette;

unsigned long wouldAutoSaveAfter = now + autoSaveAfterSec*1000;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ class RotaryEncoderUIUsermod : public Usermod {
unsigned char Enc_A_prev = 0;

bool currentEffectAndPaletteInitialized = false;
uint8_t effectCurrentIndex = 0;
uint16_t effectCurrentIndex = 0;
uint8_t effectPaletteIndex = 0;
uint8_t knownMode = 0;
uint16_t knownMode = 0;
uint8_t knownPalette = 0;
Comment on lines 155 to 157
Copy link

@coderabbitai coderabbitai bot Oct 22, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Not 16‑bit safe: byte index arrays and uint8_t loops will break >255 modes.

  • modes_alpha_indexes is byte*, and loops use uint8_t counters; they overflow for counts >255 (wrap/infinite loop, wrong indices).
  • You won’t be able to select effects with IDs ≥256 because sorted index → ID mapping truncates to 8‑bit.

Apply this minimal refactor to make the UI 16‑bit safe:

@@
-  // Array of mode indexes in alphabetical order.
-  byte *modes_alpha_indexes = nullptr;
+  // Array of mode indexes in alphabetical order (16-bit safe).
+  uint16_t *modes_alpha_indexes = nullptr;
@@
-  byte *re_initIndexArray(int numModes);
+  uint16_t *re_initIndexArray(uint16_t numModes);
@@
-  void re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip);
+  void re_sortModes(const char **modeNames, uint16_t *indexes, uint16_t count, uint16_t numSkip);
@@ void RotaryEncoderUIUsermod::sortModesAndPalettes() {
-  modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
+  modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
@@
-byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) {
-  byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
-  for (byte i = 0; i < numModes; i++) {
+uint16_t *RotaryEncoderUIUsermod::re_initIndexArray(uint16_t numModes) {
+  uint16_t *indexes = (uint16_t *)malloc(sizeof(uint16_t) * numModes);
+  for (uint16_t i = 0; i < numModes; i++) {
     indexes[i] = i;
   }
   return indexes;
 }
@@ const char **RotaryEncoderUIUsermod::re_findModeStrings(const char json[], int numModes) {
-  uint8_t modeIndex = 0;
+  uint16_t modeIndex = 0;
@@ void RotaryEncoderUIUsermod::re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip) {
-void RotaryEncoderUIUsermod::re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip) {
+void RotaryEncoderUIUsermod::re_sortModes(const char **modeNames, uint16_t *indexes, uint16_t count, uint16_t numSkip) {
   if (!modeNames) return;
   listBeingSorted = modeNames;
-  qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
+  qsort(indexes + numSkip, count - numSkip, sizeof(uint16_t), re_qstringCmp);
   listBeingSorted = nullptr;
 }
@@ void RotaryEncoderUIUsermod::findCurrentEffectAndPalette() {
-  for (uint8_t i = 0; i < strip.getModeCount(); i++) {
+  for (uint16_t i = 0; i < strip.getModeCount(); i++) {
@@
-  for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
+  for (uint16_t i = 0; i < strip.getPaletteCount(); i++) {

Also update re_qstringCmp to read uint16_t indices:

-static int re_qstringCmp(const void *ap, const void *bp) {
-  const char *a = listBeingSorted[*((byte *)ap)];
-  const char *b = listBeingSorted[*((byte *)bp)];
+static int re_qstringCmp(const void *ap, const void *bp) {
+  const char *a = listBeingSorted[*((const uint16_t *)ap)];
+  const char *b = listBeingSorted[*((const uint16_t *)bp)];

This prevents overflow and enables selecting effects beyond 255.


🏁 Script executed:

#!/bin/bash
# Locate the rotary encoder UI usermod files
fd usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h

# Search for modes_alpha_indexes declaration and usage
rg -n "modes_alpha_indexes" usermods/usermod_v2_rotary_encoder_ui_ALT

# Search for re_initIndexArray definition and usage
rg -n "re_initIndexArray" -n usermods/usermod_v2_rotary_encoder_ui_ALT

# Search for re_sortModes definition and usage
rg -n "re_sortModes" -n usermods/usermod_v2_rotary_encoder_ui_ALT

# Search for loops using uint8_t counters
rg -n "for (uint8_t" -n usermods/usermod_v2_rotary_encoder_ui_ALT

# Search for qsort calls in this module
rg -n "qsort" -n usermods/usermod_v2_rotary_encoder_ui_ALT

Length of output: 3045


🏁 Script executed:

#!/bin/bash
# Search all uint8_t usages in this module
rg -n "uint8_t" usermods/usermod_v2_rotary_encoder_ui_ALT
# Show context around strip.getModeCount usage for loops
rg -n "strip.getModeCount" -C3 usermods/usermod_v2_rotary_encoder_ui_ALT

Length of output: 4375


🏁 Script executed:

#!/bin/bash
# Show first 200 lines to find effectCurrentIndex declaration
cat -n usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h | head -200

Length of output: 6357


Make mode/palette indices and related loops 16-bit safe

  • Change byte *modes_alpha_indexes/byte *palettes_alpha_indexes to uint16_t *.
  • Update signatures:
    • byte *re_initIndexArray(int numModes)uint16_t *re_initIndexArray(uint16_t numModes)
    • void re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip)void re_sortModes(const char **modeNames, uint16_t *indexes, uint16_t count, uint16_t numSkip)
  • In re_sortModes, use qsort(..., sizeof(uint16_t), re_qstringCmp) and cast in re_qstringCmp from byte* to const uint16_t*.
  • Replace all for (uint8_t i = 0; i < strip.getModeCount(); i++) and similar palette loops with for (uint16_t i = 0; i < strip.getModeCount(); i++) to avoid overflow when counts exceed 255.

This ensures arrays, loops, and sorting handle >255 modes/palettes without truncation or wrap-around.

🤖 Prompt for AI Agents
In usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h
around lines 155-157, change all mode/palette index variables and related
arrays/functions to 16-bit to avoid overflow: make effectPaletteIndex,
knownPalette (and any similar uint8_t index vars) uint16_t; change byte
*modes_alpha_indexes and byte *palettes_alpha_indexes to uint16_t *; update
function signatures to uint16_t *re_initIndexArray(uint16_t numModes) and void
re_sortModes(const char **modeNames, uint16_t *indexes, uint16_t count, uint16_t
numSkip); in re_sortModes call qsort with sizeof(uint16_t) and ensure the
comparator casts its void*/byte* parameters to const uint16_t* before
dereferencing; and replace all for-loops that use uint8_t for mode/palette
iteration (e.g. for (uint8_t i = 0; i < strip.getModeCount(); i++)) with
uint16_t to handle counts >255.

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.

@coderabbitai sorry, this is unrelated. Mode IDs are now 16bit, but palette ID (and preset IDs, too) remain as 8bit.

Copy link

Choose a reason for hiding this comment

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

@softhack007 You’re right—thanks for the correction. Let’s scope the change strictly to modes and leave palettes 8-bit. Here’s a minimal patch that makes only the mode-side 16-bit safe while keeping palette indices and arrays as byte.

diff --git a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h
--- a/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h
+++ b/usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h
@@
 static int re_qstringCmp(const void *ap, const void *bp) {
   const char *a = listBeingSorted[*((byte *)ap)];
   const char *b = listBeingSorted[*((byte *)bp)];
   int i = 0;
   do {
@@
   return 0;
 }
 
+// 16-bit comparator for mode index sorting (palettes remain 8-bit)
+static int re_qstringCmp16(const void *ap, const void *bp) {
+  const char *a = listBeingSorted[*((const uint16_t *)ap)];
+  const char *b = listBeingSorted[*((const uint16_t *)bp)];
+  int i = 0;
+  do {
+    char aVal = pgm_read_byte_near(a + i);
+    if (aVal >= 97 && aVal <= 122) aVal -= 32;
+    char bVal = pgm_read_byte_near(b + i);
+    if (bVal >= 97 && bVal <= 122) bVal -= 32;
+    if (aVal == '"' || bVal == '"' || aVal == '\0' || bVal == '\0') {
+      if (aVal == bVal) return 0;
+      else if (aVal == '"' || aVal == '\0') return -1;
+      else return 1;
+    }
+    if (aVal == bVal) { i++; continue; }
+    return (aVal < bVal) ? -1 : 1;
+  } while (true);
+  return 0;
+}
@@ class RotaryEncoderUIUsermod : public Usermod {
 private:
@@
-  // Array of mode indexes in alphabetical order.
-  byte *modes_alpha_indexes = nullptr;
+  // Array of mode indexes in alphabetical order (16-bit for modes).
+  uint16_t *modes_alpha_indexes = nullptr;
@@
   // Pointers the start of the palette names within JSON_palette_names
   const char **palettes_qstrings = nullptr;
 
   // Array of palette indexes in alphabetical order.
   byte *palettes_alpha_indexes = nullptr;
@@
-  byte *re_initIndexArray(int numModes);
+  byte *re_initIndexArray(int numModes);                     // for palettes (8-bit)
+  uint16_t *re_initIndexArray16(uint16_t numModes);          // for modes (16-bit)
@@
-  void re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip);
+  void re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip); // palettes
+  void re_sortModes16(const char **modeNames, uint16_t *indexes, uint16_t count, uint16_t numSkip); // modes
@@ void RotaryEncoderUIUsermod::sortModesAndPalettes() {
-  //modes_qstrings = re_findModeStrings(JSON_mode_names, strip.getModeCount());
   modes_qstrings = strip.getModeDataSrc();
-  modes_alpha_indexes = re_initIndexArray(strip.getModeCount());
-  re_sortModes(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
+  modes_alpha_indexes = re_initIndexArray16(strip.getModeCount());
+  re_sortModes16(modes_qstrings, modes_alpha_indexes, strip.getModeCount(), MODE_SORT_SKIP_COUNT);
@@
   palettes_qstrings = re_findModeStrings(JSON_palette_names, strip.getPaletteCount());
   palettes_alpha_indexes = re_initIndexArray(strip.getPaletteCount());  // only use internal palettes
@@
   re_sortModes(palettes_qstrings, palettes_alpha_indexes, strip.getPaletteCount(), skipPaletteCount);
 }
@@
 byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) {
   byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
   for (byte i = 0; i < numModes; i++) {
     indexes[i] = i;
   }
   return indexes;
 }
+
+uint16_t *RotaryEncoderUIUsermod::re_initIndexArray16(uint16_t numModes) {
+  uint16_t *indexes = (uint16_t *)malloc(sizeof(uint16_t) * numModes);
+  for (uint16_t i = 0; i < numModes; i++) {
+    indexes[i] = i;
+  }
+  return indexes;
+}
@@
 void RotaryEncoderUIUsermod::re_sortModes(const char **modeNames, byte *indexes, int count, int numSkip) {
   if (!modeNames) return;
   listBeingSorted = modeNames;
   qsort(indexes + numSkip, count - numSkip, sizeof(byte), re_qstringCmp);
   listBeingSorted = nullptr;
 }
+
+void RotaryEncoderUIUsermod::re_sortModes16(const char **modeNames, uint16_t *indexes, uint16_t count, uint16_t numSkip) {
+  if (!modeNames) return;
+  listBeingSorted = modeNames;
+  qsort(indexes + numSkip, count - numSkip, sizeof(uint16_t), re_qstringCmp16);
+  listBeingSorted = nullptr;
+}
@@ void RotaryEncoderUIUsermod::findCurrentEffectAndPalette() {
   if (modes_alpha_indexes == nullptr) return; // WLEDMM bugfix
   currentEffectAndPaletteInitialized = true;
-  for (uint8_t i = 0; i < strip.getModeCount(); i++) {
+  for (uint16_t i = 0; i < strip.getModeCount(); i++) {
     if (modes_alpha_indexes[i] == effectCurrent) {
       effectCurrentIndex = i;
       break;
     }
   }
 
-  for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
+  // palettes remain 8-bit
+  for (uint8_t i = 0; i < strip.getPaletteCount(); i++) {
     if (palettes_alpha_indexes[i] == effectPalette) {
       effectPaletteIndex = i;
       break;
     }
   }
 }

Result:

  • Mode sorting/indexing is 16-bit safe, so selecting effects with IDs ≥256 works.
  • Palette sorting/indexing is unchanged and stays 8-bit as intended.

If you prefer, I can split the new 16-bit helpers into a small .cpp to keep the header slimmer.


✏️ Learnings added
Learnt from: softhack007
PR: MoonModules/WLED-MM#270
File: usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h:155-157
Timestamp: 2025-10-22T21:50:25.799Z
Learning: In WLED-MM PR #270, only Effect (Mode) IDs are migrated to 16-bit; Palette IDs and counts remain 8-bit. Usermod code should widen mode-related indices/loops to uint16_t while keeping palette-related indices/loops as uint8_t.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


uint8_t currentCCT = 128;
Expand Down
45 changes: 39 additions & 6 deletions wled00/FX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11772,19 +11772,52 @@ 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; }
/////
// WLEDMM extended to use 16bit effect IDs
// PoC: if id >= _mode.size(), we currently append at the tail and ignore the explicit id.
// TODO(WLED‑MM): honor explicit IDs by resizing/backfilling up to `id` and placing the effect
// at exactly that slot; update _modeCount = max(_modeCount, id+1).// PoC: if id >= _mode.size(), we currently append at the tail and ignore the explicit id.
// TODO(WLED‑MM): honor explicit IDs by resizing/backfilling up to `id` and placing the effect
// at exactly that slot; update _modeCount = max(_modeCount, id+1).
void WS2812FX::addEffect(uint16_t id, mode_ptr mode_fn, const char *mode_name) {
if ((id < _mode.size()) && (_modeData[id] != _data_RESERVED)) {
DEBUG_PRINTF("addEffect(%d) -> ", id);
DEBUG_PRINTF(" already in use, finding a new slot for -> %s\n", mode_name);
id = MODE_AUTO;
}
if ((id >= _mode.size()) && (id != MODE_AUTO) && (id != MODE_AUTO_LEGACY)) {
DEBUG_PRINTF("!addEffect(%d) -> slot not existing, adding new slot\n", id);
}

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 (_modeData[id] != _data_RESERVED) return; // do not overwrite alerady added effect

if ((id < _mode.size()) && (id != MODE_AUTO) && (id != MODE_AUTO_LEGACY)) { // do not overwrite legacy "auto" slot 255
if (_modeData[id] != _data_RESERVED) { // do not overwrite alerady added effect
USER_PRINTF("!addEffect(%d) failed - existing effect cannot be replaced. <=> %s\n", id, mode_name);
return;
} // all good, insert into existing RSVD slot
_mode[id] = mode_fn;
_modeData[id] = mode_name;
} else {
if (_modeCount == MODE_AUTO_LEGACY && ((id == MODE_AUTO) || (id == MODE_AUTO_LEGACY))) {
// add dummy entry to protect slot 255 = MODE_AUTO_LEGACY
DEBUG_PRINTF("+addEffect(%d) creating dummy effect 255, modecount = %d.\n", id, _modeCount);
_mode.push_back(&mode_static);
_modeData.push_back(_data_RESERVED);
if (_modeCount < _mode.size()) _modeCount++;
}
// new slot needed -> append to end of vector
_mode.push_back(mode_fn);
_modeData.push_back(mode_name);
if (_modeCount < _mode.size()) _modeCount++;
id = _modeCount;
if (_modeCount < _mode.size()) _modeCount++; // toDo: check if this works when _modeCount goes from 254 (max 8bit) to 256 (first 16bit)
}
DEBUG_PRINTF("addEffect(%d) => %s\n", id, mode_name);
}

void WS2812FX::setupEffectData() {
Expand Down
32 changes: 19 additions & 13 deletions wled00/FX.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,14 @@ bool strip_uses_global_leds(void) __attribute__((pure)); // WLEDMM implemented
#define FX_MODE_PS1DSONICBOOM 226
#define FX_MODE_PS1DSPRINGY 227

#define MODE_COUNT 228
#if defined(USERMOD_ANIMARTRIX) && !defined(WLED_DISABLE_PARTICLESYSTEM2D)
#define MODE_COUNT 275 // keep some room for animartix effects
#else
#define MODE_COUNT 228 // default including ParticleFX
#endif

#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 +399,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 +488,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 +623,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 +666,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 +869,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 +951,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 +971,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 +1011,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 +1038,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 +1150,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
Copy link
Collaborator

Choose a reason for hiding this comment

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

Todo: update for the dual channel approach

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
13 changes: 9 additions & 4 deletions wled00/fcn_declare.h
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ bool presetsActionPending(void); // WLEDMM true if presetToApply, presetToSave,
void initPresetsFile();
void handlePresets();
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE);
void applyPresetWithFallback(uint8_t presetID, uint8_t callMode, uint8_t effectID = 0, uint8_t paletteID = 0);
void applyPresetWithFallback(uint8_t presetID, uint8_t callMode, uint16_t effectID = 0, uint8_t paletteID = 0);
inline bool applyTemporaryPreset() {return applyPreset(255);};
void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject());
inline void saveTemporaryPreset() {savePreset(255);};
Expand Down 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=65535); // the real thing in 16bit
bool getVal16(JsonVariant elem, uint16_t* val, uint16_t vmin, uint16_t vmax=65535); // 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=65535);

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
Loading