diff --git a/usermods/ST7789_display/ST7789_display.h b/usermods/ST7789_display/ST7789_display.h index 93700c9182..d90ed9d7b7 100644 --- a/usermods/ST7789_display/ST7789_display.h +++ b/usermods/ST7789_display/ST7789_display.h @@ -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; diff --git a/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h b/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h index 1dd0a69a54..408393d7fa 100644 --- a/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h +++ b/usermods/usermod_v2_auto_save/usermod_v2_auto_save.h @@ -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 @@ -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; diff --git a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h index 898545716d..f18daa5c01 100644 --- a/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h +++ b/usermods/usermod_v2_four_line_display_ALT/usermod_v2_four_line_display_ALT.h @@ -158,7 +158,7 @@ class FourLineDisplayUsermod : 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; uint8_t knownMinute = 99; uint8_t knownHour = 99; 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 index 66a1401a1d..1f50fda4f0 100644 --- 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 @@ -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; uint8_t currentCCT = 128; @@ -353,7 +353,7 @@ void RotaryEncoderUIUsermod::sortModesAndPalettes() { byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) { byte *indexes = (byte *)malloc(sizeof(byte) * numModes); - for (byte i = 0; i < numModes; i++) { + for (uint16_t i = 0; i < numModes; i++) { // WLEDMM changed to uint16_t to avoid infinite loop with 16bit mode IDs indexes[i] = i; } return indexes; @@ -630,7 +630,7 @@ void RotaryEncoderUIUsermod::displayNetworkInfo() { 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++) { // WLEDMM changed to uint16_t to avoid infinite loop with 16bit mode IDs if (modes_alpha_indexes[i] == effectCurrent) { effectCurrentIndex = i; break; diff --git a/wled00/FX.cpp b/wled00/FX.cpp index 138e9690be..282ddbe1a0 100644 --- a/wled00/FX.cpp +++ b/wled00/FX.cpp @@ -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() { diff --git a/wled00/FX.h b/wled00/FX.h index 7559b09e31..cf1273ca22 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -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, @@ -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 { @@ -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 @@ -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); @@ -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); @@ -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; @@ -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), @@ -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 @@ -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 @@ -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 @@ -1144,7 +1150,7 @@ class WS2812FX { // 96 bytes bool _triggered : 1; }; - uint8_t _modeCount; + uint16_t _modeCount; std::vector _mode; // SRAM footprint: 4 bytes per element std::vector _modeData; // mode (effect) name and its slider control data array diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 4941529f18..d68aada1f6 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -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; i32767U) ? newMode : (_t ? _t->_modeP : newMode); // change effect in the middle of transition } @@ -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; @@ -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; diff --git a/wled00/e131.cpp b/wled00/e131.cpp index a6123e7a50..cfe2fe94ab 100644 --- a/wled00/e131.cpp +++ b/wled00/e131.cpp @@ -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]); diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index 1a9247a2d9..179a72b92f 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -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);}; @@ -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 @@ -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 diff --git a/wled00/ir.cpp b/wled00/ir.cpp index bfb7926433..a7b318b35e 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -71,7 +71,7 @@ void decBrightness() } } -void presetFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) +void presetFallback(uint8_t presetID, uint16_t effectID, uint8_t paletteID) { //USER_PRINTF("presetFallback1 %d %d %d\n", presetID, effectID, paletteID); //applyPreset(presetID, CALL_MODE_BUTTON_PRESET); @@ -91,7 +91,17 @@ byte relativeChange(byte property, int8_t amount, byte lowerBoundary, byte highe return (byte)constrain(new_val, 0, 255); } -void changeEffect(uint8_t fx) +// 16‑bit variant for effect IDs +uint16_t relativeChange16(uint16_t property, int16_t amount, uint16_t lowerBoundary, uint16_t higherBoundary) +{ + int32_t new_val = (int32_t)property + amount; + if (lowerBoundary >= higherBoundary) return property; + if (new_val > higherBoundary) new_val = higherBoundary; + if (new_val < lowerBoundary) new_val = lowerBoundary; + return (uint16_t)new_val; +} + +void changeEffect(uint16_t fx) { if (irApplyToAllSelected) { for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) { @@ -508,8 +518,8 @@ void decodeIR44(uint32_t code) case IR44_WARMWHITE : changeColor(COLOR_WARMWHITE, 63); changeEffect(FX_MODE_STATIC); break; case IR44_COLDWHITE : changeColor(COLOR_COLDWHITE, 191); changeEffect(FX_MODE_STATIC); break; case IR44_COLDWHITE2 : changeColor(COLOR_COLDWHITE2, 255); changeEffect(FX_MODE_STATIC); break; - case IR44_REDPLUS : changeEffect(relativeChange(effectCurrent, 1, 0, strip.getModeCount() -1)); break; - case IR44_REDMINUS : changeEffect(relativeChange(effectCurrent, -1, 0, strip.getModeCount() -1)); break; + case IR44_REDPLUS : changeEffect(relativeChange16(effectCurrent, 1, 0, strip.getModeCount() -1)); break; + case IR44_REDMINUS : changeEffect(relativeChange16(effectCurrent, -1, 0, strip.getModeCount() -1)); break; case IR44_GREENPLUS : changePalette(relativeChange(effectPalette, 1, 0, strip.getPaletteCount() -1)); break; case IR44_GREENMINUS : changePalette(relativeChange(effectPalette, -1, 0, strip.getPaletteCount() -1)); break; case IR44_BLUEPLUS : changeEffectIntensity( 16); break; @@ -568,7 +578,7 @@ void decodeIR6(uint32_t code) case IR6_POWER: toggleOnOff(); break; case IR6_CHANNEL_UP: incBrightness(); break; case IR6_CHANNEL_DOWN: decBrightness(); break; - case IR6_VOLUME_UP: changeEffect(relativeChange(effectCurrent, 1, 0, strip.getModeCount() -1)); break; + case IR6_VOLUME_UP: changeEffect(relativeChange16(effectCurrent, 1, 0, strip.getModeCount() -1)); break; case IR6_VOLUME_DOWN: changePalette(relativeChange(effectPalette, 1, 0, strip.getPaletteCount() -1)); switch(lastIR6ColourIdx) { case 0: changeColor(COLOR_RED); break; @@ -606,7 +616,7 @@ void decodeIR9(uint32_t code) case IR9_DOWN : decBrightness(); break; case IR9_LEFT : changeEffectSpeed(-16); break; case IR9_RIGHT : changeEffectSpeed(16); break; - case IR9_SELECT : changeEffect(relativeChange(effectCurrent, 1, 0, strip.getModeCount() -1)); break; + case IR9_SELECT : changeEffect(relativeChange16(effectCurrent, 1, 0, strip.getModeCount() -1)); break; default: return; } lastValidCode = code; @@ -621,8 +631,8 @@ void decodeIR24MC(uint32_t code) case IR24_MC_OFF : if (bri > 0) briLast = bri; bri = 0; break; case IR24_MC_AUTO : changeEffect(FX_MODE_FADE); break; case IR24_MC_ON : bri = briLast; break; - case IR24_MC_MODES : changeEffect(relativeChange(effectCurrent, 1, 0, strip.getModeCount() -1)); break; //WLEDMM: sound and non sound modes - case IR24_MC_MODE : changeEffect(relativeChange(effectCurrent, -1, 0, strip.getModeCount() -1)); break; //WLEDMM: sound and non sound modes + case IR24_MC_MODES : changeEffect(relativeChange16(effectCurrent, 1, 0, strip.getModeCount() -1)); break; //WLEDMM: sound and non sound modes + case IR24_MC_MODE : changeEffect(relativeChange16(effectCurrent, -1, 0, strip.getModeCount() -1)); break; //WLEDMM: sound and non sound modes case IR24_MC_BRIGHTER : incBrightness(); break; case IR24_MC_DARKER : decBrightness(); break; case IR24_MC_QUICK : changeEffectSpeed( 16); break; @@ -711,7 +721,7 @@ void decodeIRJson(uint32_t code) decBrightness(); } else if (cmdStr.startsWith(F("!presetF"))) { //!presetFallback uint8_t p1 = fdo["PL"] | 1; - uint8_t p2 = fdo["FX"] | random8(strip.getModeCount() -1); + uint16_t p2 = fdo["FX"] | random16(strip.getModeCount() -1); uint8_t p3 = fdo["FP"] | 0; presetFallback(p1, p2, p3); } diff --git a/wled00/json.cpp b/wled00/json.cpp index c51deefaf6..5d32d41ce0 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -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(); + uint16_t fx = seg.mode; + uint16_t last = strip.getModeCount(); // WLEDMM safe, because strip.getModeCount() is always >0 // partial fix for #3605 if (!elem["fx"].isNull() && elem["fx"].is()) { const char *tmp = elem["fx"].as(); 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-65535 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 } diff --git a/wled00/presets.cpp b/wled00/presets.cpp index 3e5b4142f5..3446d90158 100644 --- a/wled00/presets.cpp +++ b/wled00/presets.cpp @@ -135,7 +135,7 @@ bool applyPreset(byte index, byte callMode) } // apply preset or fallback to a effect and palette if it doesn't exist -void applyPresetWithFallback(uint8_t index, uint8_t callMode, uint8_t effectID, uint8_t paletteID) +void applyPresetWithFallback(uint8_t index, uint8_t callMode, uint16_t effectID, uint8_t paletteID) { applyPreset(index, callMode); //these two will be overwritten if preset exists in handlePresets() diff --git a/wled00/remote.cpp b/wled00/remote.cpp index 64c668c579..c0abe1f8b6 100644 --- a/wled00/remote.cpp +++ b/wled00/remote.cpp @@ -119,7 +119,7 @@ static void setOff() { } } -static void presetWithFallback(uint8_t presetID, uint8_t effectID, uint8_t paletteID) { +static void presetWithFallback(uint8_t presetID, uint16_t effectID, uint8_t paletteID) { resetNightMode(); unloadPlaylist(); applyPresetWithFallback(presetID, CALL_MODE_BUTTON_PRESET, effectID, paletteID); diff --git a/wled00/set.cpp b/wled00/set.cpp index 560de9b653..21d3e36ab3 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -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; @@ -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; } diff --git a/wled00/udp.cpp b/wled00/udp.cpp index 3fc630e9a1..dfff52aaa9 100644 --- a/wled00/udp.cpp +++ b/wled00/udp.cpp @@ -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] = min(mainseg.mode, uint16_t(255)); // WLEDMM toDo: need workaround for 16bit mode numbers - critical as this affects webUI udpOut[9] = mainseg.speed; udpOut[10] = W(col); //compatibilityVersionByte: @@ -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] = min(selseg.mode, uint16_t(255)); // WLEDMM toDo: need workaround for 16bit mode numbers udpOut[12+ofs] = selseg.speed; udpOut[13+ofs] = selseg.intensity; udpOut[14+ofs] = selseg.palette; @@ -431,7 +431,7 @@ void handleNotifications() selseg.options = (selseg.options & 0x0071U) | (udpIn[9 +ofs] & 0x0E); // ignore selected, freeze, reset & transitional selseg.setOpacity(udpIn[10+ofs]); if (applyEffects) { - strip.setMode(id, udpIn[11+ofs]); + strip.setMode(id, udpIn[11+ofs]); // WLEDMM ToDo: need to add support for 16bit effect IDs selseg.speed = udpIn[12+ofs]; selseg.intensity = udpIn[13+ofs]; selseg.palette = udpIn[14+ofs]; @@ -472,7 +472,7 @@ void handleNotifications() for (size_t i = 0; i < strip.getSegmentsNum(); i++) { Segment& seg = strip.getSegment(i); if (!seg.isActive() || !seg.isSelected()) continue; - seg.setMode(udpIn[8]); + seg.setMode(udpIn[8]); // WLEDMM ToDo: need to add support for 16bit effect IDs seg.speed = udpIn[9]; if (version > 2) seg.intensity = udpIn[16]; if (version > 4) seg.setPalette(udpIn[19]); diff --git a/wled00/util.cpp b/wled00/util.cpp index c31b20b692..0df14ecae6 100644 --- a/wled00/util.cpp +++ b/wled00/util.cpp @@ -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; maxv is "exclusive" + uint16_t temp = *val; + parseNumber16(str, &temp, (uint16_t)minv, (uint16_t)maxv); + //*val = constrain(temp, 0, 255); // unfortunately this is not compatible with legacy 8bit "r" = random + *val = temp & 0x00FF; // always works correctly, assuming *str is strictly 8bit +} + //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; maxv is "exclusive" { 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] == '~') { @@ -38,25 +46,25 @@ 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)); } bool getVal(JsonVariant elem, byte* val, byte vmin, byte vmax) { if (elem.is()) { if (elem < 0) return false; //ignore e.g. {"ps":-1} - *val = elem; + *val = elem; // ToDO: check if we need a bounds test [vmin ... vmax] before assigning the result return true; } else if (elem.is()) { const char* str = elem; @@ -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()) { + if (elem < 0) return false; //ignore e.g. {"ps":-1} + *val = elem; // ToDO: check if we need a bounds test [vmin ... vmax] before assigning the result + return true; + } else if (elem.is()) { + 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) { @@ -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) @@ -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()) { @@ -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); } @@ -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 @@ -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' }; @@ -654,7 +687,7 @@ char *cleanUpName(char *in) { return(in); } -// 32 bit hardware random number generator, inlining uses more code, use hw_random16() if speed is critical (see fcn_declare.h) +// 32 bit hardware random number generator, inlining uses more code, use hw_random16() if speed is critical (see fcn_declare.h). results are "exclusive" upperlimit uint32_t hw_random(uint32_t upperlimit) { uint32_t rnd = hw_random(); uint64_t scaled = uint64_t(rnd) * uint64_t(upperlimit); diff --git a/wled00/wled.h b/wled00/wled.h index 442473dae8..11ad4b245d 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -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);