diff --git a/usermods/BH1750_v2/usermod_bh1750.h b/usermods/BH1750_v2/usermod_bh1750.h
index 5b5fa25f..a6e7d653 100644
--- a/usermods/BH1750_v2/usermod_bh1750.h
+++ b/usermods/BH1750_v2/usermod_bh1750.h
@@ -76,7 +76,7 @@ private:
bool sensorFound = false;
// Home Assistant and MQTT
- String mqttLuminanceTopic = FPSTR("");
+ String mqttLuminanceTopic;
bool mqttInitialized = false;
bool HomeAssistantDiscovery = true; // Publish Home Assistant Discovery messages
diff --git a/usermods/audioreactive/audio_reactive.h b/usermods/audioreactive/audio_reactive.h
index 2de84087..f7c91594 100644
--- a/usermods/audioreactive/audio_reactive.h
+++ b/usermods/audioreactive/audio_reactive.h
@@ -2010,6 +2010,19 @@ class AudioReactive : public Usermod {
if (i2c_sda >= 0) sdaPin = -1; // -1 = use global
if (i2c_scl >= 0) sclPin = -1;
+ if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
+ break;
+ case 8:
+ DEBUGSR_PRINTLN(F("AR: AC101 Source (Line-In)"));
+ audioSource = new AC101Source(SAMPLE_RATE, BLOCK_SIZE, 1.0f);
+ //useInputFilter = 0; // to disable low-cut software filtering and restore previous behaviour
+ delay(100);
+ // WLEDMM align global pins
+ if ((sdaPin >= 0) && (i2c_sda < 0)) i2c_sda = sdaPin; // copy usermod prefs into globals (if globals not defined)
+ if ((sclPin >= 0) && (i2c_scl < 0)) i2c_scl = sclPin;
+ if (i2c_sda >= 0) sdaPin = -1; // -1 = use global
+ if (i2c_scl >= 0) sclPin = -1;
+
if (audioSource) audioSource->initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
break;
@@ -2911,6 +2924,11 @@ class AudioReactive : public Usermod {
#else
oappend(SET_F("addOption(dd,'WM8978 ☾',7);"));
#endif
+ #if SR_DMTYPE==8
+ oappend(SET_F("addOption(dd,'AC101 ☾ (⎌)',8);"));
+ #else
+ oappend(SET_F("addOption(dd,'AC101 ☾',8);"));
+ #endif
#ifdef SR_SQUELCH
oappend(SET_F("addInfo(ux+':config:squelch',1,'⎌ ")); oappendi(SR_SQUELCH); oappend("');"); // 0 is field type, 1 is actual field
#endif
diff --git a/usermods/audioreactive/audio_source.h b/usermods/audioreactive/audio_source.h
index cf3e3b74..d7c0478c 100644
--- a/usermods/audioreactive/audio_source.h
+++ b/usermods/audioreactive/audio_source.h
@@ -736,6 +736,109 @@ class WM8978Source : public I2SSource {
};
+class AC101Source : public I2SSource {
+ private:
+ // I2C initialization functions for WM8978
+ void _ac101I2cBegin() {
+ Wire.setClock(400000);
+ }
+
+ void _ac101I2cWrite(uint8_t reg_addr, uint16_t val) {
+ #ifndef AC101_ADDR
+ #define AC101_ADDR 0x1A
+ #endif
+ char send_buff[3];
+ send_buff[0] = reg_addr;
+ send_buff[1] = uint8_t((val >> 8) & 0xff);
+ send_buff[2] = uint8_t(val & 0xff);
+ Wire.beginTransmission(AC101_ADDR);
+ Wire.write((const uint8_t*)send_buff, 3);
+ uint8_t i2cErr = Wire.endTransmission(); // i2cErr == 0 means OK
+ if (i2cErr != 0) {
+ DEBUGSR_PRINTF("AR: AC101 I2C write failed with error=%d (addr=0x%X, reg 0x%X, val 0x%X).\n", i2cErr, AC101_ADDR, reg_addr, val);
+ }
+ }
+
+ void _ac101InitAdc() {
+ // https://files.seeedstudio.com/wiki/ReSpeaker_6-Mics_Circular_Array_kit_for_Raspberry_Pi/reg/AC101_User_Manual_v1.1.pdf
+ // This supports mostly the older AI Thinkier AudioKit A1S that has an AC101 chip
+ // Newer versions use the ES3833 chip - which we also support.
+
+ _ac101I2cBegin();
+
+ #define CHIP_AUDIO_RS 0x00
+ #define SYSCLK_CTRL 0x03
+ #define MOD_CLK_ENA 0x04
+ #define MOD_RST_CTRL 0x05
+ #define I2S_SR_CTRL 0x06
+ #define I2S1LCK_CTRL 0x10
+ #define I2S1_SDOUT_CTRL 0x11
+ #define I2S1_MXR_SRC 0x13
+ #define ADC_DIG_CTRL 0x40
+ #define ADC_APC_CTRL 0x50
+ #define ADC_SRC 0x51
+ #define ADC_SRCBST_CTRL 0x52
+ #define OMIXER_DACA_CTRL 0x53
+ #define OMIXER_SR 0x54
+ #define HPOUT_CTRL 0x56
+
+ _ac101I2cWrite(CHIP_AUDIO_RS, 0x123); // I think anything written here is a reset as 0x123 is kinda suss.
+
+ delay(100);
+
+ _ac101I2cWrite(SYSCLK_CTRL, 0b0000100000001000); // System Clock is I2S MCLK
+ _ac101I2cWrite(MOD_CLK_ENA, 0b1000000000001000); // I2S and ADC Clock Enable
+ _ac101I2cWrite(MOD_RST_CTRL, 0b1000000000001000); // I2S and ADC Clock Enable
+ _ac101I2cWrite(I2S_SR_CTRL, 0b0100000000000000); // set to 22050hz just in case
+ _ac101I2cWrite(I2S1LCK_CTRL, 0b1000000000110000); // set I2S slave mode, 24-bit word size
+ _ac101I2cWrite(I2S1_SDOUT_CTRL, 0b1100000000000000); // I2S enable Left/Right channels
+ _ac101I2cWrite(I2S1_MXR_SRC, 0b0010001000000000); // I2S digital Mixer, ADC L/R data
+ _ac101I2cWrite(ADC_SRCBST_CTRL, 0b0000000000000100); // mute all boosts. last 3 bits are reserved/default
+ _ac101I2cWrite(OMIXER_SR, 0b0000010000001000); // Line L/R to output mixer
+ _ac101I2cWrite(ADC_SRC, 0b0000010000001000); // Line L/R to ADC
+ _ac101I2cWrite(ADC_DIG_CTRL, 0b1000000000000000); // Enable ADC
+ _ac101I2cWrite(ADC_APC_CTRL, 0b1011100100000000); // ADC L/R enabled, 0dB gain
+ _ac101I2cWrite(OMIXER_DACA_CTRL, 0b0011111110000000); // L/R Analog Output Mixer enabled, headphone DC offset default
+ _ac101I2cWrite(HPOUT_CTRL, 0b1111101111110001); // Headphone out from Analog Mixer stage, no reduction in volume
+
+ }
+
+ public:
+ AC101Source(SRate_t sampleRate, int blockSize, float sampleScale = 1.0f, bool i2sMaster=true) :
+ I2SSource(sampleRate, blockSize, sampleScale, i2sMaster) {
+ _config.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT;
+ };
+
+ void initialize(int8_t i2swsPin, int8_t i2ssdPin, int8_t i2sckPin, int8_t mclkPin) {
+ DEBUGSR_PRINTLN("AC101Source:: initialize();");
+
+ // if ((i2sckPin < 0) || (mclkPin < 0)) { // WLEDMM not sure if this check is needed here, too
+ // ERRORSR_PRINTF("\nAR: invalid I2S WM8978 pin: SCK=%d, MCLK=%d\n", i2sckPin, mclkPin);
+ // return;
+ // }
+ // BUG: "use global I2C pins" are valid as -1, and -1 is seen as invalid here.
+ // Workaround: Set I2C pins here, which will also set them globally.
+ // Bug also exists in ES7243.
+ if ((i2c_sda < 0) || (i2c_scl < 0)) { // check that global I2C pins are not "undefined"
+ ERRORSR_PRINTF("\nAR: invalid AC101 global I2C pins: SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
+ return;
+ }
+ if (!pinManager.joinWire(i2c_sda, i2c_scl)) { // WLEDMM specific: start I2C with globally defined pins
+ ERRORSR_PRINTF("\nAR: failed to join I2C bus with SDA=%d, SCL=%d\n", i2c_sda, i2c_scl);
+ return;
+ }
+
+ // First route mclk, then configure ADC over I2C, then configure I2S
+ _ac101InitAdc();
+ I2SSource::initialize(i2swsPin, i2ssdPin, i2sckPin, mclkPin);
+ }
+
+ void deinitialize() {
+ I2SSource::deinitialize();
+ }
+
+};
+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)
#if !defined(SOC_I2S_SUPPORTS_ADC) && !defined(SOC_I2S_SUPPORTS_ADC_DAC)
#warning this MCU does not support analog sound input
diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h
index cf0aca22..dd47e767 100644
--- a/usermods/multi_relay/usermod_multi_relay.h
+++ b/usermods/multi_relay/usermod_multi_relay.h
@@ -626,7 +626,7 @@ void MultiRelay::addToJsonInfo(JsonObject &root) {
for (int i=0; i> 8;
uint16_t cy = rippleorigin & 0xFF;
uint8_t mag = scale8(sin8((propF>>2)), amp);
- if (propI > 0) SEGMENT.draw_circle(cx, cy, propI, color_blend(SEGMENT.getPixelColorXY(cx + propI, cy), col, mag));
+ if (propI > 0) SEGMENT.drawCircle(cx, cy, propI, color_blend(SEGMENT.getPixelColorXY(cx + propI, cy), col, mag), true);
} else
#endif
{
@@ -6271,8 +6271,8 @@ uint16_t mode_2Dfloatingblobs(void) {
}
}
uint32_t c = SEGMENT.color_from_palette(blob->color[i], false, false, 0);
- if (blob->r[i] > 1.f) SEGMENT.fill_circle(blob->x[i], blob->y[i], roundf(blob->r[i]), c);
- else SEGMENT.setPixelColorXY(blob->x[i], blob->y[i], c);
+ if (blob->r[i] > 1.f) SEGMENT.fillCircle(roundf(blob->x[i]), roundf(blob->y[i]), roundf(blob->r[i]), c);
+ else SEGMENT.setPixelColorXY((int)roundf(blob->x[i]), (int)roundf(blob->y[i]), c);
// move x
if (blob->x[i] + blob->r[i] >= cols - 1) blob->x[i] += (blob->sX[i] * ((cols - 1 - blob->x[i]) / blob->r[i] + 0.005f));
else if (blob->x[i] - blob->r[i] <= 0) blob->x[i] += (blob->sX[i] * (blob->x[i] / blob->r[i] + 0.005f));
diff --git a/wled00/FX.h b/wled00/FX.h
index f6a5fd31..97f2db27 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -122,6 +122,10 @@ bool strip_uses_global_leds(void); // WLEDMM implemented in FX_fcn.
#define PURPLE (uint32_t)0x400080
#define ORANGE (uint32_t)0xFF3000
#define PINK (uint32_t)0xFF1493
+#define GREY (uint32_t)0x808080
+#define GRAY GREY
+#define DARKGREY (uint32_t)0x333333
+#define DARKGRAY DARKGREY
#define ULTRAWHITE (uint32_t)0xFFFFFFFF
#define DARKSLATEGRAY (uint32_t)0x2F4F4F
#define DARKSLATEGREY (uint32_t)0x2F4F4F
@@ -615,11 +619,11 @@ typedef struct Segment {
// 1D strip
uint16_t virtualLength(void) const;
void setPixelColor(int n, uint32_t c); // set relative pixel within segment with color
- void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline
- void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
+ inline void setPixelColor(int n, byte r, byte g, byte b, byte w = 0) { setPixelColor(n, RGBW32(r,g,b,w)); } // automatically inline
+ inline void setPixelColor(int n, CRGB c) { setPixelColor(n, RGBW32(c.r,c.g,c.b,0)); } // automatically inline
void setPixelColor(float i, uint32_t c, bool aa = true);
- void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); }
- void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); }
+ inline void setPixelColor(float i, uint8_t r, uint8_t g, uint8_t b, uint8_t w = 0, bool aa = true) { setPixelColor(i, RGBW32(r,g,b,w), aa); }
+ inline void setPixelColor(float i, CRGB c, bool aa = true) { setPixelColor(i, RGBW32(c.r,c.g,c.b,0), aa); }
uint32_t __attribute__((pure)) getPixelColor(int i); // WLEDMM attribute added
// 1D support functions (some implement 2D as well)
void blur(uint8_t, bool smear = false);
@@ -627,10 +631,10 @@ typedef struct Segment {
void fade_out(uint8_t r);
void fadeToBlackBy(uint8_t fadeBy);
void blendPixelColor(int n, uint32_t color, uint8_t blend);
- void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); }
+ inline void blendPixelColor(int n, CRGB c, uint8_t blend) { blendPixelColor(n, RGBW32(c.r,c.g,c.b,0), blend); }
void addPixelColor(int n, uint32_t color, bool fast = false);
- void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline
- void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline
+ inline void addPixelColor(int n, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColor(n, RGBW32(r,g,b,w), fast); } // automatically inline
+ inline void addPixelColor(int n, CRGB c, bool fast = false) { addPixelColor(n, RGBW32(c.r,c.g,c.b,0), fast); } // automatically inline
void fadePixelColor(uint16_t n, uint8_t fade);
uint8_t get_random_wheel_index(uint8_t pos);
uint32_t __attribute__((pure)) color_from_palette(uint_fast16_t, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri = 255);
@@ -666,6 +670,7 @@ typedef struct Segment {
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColorXY(int(x), int(y), c); }
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColorXY(x, y, RGBW32(r,g,b,w)); }
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0)); }
+ inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColorXY(int(x), int(y), RGBW32(c.r,c.g,c.b,0)); }
//#ifdef WLED_USE_AA_PIXELS
void setPixelColorXY(float x, float y, uint32_t c, bool aa = true, bool fast=true);
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColorXY(x, y, RGBW32(r,g,b,w), aa); }
@@ -674,10 +679,10 @@ typedef struct Segment {
uint32_t __attribute__((pure)) getPixelColorXY(int x, int y);
// 2D support functions
void blendPixelColorXY(uint16_t x, uint16_t y, uint32_t color, uint8_t blend);
- void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); }
+ inline void blendPixelColorXY(uint16_t x, uint16_t y, CRGB c, uint8_t blend) { blendPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), blend); }
void addPixelColorXY(int x, int y, uint32_t color, bool fast = false);
- void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColorXY(x, y, RGBW32(r,g,b,w), fast); } // automatically inline
- void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), fast); }
+ inline void addPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0, bool fast = false) { addPixelColorXY(x, y, RGBW32(r,g,b,w), fast); } // automatically inline
+ inline void addPixelColorXY(int x, int y, CRGB c, bool fast = false) { addPixelColorXY(x, y, RGBW32(c.r,c.g,c.b,0), fast); }
void fadePixelColorXY(uint16_t x, uint16_t y, uint8_t fade);
void box_blur(uint16_t i, bool vertical, fract8 blur_amount); // 1D box blur (with weight)
void blurRow(uint32_t row, fract8 blur_amount, bool smear = false);
@@ -685,18 +690,20 @@ typedef struct Segment {
void moveX(int8_t delta, bool wrap = false);
void moveY(int8_t delta, bool wrap = false);
void move(uint8_t dir, uint8_t delta, bool wrap = false);
- void draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c);
- void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c);
- void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, uint16_t d = UINT16_MAX);
- void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, uint16_t d = UINT16_MAX) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), d); } // automatic inline
+ void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false);
+ inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { drawCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); }
+ void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false);
+ inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) { fillCircle(cx, cy, radius, RGBW32(c.r,c.g,c.b,0), soft); }
+ void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false, uint16_t distance = UINT16_MAX);
+ inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false, uint16_t distance = UINT16_MAX)) { drawLine(x0, y0, x1, y1, RGBW32(c.r,c.g,c.b,0), soft, distance); } // automatic inline
void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, uint32_t color, uint32_t fillColor = 0);
- void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, CRGB color, CRGB fillColor = BLACK) { drawArc(x0, y0, radius, RGBW32(color.r,color.g,color.b,0), RGBW32(fillColor.r,fillColor.g,fillColor.b,0)); } // automatic inline
+ inline void drawArc(uint16_t x0, uint16_t y0, uint16_t radius, CRGB color, CRGB fillColor = BLACK) { drawArc(x0, y0, radius, RGBW32(color.r,color.g,color.b,0), RGBW32(fillColor.r,fillColor.g,fillColor.b,0)); } // automatic inline
void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t col2 = 0);
- void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0)); } // automatic inline
+ inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2) { drawCharacter(chr, x, y, w, h, RGBW32(c.r,c.g,c.b,0), RGBW32(c2.r,c2.g,c2.b,0)); } // automatic inline
void wu_pixel(uint32_t x, uint32_t y, CRGB c);
- void blur1d(fract8 blur_amount); // blur all rows in 1 dimension
+ //void blur1d(fract8 blur_amount); // blur all rows in 1 dimension
void blur2d(fract8 blur_amount) { blur(blur_amount); }
- void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); }
+ inline void fill_solid(CRGB c) { fill(RGBW32(c.r,c.g,c.b,0)); }
void nscale8(uint8_t scale);
bool jsonToPixels(char *name, uint8_t fileNr); //WLEDMM for artifx
#else
@@ -705,6 +712,7 @@ typedef struct Segment {
inline void setPixelColorXY(unsigned x, unsigned y, uint32_t c) { setPixelColor(int(x), c); }
inline void setPixelColorXY(int x, int y, byte r, byte g, byte b, byte w = 0) { setPixelColor(x, RGBW32(r,g,b,w)); }
inline void setPixelColorXY(int x, int y, CRGB c) { setPixelColor(x, RGBW32(c.r,c.g,c.b,0)); }
+ inline void setPixelColorXY(unsigned x, unsigned y, CRGB c) { setPixelColor(int(x), RGBW32(c.r,c.g,c.b,0)); }
//#ifdef WLED_USE_AA_PIXELS
inline void setPixelColorXY(float x, float y, uint32_t c, bool aa = true) { setPixelColor(x, c, aa); }
inline void setPixelColorXY(float x, float y, byte r, byte g, byte b, byte w = 0, bool aa = true) { setPixelColor(x, RGBW32(r,g,b,w), aa); }
@@ -723,9 +731,12 @@ typedef struct Segment {
inline void moveX(int8_t delta, bool wrap = false) {}
inline void moveY(int8_t delta, bool wrap = false) {}
inline void move(uint8_t dir, uint8_t delta, bool wrap = false) {}
- inline void fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c) {}
- inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c) {}
- inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c) {}
+ inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false) {}
+ inline void drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) {}
+ inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t c, bool soft = false) {}
+ inline void fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB c, bool soft = false) {}
+ inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft = false) {}
+ inline void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, CRGB c, bool soft = false) {}
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, uint32_t color, uint32_t = 0, int8_t = 0) {}
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB color) {}
inline void drawCharacter(unsigned char chr, int16_t x, int16_t y, uint8_t w, uint8_t h, CRGB c, CRGB c2, int8_t rotate = 0) {}
diff --git a/wled00/FX_2Dfcn.cpp b/wled00/FX_2Dfcn.cpp
index 83599c97..93810301 100644
--- a/wled00/FX_2Dfcn.cpp
+++ b/wled00/FX_2Dfcn.cpp
@@ -214,7 +214,10 @@ uint32_t WS2812FX::getPixelColorXY(uint16_t x, uint16_t y) {
void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM: IRAM_ATTR conditionally
{
if (Segment::maxHeight==1) return; // not a matrix set-up
- if (x<0 || y<0 || x >= virtualWidth() || y >= virtualHeight()) return; // if pixel would fall out of virtual segment just exit
+ const int_fast16_t cols = virtualWidth(); // WLEDMM optimization
+ const int_fast16_t rows = virtualHeight();
+
+ if (x<0 || y<0 || x >= cols || y >= rows) return; // if pixel would fall out of virtual segment just exit
unsigned i = UINT_MAX;
bool sameColor = false;
@@ -231,11 +234,11 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM:
}
#if 0 // this is a dangerous optimization
- if ((i < UINT_MAX) && sameColor && (call > 0) && (ledsrgb[i] == CRGB(col)) && (_globalLeds == nullptr)) return; // WLEDMM looks like nothing to do (but we don't trust globalleds)
+ if ((i < UINT_MAX) && sameColor && (call > 0) && (!transitional) && (ledsrgb[i] == CRGB(col)) && (_globalLeds == nullptr)) return; // WLEDMM looks like nothing to do (but we don't trust globalleds)
#endif
- if (reverse ) x = virtualWidth() - x - 1;
- if (reverse_y) y = virtualHeight() - y - 1;
+ if (reverse ) x = cols - x - 1;
+ if (reverse_y) y = rows - y - 1;
if (transpose) { uint16_t t = x; x = y; y = t; } // swap X & Y if segment transposed
// WLEDMM shortcut when no grouping/spacing used
@@ -245,27 +248,32 @@ void IRAM_ATTR_YN Segment::setPixelColorXY(int x, int y, uint32_t col) //WLEDMM:
return;
}
- x *= groupLength(); // expand to physical pixels
- y *= groupLength(); // expand to physical pixels
- if (x >= width() || y >= height()) return; // if pixel would fall out of segment just exit
+ const int_fast16_t glen_ = groupLength(); // WLEDMM optimization
+ const int_fast16_t wid_ = width();
+ const int_fast16_t hei_ = height();
- for (int j = 0; j < grouping; j++) { // groupping vertically
- for (int g = 0; g < grouping; g++) { // groupping horizontally
+ x *= glen_; // expand to physical pixels
+ y *= glen_; // expand to physical pixels
+ if (x >= wid_ || y >= hei_) return; // if pixel would fall out of segment just exit
+
+ const int grp_ = grouping; // WLEDMM optimization
+ for (int j = 0; j < grp_; j++) { // groupping vertically
+ for (int g = 0; g < grp_; g++) { // groupping horizontally
uint_fast16_t xX = (x+g), yY = (y+j); //WLEDMM: use fast types
- if (xX >= width() || yY >= height()) continue; // we have reached one dimension's end
+ if (xX >= wid_ || yY >= hei_) continue; // we have reached one dimension's end
strip.setPixelColorXY(start + xX, startY + yY, col);
if (mirror) { //set the corresponding horizontally mirrored pixel
- if (transpose) strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col);
- else strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col);
+ if (transpose) strip.setPixelColorXY(start + xX, startY + hei_ - yY - 1, col);
+ else strip.setPixelColorXY(start + wid_ - xX - 1, startY + yY, col);
}
if (mirror_y) { //set the corresponding vertically mirrored pixel
- if (transpose) strip.setPixelColorXY(start + width() - xX - 1, startY + yY, col);
- else strip.setPixelColorXY(start + xX, startY + height() - yY - 1, col);
+ if (transpose) strip.setPixelColorXY(start + wid_ - xX - 1, startY + yY, col);
+ else strip.setPixelColorXY(start + xX, startY + hei_ - yY - 1, col);
}
if (mirror_y && mirror) { //set the corresponding vertically AND horizontally mirrored pixel
- strip.setPixelColorXY(width() - xX - 1, height() - yY - 1, col);
+ strip.setPixelColorXY(wid_ - xX - 1, hei_ - yY - 1, col);
}
}
}
@@ -427,56 +435,38 @@ void Segment::blurCol(uint32_t col, fract8 blur_amount, bool smear) {
}
// 1D Box blur (with added weight - blur_amount: [0=no blur, 255=max blur])
-void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) { //WLEDMM: use fast types
- const uint_fast16_t cols = virtualWidth();
- const uint_fast16_t rows = virtualHeight();
- const uint_fast16_t dim1 = vertical ? rows : cols;
- const uint_fast16_t dim2 = vertical ? cols : rows;
+void Segment::box_blur(uint16_t i, bool vertical, fract8 blur_amount) {
+ if (!isActive() || blur_amount == 0) return; // not active
+ const int cols = virtualWidth();
+ const int rows = virtualHeight();
+ const int dim1 = vertical ? rows : cols;
+ const int dim2 = vertical ? cols : rows;
if (i >= dim2) return;
const float seep = blur_amount/255.f;
const float keep = 3.f - 2.f*seep;
// 1D box blur
- CRGB tmp[dim1];
- for (uint_fast16_t j = 0; j < dim1; j++) {
- uint_fast16_t x = vertical ? i : j;
- uint_fast16_t y = vertical ? j : i;
- int_fast16_t xp = vertical ? x : x-1; // "signed" to prevent underflow
- int_fast16_t yp = vertical ? y-1 : y; // "signed" to prevent underflow
- uint_fast16_t xn = vertical ? x : x+1;
- uint_fast16_t yn = vertical ? y+1 : y;
- CRGB curr = getPixelColorXY(x,y);
- CRGB prev = (xp<0 || yp<0) ? CRGB::Black : getPixelColorXY(xp,yp);
- CRGB next = ((vertical && yn>=dim1) || (!vertical && xn>=dim1)) ? CRGB::Black : getPixelColorXY(xn,yn);
- uint16_t r, g, b;
- r = (curr.r*keep + (prev.r + next.r)*seep) / 3;
- g = (curr.g*keep + (prev.g + next.g)*seep) / 3;
- b = (curr.b*keep + (prev.b + next.b)*seep) / 3;
- tmp[j] = CRGB(r,g,b);
+ uint32_t out[dim1], in[dim1];
+ for (int j = 0; j < dim1; j++) {
+ int x = vertical ? i : j;
+ int y = vertical ? j : i;
+ in[j] = getPixelColorXY(x, y);
}
- for (uint_fast16_t j = 0; j < dim1; j++) {
- uint_fast16_t x = vertical ? i : j;
- uint_fast16_t y = vertical ? j : i;
- setPixelColorXY((int)x, (int)y, tmp[j]);
+ for (int j = 0; j < dim1; j++) {
+ uint32_t curr = in[j];
+ uint32_t prev = j > 0 ? in[j-1] : BLACK;
+ uint32_t next = j < dim1-1 ? in[j+1] : BLACK;
+ uint8_t r, g, b, w;
+ r = (R(curr)*keep + (R(prev) + R(next))*seep) / 3;
+ g = (G(curr)*keep + (G(prev) + G(next))*seep) / 3;
+ b = (B(curr)*keep + (B(prev) + B(next))*seep) / 3;
+ w = (W(curr)*keep + (W(prev) + W(next))*seep) / 3;
+ out[j] = RGBW32(r,g,b,w);
+ }
+ for (int j = 0; j < dim1; j++) {
+ int x = vertical ? i : j;
+ int y = vertical ? j : i;
+ setPixelColorXY(x, y, out[j]);
}
-}
-
-// blur1d: one-dimensional blur filter. Spreads light to 2 line neighbors.
-// blur2d: two-dimensional blur filter. Spreads light to 8 XY neighbors.
-//
-// 0 = no spread at all
-// 64 = moderate spreading
-// 172 = maximum smooth, even spreading
-//
-// 173..255 = wider spreading, but increasing flicker
-//
-// Total light is NOT entirely conserved, so many repeated
-// calls to 'blur' will also result in the light fading,
-// eventually all the way to black; this is by design so that
-// it can be used to (slowly) clear the LEDs to black.
-
-void Segment::blur1d(fract8 blur_amount) { //WLEDMM: use fast types
- const uint_fast16_t rows = virtualHeight();
- for (uint_fast16_t y = 0; y < rows; y++) blurRow(y, blur_amount);
}
void Segment::moveX(int8_t delta, bool wrap) {
@@ -533,37 +523,71 @@ void Segment::move(uint8_t dir, uint8_t delta, bool wrap) {
}
}
-void Segment::draw_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
- if (!isActive()) return; // not active
- // Bresenham’s Algorithm
- int d = 3 - (2*radius);
- int y = radius, x = 0;
- while (y >= x) {
- setPixelColorXY(cx+x, cy+y, col);
- setPixelColorXY(cx-x, cy+y, col);
- setPixelColorXY(cx+x, cy-y, col);
- setPixelColorXY(cx-x, cy-y, col);
- setPixelColorXY(cx+y, cy+x, col);
- setPixelColorXY(cx-y, cy+x, col);
- setPixelColorXY(cx+y, cy-x, col);
- setPixelColorXY(cx-y, cy-x, col);
- x++;
- if (d > 0) {
- y--;
- d += 4 * (x - y) + 10;
- } else {
- d += 4 * x + 6;
+void Segment::drawCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) {
+ if (!isActive() || radius == 0) return; // not active
+ if (soft) {
+ // Xiaolin Wu’s algorithm
+ int rsq = radius*radius;
+ int x = 0;
+ int y = radius;
+ unsigned oldFade = 0;
+ while (x < y) {
+ float yf = sqrtf(float(rsq - x*x)); // needs to be floating point
+ unsigned fade = float(0xFFFF) * (ceilf(yf) - yf); // how much color to keep
+ if (oldFade > fade) y--;
+ oldFade = fade;
+ setPixelColorXY(cx+x, cy+y, color_blend(col, getPixelColorXY(cx+x, cy+y), fade, true));
+ setPixelColorXY(cx-x, cy+y, color_blend(col, getPixelColorXY(cx-x, cy+y), fade, true));
+ setPixelColorXY(cx+x, cy-y, color_blend(col, getPixelColorXY(cx+x, cy-y), fade, true));
+ setPixelColorXY(cx-x, cy-y, color_blend(col, getPixelColorXY(cx-x, cy-y), fade, true));
+ setPixelColorXY(cx+y, cy+x, color_blend(col, getPixelColorXY(cx+y, cy+x), fade, true));
+ setPixelColorXY(cx-y, cy+x, color_blend(col, getPixelColorXY(cx-y, cy+x), fade, true));
+ setPixelColorXY(cx+y, cy-x, color_blend(col, getPixelColorXY(cx+y, cy-x), fade, true));
+ setPixelColorXY(cx-y, cy-x, color_blend(col, getPixelColorXY(cx-y, cy-x), fade, true));
+ setPixelColorXY(cx+x, cy+y-1, color_blend(getPixelColorXY(cx+x, cy+y-1), col, fade, true));
+ setPixelColorXY(cx-x, cy+y-1, color_blend(getPixelColorXY(cx-x, cy+y-1), col, fade, true));
+ setPixelColorXY(cx+x, cy-y+1, color_blend(getPixelColorXY(cx+x, cy-y+1), col, fade, true));
+ setPixelColorXY(cx-x, cy-y+1, color_blend(getPixelColorXY(cx-x, cy-y+1), col, fade, true));
+ setPixelColorXY(cx+y-1, cy+x, color_blend(getPixelColorXY(cx+y-1, cy+x), col, fade, true));
+ setPixelColorXY(cx-y+1, cy+x, color_blend(getPixelColorXY(cx-y+1, cy+x), col, fade, true));
+ setPixelColorXY(cx+y-1, cy-x, color_blend(getPixelColorXY(cx+y-1, cy-x), col, fade, true));
+ setPixelColorXY(cx-y+1, cy-x, color_blend(getPixelColorXY(cx-y+1, cy-x), col, fade, true));
+ x++;
+ }
+ } else {
+ // Bresenham’s Algorithm
+ int d = 3 - (2*radius);
+ int y = radius, x = 0;
+ while (y >= x) {
+ setPixelColorXY(cx+x, cy+y, col);
+ setPixelColorXY(cx-x, cy+y, col);
+ setPixelColorXY(cx+x, cy-y, col);
+ setPixelColorXY(cx-x, cy-y, col);
+ setPixelColorXY(cx+y, cy+x, col);
+ setPixelColorXY(cx-y, cy+x, col);
+ setPixelColorXY(cx+y, cy-x, col);
+ setPixelColorXY(cx-y, cy-x, col);
+ x++;
+ if (d > 0) {
+ y--;
+ d += 4 * (x - y) + 10;
+ } else {
+ d += 4 * x + 6;
+ }
}
}
}
// by stepko, taken from https://editor.soulmatelights.com/gallery/573-blobs
-void Segment::fill_circle(uint16_t cx, uint16_t cy, uint8_t radius, CRGB col) {
- if (!isActive()) return; // not active
- const uint16_t cols = virtualWidth();
- const uint16_t rows = virtualHeight();
- for (int16_t y = -radius; y <= radius; y++) {
- for (int16_t x = -radius; x <= radius; x++) {
+void Segment::fillCircle(uint16_t cx, uint16_t cy, uint8_t radius, uint32_t col, bool soft) {
+ if (!isActive() || radius == 0) return; // not active
+ // draw soft bounding circle
+ if (soft) drawCircle(cx, cy, radius, col, soft);
+ // fill it
+ const int cols = virtualWidth();
+ const int rows = virtualHeight();
+ for (int y = -radius; y <= radius; y++) {
+ for (int x = -radius; x <= radius; x++) {
if (x * x + y * y <= radius * radius &&
int16_t(cx)+x>=0 && int16_t(cy)+y>=0 &&
int16_t(cx)+x= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
- const int16_t dx = abs(x1-x0), sx = x0dy ? dx : -dy)/2, e2;
- for (uint_fast16_t d=0; d-dx) { err -= dy; x0 += sx; }
- if (e2 < dy) { err += dx; y0 += sy; }
+
+ const int dx = abs(x1-x0), sx = x0 dx;
+ if (steep) {
+ // we need to go along longest dimension
+ std::swap(x0,y0);
+ std::swap(x1,y1);
+ }
+ if (x0 > x1) {
+ // we need to go in increasing fashion
+ std::swap(x0,x1);
+ std::swap(y0,y1);
+ }
+ float gradient = x1-x0 == 0 ? 1.0f : float(y1-y0) / float(x1-x0);
+ float intersectY = y0;
+ for (int x = x0; x <= x1; x++) {
+ unsigned keep = float(0xFFFF) * (intersectY-int(intersectY)); // how much color to keep
+ unsigned seep = 0xFFFF - keep; // how much background to keep
+ int y = int(intersectY);
+ if (steep) std::swap(x,y); // temporarily swap if steep
+ // pixel coverage is determined by fractional part of y co-ordinate
+ setPixelColorXY(x, y, color_blend(c, getPixelColorXY(x, y), keep, true));
+ setPixelColorXY(x+int(steep), y+int(!steep), color_blend(c, getPixelColorXY(x+int(steep), y+int(!steep)), seep, true));
+ intersectY += gradient;
+ if (steep) std::swap(x,y); // restore if steep
+ }
+ } else {
+ // Bresenham's algorithm
+ int err = (dx>dy ? dx : -dy)/2; // error direction
+ for (uint_fast16_t d=0; d-dx) { err -= dy; x0 += sx; }
+ if (e2 < dy) { err += dx; y0 += sy; }
+ }
}
}
diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp
index 2bd0a338..9ad6c948 100644
--- a/wled00/bus_manager.cpp
+++ b/wled00/bus_manager.cpp
@@ -513,6 +513,10 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
mxconfig.mx_height = 32 / 2;
break;
case 106:
+ mxconfig.mx_width = 64 * 2;
+ mxconfig.mx_height = 32 / 2;
+ break;
+ case 107:
mxconfig.mx_width = 64 * 2;
mxconfig.mx_height = 64 / 2;
break;
diff --git a/wled00/data/index.js b/wled00/data/index.js
index 1fcbfcaf..e89ad78e 100644
--- a/wled00/data/index.js
+++ b/wled00/data/index.js
@@ -247,6 +247,7 @@ function onLoad()
selectSlot(0);
updateTablinks(0);
+ handleLocationHash();
pmtLS = localStorage.getItem('wledPmt');
// Load initial data
@@ -279,7 +280,6 @@ function updateTablinks(tabI)
{
var tablinks = gEBCN("tablinks");
for (var i of tablinks) i.classList.remove('active');
- if (pcMode) return;
tablinks[tabI].classList.add('active');
}
@@ -290,6 +290,21 @@ function openTab(tabI, force = false)
_C.classList.toggle('smooth', false);
_C.style.setProperty('--i', iSlide);
updateTablinks(tabI);
+ switch (tabI) {
+ case 0: window.location.hash = "Colors"; break;
+ case 1: window.location.hash = "Effects"; break;
+ case 2: window.location.hash = "Segments"; break;
+ case 3: window.location.hash = "Presets"; break;
+ }
+}
+
+function handleLocationHash() {
+ switch (window.location.hash) {
+ case "#Colors": openTab(0); break;
+ case "#Effects": openTab(1); break;
+ case "#Segments": openTab(2); break;
+ case "#Presets": openTab(3); break;
+ }
}
var timeout;
@@ -3615,12 +3630,11 @@ function togglePcMode(fromB = false)
if (fromB) {
pcModeA = !pcModeA;
localStorage.setItem('pcm', pcModeA);
+ openTab(0, true);
}
pcMode = (wW >= 1024) && pcModeA;
if (cpick) cpick.resize(pcMode && wW>1023 && wW<1250 ? 230 : 260); // for tablet in landscape
if (!fromB && ((wW < 1024 && lastw < 1024) || (wW >= 1024 && lastw >= 1024))) return; // no change in size and called from size()
- openTab(0, true);
- updateTablinks(0);
gId('buttonPcm').className = (pcMode) ? "active":"";
gId('bot').style.height = (pcMode && !cfg.comp.pcmbot) ? "0":"auto";
sCol('--bh', gId('bot').clientHeight + "px");
@@ -3652,6 +3666,7 @@ size();
_C.style.setProperty('--n', N);
window.addEventListener('resize', size, true);
+window.addEventListener('hashchange', handleLocationHash);
_C.addEventListener('mousedown', lock, false);
_C.addEventListener('touchstart', lock, false);
diff --git a/wled00/json.cpp b/wled00/json.cpp
index 5456b455..444f0e4d 100644
--- a/wled00/json.cpp
+++ b/wled00/json.cpp
@@ -340,8 +340,8 @@ bool deserializeSegment(JsonObject elem, byte it, byte presetId)
seg.fill(BLACK); // WLEDMM why now?
}
- uint16_t start = 0, stop = 0;
- byte set = 0; //0 nothing set, 1 start set, 2 range set
+ start = 0, stop = 0;
+ set = 0; //0 nothing set, 1 start set, 2 range set
for (size_t i = 0; i < iarr.size(); i++) {
if(iarr[i].is()) {
@@ -1554,10 +1554,20 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
uint16_t used = strip.getLengthTotal();
uint16_t n = (used -1) /MAX_LIVE_LEDS +1; //only serve every n'th LED if count over MAX_LIVE_LEDS
- char buffer[2000];
- strcpy_P(buffer, PSTR("{\"leds\":["));
- obuf = buffer;
- olen = 9;
+#ifndef WLED_DISABLE_2D
+ if (strip.isMatrix) {
+ // ignore anything behid matrix (i.e. extra strip)
+ used = Segment::maxWidth*Segment::maxHeight; // always the size of matrix (more or less than strip.getLengthTotal())
+ n = 1;
+ if (used > MAX_LIVE_LEDS) n = 2;
+ if (used > MAX_LIVE_LEDS*4) n = 4;
+ }
+#endif
+
+ DynamicBuffer buffer(9 + (9*MAX_LIVE_LEDS) + 7 + 5 + 6 + 5 + 6 + 5 + 2);
+ char* buf = buffer.data(); // assign buffer for oappnd() functions
+ strncpy_P(buffer.data(), PSTR("{\"leds\":["), buffer.size());
+ buf += 9; // sizeof(PSTR()) from last line
for (size_t i= 0; i < used; i += n)
{
@@ -1575,20 +1585,21 @@ bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient)
g = qadd8(w, G(c));
b = qadd8(w, B(c));
}
- olen += sprintf(obuf + olen, "\"%06X\",", RGBW32(r,g,b,0));
+ buf += sprintf_P(buf, PSTR("\"%06X\","), RGBW32(r,g,b,0));
}
- olen -= 1;
- oappend((const char*)F("],\"n\":"));
- oappendi(n);
- oappend("}");
+ buf--; // remove last comma
+ buf += sprintf_P(buf, PSTR("],\"n\":%d"), n);
+ (*buf++) = '}';
+ (*buf++) = 0;
+
if (request) {
- request->send(200, "application/json", buffer);
+ request->send(200, "application/json", toString(std::move(buffer)));
}
#ifdef WLED_ENABLE_WEBSOCKETS
else {
- wsc->text(obuf, olen);
+ wsc->text(toString(std::move(buffer)));
}
- #endif
+ #endif
return true;
}
#endif
diff --git a/wled00/overlay.cpp b/wled00/overlay.cpp
index cfee7e81..6ffc6c8e 100644
--- a/wled00/overlay.cpp
+++ b/wled00/overlay.cpp
@@ -25,11 +25,11 @@ void _overlayAnalogClock()
{
if (secondPixel < analogClock12pixel)
{
- strip.setRange(analogClock12pixel, overlayMax, 0xFF0000);
- strip.setRange(overlayMin, secondPixel, 0xFF0000);
+ strip.setRange(analogClock12pixel, overlayMax, color_fade(0xFF0000, bri));
+ strip.setRange(overlayMin, secondPixel, color_fade(0xFF0000, bri));
} else
{
- strip.setRange(analogClock12pixel, secondPixel, 0xFF0000);
+ strip.setRange(analogClock12pixel, secondPixel, color_fade(0xFF0000, bri));
}
}
if (analogClock5MinuteMarks)
@@ -38,12 +38,12 @@ void _overlayAnalogClock()
{
int pix = analogClock12pixel + roundf((overlaySize / 12.0f) *i);
if (pix > overlayMax) pix -= overlaySize;
- strip.setPixelColor(pix, 0x00FFAA);
+ strip.setPixelColor(pix, color_fade(0x00FFAA, bri));
}
}
- if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, 0xFF0000);
- strip.setPixelColor(minutePixel, 0x00FF00);
- strip.setPixelColor(hourPixel, 0x0000FF);
+ if (!analogClockSecondsTrail) strip.setPixelColor(secondPixel, color_fade(0xFF0000, bri));
+ strip.setPixelColor(minutePixel, color_fade(0x00FF00, bri));
+ strip.setPixelColor(hourPixel, color_fade(0x0000FF, bri));
}
diff --git a/wled00/util.cpp b/wled00/util.cpp
index 1bcf2ad0..87e14c2c 100644
--- a/wled00/util.cpp
+++ b/wled00/util.cpp
@@ -267,8 +267,8 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
} else return 0;
}
- if (src == JSON_palette_names && mode > GRADIENT_PALETTE_COUNT) {
- snprintf_P(dest, maxLen, PSTR("~ Custom %d~"), 255-mode);
+ if (src == JSON_palette_names && mode > (GRADIENT_PALETTE_COUNT + 13)) {
+ snprintf_P(dest, maxLen, PSTR("~ Custom %d ~"), 255-mode);
dest[maxLen-1] = '\0';
return strlen(dest);
}
diff --git a/wled00/ws.cpp b/wled00/ws.cpp
index 5b8bda42..22c5afd2 100644
--- a/wled00/ws.cpp
+++ b/wled00/ws.cpp
@@ -108,7 +108,6 @@ void sendDataWs(AsyncWebSocketClient * client)
{
DEBUG_PRINTF("sendDataWs\n");
if (!ws.count()) return;
- AsyncWebSocketMessageBuffer * buffer;
if (!requestJSONBufferLock(12)) {
if (client) {
@@ -138,17 +137,7 @@ void sendDataWs(AsyncWebSocketClient * client)
// DEBUG_PRINTF("%s min free stack %d\n", pcTaskGetTaskName(NULL), uxTaskGetStackHighWaterMark(NULL)); //WLEDMM
#endif
if (len < 1) return; // WLEDMM do not allocate 0 size buffer
-
- // WLEDMM use exceptions to catch out-of-memory errors
- #if __cpp_exceptions
- try{
- buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes on ESP8266
- } catch(...) {
- buffer = nullptr;
- }
- #else
- buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes on ESP8266
- #endif
+ AsyncWebSocketBuffer buffer(len);
#ifdef ESP8266
size_t heap2 = ESP.getFreeHeap();
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
@@ -161,24 +150,19 @@ void sendDataWs(AsyncWebSocketClient * client)
USER_PRINTLN(F("WS buffer allocation failed."));
ws.closeAll(1013); //code 1013 = temporary overload, try again later
ws.cleanupClients(0); //disconnect all clients to release memory
- ws._cleanBuffers();
errorFlag = ERR_LOW_WS_MEM;
return; //out of memory
}
-
- buffer->lock();
- serializeJson(doc, (char *)buffer->get(), len);
+ serializeJson(doc, (char *)buffer.data(), len);
DEBUG_PRINT(F("Sending WS data "));
if (client) {
- client->text(buffer);
+ client->text(std::move(buffer));
DEBUG_PRINTLN(F("to a single client."));
} else {
- ws.textAll(buffer);
+ ws.textAll(std::move(buffer));
DEBUG_PRINTLN(F("to multiple clients."));
}
- buffer->unlock();
- ws._cleanBuffers();
releaseJSONBufferLock();
}
@@ -227,32 +211,21 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static"
#endif
size_t pos = (strip.isMatrix ? 4 : 2);
size_t bufSize = pos + (used/n)*3;
-
- if ((bufSize < 1) || (used < 1)) return(false); // WLEDMM should not happen
- //AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(bufSize);
- // WLEDMM protect against exceptions due to low memory
- AsyncWebSocketMessageBuffer * wsBuf = nullptr;
-#if __cpp_exceptions
- try{
-#endif
- wsBuf = ws.makeBuffer(bufSize);
-#if __cpp_exceptions
- } catch(...) {
-#else
- if (wsBuf == nullptr) { // 8266 does not support exceptions
-#endif
- wsBuf = nullptr;
- USER_PRINTLN(F("WS buffer allocation failed."));
- //ws.closeAll(1013); //code 1013 = temporary overload, try again later
- //ws.cleanupClients(0); //disconnect all clients to release memory
- ws._cleanBuffers();
- }
- if (!wsBuf) return false; //out of memory
- uint8_t* buffer = wsBuf->get();
- if (!buffer) return false; //out of memory
+ if ((bufSize < 1) || (used < 1)) return(false); // WLEDMM should not happen
+ AsyncWebSocketBuffer wsBuf(bufSize);
+ if (!wsBuf) {
+ USER_PRINTLN(F("WS buffer allocation failed."));
+ errorFlag = ERR_LOW_WS_MEM;
+ return false; //out of memory
+ }
+ uint8_t* buffer = reinterpret_cast(wsBuf.data());
+ if (!buffer) {
+ USER_PRINTLN(F("WS buffer allocation failed."));
+ errorFlag = ERR_LOW_WS_MEM;
+ return false; //out of memory
+ }
- wsBuf->lock(); // protect buffer from being cleaned by another WS instance
buffer[0] = 'L';
buffer[1] = 1; //version
#ifndef WLED_DISABLE_2D
@@ -290,9 +263,7 @@ static bool sendLiveLedsWs(uint32_t wsClient) // WLEDMM added "static"
}
}
- wsc->binary(wsBuf);
- wsBuf->unlock(); // un-protect buffer
- ws._cleanBuffers(); // cleans up if the message is not added to any clients.
+ wsc->binary(std::move(wsBuf));
return true;
}