Merge branch 'mdev' into 3DGEQ

This commit is contained in:
Troy
2024-07-12 22:10:39 -04:00
committed by GitHub
13 changed files with 390 additions and 196 deletions

View File

@@ -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

View File

@@ -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,'<i>&#9100; ")); oappendi(SR_SQUELCH); oappend("</i>');"); // 0 is field type, 1 is actual field
#endif

View File

@@ -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

View File

@@ -626,7 +626,7 @@ void MultiRelay::addToJsonInfo(JsonObject &root) {
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if ((_relay[i].pin<0 && !usePcf8574) || !_relay[i].external) continue;
uiDomString = F("Relay "); uiDomString += i;
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
infoArr = user.createNestedArray(uiDomString); // timer value
uiDomString = F("<button class=\"btn btn-xs\" onclick=\"requestJson({");
uiDomString += FPSTR(_name);

View File

@@ -2551,7 +2551,7 @@ uint16_t ripple_base()
uint16_t cx = rippleorigin >> 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));

View File

@@ -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) {}

View File

@@ -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
// Bresenhams 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 Wus 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 {
// Bresenhams 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 && int16_t(cy)+y<rows)
@@ -582,20 +606,57 @@ void Segment::nscale8(uint8_t scale) { //WLEDMM: use fast types
}
//line function
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, uint16_t distance) {
void Segment::drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t c, bool soft, uint16_t distance) {
if (!isActive()) return; // not active
const uint16_t cols = virtualWidth();
const uint16_t rows = virtualHeight();
const int cols = virtualWidth();
const int rows = virtualHeight();
if (x0 >= cols || x1 >= cols || y0 >= rows || y1 >= rows) return;
const int16_t dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
const int16_t dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
int16_t err = (dx>dy ? dx : -dy)/2, e2;
for (uint_fast16_t d=0; d<distance; d++) {
setPixelColorXY(x0,y0,c);
if (x0==x1 && y0==y1) break;
e2 = err;
if (e2 >-dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; }
const int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1; // x distance & step
const int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1; // y distance & step
// single pixel (line length == 0)
if (dx+dy == 0) {
setPixelColorXY(x0, y0, c);
return;
}
if (soft) {
// Xiaolin Wus algorithm
const bool steep = dy > 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<distance; d++) {
setPixelColorXY(x0, y0, c);
if (x0==x1 && y0==y1) break;
int e2 = err;
if (e2 >-dx) { err -= dy; x0 += sx; }
if (e2 < dy) { err += dx; y0 += sy; }
}
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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<JsonInteger>()) {
@@ -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

View File

@@ -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));
}

View File

@@ -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);
}

View File

@@ -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<uint8_t*>(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;
}