Merge remote-tracking branch 'origin/dev' into effect-changes
This commit is contained in:
@@ -1982,7 +1982,7 @@ uint16_t mode_fire_2012()
|
||||
uint8_t cool = random8((((20 + SEGMENT.speed/3) * 16) / SEGLEN)+2);
|
||||
uint8_t minTemp = 0;
|
||||
if (i<ignition) {
|
||||
cool /= (ignition-i)/3 + 1; // ignition area cools slower
|
||||
//cool /= (ignition-i)/3 + 1; // ignition area cools slower
|
||||
minTemp = (ignition-i)/4 + 16; // and should not become black
|
||||
}
|
||||
uint8_t temp = qsub8(heat[i], cool);
|
||||
@@ -2290,24 +2290,23 @@ uint16_t mode_meteor() {
|
||||
{
|
||||
byte meteorTrailDecay = 128 + random8(127);
|
||||
trail[i] = scale8(trail[i], meteorTrailDecay);
|
||||
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(trail[i], false, true, 255));
|
||||
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, false, 0, trail[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// draw meteor
|
||||
for (int j = 0; j < meteorSize; j++) {
|
||||
uint16_t index = in + j;
|
||||
if(index >= SEGLEN) {
|
||||
index = (in + j - SEGLEN);
|
||||
if (index >= SEGLEN) {
|
||||
index -= SEGLEN;
|
||||
}
|
||||
|
||||
trail[index] = 240;
|
||||
SEGMENT.setPixelColor(index, SEGMENT.color_from_palette(trail[index], false, true, 255));
|
||||
SEGMENT.setPixelColor(index, SEGMENT.color_from_palette(index, true, false, 0, 255));
|
||||
}
|
||||
|
||||
return FRAMETIME;
|
||||
}
|
||||
static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail length;!,!,;!;1d";
|
||||
static const char _data_FX_MODE_METEOR[] PROGMEM = "Meteor@!,Trail length;!,,;!;1d";
|
||||
|
||||
|
||||
// smooth meteor effect
|
||||
@@ -2329,24 +2328,24 @@ uint16_t mode_meteor_smooth() {
|
||||
trail[i] += change;
|
||||
if (trail[i] > 245) trail[i] = 0;
|
||||
if (trail[i] > 240) trail[i] = 240;
|
||||
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(trail[i], false, true, 255));
|
||||
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, false, 0, trail[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// draw meteor
|
||||
for (int j = 0; j < meteorSize; j++) {
|
||||
uint16_t index = in + j;
|
||||
if(in + j >= SEGLEN) {
|
||||
index = (in + j - SEGLEN);
|
||||
for (int j = 0; j < meteorSize; j++) {
|
||||
uint16_t index = in + j;
|
||||
if (index >= SEGLEN) {
|
||||
index -= SEGLEN;
|
||||
}
|
||||
SEGMENT.setPixelColor(index, color_blend(SEGMENT.getPixelColor(index), SEGMENT.color_from_palette(240, false, true, 255), 48));
|
||||
trail[index] = 240;
|
||||
SEGMENT.setPixelColor(index, SEGMENT.color_from_palette(index, true, false, 0, 255));
|
||||
}
|
||||
|
||||
SEGENV.step += SEGMENT.speed +1;
|
||||
return FRAMETIME;
|
||||
}
|
||||
static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail length;!,!,;!;1d";
|
||||
static const char _data_FX_MODE_METEOR_SMOOTH[] PROGMEM = "Meteor Smooth@!,Trail length;!,,;!;1d";
|
||||
|
||||
|
||||
//Railway Crossing / Christmas Fairy lights
|
||||
@@ -3584,10 +3583,13 @@ uint16_t mode_percent(void) {
|
||||
uint8_t size = (1 + ((SEGMENT.speed * SEGLEN) >> 11));
|
||||
if (SEGMENT.speed == 255) size = 255;
|
||||
|
||||
if (percent < 100) {
|
||||
if (percent <= 100) {
|
||||
for (int i = 0; i < SEGLEN; i++) {
|
||||
if (i < SEGENV.aux1) {
|
||||
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
|
||||
if (SEGMENT.check1)
|
||||
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(map(percent,0,100,0,255), false, false, 0));
|
||||
else
|
||||
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
|
||||
}
|
||||
else {
|
||||
SEGMENT.setPixelColor(i, SEGCOLOR(1));
|
||||
@@ -3599,7 +3601,10 @@ uint16_t mode_percent(void) {
|
||||
SEGMENT.setPixelColor(i, SEGCOLOR(1));
|
||||
}
|
||||
else {
|
||||
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
|
||||
if (SEGMENT.check1)
|
||||
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(map(percent,100,200,255,0), false, false, 0));
|
||||
else
|
||||
SEGMENT.setPixelColor(i, SEGMENT.color_from_palette(i, true, PALETTE_SOLID_WRAP, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3614,7 +3619,7 @@ uint16_t mode_percent(void) {
|
||||
|
||||
return FRAMETIME;
|
||||
}
|
||||
static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@,% of fill;!,!,;!;1d";
|
||||
static const char _data_FX_MODE_PERCENT[] PROGMEM = "Percent@,% of fill,,,,One color;!,!,;!;1d";
|
||||
|
||||
|
||||
/*
|
||||
@@ -4103,14 +4108,14 @@ uint16_t mode_dancing_shadows(void)
|
||||
|
||||
if (spotlights[i].width <= 1) {
|
||||
if (start >= 0 && start < SEGLEN) {
|
||||
SEGMENT.blendPixelColor(start, color, 128); // TODO
|
||||
SEGMENT.blendPixelColor(start, color, 128);
|
||||
}
|
||||
} else {
|
||||
switch (spotlights[i].type) {
|
||||
case SPOT_TYPE_SOLID:
|
||||
for (size_t j = 0; j < spotlights[i].width; j++) {
|
||||
if ((start + j) >= 0 && (start + j) < SEGLEN) {
|
||||
SEGMENT.blendPixelColor(start + j, color, 128); // TODO
|
||||
SEGMENT.blendPixelColor(start + j, color, 128);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -4118,7 +4123,7 @@ uint16_t mode_dancing_shadows(void)
|
||||
case SPOT_TYPE_GRADIENT:
|
||||
for (size_t j = 0; j < spotlights[i].width; j++) {
|
||||
if ((start + j) >= 0 && (start + j) < SEGLEN) {
|
||||
SEGMENT.blendPixelColor(start + j, color, cubicwave8(map(j, 0, spotlights[i].width - 1, 0, 255))); // TODO
|
||||
SEGMENT.blendPixelColor(start + j, color, cubicwave8(map(j, 0, spotlights[i].width - 1, 0, 255)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -4126,7 +4131,7 @@ uint16_t mode_dancing_shadows(void)
|
||||
case SPOT_TYPE_2X_GRADIENT:
|
||||
for (size_t j = 0; j < spotlights[i].width; j++) {
|
||||
if ((start + j) >= 0 && (start + j) < SEGLEN) {
|
||||
SEGMENT.blendPixelColor(start + j, color, cubicwave8(2 * map(j, 0, spotlights[i].width - 1, 0, 255))); // TODO
|
||||
SEGMENT.blendPixelColor(start + j, color, cubicwave8(2 * map(j, 0, spotlights[i].width - 1, 0, 255)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -4134,7 +4139,7 @@ uint16_t mode_dancing_shadows(void)
|
||||
case SPOT_TYPE_2X_DOT:
|
||||
for (size_t j = 0; j < spotlights[i].width; j += 2) {
|
||||
if ((start + j) >= 0 && (start + j) < SEGLEN) {
|
||||
SEGMENT.blendPixelColor(start + j, color, 128); // TODO
|
||||
SEGMENT.blendPixelColor(start + j, color, 128);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -4142,7 +4147,7 @@ uint16_t mode_dancing_shadows(void)
|
||||
case SPOT_TYPE_3X_DOT:
|
||||
for (size_t j = 0; j < spotlights[i].width; j += 3) {
|
||||
if ((start + j) >= 0 && (start + j) < SEGLEN) {
|
||||
SEGMENT.blendPixelColor(start + j, color, 128); // TODO
|
||||
SEGMENT.blendPixelColor(start + j, color, 128);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -4150,7 +4155,7 @@ uint16_t mode_dancing_shadows(void)
|
||||
case SPOT_TYPE_4X_DOT:
|
||||
for (size_t j = 0; j < spotlights[i].width; j += 4) {
|
||||
if ((start + j) >= 0 && (start + j) < SEGLEN) {
|
||||
SEGMENT.blendPixelColor(start + j, color, 128); // TODO
|
||||
SEGMENT.blendPixelColor(start + j, color, 128);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -4160,7 +4165,7 @@ uint16_t mode_dancing_shadows(void)
|
||||
|
||||
return FRAMETIME;
|
||||
}
|
||||
static const char _data_FX_MODE_DANCING_SHADOWS[] PROGMEM = "Dancing Shadows@!,# of shadows;!,,;!;1d";
|
||||
static const char _data_FX_MODE_DANCING_SHADOWS[] PROGMEM = "Dancing Shadows@!,# of shadows;!,!,!;!;1d";
|
||||
|
||||
|
||||
/*
|
||||
@@ -4521,7 +4526,7 @@ uint16_t mode_perlinmove(void) {
|
||||
|
||||
return FRAMETIME;
|
||||
} // mode_perlinmove()
|
||||
static const char _data_FX_MODE_PERLINMOVE[] PROGMEM = "Perlin Move@!,# of pixels,fade rate;,!;!;1d";
|
||||
static const char _data_FX_MODE_PERLINMOVE[] PROGMEM = "Perlin Move@!,# of pixels,fade rate;!,!;!;1d";
|
||||
|
||||
|
||||
/////////////////////////
|
||||
@@ -7102,7 +7107,7 @@ uint16_t mode_2DGEQ(void) { // By Will Tatam. Code reduction by Ewoud Wijma.
|
||||
if (SEGENV.call == 0) for (int i=0; i<cols; i++) previousBarHeight[i] = 0;
|
||||
|
||||
bool rippleTime = false;
|
||||
if (millis() - SEGENV.step >= (256 - SEGMENT.intensity)) {
|
||||
if (millis() - SEGENV.step >= (256U - SEGMENT.intensity)) {
|
||||
SEGENV.step = millis();
|
||||
rippleTime = true;
|
||||
}
|
||||
@@ -7211,7 +7216,7 @@ static uint8_t akemi[] PROGMEM = {
|
||||
0,0,0,0,0,0,0,3,2,0,6,5,5,5,5,5,5,5,5,5,5,4,0,2,3,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,3,2,3,6,5,5,7,7,5,5,5,5,7,7,5,5,4,3,2,3,0,0,0,0,0,0,
|
||||
0,0,0,0,0,2,3,1,3,6,5,1,7,7,7,5,5,1,7,7,7,5,4,3,1,3,2,0,0,0,0,0,
|
||||
0,0,0,0,0,8,3,1,3,6,5,1,7,7,7,5,5,1,7,7,7,5,4,3,1,3,8,9,0,0,0,0,
|
||||
0,0,0,0,0,8,3,1,3,6,5,1,7,7,7,5,5,1,7,7,7,5,4,3,1,3,8,0,0,0,0,0,
|
||||
0,0,0,0,0,8,3,1,3,6,5,5,1,1,5,5,5,5,1,1,5,5,4,3,1,3,8,0,0,0,0,0,
|
||||
0,0,0,0,0,2,3,1,3,6,5,5,5,5,5,5,5,5,5,5,5,5,4,3,1,3,2,0,0,0,0,0,
|
||||
0,0,0,0,0,0,3,2,3,6,5,5,5,5,5,5,5,5,5,5,5,5,4,3,2,3,0,0,0,0,0,0,
|
||||
@@ -7261,7 +7266,6 @@ uint16_t mode_2DAkemi(void) {
|
||||
CRGB armsAndLegsColor = SEGCOLOR(1) > 0 ? SEGCOLOR(1) : 0xFFE0A0; //default warmish white 0xABA8FF; //0xFF52e5;//
|
||||
uint8_t ak = pgm_read_byte_near(akemi + ((y * 32)/rows) * 32 + (x * 32)/cols); // akemi[(y * 32)/rows][(x * 32)/cols]
|
||||
switch (ak) {
|
||||
case 0: color = BLACK; break;
|
||||
case 3: armsAndLegsColor.r *= lightFactor; armsAndLegsColor.g *= lightFactor; armsAndLegsColor.b *= lightFactor; color = armsAndLegsColor; break; //light arms and legs 0x9B9B9B
|
||||
case 2: armsAndLegsColor.r *= normalFactor; armsAndLegsColor.g *= normalFactor; armsAndLegsColor.b *= normalFactor; color = armsAndLegsColor; break; //normal arms and legs 0x888888
|
||||
case 1: color = armsAndLegsColor; break; //dark arms and legs 0x686868
|
||||
@@ -7270,7 +7274,7 @@ uint16_t mode_2DAkemi(void) {
|
||||
case 4: color = faceColor; break; //dark face 0x007DC6
|
||||
case 7: color = SEGCOLOR(2) > 0 ? SEGCOLOR(2) : 0xFFFFFF; break; //eyes and mouth default white
|
||||
case 8: if (base > 0.4) {soundColor.r *= base; soundColor.g *= base; soundColor.b *= base; color=soundColor;} else color = armsAndLegsColor; break;
|
||||
default: color = BLACK;
|
||||
default: color = BLACK; break;
|
||||
}
|
||||
|
||||
if (SEGMENT.intensity > 128 && fftResult && fftResult[0] > 128) { //dance if base is high
|
||||
|
||||
@@ -502,6 +502,8 @@ 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);
|
||||
void setPalette(uint8_t pal);
|
||||
uint8_t differs(Segment& b) const;
|
||||
void refreshLightCapabilities(void);
|
||||
|
||||
|
||||
@@ -396,6 +396,48 @@ void Segment::setOption(uint8_t n, bool val) {
|
||||
else options &= ~(0x01 << n);
|
||||
}
|
||||
|
||||
void Segment::setMode(uint8_t fx, bool loadDefaults) {
|
||||
// if we have a valid mode & is not reserved
|
||||
if (fx < strip.getModeCount() && strncmp_P("RSVD", strip.getModeData(fx), 4)) {
|
||||
if (fx != mode) {
|
||||
startTransition(strip.getTransition()); // set effect transitions
|
||||
//markForReset(); // transition will handle this
|
||||
mode = fx;
|
||||
|
||||
// load default values from effect string
|
||||
if (loadDefaults) {
|
||||
int16_t sOpt;
|
||||
sOpt = extractModeDefaults(fx, "sx"); if (sOpt >= 0) speed = sOpt;
|
||||
sOpt = extractModeDefaults(fx, "ix"); if (sOpt >= 0) intensity = sOpt;
|
||||
sOpt = extractModeDefaults(fx, "c1"); if (sOpt >= 0) custom1 = sOpt;
|
||||
sOpt = extractModeDefaults(fx, "c2"); if (sOpt >= 0) custom2 = sOpt;
|
||||
sOpt = extractModeDefaults(fx, "c3"); if (sOpt >= 0) custom3 = sOpt;
|
||||
sOpt = extractModeDefaults(fx, "mp12"); if (sOpt >= 0) map1D2D = constrain(sOpt, 0, 7);
|
||||
sOpt = extractModeDefaults(fx, "ssim"); if (sOpt >= 0) soundSim = constrain(sOpt, 0, 7);
|
||||
sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) reverse = (bool)sOpt;
|
||||
sOpt = extractModeDefaults(fx, "mi"); if (sOpt >= 0) mirror = (bool)sOpt; // NOTE: setting this option is a risky business
|
||||
sOpt = extractModeDefaults(fx, "rY"); if (sOpt >= 0) reverse_y = (bool)sOpt;
|
||||
sOpt = extractModeDefaults(fx, "mY"); if (sOpt >= 0) mirror_y = (bool)sOpt; // NOTE: setting this option is a risky business
|
||||
sOpt = extractModeDefaults(fx, "pal");
|
||||
if (sOpt >= 0 && (size_t)sOpt < strip.getPaletteCount() + strip.customPalettes.size()) {
|
||||
if (sOpt != palette) {
|
||||
palette = sOpt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Segment::setPalette(uint8_t pal) {
|
||||
if (pal < strip.getPaletteCount()) {
|
||||
if (pal != palette) {
|
||||
if (strip.paletteFade) startTransition(strip.getTransition());
|
||||
palette = pal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2D matrix
|
||||
uint16_t Segment::virtualWidth() const {
|
||||
uint16_t groupLen = groupLength();
|
||||
@@ -1136,7 +1178,7 @@ void WS2812FX::setColor(uint8_t slot, uint32_t c) {
|
||||
if (slot >= NUM_COLORS) return;
|
||||
|
||||
for (segment &seg : _segments) {
|
||||
if (seg.isSelected()) {
|
||||
if (seg.isActive() && seg.isSelected()) {
|
||||
seg.setColor(slot, c);
|
||||
}
|
||||
}
|
||||
@@ -1172,7 +1214,7 @@ uint8_t WS2812FX::getFirstSelectedSegId(void)
|
||||
{
|
||||
size_t i = 0;
|
||||
for (segment &seg : _segments) {
|
||||
if (seg.isSelected()) return i;
|
||||
if (seg.isActive() && seg.isSelected()) return i;
|
||||
i++;
|
||||
}
|
||||
// if none selected, use the main segment
|
||||
@@ -1459,9 +1501,7 @@ void WS2812FX::loadCustomPalettes()
|
||||
CRGBPalette16 targetPalette;
|
||||
for (int index = 0; index<10; index++) {
|
||||
char fileName[32];
|
||||
strcpy_P(fileName, PSTR("/palette"));
|
||||
sprintf(fileName +8, "%d", index);
|
||||
strcat(fileName, ".json");
|
||||
sprintf_P(fileName, PSTR("/palette%d.json"), index);
|
||||
|
||||
StaticJsonDocument<1536> pDoc; // barely enough to fit 72 numbers
|
||||
if (WLED_FS.exists(fileName)) {
|
||||
|
||||
@@ -504,7 +504,7 @@ class BusOnOff : public Bus {
|
||||
|
||||
uint8_t currentPin = bc.pins[0];
|
||||
if (!pinManager.allocatePin(currentPin, true, PinOwner::BusOnOff)) {
|
||||
deallocatePins(); return;
|
||||
return;
|
||||
}
|
||||
_pin = currentPin; //store only after allocatePin() succeeds
|
||||
pinMode(_pin, OUTPUT);
|
||||
@@ -540,7 +540,7 @@ class BusOnOff : public Bus {
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
deallocatePins();
|
||||
pinManager.deallocatePin(_pin, PinOwner::BusOnOff);
|
||||
}
|
||||
|
||||
~BusOnOff() {
|
||||
@@ -550,10 +550,6 @@ class BusOnOff : public Bus {
|
||||
private:
|
||||
uint8_t _pin = 255;
|
||||
uint8_t _data = 0;
|
||||
|
||||
void deallocatePins() {
|
||||
pinManager.deallocatePin(_pin, PinOwner::BusOnOff);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -689,6 +685,8 @@ class BusManager {
|
||||
busses[numBusses] = new BusNetwork(bc);
|
||||
} else if (IS_DIGITAL(bc.type)) {
|
||||
busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap);
|
||||
} else if (bc.type == TYPE_ONOFF) {
|
||||
busses[numBusses] = new BusOnOff(bc);
|
||||
} else {
|
||||
busses[numBusses] = new BusPwm(bc);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,21 @@
|
||||
|
||||
#include "NeoPixelBrightnessBus.h"
|
||||
|
||||
// temporary - these defines should actually be set in platformio.ini
|
||||
// C3: I2S0 and I2S1 methods not supported (has one I2S bus)
|
||||
// S2: I2S1 methods not supported (has one I2S bus)
|
||||
// S3: I2S0 and I2S1 methods not supported yet (has two I2S buses)
|
||||
// https://github.com/Makuna/NeoPixelBus/blob/b32f719e95ef3c35c46da5c99538017ef925c026/src/internal/Esp32_i2s.h#L4
|
||||
// https://github.com/Makuna/NeoPixelBus/blob/b32f719e95ef3c35c46da5c99538017ef925c026/src/internal/NeoEsp32RmtMethod.h#L857
|
||||
|
||||
#if !defined(WLED_NO_I2S0_PIXELBUS) && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3))
|
||||
#define WLED_NO_I2S0_PIXELBUS
|
||||
#endif
|
||||
#if !defined(WLED_NO_I2S1_PIXELBUS) && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2))
|
||||
#define WLED_NO_I2S1_PIXELBUS
|
||||
#endif
|
||||
// temporary end
|
||||
|
||||
//Hardware SPI Pins
|
||||
#define P_8266_HS_MOSI 13
|
||||
#define P_8266_HS_CLK 14
|
||||
@@ -34,54 +49,57 @@
|
||||
#define I_8266_DM_TM1_4 15
|
||||
#define I_8266_BB_TM1_4 16
|
||||
//TM1829 (RGB)
|
||||
#define I_8266_U0_TM2_3 39
|
||||
#define I_8266_U1_TM2_3 40
|
||||
#define I_8266_DM_TM2_3 41
|
||||
#define I_8266_BB_TM2_3 42
|
||||
#define I_8266_U0_TM2_3 17
|
||||
#define I_8266_U1_TM2_3 18
|
||||
#define I_8266_DM_TM2_3 19
|
||||
#define I_8266_BB_TM2_3 20
|
||||
|
||||
/*** ESP32 Neopixel methods ***/
|
||||
//RGB
|
||||
#define I_32_RN_NEO_3 17
|
||||
#define I_32_I0_NEO_3 18
|
||||
#define I_32_I1_NEO_3 19
|
||||
#define I_32_RN_NEO_3 21
|
||||
#define I_32_I0_NEO_3 22
|
||||
#define I_32_I1_NEO_3 23
|
||||
#define I_32_BB_NEO_3 24 // bitbangging on ESP32 not recommended
|
||||
//RGBW
|
||||
#define I_32_RN_NEO_4 20
|
||||
#define I_32_I0_NEO_4 21
|
||||
#define I_32_I1_NEO_4 22
|
||||
#define I_32_RN_NEO_4 25
|
||||
#define I_32_I0_NEO_4 26
|
||||
#define I_32_I1_NEO_4 27
|
||||
#define I_32_BB_NEO_4 28 // bitbangging on ESP32 not recommended
|
||||
//400Kbps
|
||||
#define I_32_RN_400_3 23
|
||||
#define I_32_I0_400_3 24
|
||||
#define I_32_I1_400_3 25
|
||||
#define I_32_RN_400_3 29
|
||||
#define I_32_I0_400_3 30
|
||||
#define I_32_I1_400_3 31
|
||||
#define I_32_BB_400_3 32 // bitbangging on ESP32 not recommended
|
||||
//TM1814 (RGBW)
|
||||
#define I_32_RN_TM1_4 26
|
||||
#define I_32_I0_TM1_4 27
|
||||
#define I_32_I1_TM1_4 28
|
||||
#define I_32_RN_TM1_4 33
|
||||
#define I_32_I0_TM1_4 34
|
||||
#define I_32_I1_TM1_4 35
|
||||
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
|
||||
//TM1829 (RGB)
|
||||
#define I_32_RN_TM2_3 43
|
||||
#define I_32_I0_TM2_3 44
|
||||
#define I_32_I1_TM2_3 45
|
||||
#define I_32_RN_TM2_3 36
|
||||
#define I_32_I0_TM2_3 37
|
||||
#define I_32_I1_TM2_3 38
|
||||
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
|
||||
|
||||
//APA102
|
||||
#define I_HS_DOT_3 29 //hardware SPI
|
||||
#define I_SS_DOT_3 30 //soft SPI
|
||||
#define I_HS_DOT_3 39 //hardware SPI
|
||||
#define I_SS_DOT_3 40 //soft SPI
|
||||
|
||||
//LPD8806
|
||||
#define I_HS_LPD_3 31
|
||||
#define I_SS_LPD_3 32
|
||||
#define I_HS_LPD_3 41
|
||||
#define I_SS_LPD_3 42
|
||||
|
||||
//WS2801
|
||||
#define I_HS_WS1_3 33
|
||||
#define I_SS_WS1_3 34
|
||||
#define I_HS_WS1_3 43
|
||||
#define I_SS_WS1_3 44
|
||||
|
||||
//P9813
|
||||
#define I_HS_P98_3 35
|
||||
#define I_SS_P98_3 36
|
||||
#define I_HS_P98_3 45
|
||||
#define I_SS_P98_3 46
|
||||
|
||||
//LPD6803
|
||||
#define I_HS_LPO_3 37
|
||||
#define I_SS_LPO_3 38
|
||||
#define I_HS_LPO_3 47
|
||||
#define I_SS_LPO_3 48
|
||||
|
||||
|
||||
/*** ESP8266 Neopixel methods ***/
|
||||
@@ -117,43 +135,46 @@
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
//RGB
|
||||
#define B_32_RN_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32RmtNWs2812xMethod>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
#define B_32_I0_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s0800KbpsMethod>
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#define B_32_I1_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s1800KbpsMethod>
|
||||
#endif
|
||||
//#define B_32_BB_NEO_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32BitBang800KbpsMethod>
|
||||
//RGBW
|
||||
#define B_32_RN_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32RmtNWs2812xMethod>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
#define B_32_I0_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32I2s0800KbpsMethod>
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#define B_32_I1_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32I2s1800KbpsMethod>
|
||||
#endif
|
||||
//#define B_32_BB_NEO_4 NeoPixelBrightnessBus<NeoGrbwFeature, NeoEsp32BitBang800KbpsMethod>
|
||||
//400Kbps
|
||||
#define B_32_RN_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32RmtN400KbpsMethod>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
#define B_32_I0_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s0400KbpsMethod>
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#define B_32_I1_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32I2s1400KbpsMethod>
|
||||
#endif
|
||||
//#define B_32_BB_400_3 NeoPixelBrightnessBus<NeoGrbFeature, NeoEsp32BitBang400KbpsMethod>
|
||||
//TM1814 (RGBW)
|
||||
#define B_32_RN_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32RmtNTm1814Method>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
#define B_32_I0_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32I2s0Tm1814Method>
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#define B_32_I1_TM1_4 NeoPixelBrightnessBus<NeoWrgbTm1814Feature, NeoEsp32I2s1Tm1814Method>
|
||||
#endif
|
||||
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
|
||||
//TM1829 (RGB)
|
||||
#define B_32_RN_TM2_3 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp32RmtNTm1829Method>
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
#define B_32_I0_TM2_3 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp32I2s0Tm1829Method>
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
#define B_32_I1_TM2_3 NeoPixelBrightnessBus<NeoBrgFeature, NeoEsp32I2s1Tm1829Method>
|
||||
#endif
|
||||
//Bit Bang theoratically possible, but very undesirable and not needed (no pin restrictions on RMT and I2S)
|
||||
@@ -237,33 +258,36 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Begin(); break;
|
||||
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->Begin(); break;
|
||||
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->Begin(); break;
|
||||
case I_32_RN_TM1_4: beginTM1814<B_32_RN_TM1_4*>(busPtr); break;
|
||||
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->Begin(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1_4: beginTM1814<B_32_I0_TM1_4*>(busPtr); break;
|
||||
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1_4: beginTM1814<B_32_I1_TM1_4*>(busPtr); break;
|
||||
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->Begin(); break;
|
||||
#endif
|
||||
@@ -309,33 +333,36 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: busPtr = new B_32_RN_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: busPtr = new B_32_I0_NEO_3(len, pins[0]); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: busPtr = new B_32_I1_NEO_3(len, pins[0]); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_3: busPtr = new B_32_BB_NEO_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
case I_32_RN_NEO_4: busPtr = new B_32_RN_NEO_4(len, pins[0], (NeoBusChannel)channel); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_4: busPtr = new B_32_I0_NEO_4(len, pins[0]); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_4: busPtr = new B_32_I1_NEO_4(len, pins[0]); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_4: busPtr = new B_32_BB_NEO_4(len, pins[0], (NeoBusChannel)channel); break;
|
||||
case I_32_RN_400_3: busPtr = new B_32_RN_400_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_400_3: busPtr = new B_32_I0_400_3(len, pins[0]); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_400_3: busPtr = new B_32_I1_400_3(len, pins[0]); break;
|
||||
#endif
|
||||
// case I_32_BB_400_3: busPtr = new B_32_BB_400_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
case I_32_RN_TM1_4: busPtr = new B_32_RN_TM1_4(len, pins[0], (NeoBusChannel)channel); break;
|
||||
case I_32_RN_TM2_3: busPtr = new B_32_RN_TM2_3(len, pins[0], (NeoBusChannel)channel); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1_4: busPtr = new B_32_I0_TM1_4(len, pins[0]); break;
|
||||
case I_32_I0_TM2_3: busPtr = new B_32_I0_TM2_3(len, pins[0]); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1_4: busPtr = new B_32_I1_TM1_4(len, pins[0]); break;
|
||||
case I_32_I1_TM2_3: busPtr = new B_32_I1_TM2_3(len, pins[0]); break;
|
||||
#endif
|
||||
@@ -382,33 +409,36 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->Show(); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->Show(); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->Show(); break;
|
||||
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->Show(); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->Show(); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->Show(); break;
|
||||
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->Show(); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->Show(); break;
|
||||
#endif
|
||||
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->Show(); break;
|
||||
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->Show(); break;
|
||||
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->Show(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->Show(); break;
|
||||
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->Show(); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->Show(); break;
|
||||
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->Show(); break;
|
||||
#endif
|
||||
@@ -452,33 +482,36 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: return (static_cast<B_32_I0_NEO_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: return (static_cast<B_32_I1_NEO_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_3: return (static_cast<B_32_BB_NEO_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_RN_NEO_4: return (static_cast<B_32_RN_NEO_4*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_4: return (static_cast<B_32_I0_NEO_4*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_4: return (static_cast<B_32_I1_NEO_4*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_4: return (static_cast<B_32_BB_NEO_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_RN_400_3: return (static_cast<B_32_RN_400_3*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_400_3: return (static_cast<B_32_I0_400_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_400_3: return (static_cast<B_32_I1_400_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
// case I_32_BB_400_3: return (static_cast<B_32_BB_400_3*>(busPtr))->CanShow(); break;
|
||||
case I_32_RN_TM1_4: return (static_cast<B_32_RN_TM1_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_RN_TM2_3: return (static_cast<B_32_RN_TM2_3*>(busPtr))->CanShow(); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1_4: return (static_cast<B_32_I0_TM1_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_I0_TM2_3: return (static_cast<B_32_I0_TM2_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1_4: return (static_cast<B_32_I1_TM1_4*>(busPtr))->CanShow(); break;
|
||||
case I_32_I1_TM2_3: return (static_cast<B_32_I1_TM2_3*>(busPtr))->CanShow(); break;
|
||||
#endif
|
||||
@@ -546,33 +579,36 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#endif
|
||||
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetPixelColor(pix, col); break;
|
||||
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetPixelColor(pix, RgbColor(col.R,col.G,col.B)); break;
|
||||
#endif
|
||||
@@ -616,33 +652,36 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->SetBrightness(b); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: (static_cast<B_32_I1_NEO_3*>(busPtr))->SetBrightness(b); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_3: (static_cast<B_32_BB_NEO_3*>(busPtr))->SetBrightness(b); break;
|
||||
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->SetBrightness(b); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_4: (static_cast<B_32_I1_NEO_4*>(busPtr))->SetBrightness(b); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_4: (static_cast<B_32_BB_NEO_4*>(busPtr))->SetBrightness(b); break;
|
||||
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->SetBrightness(b); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_400_3: (static_cast<B_32_I1_400_3*>(busPtr))->SetBrightness(b); break;
|
||||
#endif
|
||||
// case I_32_BB_400_3: (static_cast<B_32_BB_400_3*>(busPtr))->SetBrightness(b); break;
|
||||
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->SetBrightness(b); break;
|
||||
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->SetBrightness(b); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->SetBrightness(b); break;
|
||||
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->SetBrightness(b); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1_4: (static_cast<B_32_I1_TM1_4*>(busPtr))->SetBrightness(b); break;
|
||||
case I_32_I1_TM2_3: (static_cast<B_32_I1_TM2_3*>(busPtr))->SetBrightness(b); break;
|
||||
#endif
|
||||
@@ -687,33 +726,36 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: col = (static_cast<B_32_RN_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: col = (static_cast<B_32_I0_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: col = (static_cast<B_32_I1_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_3: col = (static_cast<B_32_BB_NEO_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_RN_NEO_4: col = (static_cast<B_32_RN_NEO_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_4: col = (static_cast<B_32_I0_NEO_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_4: col = (static_cast<B_32_I1_NEO_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_4: col = (static_cast<B_32_BB_NEO_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_RN_400_3: col = (static_cast<B_32_RN_400_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_400_3: col = (static_cast<B_32_I0_400_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_400_3: col = (static_cast<B_32_I1_400_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
// case I_32_BB_400_3: col = (static_cast<B_32_BB_400_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_RN_TM1_4: col = (static_cast<B_32_RN_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_RN_TM2_3: col = (static_cast<B_32_RN_TM2_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1_4: col = (static_cast<B_32_I0_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I0_TM2_3: col = (static_cast<B_32_I0_TM2_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1_4: col = (static_cast<B_32_I1_TM1_4*>(busPtr))->GetPixelColor(pix); break;
|
||||
case I_32_I1_TM2_3: col = (static_cast<B_32_I1_TM2_3*>(busPtr))->GetPixelColor(pix); break;
|
||||
#endif
|
||||
@@ -777,33 +819,36 @@ class PolyBus {
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
case I_32_RN_NEO_3: delete (static_cast<B_32_RN_NEO_3*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_3: delete (static_cast<B_32_I0_NEO_3*>(busPtr)); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_3: delete (static_cast<B_32_I1_NEO_3*>(busPtr)); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_3: delete (static_cast<B_32_BB_NEO_3*>(busPtr)); break;
|
||||
case I_32_RN_NEO_4: delete (static_cast<B_32_RN_NEO_4*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_NEO_4: delete (static_cast<B_32_I0_NEO_4*>(busPtr)); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_NEO_4: delete (static_cast<B_32_I1_NEO_4*>(busPtr)); break;
|
||||
#endif
|
||||
// case I_32_BB_NEO_4: delete (static_cast<B_32_BB_NEO_4*>(busPtr)); break;
|
||||
case I_32_RN_400_3: delete (static_cast<B_32_RN_400_3*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_400_3: delete (static_cast<B_32_I0_400_3*>(busPtr)); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_400_3: delete (static_cast<B_32_I1_400_3*>(busPtr)); break;
|
||||
#endif
|
||||
// case I_32_BB_400_3: delete (static_cast<B_32_BB_400_3*>(busPtr)); break;
|
||||
case I_32_RN_TM1_4: delete (static_cast<B_32_RN_TM1_4*>(busPtr)); break;
|
||||
case I_32_RN_TM2_3: delete (static_cast<B_32_RN_TM2_3*>(busPtr)); break;
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
#ifndef WLED_NO_I2S0_PIXELBUS
|
||||
case I_32_I0_TM1_4: delete (static_cast<B_32_I0_TM1_4*>(busPtr)); break;
|
||||
case I_32_I0_TM2_3: delete (static_cast<B_32_I0_TM2_3*>(busPtr)); break;
|
||||
#endif
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#ifndef WLED_NO_I2S1_PIXELBUS
|
||||
case I_32_I1_TM1_4: delete (static_cast<B_32_I1_TM1_4*>(busPtr)); break;
|
||||
case I_32_I1_TM2_3: delete (static_cast<B_32_I1_TM2_3*>(busPtr)); break;
|
||||
#endif
|
||||
@@ -863,12 +908,22 @@ class PolyBus {
|
||||
}
|
||||
#else //ESP32
|
||||
uint8_t offset = 0; //0 = RMT (num 0-7) 8 = I2S0 9 = I2S1
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S2
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
// ESP32-S2 only has 4 RMT channels
|
||||
if (num > 4) return I_NONE;
|
||||
if (num > 3) offset = 1; // only one I2S
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
// On ESP32-C3 only the first 2 RMT channels are usable for transmitting
|
||||
if (num > 1) return I_NONE;
|
||||
//if (num > 1) offset = 1; // I2S not supported yet (only 1 I2S)
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// On ESP32-S3 only the first 4 RMT channels are usable for transmitting
|
||||
if (num > 3) return I_NONE;
|
||||
//if (num > 3) offset = num -4; // I2S not supported yet
|
||||
#else
|
||||
// standard ESP32 has 8 RMT and 2 I2S channels
|
||||
if (num > 9) return I_NONE;
|
||||
if (num > 7) offset = num -7;
|
||||
#else //ESP32 S2 only has 4 RMT channels
|
||||
if (num > 5) return I_NONE;
|
||||
if (num > 4) offset = num -4;
|
||||
#endif
|
||||
switch (busType) {
|
||||
case TYPE_WS2812_RGB:
|
||||
|
||||
@@ -200,7 +200,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
int8_t pin = btn["pin"][0] | -1;
|
||||
if (pin > -1 && pinManager.allocatePin(pin, false, PinOwner::Button)) {
|
||||
btnPin[s] = pin;
|
||||
#ifdef ESP32
|
||||
pinMode(btnPin[s], buttonType[s]==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
|
||||
#else
|
||||
pinMode(btnPin[s], INPUT_PULLUP);
|
||||
#endif
|
||||
} else {
|
||||
btnPin[s] = -1;
|
||||
}
|
||||
@@ -373,6 +377,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
|
||||
CJSON(e131Universe, if_live_dmx[F("uni")]);
|
||||
CJSON(e131SkipOutOfSequence, if_live_dmx[F("seqskip")]);
|
||||
CJSON(DMXAddress, if_live_dmx[F("addr")]);
|
||||
if (!DMXAddress || DMXAddress > 510) DMXAddress = 1;
|
||||
CJSON(DMXMode, if_live_dmx["mode"]);
|
||||
|
||||
tdd = if_live[F("timeout")] | -1;
|
||||
@@ -540,8 +545,8 @@ void deserializeConfigFromFS() {
|
||||
if (!success) { //if file does not exist, try reading from EEPROM
|
||||
#ifdef WLED_ADD_EEPROM_SUPPORT
|
||||
deEEPSettings();
|
||||
#endif
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!requestJSONBufferLock(1)) return;
|
||||
@@ -946,6 +951,8 @@ void serializeConfig() {
|
||||
if (f) serializeJson(doc, f);
|
||||
f.close();
|
||||
releaseJSONBufferLock();
|
||||
|
||||
doSerializeConfig = false;
|
||||
}
|
||||
|
||||
//settings in /wsec.json, not accessible via webserver, for passwords and tokens
|
||||
|
||||
@@ -25,10 +25,22 @@
|
||||
#ifdef ESP8266
|
||||
#define WLED_MAX_BUSSES 3
|
||||
#else
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S2
|
||||
#define WLED_MAX_BUSSES 5
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, only has 1 I2S but NPB does not support it ATM
|
||||
#define WLED_MAX_BUSSES 2
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, only has 1 I2S bus, supported in NPB
|
||||
#if defined(USERMOD_AUDIOREACTIVE) // requested by @softhack007 https://github.com/blazoncek/WLED/issues/33
|
||||
#define WLED_MAX_BUSSES 4
|
||||
#else
|
||||
#define WLED_MAX_BUSSES 5
|
||||
#endif
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, has 2 I2S but NPB does not support them ATM
|
||||
#define WLED_MAX_BUSSES 4
|
||||
#else
|
||||
#define WLED_MAX_BUSSES 10
|
||||
#if defined(USERMOD_AUDIOREACTIVE) // requested by @softhack007 https://github.com/blazoncek/WLED/issues/33
|
||||
#define WLED_MAX_BUSSES 8
|
||||
#else
|
||||
#define WLED_MAX_BUSSES 10
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
@@ -279,11 +291,15 @@
|
||||
#endif
|
||||
|
||||
#ifndef MAX_LED_MEMORY
|
||||
#ifdef ESP8266
|
||||
#define MAX_LED_MEMORY 4000
|
||||
#else
|
||||
#define MAX_LED_MEMORY 64000
|
||||
#endif
|
||||
#ifdef ESP8266
|
||||
#define MAX_LED_MEMORY 4000
|
||||
#else
|
||||
#if defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32C3)
|
||||
#define MAX_LED_MEMORY 32000
|
||||
#else
|
||||
#define MAX_LED_MEMORY 64000
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MAX_LEDS_PER_BUS
|
||||
@@ -308,9 +324,11 @@
|
||||
#endif
|
||||
|
||||
#ifndef ABL_MILLIAMPS_DEFAULT
|
||||
#define ABL_MILLIAMPS_DEFAULT 850 // auto lower brightness to stay close to milliampere limit
|
||||
#define ABL_MILLIAMPS_DEFAULT 850 // auto lower brightness to stay close to milliampere limit
|
||||
#else
|
||||
#if ABL_MILLIAMPS_DEFAULT < 250 // make sure value is at least 250
|
||||
#if ABL_MILLIAMPS_DEFAULT == 0 // disable ABL
|
||||
#elif ABL_MILLIAMPS_DEFAULT < 250 // make sure value is at least 250
|
||||
#warning "make sure value is at least 250"
|
||||
#define ABL_MILLIAMPS_DEFAULT 250
|
||||
#endif
|
||||
#endif
|
||||
@@ -333,7 +351,8 @@
|
||||
#define JSON_BUFFER_SIZE 24576
|
||||
#endif
|
||||
|
||||
#define MIN_HEAP_SIZE (MAX_LED_MEMORY+2048)
|
||||
//#define MIN_HEAP_SIZE (MAX_LED_MEMORY+2048)
|
||||
#define MIN_HEAP_SIZE (8192)
|
||||
|
||||
// Maximum size of node map (list of other WLED instances)
|
||||
#ifdef ESP8266
|
||||
@@ -344,7 +363,7 @@
|
||||
|
||||
//this is merely a default now and can be changed at runtime
|
||||
#ifndef LEDPIN
|
||||
#if defined(ESP8266) || (defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM))
|
||||
#if defined(ESP8266) || (defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)) || defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#define LEDPIN 2 // GPIO2 (D4) on Wemod D1 mini compatible boards
|
||||
#else
|
||||
#define LEDPIN 16 // aligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards
|
||||
|
||||
@@ -104,7 +104,7 @@ button {
|
||||
color: var(--c-d); /* must remain bright with dark shadow (see below) to be legible on gray background */
|
||||
cursor: pointer;
|
||||
writing-mode: vertical-rl;
|
||||
transform: rotate(180deg);
|
||||
/* transform: rotate(180deg); */
|
||||
}
|
||||
|
||||
.bri {
|
||||
@@ -1194,7 +1194,7 @@ TD .checkmark, TD .radiomark {
|
||||
/*filter: brightness(1);*/
|
||||
font-size: 19px;
|
||||
border-radius: 21px;
|
||||
min-width: 280px;
|
||||
/*min-width: 264px;*/
|
||||
}
|
||||
|
||||
.seg {
|
||||
|
||||
@@ -399,7 +399,7 @@ function presetError(empty)
|
||||
if (bckstr.length > 10) hasBackup = true;
|
||||
} catch (e) {}
|
||||
|
||||
var cn = `<div class="pres c" ${empty?'style="padding:8px 0;margin-top: 15px;"':'onclick="loadPresets()" style="cursor:pointer;padding:8px 0;"'}>`;
|
||||
var cn = `<div class="pres c" ${empty?'style="padding:8px;margin-top: 16px;"':'onclick="pmtLast=0;loadPresets();" style="cursor:pointer;padding:8px;margin-top: 16px;"'}>`;
|
||||
if (empty)
|
||||
cn += `You have no presets yet!`;
|
||||
else
|
||||
@@ -594,7 +594,9 @@ function parseInfo(i) {
|
||||
lastinfo = i;
|
||||
var name = i.name;
|
||||
gId('namelabel').innerHTML = name;
|
||||
//if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)";
|
||||
if (!name.match(/[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\uff66-\uff9f\u3131-\uD79D]/))
|
||||
gId('namelabel').style.transform = "rotate(180deg)"; // rotate if no CJK characters
|
||||
if (name === "Dinnerbone") d.documentElement.style.transform = "rotate(180deg)"; // Minecraft easter egg
|
||||
if (i.live) name = "(Live) " + name;
|
||||
if (loc) name = "(L) " + name;
|
||||
d.title = name;
|
||||
@@ -1393,7 +1395,7 @@ function setEffectParameters(idx)
|
||||
if (slOnOff.length>5) {
|
||||
gId('fxopt').classList.remove('fade');
|
||||
for (let i = 0; i<3; i++) {
|
||||
if (slOnOff[5+i]!=='') {
|
||||
if (5+i<slOnOff.length && slOnOff[5+i]!=='') {
|
||||
gId('opt'+i).classList.remove('hide');
|
||||
gId('optLabel'+i).innerHTML = slOnOff[5+i]=="!" ? 'Option' : slOnOff[5+i].substr(0,16);
|
||||
} else
|
||||
@@ -1529,7 +1531,7 @@ function requestJson(command=null)
|
||||
div.outerHTML = generateListItemHtml(
|
||||
'palette',
|
||||
255-j,
|
||||
'~ Custom '+j+1+' ~',
|
||||
'~ Custom '+j+' ~',
|
||||
'setPalette',
|
||||
`<div class="lstIprev" style="${genPalPrevCss(255-j)}"></div>`
|
||||
);
|
||||
@@ -2238,7 +2240,7 @@ function saveP(i,pl)
|
||||
}
|
||||
populatePresets();
|
||||
resetPUtil();
|
||||
setTimeout(()=>{pmtLast=0; loadPresets();}, 500); // force reloading of presets
|
||||
setTimeout(()=>{pmtLast=0; loadPresets();}, 750); // force reloading of presets
|
||||
}
|
||||
|
||||
function testPl(i,bt) {
|
||||
@@ -2267,6 +2269,7 @@ function delP(i) {
|
||||
requestJson(obj);
|
||||
delete pJson[i];
|
||||
populatePresets();
|
||||
gId('putil').classList.add("staybot");
|
||||
} else {
|
||||
bt.style.color = "var(--c-r)";
|
||||
bt.innerHTML = "<i class='icons btn-icon'></i>Delete!";
|
||||
|
||||
@@ -23,7 +23,12 @@
|
||||
// success event
|
||||
scE.addEventListener("load", () => {
|
||||
//console.log("File loaded");
|
||||
d.um_p = [];
|
||||
d.rsvd = [];
|
||||
d.ro_pins = [];
|
||||
d.max_gpio = 39;
|
||||
GetV();checkSi();setABL();
|
||||
if (d.um_p[0]==-1) d.um_p.shift();
|
||||
});
|
||||
// error event
|
||||
scE.addEventListener("error", (ev) => {
|
||||
@@ -57,9 +62,11 @@
|
||||
//check for pin conflicts
|
||||
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR")
|
||||
if (LCs[i].value!="" && LCs[i].value!="-1") {
|
||||
if (d.um_p && d.um_p.some((e)=>e==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(d.um_p)} can't be used.`);LCs[i].value="";LCs[i].focus();return false;}
|
||||
else if (LCs[i].value > 5 && LCs[i].value < 12) {alert("Sorry, pins 6-11 can not be used.");LCs[i].value="";LCs[i].focus();return false;}
|
||||
else if (!(nm == "IR" || nm=="BT") && LCs[i].value > 33) {alert("Sorry, pins >33 are input only.");LCs[i].value="";LCs[i].focus();return false;}
|
||||
var p = []; // used pin array
|
||||
for (k=0;k<d.rsvd.length;k++) p.push(d.rsvd[k]); // fill with reservations
|
||||
for (k=0;k<d.um_p.length;k++) p.push(d.um_p[k]); // fill with usermod pins
|
||||
if (p.some((e)=>e==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(p)} can't be used.`);LCs[i].value="";LCs[i].focus();return false;}
|
||||
else if (!(nm == "IR" || nm=="BT") && d.ro_pins.some((e)=>e==parseInt(LCs[i].value,10))) {alert(`Sorry, pins ${JSON.stringify(d.ro_gpio)} are input only.`);LCs[i].value="";LCs[i].focus();return false;}
|
||||
for (j=i+1; j<LCs.length; j++)
|
||||
{
|
||||
var n2 = LCs[j].name.substring(0,2);
|
||||
@@ -230,7 +237,7 @@
|
||||
LCs[i].style.color="#fff";
|
||||
continue; // do not check conflicts
|
||||
} else {
|
||||
LCs[i].max = 33;
|
||||
LCs[i].max = d.max_gpio;
|
||||
LCs[i].min = -1;
|
||||
}
|
||||
}
|
||||
@@ -238,7 +245,8 @@
|
||||
if (nm=="L0" || nm=="L1" || nm=="L2" || nm=="L3" || nm=="L4" || nm=="RL" || nm=="BT" || nm=="IR")
|
||||
if (LCs[i].value!="" && LCs[i].value!="-1") {
|
||||
var p = []; // used pin array
|
||||
if (d.um_p && Array.isArray(d.um_p)) for (k=0;k<d.um_p.length;k++) p.push(d.um_p[k]); // fill with reservations
|
||||
for (k=0;k<d.rsvd.length;k++) p.push(d.rsvd[k]); // fill with reservations
|
||||
for (k=0;k<d.um_p.length;k++) p.push(d.um_p[k]); // fill with usermod pins
|
||||
for (j=0; j<LCs.length; j++) {
|
||||
if (i==j) continue;
|
||||
var n2 = LCs[j].name.substring(0,2);
|
||||
@@ -252,8 +260,13 @@
|
||||
}
|
||||
}
|
||||
// now check for conflicts
|
||||
if (p.some((e)=>e==parseInt(LCs[i].value,10))) LCs[i].style.color="red"; else LCs[i].style.color=parseInt(LCs[i].value,10)>33?"orange":"#fff";
|
||||
if (p.some((e)=>e==parseInt(LCs[i].value,10))) LCs[i].style.color="red"; else LCs[i].style.color=d.ro_gpio.some((e)=>e==parseInt(LCs[i].value,10))?"orange":"#fff";
|
||||
}
|
||||
// check buttons, IR & relay
|
||||
if (nm=="IR" || nm=="BT" || nm=="RL") {
|
||||
LCs[i].max = d.max_gpio;
|
||||
LCs[i].min = -1;
|
||||
}
|
||||
}
|
||||
// update total led count
|
||||
gId("lc").textContent = sLC;
|
||||
@@ -268,25 +281,25 @@
|
||||
gId('wreason').innerHTML = (bquot > 80) ? "80% of max. LED memory" +(bquot>100 ? ` (<b>ERROR: Using over ${maxM}B!</b>)` : "") : "800 LEDs per output";
|
||||
// calculate power
|
||||
var val = Math.ceil((100 + sPC * laprev)/500)/2;
|
||||
val = (val > 5) ? Math.ceil(val) : val;
|
||||
var s = "";
|
||||
val = (val > 5) ? Math.ceil(val) : val;
|
||||
var s = "";
|
||||
var is12V = (d.Sf.LAsel.value == 30);
|
||||
var isWS2815 = (d.Sf.LAsel.value == 255);
|
||||
if (val < 1.02 && !is12V && !isWS2815)
|
||||
{
|
||||
s = "ESP 5V pin with 1A USB supply";
|
||||
} else
|
||||
{
|
||||
s += is12V ? "12V ": isWS2815 ? "WS2815 12V " : "5V ";
|
||||
s += val;
|
||||
s += "A supply connected to LEDs";
|
||||
}
|
||||
if (val < 1.02 && !is12V && !isWS2815)
|
||||
{
|
||||
s = "ESP 5V pin with 1A USB supply";
|
||||
} else
|
||||
{
|
||||
s += is12V ? "12V ": isWS2815 ? "WS2815 12V " : "5V ";
|
||||
s += val;
|
||||
s += "A supply connected to LEDs";
|
||||
}
|
||||
var val2 = Math.ceil((100 + sPC * laprev)/1500)/2;
|
||||
val2 = (val2 > 5) ? Math.ceil(val2) : val2;
|
||||
var s2 = "(for most effects, ~";
|
||||
s2 += val2;
|
||||
s2 += "A is enough)<br>";
|
||||
gId('psu').innerHTML = s;
|
||||
gId('psu').innerHTML = s;
|
||||
gId('psu2').innerHTML = isWS2815 ? "" : s2;
|
||||
gId("json").style.display = d.Sf.IT.value==8 ? "" : "none";
|
||||
}
|
||||
@@ -348,11 +361,11 @@ ${i+1}:
|
||||
<span id="psd${i}">Start:</span> <input type="number" name="LS${i}" id="ls${i}" class="l starts" min="0" max="8191" value="${lastEnd(i)}" oninput="startsDirty[${i}]=true;UI();" required />
|
||||
<div id="dig${i}c" style="display:inline">Length: <input type="number" name="LC${i}" class="l" min="1" max="${maxPB}" value="1" required oninput="UI()" /></div><br>
|
||||
</div>
|
||||
<span id="p0d${i}">GPIO:</span> <input type="number" name="L0${i}" min="0" max="33" required class="s" onchange="UI()"/>
|
||||
<span id="p1d${i}"></span><input type="number" name="L1${i}" min="0" max="33" class="s" onchange="UI()"/>
|
||||
<span id="p2d${i}"></span><input type="number" name="L2${i}" min="0" max="33" class="s" onchange="UI()"/>
|
||||
<span id="p3d${i}"></span><input type="number" name="L3${i}" min="0" max="33" class="s" onchange="UI()"/>
|
||||
<span id="p4d${i}"></span><input type="number" name="L4${i}" min="0" max="33" class="s" onchange="UI()"/>
|
||||
<span id="p0d${i}">GPIO:</span> <input type="number" name="L0${i}" min="0" max="48" required class="s" onchange="UI()"/>
|
||||
<span id="p1d${i}"></span><input type="number" name="L1${i}" min="0" max="48" class="s" onchange="UI()"/>
|
||||
<span id="p2d${i}"></span><input type="number" name="L2${i}" min="0" max="48" class="s" onchange="UI()"/>
|
||||
<span id="p3d${i}"></span><input type="number" name="L3${i}" min="0" max="48" class="s" onchange="UI()"/>
|
||||
<span id="p4d${i}"></span><input type="number" name="L4${i}" min="0" max="48" class="s" onchange="UI()"/>
|
||||
<div id="dig${i}r" style="display:inline"><br><span id="rev${i}">Reversed</span>: <input type="checkbox" name="CV${i}"></div>
|
||||
<div id="dig${i}s" style="display:inline"><br>Skip first LEDs: <input type="number" name="SL${i}" min="0" max="255" value="0" oninput="UI()"></div>
|
||||
<div id="dig${i}f" style="display:inline"><br>Off Refresh: <input id="rf${i}" type="checkbox" name="RF${i}"></div>
|
||||
@@ -422,7 +435,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
var c = gId("btns").innerHTML;
|
||||
var bt = "BT" + String.fromCharCode((i<10?48:55)+i);
|
||||
var be = "BE" + String.fromCharCode((i<10?48:55)+i);
|
||||
c += `Button ${i} GPIO: <input type="number" min="-1" max="40" name="${bt}" onchange="UI()" class="xs" value="${p}">`;
|
||||
c += `Button ${i} GPIO: <input type="number" min="-1" max="48" name="${bt}" onchange="UI()" class="xs" value="${p}">`;
|
||||
c += ` <select name="${be}">`
|
||||
c += `<option value="0" ${t==0?"selected":""}>Disabled</option>`;
|
||||
c += `<option value="2" ${t==2?"selected":""}>Pushbutton</option>`;
|
||||
@@ -605,7 +618,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
<hr style="width:260px">
|
||||
<div id="btns"></div>
|
||||
Touch threshold: <input type="number" class="s" min="0" max="100" name="TT" required><br>
|
||||
IR GPIO: <input type="number" min="-1" max="40" name="IR" onchange="UI()" class="xs"><select name="IT" onchange="UI()">
|
||||
IR GPIO: <input type="number" min="-1" max="48" name="IR" onchange="UI()" class="xs"><select name="IT" onchange="UI()">
|
||||
<option value=0>Remote disabled</option>
|
||||
<option value=1>24-key RGB</option>
|
||||
<option value=2>24-key with CT</option>
|
||||
@@ -619,7 +632,7 @@ Length: <input type="number" name="XC${i}" id="xc${i}" class="l" min="1" max="65
|
||||
Apply IR change to main segment only: <input type="checkbox" name="MSO"><br>
|
||||
<div id="json" style="display:none;">JSON file: <input type="file" name="data" accept=".json"><button type="button" class="sml" onclick="uploadFile('/ir.json')">Upload</button><br></div>
|
||||
<a href="https://kno.wled.ge/interfaces/infrared/" target="_blank">IR info</a><br>
|
||||
Relay GPIO: <input type="number" min="-1" max="33" name="RL" onchange="UI()" class="xs"> Invert <input type="checkbox" name="RM"><span style="cursor: pointer;" onclick="off('RL')"> ✕</span><br>
|
||||
Relay GPIO: <input type="number" min="-1" max="48" name="RL" onchange="UI()" class="xs"> Invert <input type="checkbox" name="RM"><span style="cursor: pointer;" onclick="off('RL')"> ✕</span><br>
|
||||
<hr style="width:260px">
|
||||
<h3>Defaults</h3>
|
||||
Turn LEDs on after power up/reset: <input type="checkbox" name="BO"><br>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
}
|
||||
function H(){window.open("https://kno.wled.ge/interfaces/udp-notifier/");}
|
||||
function B(){window.open("/settings","_self");}
|
||||
function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.DA.value == 1) d.Sf.DA.value = 0; if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;}
|
||||
function adj(){if (d.Sf.DI.value == 6454) {if (d.Sf.EU.value == 1) d.Sf.EU.value = 0;}
|
||||
else if (d.Sf.DI.value == 5568) {if (d.Sf.DA.value == 0) d.Sf.DA.value = 1; if (d.Sf.EU.value == 0) d.Sf.EU.value = 1;} }
|
||||
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
|
||||
function loadJS(FILE_URL, async = true) {
|
||||
@@ -147,7 +147,7 @@ Multicast: <input type="checkbox" name="EM"><br>
|
||||
Start universe: <input name="EU" type="number" min="0" max="63999" required><br>
|
||||
<i>Reboot required.</i> Check out <a href="https://github.com/LedFx/LedFx" target="_blank">LedFx</a>!<br>
|
||||
Skip out-of-sequence packets: <input type="checkbox" name="ES"><br>
|
||||
DMX start address: <input name="DA" type="number" min="0" max="510" required><br>
|
||||
DMX start address: <input name="DA" type="number" min="1" max="510" required><br>
|
||||
DMX mode:
|
||||
<select name=DM>
|
||||
<option value=0>Disabled</option>
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head lang="en">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>Usermod Settings</title>
|
||||
<script>
|
||||
var d = document;
|
||||
var umCfg = {};
|
||||
var pins = [6,7,8,9,10,11];
|
||||
var pinO = ["rsvd","rsvd","rsvd","rsvd","rsvd","rsvd"], owner;
|
||||
var loc = false, locip;
|
||||
var urows;
|
||||
var numM = 0;
|
||||
function gId(s) { return d.getElementById(s); }
|
||||
function isO(i) { return (i && typeof i === 'object' && !Array.isArray(i)); }
|
||||
function H() { window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings"); }
|
||||
function B() { window.open("/settings","_self"); }
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=500">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
||||
<title>Usermod Settings</title>
|
||||
<script>
|
||||
var d = document;
|
||||
var umCfg = {};
|
||||
var pins = [], pinO = [], owner;
|
||||
var loc = false, locip;
|
||||
var urows;
|
||||
var numM = 0;
|
||||
function gId(s) { return d.getElementById(s); }
|
||||
function isO(i) { return (i && typeof i === 'object' && !Array.isArray(i)); }
|
||||
function H() { window.open("https://github.com/Aircoookie/WLED/wiki/Settings#usermod-settings"); }
|
||||
function B() { window.open("/settings","_self"); }
|
||||
// https://www.educative.io/edpresso/how-to-dynamically-load-a-js-file-in-javascript
|
||||
function loadJS(FILE_URL, async = true) {
|
||||
let scE = d.createElement("script");
|
||||
@@ -27,7 +26,18 @@
|
||||
// success event
|
||||
scE.addEventListener("load", () => {
|
||||
//console.log("File loaded");
|
||||
d.um_p = [];
|
||||
d.rsvd = [];
|
||||
d.ro_pins = [];
|
||||
d.max_gpio = 39;
|
||||
GetV();
|
||||
for (let k=0; k<d.rsvd.length; k++) { pins.push(d.rsvd[k]); pinO.push("rsvd"); }
|
||||
if (d.um_p[0]==-1) d.um_p.shift();
|
||||
d.Sf.SDA.max = d.max_gpio;
|
||||
d.Sf.SCL.max = d.max_gpio;
|
||||
d.Sf.MOSI.max = d.max_gpio;
|
||||
d.Sf.SCLK.max = d.max_gpio;
|
||||
d.Sf.MISO.max = d.max_gpio;
|
||||
});
|
||||
// error event
|
||||
scE.addEventListener("error", (ev) => {
|
||||
@@ -35,182 +45,182 @@
|
||||
alert("Loading of configuration script failed.\nIncomplete page data!");
|
||||
});
|
||||
}
|
||||
function S() {
|
||||
if (window.location.protocol == "file:") {
|
||||
loc = true;
|
||||
locip = localStorage.getItem('locIp');
|
||||
if (!locip) {
|
||||
locip = prompt("File Mode. Please enter WLED IP!");
|
||||
localStorage.setItem('locIp', locip);
|
||||
}
|
||||
}
|
||||
ldS();
|
||||
if (!numM) gId("um").innerHTML = "No Usermods installed.";
|
||||
}
|
||||
// https://stackoverflow.com/questions/3885817/how-do-i-check-that-a-number-is-float-or-integer
|
||||
function isF(n) { return n === +n && n !== (n|0); }
|
||||
function isI(n) { return n === +n && n === (n|0); }
|
||||
function check(o,k) { // input object, pin owner key
|
||||
var n = o.name.replace("[]","").substr(-3);
|
||||
if (o.type=="number" && n.substr(0,3)=="pin") {
|
||||
for (var i=0; i<pins.length; i++) {
|
||||
if (k==pinO[i]) continue;
|
||||
if (o.value==pins[i] && pinO[i]==="if") { o.style.color="lime"; break; }
|
||||
if (o.value==pins[i] || o.value<-1 || o.value>39) { o.style.color="red"; break; } else o.style.color=o.value>33?"orange":"#fff";
|
||||
}
|
||||
} else {
|
||||
switch (o.name) {
|
||||
case "SDA": break;
|
||||
case "SCL": break;
|
||||
case "MOSI": break;
|
||||
case "SCLK": break;
|
||||
case "CS": break;
|
||||
default: return;
|
||||
}
|
||||
for (var i=0; i<pins.length; i++) {
|
||||
if (k==pinO[i]) continue;
|
||||
if (o.value==pins[i] && pinO[i]==="if") { o.style.color="lime"; break; }
|
||||
if (o.value==pins[i] || o.value<-1 || o.value>39) { o.style.color="red"; break; } else o.style.color=o.value>33?"orange":"#fff";
|
||||
}
|
||||
}
|
||||
}
|
||||
function getPins(o) {
|
||||
if (isO(o)) {
|
||||
for (const [k,v] of Object.entries(o)) {
|
||||
if (isO(v)) {
|
||||
owner = k;
|
||||
getPins(v);
|
||||
continue;
|
||||
}
|
||||
if (k.replace("[]","").substr(-3)=="pin") {
|
||||
if (Array.isArray(v)) {
|
||||
for (var i=0; i<v.length; i++) if (v[i]>=0) { pins.push(v[i]); pinO.push(owner); }
|
||||
} else {
|
||||
if (v>=0) { pins.push(v); pinO.push(owner); }
|
||||
}
|
||||
} else if (Array.isArray(v)) {
|
||||
for (var i=0; i<v.length; i++) getPins(v[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function addField(k,f,o,a=false) { //key, field, (sub)object, isArray
|
||||
if (isO(o)) {
|
||||
urows += '<hr style="width:260px">';
|
||||
for (const [s,v] of Object.entries(o)) {
|
||||
// possibility to nest objects (only 1 level)
|
||||
if (f!=='unknown' && !k.includes(":")) addField(k+":"+f,s,v);
|
||||
else addField(k,s,v);
|
||||
}
|
||||
} else if (Array.isArray(o)) {
|
||||
for (var j=0; j<o.length; j++) {
|
||||
addField(k,f,o[j],true);
|
||||
}
|
||||
} else {
|
||||
var c, t = typeof o;
|
||||
switch (t) {
|
||||
case "boolean":
|
||||
t = "checkbox"; c = 'value="true"' + (o ? ' checked' : '');
|
||||
break;
|
||||
case "number":
|
||||
c = `value="${o}"`;
|
||||
if (f.substr(-3)==="pin") {
|
||||
c += ' max="39" min="-1" class="s"';
|
||||
t = "int";
|
||||
} else {
|
||||
c += ' step="any" class="xxl"';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
t = "text"; c = `value="${o}" style="width:250px;"`;
|
||||
break;
|
||||
}
|
||||
if (k.includes(":")) urows += k.substr(k.indexOf(":")+1);
|
||||
urows += ` ${f}: `;
|
||||
// https://stackoverflow.com/questions/11657123/posting-both-checked-and-unchecked-checkboxes
|
||||
if (t=="checkbox") urows += `<input type="hidden" name="${k}:${f}${a?"[]":""}" value="false">`;
|
||||
else if (!a) urows += `<input type="hidden" name="${k}:${f}${a?"[]":""}" value="${t}">`;
|
||||
urows += `<input type="${t==="int"?"number":t}" name="${k}:${f}${a?"[]":""}" ${c} oninput="check(this,'${k.substr(k.indexOf(":")+1)}')"><br>`;
|
||||
}
|
||||
}
|
||||
// https://stackoverflow.com/questions/39729741/javascript-change-input-text-to-select-option
|
||||
function addDropdown(um,fld) {
|
||||
let sel = d.createElement('select');
|
||||
let arr = d.getElementsByName(um+":"+fld);
|
||||
let inp = arr[1]; // assume 1st field to be hidden (type)
|
||||
if (inp && inp.tagName === "INPUT" && (inp.type === "text" || inp.type === "number")) { // may also use nodeName
|
||||
let v = inp.value;
|
||||
let n = inp.name;
|
||||
// copy the existing input element's attributes to the new select element
|
||||
for (var i = 0; i < inp.attributes.length; ++ i) {
|
||||
var att = inp.attributes[i];
|
||||
// type and value don't apply, so skip them
|
||||
// ** you might also want to skip style, or others -- modify as needed **
|
||||
if (att.name != 'type' && att.name != 'value' && att.name != 'class' && att.name != 'style') {
|
||||
sel.setAttribute(att.name, att.value);
|
||||
}
|
||||
}
|
||||
sel.setAttribute("data-val", v);
|
||||
// finally, replace the old input element with the new select element
|
||||
inp.parentElement.replaceChild(sel, inp);
|
||||
return sel;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function addOption(sel,txt,val) {
|
||||
if (sel===null) return; // select object missing
|
||||
let opt = d.createElement("option");
|
||||
opt.value = val;
|
||||
opt.text = txt;
|
||||
sel.appendChild(opt);
|
||||
for (let i=0; i<sel.childNodes.length; i++) {
|
||||
let c = sel.childNodes[i];
|
||||
if (c.value == sel.dataset.val) sel.selectedIndex = i;
|
||||
}
|
||||
}
|
||||
// https://stackoverflow.com/questions/26440494/insert-text-after-this-input-element-with-javascript
|
||||
function addInfo(name,el,txt) {
|
||||
let obj = d.getElementsByName(name);
|
||||
if (!obj.length) return;
|
||||
if (typeof el === "string" && obj[0]) obj[0].placeholder = el;
|
||||
else if (obj[el]) obj[el].insertAdjacentHTML('afterend', ' '+txt);
|
||||
}
|
||||
// load settings and insert values into DOM
|
||||
function ldS() {
|
||||
var url = (loc?`http://${locip}`:'') + '/cfg.json';
|
||||
fetch(url, {
|
||||
method: 'get'
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) gId('lserr').style.display = "inline";
|
||||
return res.json();
|
||||
})
|
||||
.then(json => {
|
||||
umCfg = json.um;
|
||||
getPins(json);
|
||||
urows="";
|
||||
if (isO(umCfg)) {
|
||||
for (const [k,o] of Object.entries(umCfg)) {
|
||||
urows += `<hr><h3>${k}</h3>`;
|
||||
addField(k,'unknown',o);
|
||||
}
|
||||
}
|
||||
if (urows==="") urows = "Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults.";
|
||||
gId("um").innerHTML = urows;
|
||||
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=8';
|
||||
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
})
|
||||
.catch((error)=>{
|
||||
gId('lserr').style.display = "inline";
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
function svS(e) {
|
||||
e.preventDefault();
|
||||
if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914
|
||||
}
|
||||
</script>
|
||||
<style>@import url("style.css");</style>
|
||||
function S() {
|
||||
if (window.location.protocol == "file:") {
|
||||
loc = true;
|
||||
locip = localStorage.getItem('locIp');
|
||||
if (!locip) {
|
||||
locip = prompt("File Mode. Please enter WLED IP!");
|
||||
localStorage.setItem('locIp', locip);
|
||||
}
|
||||
}
|
||||
ldS();
|
||||
if (!numM) gId("um").innerHTML = "No Usermods installed.";
|
||||
}
|
||||
// https://stackoverflow.com/questions/3885817/how-do-i-check-that-a-number-is-float-or-integer
|
||||
function isF(n) { return n === +n && n !== (n|0); }
|
||||
function isI(n) { return n === +n && n === (n|0); }
|
||||
function check(o,k) { // input object, pin owner key
|
||||
var n = o.name.replace("[]","").substr(-3);
|
||||
if (o.type=="number" && n.substr(0,3)=="pin") {
|
||||
for (var i=0; i<pins.length; i++) {
|
||||
if (k==pinO[i]) continue;
|
||||
if (o.value==pins[i] && pinO[i]==="if") { o.style.color="lime"; break; }
|
||||
if (o.value==pins[i] || o.value<-1 || o.value>d.max_gpio) { o.style.color="red"; break; } else o.style.color=d.ro_gpio.some((e)=>e==parseInt(o.value,10))?"orange":"#fff";
|
||||
}
|
||||
} else {
|
||||
switch (o.name) {
|
||||
case "SDA": break;
|
||||
case "SCL": break;
|
||||
case "MOSI": break;
|
||||
case "SCLK": break;
|
||||
case "MISO": break;
|
||||
default: return;
|
||||
}
|
||||
for (var i=0; i<pins.length; i++) {
|
||||
//if (k==pinO[i]) continue; // same owner
|
||||
if (o.value==pins[i] && pinO[i]==="if") { o.style.color="tomato"; break; }
|
||||
if (o.value==pins[i] || o.value<-1 || o.value>d.max_gpio) { o.style.color="red"; break; } else o.style.color=d.ro_gpio.some((e)=>e==parseInt(o.value,10))?"orange":"#fff";
|
||||
}
|
||||
}
|
||||
}
|
||||
function getPins(o) {
|
||||
if (isO(o)) {
|
||||
for (const [k,v] of Object.entries(o)) {
|
||||
if (isO(v)) {
|
||||
owner = k;
|
||||
getPins(v);
|
||||
continue;
|
||||
}
|
||||
if (k.replace("[]","").substr(-3)=="pin") {
|
||||
if (Array.isArray(v)) {
|
||||
for (var i=0; i<v.length; i++) if (v[i]>=0) { pins.push(v[i]); pinO.push(owner); }
|
||||
} else {
|
||||
if (v>=0) { pins.push(v); pinO.push(owner); }
|
||||
}
|
||||
} else if (Array.isArray(v)) {
|
||||
for (var i=0; i<v.length; i++) getPins(v[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function addField(k,f,o,a=false) { //key, field, (sub)object, isArray
|
||||
if (isO(o)) {
|
||||
urows += '<hr style="width:260px">';
|
||||
for (const [s,v] of Object.entries(o)) {
|
||||
// possibility to nest objects (only 1 level)
|
||||
if (f!=='unknown' && !k.includes(":")) addField(k+":"+f,s,v);
|
||||
else addField(k,s,v);
|
||||
}
|
||||
} else if (Array.isArray(o)) {
|
||||
for (var j=0; j<o.length; j++) {
|
||||
addField(k,f,o[j],true);
|
||||
}
|
||||
} else {
|
||||
var c, t = typeof o;
|
||||
switch (t) {
|
||||
case "boolean":
|
||||
t = "checkbox"; c = 'value="true"' + (o ? ' checked' : '');
|
||||
break;
|
||||
case "number":
|
||||
c = `value="${o}"`;
|
||||
if (f.substr(-3)==="pin") {
|
||||
c += ` max="${d.max_gpio}" min="-1" class="s"`;
|
||||
t = "int";
|
||||
} else {
|
||||
c += ' step="any" class="xxl"';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
t = "text"; c = `value="${o}" style="width:250px;"`;
|
||||
break;
|
||||
}
|
||||
if (k.includes(":")) urows += k.substr(k.indexOf(":")+1);
|
||||
urows += ` ${f}: `;
|
||||
// https://stackoverflow.com/questions/11657123/posting-both-checked-and-unchecked-checkboxes
|
||||
if (t=="checkbox") urows += `<input type="hidden" name="${k}:${f}${a?"[]":""}" value="false">`;
|
||||
else if (!a) urows += `<input type="hidden" name="${k}:${f}${a?"[]":""}" value="${t}">`;
|
||||
urows += `<input type="${t==="int"?"number":t}" name="${k}:${f}${a?"[]":""}" ${c} oninput="check(this,'${k.substr(k.indexOf(":")+1)}')"><br>`;
|
||||
}
|
||||
}
|
||||
// https://stackoverflow.com/questions/39729741/javascript-change-input-text-to-select-option
|
||||
function addDropdown(um,fld) {
|
||||
let sel = d.createElement('select');
|
||||
let arr = d.getElementsByName(um+":"+fld);
|
||||
let inp = arr[1]; // assume 1st field to be hidden (type)
|
||||
if (inp && inp.tagName === "INPUT" && (inp.type === "text" || inp.type === "number")) { // may also use nodeName
|
||||
let v = inp.value;
|
||||
let n = inp.name;
|
||||
// copy the existing input element's attributes to the new select element
|
||||
for (var i = 0; i < inp.attributes.length; ++ i) {
|
||||
var att = inp.attributes[i];
|
||||
// type and value don't apply, so skip them
|
||||
// ** you might also want to skip style, or others -- modify as needed **
|
||||
if (att.name != 'type' && att.name != 'value' && att.name != 'class' && att.name != 'style') {
|
||||
sel.setAttribute(att.name, att.value);
|
||||
}
|
||||
}
|
||||
sel.setAttribute("data-val", v);
|
||||
// finally, replace the old input element with the new select element
|
||||
inp.parentElement.replaceChild(sel, inp);
|
||||
return sel;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function addOption(sel,txt,val) {
|
||||
if (sel===null) return; // select object missing
|
||||
let opt = d.createElement("option");
|
||||
opt.value = val;
|
||||
opt.text = txt;
|
||||
sel.appendChild(opt);
|
||||
for (let i=0; i<sel.childNodes.length; i++) {
|
||||
let c = sel.childNodes[i];
|
||||
if (c.value == sel.dataset.val) sel.selectedIndex = i;
|
||||
}
|
||||
}
|
||||
// https://stackoverflow.com/questions/26440494/insert-text-after-this-input-element-with-javascript
|
||||
function addInfo(name,el,txt) {
|
||||
let obj = d.getElementsByName(name);
|
||||
if (!obj.length) return;
|
||||
if (typeof el === "string" && obj[0]) obj[0].placeholder = el;
|
||||
else if (obj[el]) obj[el].insertAdjacentHTML('afterend', ' '+txt);
|
||||
}
|
||||
// load settings and insert values into DOM
|
||||
function ldS() {
|
||||
var url = (loc?`http://${locip}`:'') + '/cfg.json';
|
||||
fetch(url, {
|
||||
method: 'get'
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) gId('lserr').style.display = "inline";
|
||||
return res.json();
|
||||
})
|
||||
.then(json => {
|
||||
umCfg = json.um;
|
||||
getPins(json);
|
||||
urows="";
|
||||
if (isO(umCfg)) {
|
||||
for (const [k,o] of Object.entries(umCfg)) {
|
||||
urows += `<hr><h3>${k}</h3>`;
|
||||
addField(k,'unknown',o);
|
||||
}
|
||||
}
|
||||
if (urows==="") urows = "Usermods configuration not found.<br>Press <i>Save</i> to initialize defaults.";
|
||||
gId("um").innerHTML = urows;
|
||||
var url = (loc?`http://${locip}`:'') + '/settings/s.js?p=8';
|
||||
loadJS(url, false); // If we set async false, file is loaded and executed, then next statement is processed
|
||||
})
|
||||
.catch((error)=>{
|
||||
gId('lserr').style.display = "inline";
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
function svS(e) {
|
||||
e.preventDefault();
|
||||
if (d.Sf.checkValidity()) d.Sf.submit(); //https://stackoverflow.com/q/37323914
|
||||
}
|
||||
</script>
|
||||
<style>@import url("style.css");</style>
|
||||
</head>
|
||||
|
||||
<body onload="S()">
|
||||
@@ -222,17 +232,17 @@
|
||||
<span id="lserr" style="color:red; display:none">⚠ Could not load configuration.</span><hr>
|
||||
</div>
|
||||
<h2>Usermod Setup</h2>
|
||||
Global I<sup>2</sup>C GPIOs (HW)<br>
|
||||
<i style="color: orange;">(only changable on ESP32, change requires reboot!)</i><br>
|
||||
SDA:<input type="number" min="-1" max="33" name="SDA" onchange="check(this,'if')" class="s" placeholder="SDA">
|
||||
SCL:<input type="number" min="-1" max="33" name="SCL" onchange="check(this,'if')" class="s" placeholder="SCL">
|
||||
<hr style="width:260px">
|
||||
Global SPI GPIOs (HW)<br>
|
||||
<i style="color: orange;">(only changable on ESP32, change requires reboot!)</i><br>
|
||||
MOSI:<input type="number" min="-1" max="33" name="MOSI" onchange="check(this,'if')" class="s" placeholder="MOSI">
|
||||
MISO:<input type="number" min="-1" max="33" name="MISO" onchange="check(this,'if')" class="s" placeholder="MISO">
|
||||
SCLK:<input type="number" min="-1" max="33" name="SCLK" onchange="check(this,'if')" class="s" placeholder="SCLK">
|
||||
<div id="um">Loading settings...</div>
|
||||
Global I<sup>2</sup>C GPIOs (HW)<br>
|
||||
<i style="color: orange;">(only changable on ESP32, change requires reboot!)</i><br>
|
||||
SDA:<input type="number" min="-1" max="48" name="SDA" onchange="check(this,'if')" class="s" placeholder="SDA">
|
||||
SCL:<input type="number" min="-1" max="48" name="SCL" onchange="check(this,'if')" class="s" placeholder="SCL">
|
||||
<hr style="width:260px">
|
||||
Global SPI GPIOs (HW)<br>
|
||||
<i style="color: orange;">(only changable on ESP32, change requires reboot!)</i><br>
|
||||
MOSI:<input type="number" min="-1" max="48" name="MOSI" onchange="check(this,'if')" class="s" placeholder="MOSI">
|
||||
MISO:<input type="number" min="-1" max="48" name="MISO" onchange="check(this,'if')" class="s" placeholder="MISO">
|
||||
SCLK:<input type="number" min="-1" max="48" name="SCLK" onchange="check(this,'if')" class="s" placeholder="SCLK">
|
||||
<div id="um">Loading settings...</div>
|
||||
<hr><button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
|
||||
261
wled00/e131.cpp
261
wled00/e131.cpp
@@ -60,6 +60,10 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
|
||||
if (protocol == P_ARTNET)
|
||||
{
|
||||
if (p->art_opcode == ARTNET_OPCODE_OPPOLL) {
|
||||
handleArtnetPollReply(clientIP);
|
||||
return;
|
||||
}
|
||||
uni = p->art_universe;
|
||||
dmxChannels = htons(p->art_length);
|
||||
e131_data = p->art_data;
|
||||
@@ -86,14 +90,14 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
#endif
|
||||
|
||||
// only listen for universes we're handling & allocated memory
|
||||
if (uni >= (e131Universe + E131_MAX_UNIVERSE_COUNT)) return;
|
||||
if (uni < e131Universe || uni >= (e131Universe + E131_MAX_UNIVERSE_COUNT)) return;
|
||||
|
||||
uint8_t previousUniverses = uni - e131Universe;
|
||||
|
||||
if (e131SkipOutOfSequence)
|
||||
if (seq < e131LastSequenceNumber[uni-e131Universe] && seq > 20 && e131LastSequenceNumber[uni-e131Universe] < 250){
|
||||
if (seq < e131LastSequenceNumber[previousUniverses] && seq > 20 && e131LastSequenceNumber[previousUniverses] < 250){
|
||||
DEBUG_PRINT("skipping E1.31 frame (last seq=");
|
||||
DEBUG_PRINT(e131LastSequenceNumber[uni-e131Universe]);
|
||||
DEBUG_PRINT(e131LastSequenceNumber[previousUniverses]);
|
||||
DEBUG_PRINT(", current seq=");
|
||||
DEBUG_PRINT(seq);
|
||||
DEBUG_PRINT(", universe=");
|
||||
@@ -101,15 +105,23 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
DEBUG_PRINTLN(")");
|
||||
return;
|
||||
}
|
||||
e131LastSequenceNumber[uni-e131Universe] = seq;
|
||||
e131LastSequenceNumber[previousUniverses] = seq;
|
||||
|
||||
// update status info
|
||||
realtimeIP = clientIP;
|
||||
byte wChannel = 0;
|
||||
uint16_t totalLen = strip.getLengthTotal();
|
||||
uint16_t availDMXLen = dmxChannels - DMXAddress + 1;
|
||||
uint16_t availDMXLen = 0;
|
||||
uint16_t dataOffset = DMXAddress;
|
||||
|
||||
// For legacy DMX start address 0 the available DMX length offset is 0
|
||||
const uint16_t dmxLenOffset = (DMXAddress == 0) ? 0 : 1;
|
||||
|
||||
// Check if DMX start address fits in available channels
|
||||
if (dmxChannels >= DMXAddress) {
|
||||
availDMXLen = (dmxChannels - DMXAddress) + dmxLenOffset;
|
||||
}
|
||||
|
||||
// DMX data in Art-Net packet starts at index 0, for E1.31 at index 1
|
||||
if (protocol == P_ARTNET && dataOffset > 0) {
|
||||
dataOffset--;
|
||||
@@ -123,8 +135,11 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
case DMX_MODE_SINGLE_RGB: // RGB only
|
||||
if (uni != e131Universe) return;
|
||||
if (availDMXLen < 3) return;
|
||||
|
||||
realtimeLock(realtimeTimeoutMs, mde);
|
||||
|
||||
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
|
||||
|
||||
wChannel = (availDMXLen > 3) ? e131_data[dataOffset+3] : 0;
|
||||
for (uint16_t i = 0; i < totalLen; i++)
|
||||
setRealtimePixel(i, e131_data[dataOffset+0], e131_data[dataOffset+1], e131_data[dataOffset+2], wChannel);
|
||||
@@ -133,14 +148,16 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
case DMX_MODE_SINGLE_DRGB: // Dimmer + RGB
|
||||
if (uni != e131Universe) return;
|
||||
if (availDMXLen < 4) return;
|
||||
|
||||
realtimeLock(realtimeTimeoutMs, mde);
|
||||
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
|
||||
wChannel = (availDMXLen > 4) ? e131_data[dataOffset+4] : 0;
|
||||
if (DMXOldDimmer != e131_data[dataOffset+0]) {
|
||||
DMXOldDimmer = e131_data[dataOffset+0];
|
||||
|
||||
if (bri != e131_data[dataOffset+0]) {
|
||||
bri = e131_data[dataOffset+0];
|
||||
strip.setBrightness(bri, true);
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < totalLen; i++)
|
||||
setRealtimePixel(i, e131_data[dataOffset+1], e131_data[dataOffset+2], e131_data[dataOffset+3], wChannel);
|
||||
break;
|
||||
@@ -152,12 +169,12 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
applyPreset(e131_data[dataOffset+0], CALL_MODE_NOTIFICATION);
|
||||
return;
|
||||
}
|
||||
if (DMXOldDimmer != e131_data[dataOffset+0]) {
|
||||
DMXOldDimmer = e131_data[dataOffset+0];
|
||||
|
||||
if (bri != e131_data[dataOffset+0]) {
|
||||
bri = e131_data[dataOffset+0];
|
||||
}
|
||||
if (e131_data[dataOffset+1] < strip.getModeCount())
|
||||
effectCurrent = e131_data[dataOffset+ 1];
|
||||
effectCurrent = e131_data[dataOffset+ 1];
|
||||
effectSpeed = e131_data[dataOffset+ 2]; // flickers
|
||||
effectIntensity = e131_data[dataOffset+ 3];
|
||||
effectPalette = e131_data[dataOffset+ 4];
|
||||
@@ -181,19 +198,19 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
case DMX_MODE_MULTIPLE_RGB:
|
||||
case DMX_MODE_MULTIPLE_RGBW:
|
||||
{
|
||||
realtimeLock(realtimeTimeoutMs, mde);
|
||||
bool is4Chan = (DMXMode == DMX_MODE_MULTIPLE_RGBW);
|
||||
const uint16_t dmxChannelsPerLed = is4Chan ? 4 : 3;
|
||||
const uint16_t ledsPerUniverse = is4Chan ? MAX_4_CH_LEDS_PER_UNIVERSE : MAX_3_CH_LEDS_PER_UNIVERSE;
|
||||
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
|
||||
uint8_t stripBrightness = bri;
|
||||
uint16_t previousLeds, dmxOffset, ledsTotal;
|
||||
|
||||
if (previousUniverses == 0) {
|
||||
if (availDMXLen < 1) return;
|
||||
dmxOffset = dataOffset;
|
||||
previousLeds = 0;
|
||||
// First DMX address is dimmer in DMX_MODE_MULTIPLE_DRGB mode.
|
||||
if (DMXMode == DMX_MODE_MULTIPLE_DRGB) {
|
||||
strip.setBrightness(e131_data[dmxOffset++], true);
|
||||
stripBrightness = e131_data[dmxOffset++];
|
||||
ledsTotal = (availDMXLen - 1) / dmxChannelsPerLed;
|
||||
} else {
|
||||
ledsTotal = availDMXLen / dmxChannelsPerLed;
|
||||
@@ -201,11 +218,31 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
} else {
|
||||
// All subsequent universes start at the first channel.
|
||||
dmxOffset = (protocol == P_ARTNET) ? 0 : 1;
|
||||
uint16_t dimmerOffset = (DMXMode == DMX_MODE_MULTIPLE_DRGB) ? 1 : 0;
|
||||
uint16_t ledsInFirstUniverse = ((MAX_CHANNELS_PER_UNIVERSE - DMXAddress + 1) - dimmerOffset) / dmxChannelsPerLed;
|
||||
const uint16_t dimmerOffset = (DMXMode == DMX_MODE_MULTIPLE_DRGB) ? 1 : 0;
|
||||
uint16_t ledsInFirstUniverse = (((MAX_CHANNELS_PER_UNIVERSE - DMXAddress) + dmxLenOffset) - dimmerOffset) / dmxChannelsPerLed;
|
||||
previousLeds = ledsInFirstUniverse + (previousUniverses - 1) * ledsPerUniverse;
|
||||
ledsTotal = previousLeds + (dmxChannels / dmxChannelsPerLed);
|
||||
}
|
||||
|
||||
// All LEDs already have values
|
||||
if (previousLeds >= totalLen) {
|
||||
return;
|
||||
}
|
||||
|
||||
realtimeLock(realtimeTimeoutMs, mde);
|
||||
if (realtimeOverride && !(realtimeMode && useMainSegmentOnly)) return;
|
||||
|
||||
if (ledsTotal > totalLen) {
|
||||
ledsTotal = totalLen;
|
||||
}
|
||||
|
||||
if (DMXMode == DMX_MODE_MULTIPLE_DRGB && previousUniverses == 0) {
|
||||
if (bri != stripBrightness) {
|
||||
bri = stripBrightness;
|
||||
strip.setBrightness(bri, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is4Chan) {
|
||||
for (uint16_t i = previousLeds; i < ledsTotal; i++) {
|
||||
setRealtimePixel(i, e131_data[dmxOffset], e131_data[dmxOffset+1], e131_data[dmxOffset+2], 0);
|
||||
@@ -227,3 +264,197 @@ void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol){
|
||||
|
||||
e131NewData = true;
|
||||
}
|
||||
|
||||
void handleArtnetPollReply(IPAddress ipAddress) {
|
||||
ArtPollReply artnetPollReply;
|
||||
prepareArtnetPollReply(&artnetPollReply);
|
||||
|
||||
uint16_t startUniverse = e131Universe;
|
||||
uint16_t endUniverse = e131Universe;
|
||||
|
||||
switch (DMXMode) {
|
||||
case DMX_MODE_DISABLED:
|
||||
return; // nothing to do
|
||||
break;
|
||||
|
||||
case DMX_MODE_SINGLE_RGB:
|
||||
case DMX_MODE_SINGLE_DRGB:
|
||||
case DMX_MODE_EFFECT:
|
||||
break; // 1 universe is enough
|
||||
|
||||
case DMX_MODE_MULTIPLE_DRGB:
|
||||
case DMX_MODE_MULTIPLE_RGB:
|
||||
case DMX_MODE_MULTIPLE_RGBW:
|
||||
{
|
||||
bool is4Chan = (DMXMode == DMX_MODE_MULTIPLE_RGBW);
|
||||
const uint16_t dmxChannelsPerLed = is4Chan ? 4 : 3;
|
||||
const uint16_t dimmerOffset = (DMXMode == DMX_MODE_MULTIPLE_DRGB) ? 1 : 0;
|
||||
const uint16_t dmxLenOffset = (DMXAddress == 0) ? 0 : 1; // For legacy DMX start address 0
|
||||
const uint16_t ledsInFirstUniverse = (((MAX_CHANNELS_PER_UNIVERSE - DMXAddress) + dmxLenOffset) - dimmerOffset) / dmxChannelsPerLed;
|
||||
const uint16_t totalLen = strip.getLengthTotal();
|
||||
|
||||
if (totalLen > ledsInFirstUniverse) {
|
||||
const uint16_t ledsPerUniverse = is4Chan ? MAX_4_CH_LEDS_PER_UNIVERSE : MAX_3_CH_LEDS_PER_UNIVERSE;
|
||||
const uint16_t remainLED = totalLen - ledsInFirstUniverse;
|
||||
|
||||
endUniverse += (remainLED / ledsPerUniverse);
|
||||
|
||||
if ((remainLED % ledsPerUniverse) > 0) {
|
||||
endUniverse++;
|
||||
}
|
||||
|
||||
if ((endUniverse - startUniverse) > E131_MAX_UNIVERSE_COUNT) {
|
||||
endUniverse = startUniverse + E131_MAX_UNIVERSE_COUNT - 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DEBUG_PRINTLN(F("unknown E1.31 DMX mode"));
|
||||
return; // nothing to do
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint16_t i = startUniverse; i <= endUniverse; ++i) {
|
||||
sendArtnetPollReply(&artnetPollReply, ipAddress, i);
|
||||
}
|
||||
}
|
||||
|
||||
void prepareArtnetPollReply(ArtPollReply *reply) {
|
||||
// Art-Net
|
||||
reply->reply_id[0] = 0x41;
|
||||
reply->reply_id[1] = 0x72;
|
||||
reply->reply_id[2] = 0x74;
|
||||
reply->reply_id[3] = 0x2d;
|
||||
reply->reply_id[4] = 0x4e;
|
||||
reply->reply_id[5] = 0x65;
|
||||
reply->reply_id[6] = 0x74;
|
||||
reply->reply_id[7] = 0x00;
|
||||
|
||||
reply->reply_opcode = ARTNET_OPCODE_OPPOLLREPLY;
|
||||
|
||||
IPAddress localIP = Network.localIP();
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
reply->reply_ip[i] = localIP[i];
|
||||
}
|
||||
|
||||
reply->reply_port = ARTNET_DEFAULT_PORT;
|
||||
|
||||
char * numberEnd = versionString;
|
||||
reply->reply_version_h = (uint8_t)strtol(numberEnd, &numberEnd, 10);
|
||||
numberEnd++;
|
||||
reply->reply_version_l = (uint8_t)strtol(numberEnd, &numberEnd, 10);
|
||||
|
||||
// Switch values depend on universe, set before sending
|
||||
reply->reply_net_sw = 0x00;
|
||||
reply->reply_sub_sw = 0x00;
|
||||
|
||||
reply->reply_oem_h = 0x00; // TODO add assigned oem code
|
||||
reply->reply_oem_l = 0x00;
|
||||
|
||||
reply->reply_ubea_ver = 0x00;
|
||||
|
||||
// Indicators in Normal Mode
|
||||
// All or part of Port-Address programmed by network or Web browser
|
||||
reply->reply_status_1 = 0xE0;
|
||||
|
||||
reply->reply_esta_man = 0x0000;
|
||||
|
||||
strlcpy((char *)(reply->reply_short_name), serverDescription, 18);
|
||||
strlcpy((char *)(reply->reply_long_name), serverDescription, 64);
|
||||
|
||||
reply->reply_node_report[0] = '\0';
|
||||
|
||||
reply->reply_num_ports_h = 0x00;
|
||||
reply->reply_num_ports_l = 0x01; // One output port
|
||||
|
||||
reply->reply_port_types[0] = 0x80; // Output DMX data
|
||||
reply->reply_port_types[1] = 0x00;
|
||||
reply->reply_port_types[2] = 0x00;
|
||||
reply->reply_port_types[3] = 0x00;
|
||||
|
||||
// No inputs
|
||||
reply->reply_good_input[0] = 0x00;
|
||||
reply->reply_good_input[1] = 0x00;
|
||||
reply->reply_good_input[2] = 0x00;
|
||||
reply->reply_good_input[3] = 0x00;
|
||||
|
||||
// One output
|
||||
reply->reply_good_output_a[0] = 0x80; // Data is being transmitted
|
||||
reply->reply_good_output_a[1] = 0x00;
|
||||
reply->reply_good_output_a[2] = 0x00;
|
||||
reply->reply_good_output_a[3] = 0x00;
|
||||
|
||||
// Values depend on universe, set before sending
|
||||
reply->reply_sw_in[0] = 0x00;
|
||||
reply->reply_sw_in[1] = 0x00;
|
||||
reply->reply_sw_in[2] = 0x00;
|
||||
reply->reply_sw_in[3] = 0x00;
|
||||
|
||||
// Values depend on universe, set before sending
|
||||
reply->reply_sw_out[0] = 0x00;
|
||||
reply->reply_sw_out[1] = 0x00;
|
||||
reply->reply_sw_out[2] = 0x00;
|
||||
reply->reply_sw_out[3] = 0x00;
|
||||
|
||||
reply->reply_sw_video = 0x00;
|
||||
reply->reply_sw_macro = 0x00;
|
||||
reply->reply_sw_remote = 0x00;
|
||||
|
||||
reply->reply_spare[0] = 0x00;
|
||||
reply->reply_spare[1] = 0x00;
|
||||
reply->reply_spare[2] = 0x00;
|
||||
|
||||
// A DMX to / from Art-Net device
|
||||
reply->reply_style = 0x00;
|
||||
|
||||
Network.localMAC(reply->reply_mac);
|
||||
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
reply->reply_bind_ip[i] = localIP[i];
|
||||
}
|
||||
|
||||
reply->reply_bind_index = 1;
|
||||
|
||||
// Product supports web browser configuration
|
||||
// Node’s IP is DHCP or manually configured
|
||||
// Node is DHCP capable
|
||||
// Node supports 15 bit Port-Address (Art-Net 3 or 4)
|
||||
// Node is able to switch between ArtNet and sACN
|
||||
reply->reply_status_2 = (staticIP[0] == 0) ? 0x1F : 0x1D;
|
||||
|
||||
// RDM is disabled
|
||||
// Output style is continuous
|
||||
reply->reply_good_output_b[0] = 0xC0;
|
||||
reply->reply_good_output_b[1] = 0xC0;
|
||||
reply->reply_good_output_b[2] = 0xC0;
|
||||
reply->reply_good_output_b[3] = 0xC0;
|
||||
|
||||
// Fail-over state: Hold last state
|
||||
// Node does not support fail-over
|
||||
reply->reply_status_3 = 0x00;
|
||||
|
||||
for (uint8_t i = 0; i < 21; i++) {
|
||||
reply->reply_filler[i] = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
void sendArtnetPollReply(ArtPollReply *reply, IPAddress ipAddress, uint16_t portAddress) {
|
||||
reply->reply_net_sw = (uint8_t)((portAddress >> 8) & 0x007F);
|
||||
reply->reply_sub_sw = (uint8_t)((portAddress >> 4) & 0x000F);
|
||||
reply->reply_sw_out[0] = (uint8_t)(portAddress & 0x000F);
|
||||
|
||||
sprintf((char *)reply->reply_node_report, "#0001 [%04u] OK - WLED v" TOSTRING(WLED_VERSION), pollReplyCount);
|
||||
|
||||
if (pollReplyCount < 9999) {
|
||||
pollReplyCount++;
|
||||
} else {
|
||||
pollReplyCount = 0;
|
||||
}
|
||||
|
||||
notifierUdp.beginPacket(ipAddress, ARTNET_DEFAULT_PORT);
|
||||
notifierUdp.write(reply->raw, sizeof(ArtPollReply));
|
||||
notifierUdp.endPacket();
|
||||
|
||||
reply->reply_bind_index++;
|
||||
}
|
||||
@@ -82,6 +82,9 @@ void handleDMX();
|
||||
|
||||
//e131.cpp
|
||||
void handleE131Packet(e131_packet_t* p, IPAddress clientIP, byte protocol);
|
||||
void handleArtnetPollReply(IPAddress ipAddress);
|
||||
void prepareArtnetPollReply(ArtPollReply* reply);
|
||||
void sendArtnetPollReply(ArtPollReply* reply, IPAddress ipAddress, uint16_t portAddress);
|
||||
|
||||
//file.cpp
|
||||
bool handleFileRead(AsyncWebServerRequest*, String path);
|
||||
@@ -132,7 +135,7 @@ void handleIR();
|
||||
void deserializeSegment(JsonObject elem, byte it, byte presetId = 0);
|
||||
bool deserializeState(JsonObject root, byte callMode = CALL_MODE_DIRECT_CHANGE, byte presetId = 0);
|
||||
void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true);
|
||||
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true);
|
||||
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true, bool selectedSegmentsOnly = false);
|
||||
void serializeInfo(JsonObject root);
|
||||
void serializeModeNames(JsonArray arr, const char *qstring);
|
||||
void serializeModeData(JsonObject root);
|
||||
@@ -189,10 +192,11 @@ void shufflePlaylist();
|
||||
void unloadPlaylist();
|
||||
int16_t loadPlaylist(JsonObject playlistObject, byte presetId = 0);
|
||||
void handlePlaylist();
|
||||
void serializePlaylist(JsonObject obj);
|
||||
|
||||
//presets.cpp
|
||||
void handlePresets(bool force = false);
|
||||
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE, bool fromJson = false);
|
||||
void handlePresets();
|
||||
bool applyPreset(byte index, byte callMode = CALL_MODE_DIRECT_CHANGE);
|
||||
inline bool applyTemporaryPreset() {return applyPreset(255);};
|
||||
void savePreset(byte index, const char* pname = nullptr, JsonObject saveobj = JsonObject());
|
||||
inline void saveTemporaryPreset() {savePreset(255);};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
3755
wled00/html_ui.h
3755
wled00/html_ui.h
File diff suppressed because it is too large
Load Diff
@@ -728,7 +728,7 @@ void handleIR()
|
||||
{
|
||||
if (results.value != 0) // only print results if anything is received ( != 0 )
|
||||
{
|
||||
if (!pinManager.isPinAllocated(1) || pinManager.getPinOwner(1) == PinOwner::DebugOut) //GPIO 1 - Serial TX pin
|
||||
if (!pinManager.isPinAllocated(hardwareTX) || pinManager.getPinOwner(hardwareTX) == PinOwner::DebugOut) // Serial TX pin (GPIO 1 on ESP32 and ESP8266)
|
||||
Serial.printf_P(PSTR("IR recv: 0x%lX\n"), (unsigned long)results.value);
|
||||
}
|
||||
decodeIR(results.value);
|
||||
|
||||
@@ -174,47 +174,16 @@ void deserializeSegment(JsonObject elem, byte it, byte presetId)
|
||||
byte fx = seg.mode;
|
||||
if (getVal(elem["fx"], &fx, 0, strip.getModeCount())) { //load effect ('r' random, '~' inc/dec, 0-255 exact value)
|
||||
if (!presetId && currentPlaylist>=0) unloadPlaylist();
|
||||
if (fx != seg.mode) {
|
||||
seg.startTransition(strip.getTransition()); // set effect transitions
|
||||
//seg.markForReset();
|
||||
seg.mode = fx;
|
||||
}
|
||||
}
|
||||
|
||||
// load default values from effect string
|
||||
if (elem[F("fxdef")])
|
||||
{
|
||||
int16_t sOpt;
|
||||
sOpt = extractModeDefaults(fx, "sx"); if (sOpt >= 0) seg.speed = sOpt;
|
||||
sOpt = extractModeDefaults(fx, "ix"); if (sOpt >= 0) seg.intensity = sOpt;
|
||||
sOpt = extractModeDefaults(fx, "c1"); if (sOpt >= 0) seg.custom1 = sOpt;
|
||||
sOpt = extractModeDefaults(fx, "c2"); if (sOpt >= 0) seg.custom2 = sOpt;
|
||||
sOpt = extractModeDefaults(fx, "c3"); if (sOpt >= 0) seg.custom3 = sOpt;
|
||||
sOpt = extractModeDefaults(fx, "mp12"); if (sOpt >= 0) seg.map1D2D = constrain(sOpt, 0, 7);
|
||||
sOpt = extractModeDefaults(fx, "ssim"); if (sOpt >= 0) seg.soundSim = constrain(sOpt, 0, 7);
|
||||
sOpt = extractModeDefaults(fx, "rev"); if (sOpt >= 0) seg.reverse = (bool)sOpt;
|
||||
sOpt = extractModeDefaults(fx, "mi"); if (sOpt >= 0) seg.mirror = (bool)sOpt; // NOTE: setting this option is a risky business
|
||||
sOpt = extractModeDefaults(fx, "rY"); if (sOpt >= 0) seg.reverse_y = (bool)sOpt;
|
||||
sOpt = extractModeDefaults(fx, "mY"); if (sOpt >= 0) seg.mirror_y = (bool)sOpt; // NOTE: setting this option is a risky business
|
||||
sOpt = extractModeDefaults(fx, "pal");
|
||||
if (sOpt >= 0 && sOpt < strip.getPaletteCount() + strip.customPalettes.size()) {
|
||||
if (sOpt != seg.palette) {
|
||||
if (strip.paletteFade && !seg.transitional) seg.startTransition(strip.getTransition());
|
||||
seg.palette = sOpt;
|
||||
}
|
||||
}
|
||||
if (fx != seg.mode) seg.setMode(fx, elem[F("fxdef")]);
|
||||
}
|
||||
|
||||
//getVal also supports inc/decrementing and random
|
||||
getVal(elem["sx"], &seg.speed);
|
||||
getVal(elem["ix"], &seg.intensity);
|
||||
|
||||
uint8_t pal = seg.palette;
|
||||
if (getVal(elem["pal"], &pal, 1, strip.getPaletteCount())) {
|
||||
if (pal != seg.palette) {
|
||||
if (strip.paletteFade && !seg.transitional) seg.startTransition(strip.getTransition());
|
||||
seg.palette = pal;
|
||||
}
|
||||
}
|
||||
if (getVal(elem["pal"], &pal, 1, strip.getPaletteCount())) seg.setPalette(pal);
|
||||
|
||||
getVal(elem["c1"], &seg.custom1);
|
||||
getVal(elem["c2"], &seg.custom2);
|
||||
uint8_t cust3 = seg.custom3;
|
||||
@@ -340,7 +309,7 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
receiveNotifications = udpn["recv"] | receiveNotifications;
|
||||
if ((bool)udpn[F("nn")]) callMode = CALL_MODE_NO_NOTIFY; //send no notification just for this request
|
||||
|
||||
unsigned long timein = root[F("time")] | UINT32_MAX; //backup time source if NTP not synced
|
||||
unsigned long timein = root["time"] | UINT32_MAX; //backup time source if NTP not synced
|
||||
if (timein != UINT32_MAX) {
|
||||
setTimeFromAPI(timein);
|
||||
if (presetsModifiedTime == 0) presetsModifiedTime = timein;
|
||||
@@ -426,11 +395,15 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
|
||||
} else if (root["win"].isNull() && getVal(root["ps"], &ps, 0, 0) && ps > 0 && ps < 251 && ps != currentPreset) {
|
||||
// b) preset ID only or preset that does not change state (use embedded cycling limits if they exist in getVal())
|
||||
presetCycCurr = ps;
|
||||
root.remove(F("v")); // may be added in UI call
|
||||
root.remove(F("time")); // may be added in UI call
|
||||
presetId = ps;
|
||||
root.remove("v"); // may be added in UI call
|
||||
root.remove("time"); // may be added in UI call
|
||||
root.remove("ps");
|
||||
if (root.size() == 0) applyPreset(ps, callMode); // async load (only preset ID was specified)
|
||||
return stateResponse;
|
||||
if (root.size() == 0) {
|
||||
unloadPlaylist(); // we need to unload playlist
|
||||
applyPreset(ps, callMode); // async load (only preset ID was specified)
|
||||
return stateResponse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,7 +482,7 @@ void serializeSegment(JsonObject& root, Segment& seg, byte id, bool forPreset, b
|
||||
root["mp12"] = seg.map1D2D;
|
||||
}
|
||||
|
||||
void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segmentBounds)
|
||||
void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segmentBounds, bool selectedSegmentsOnly)
|
||||
{
|
||||
if (includeBri) {
|
||||
root["on"] = (bri > 0);
|
||||
@@ -545,11 +518,10 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
|
||||
|
||||
root[F("mainseg")] = strip.getMainSegmentId();
|
||||
|
||||
bool selectedSegmentsOnly = root[F("sc")] | false;
|
||||
JsonArray seg = root.createNestedArray("seg");
|
||||
for (size_t s = 0; s < strip.getMaxSegments(); s++) {
|
||||
if (s >= strip.getSegmentsNum()) {
|
||||
if (forPreset && segmentBounds) { //disable segments not part of preset
|
||||
if (forPreset && segmentBounds && !selectedSegmentsOnly) { //disable segments not part of preset
|
||||
JsonObject seg0 = seg.createNestedObject();
|
||||
seg0["stop"] = 0;
|
||||
continue;
|
||||
@@ -557,7 +529,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
|
||||
break;
|
||||
}
|
||||
Segment &sg = strip.getSegment(s);
|
||||
if (!forPreset && selectedSegmentsOnly && !sg.isSelected()) continue;
|
||||
if (forPreset && selectedSegmentsOnly && !sg.isSelected()) continue;
|
||||
if (sg.isActive()) {
|
||||
JsonObject seg0 = seg.createNestedObject();
|
||||
serializeSegment(seg0, sg, s, forPreset, segmentBounds);
|
||||
@@ -580,6 +552,8 @@ void serializeInfo(JsonObject root)
|
||||
leds["fps"] = strip.getFps();
|
||||
leds[F("maxpwr")] = (strip.currentMilliamps)? strip.ablMilliampsMax : 0;
|
||||
leds[F("maxseg")] = strip.getMaxSegments();
|
||||
//leds[F("actseg")] = strip.getActiveSegmentsNum();
|
||||
//leds[F("seglock")] = false; //might be used in the future to prevent modifications to segment config
|
||||
leds[F("cpal")] = strip.customPalettes.size(); //number of custom palettes
|
||||
|
||||
#ifndef WLED_DISABLE_2D
|
||||
@@ -675,7 +649,11 @@ void serializeInfo(JsonObject root)
|
||||
wifi_info[F("txPower")] = (int) WiFi.getTxPower();
|
||||
wifi_info[F("sleep")] = (bool) WiFi.getSleep();
|
||||
#endif
|
||||
root[F("arch")] = "esp32";
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
root[F("arch")] = "esp32";
|
||||
#else
|
||||
root[F("arch")] = ESP.getChipModel();
|
||||
#endif
|
||||
root[F("core")] = ESP.getSdkVersion();
|
||||
//root[F("maxalloc")] = ESP.getMaxAllocHeap();
|
||||
#ifdef WLED_DEBUG
|
||||
|
||||
@@ -93,12 +93,12 @@ void updateTimezone() {
|
||||
break;
|
||||
}
|
||||
case TZ_AUSTRALIA_EASTERN : {
|
||||
tcrDaylight = {Second, Sun, Oct, 2, 660}; //AEDT = UTC + 11 hours
|
||||
tcrDaylight = {First, Sun, Oct, 2, 660}; //AEDT = UTC + 11 hours
|
||||
tcrStandard = {First, Sun, Apr, 3, 600}; //AEST = UTC + 10 hours
|
||||
break;
|
||||
}
|
||||
case TZ_NEW_ZEALAND : {
|
||||
tcrDaylight = {Second, Sun, Sep, 2, 780}; //NZDT = UTC + 13 hours
|
||||
tcrDaylight = {Last, Sun, Sep, 2, 780}; //NZDT = UTC + 13 hours
|
||||
tcrStandard = {First, Sun, Apr, 3, 720}; //NZST = UTC + 12 hours
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -160,7 +160,23 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
|
||||
bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag)
|
||||
{
|
||||
// HW I2C & SPI pins have to be allocated using allocateMultiplePins variant since there is always SCL/SDA pair
|
||||
if (!isPinOk(gpio, output) || tag==PinOwner::HW_I2C || tag==PinOwner::HW_SPI) return false;
|
||||
if (!isPinOk(gpio, output) || tag==PinOwner::HW_I2C || tag==PinOwner::HW_SPI) {
|
||||
#ifdef WLED_DEBUG
|
||||
if (gpio < 255) { // 255 (-1) is the "not defined GPIO"
|
||||
if (!isPinOk(gpio, output)) {
|
||||
DEBUG_PRINT(F("PIN ALLOC: FAIL for owner "));
|
||||
DebugPrintOwnerTag(tag);
|
||||
DEBUG_PRINT(F(": GPIO ")); DEBUG_PRINT(gpio);
|
||||
if (output) DEBUG_PRINTLN(F(" cannot be used for i/o on this MCU."));
|
||||
else DEBUG_PRINTLN(F(" cannot be used as input on this MCU."));
|
||||
} else {
|
||||
DEBUG_PRINT(F("PIN ALLOC: FAIL: GPIO ")); DEBUG_PRINT(gpio);
|
||||
DEBUG_PRINTLN(F(" - HW I2C & SPI pins have to be allocated using allocateMultiplePins()"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
if (isPinAllocated(gpio)) {
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("PIN ALLOC: Pin "));
|
||||
@@ -179,7 +195,7 @@ bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag)
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINT(F("PIN ALLOC: Pin "));
|
||||
DEBUG_PRINT(gpio);
|
||||
DEBUG_PRINT(F(" allocated by "));
|
||||
DEBUG_PRINT(F(" successfully allocated by "));
|
||||
DebugPrintOwnerTag(tag);
|
||||
DEBUG_PRINTLN(F(""));
|
||||
#endif
|
||||
@@ -198,18 +214,53 @@ bool PinManagerClass::isPinAllocated(byte gpio, PinOwner tag)
|
||||
return bitRead(pinAlloc[by], bi);
|
||||
}
|
||||
|
||||
/* see https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/gpio.html
|
||||
* The ESP32-S3 chip features 45 physical GPIO pins (GPIO0 ~ GPIO21 and GPIO26 ~ GPIO48). Each pin can be used as a general-purpose I/O
|
||||
* Strapping pins: GPIO0, GPIO3, GPIO45 and GPIO46 are strapping pins. For more infomation, please refer to ESP32-S3 datasheet.
|
||||
* Serial TX = GPIO43, RX = GPIO44; LED BUILTIN is usually GPIO39
|
||||
* USB-JTAG: GPIO 19 and 20 are used by USB-JTAG by default. In order to use them as GPIOs, USB-JTAG will be disabled by the drivers.
|
||||
* SPI0/1: GPIO26-32 are usually used for SPI flash and PSRAM and not recommended for other uses.
|
||||
* When using Octal Flash or Octal PSRAM or both, GPIO33~37 are connected to SPIIO4 ~ SPIIO7 and SPIDQS. Therefore, on boards embedded with ESP32-S3R8 / ESP32-S3R8V chip, GPIO33~37 are also not recommended for other uses.
|
||||
*
|
||||
* see https://docs.espressif.com/projects/esp-idf/en/v4.4.2/esp32s3/api-reference/peripherals/adc.html
|
||||
* https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/adc_oneshot.html
|
||||
* ADC1: GPIO1 - GPIO10 (channel 0..9)
|
||||
* ADC2: GPIO11 - GPIO20 (channel 0..9)
|
||||
* adc_power_acquire(): Please do not use the interrupt of GPIO36 and GPIO39 when using ADC or Wi-Fi and Bluetooth with sleep mode enabled. As a workaround, call adc_power_acquire() in the APP.
|
||||
* Since the ADC2 module is also used by the Wi-Fi, reading operation of adc2_get_raw() may fail between esp_wifi_start() and esp_wifi_stop(). Use the return code to see whether the reading is successful.
|
||||
*/
|
||||
|
||||
// Check if supplied GPIO is ok to use
|
||||
bool PinManagerClass::isPinOk(byte gpio, bool output)
|
||||
{
|
||||
if (gpio < 6) return true;
|
||||
if (gpio < 12) return false; //SPI flash pins
|
||||
|
||||
#ifdef ESP8266
|
||||
if (gpio < 17) return true;
|
||||
#else //ESP32
|
||||
if (gpio < 34) return true;
|
||||
if (gpio < 40 && !output) return true; //34-39 input only
|
||||
#ifdef ESP32
|
||||
if (digitalPinIsValid(gpio)) {
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
// strapping pins: 2, 8, & 9
|
||||
if (gpio > 11 && gpio < 18) return false; // 11-17 SPI FLASH
|
||||
if (gpio > 17 && gpio < 20) return false; // 18-19 USB-JTAG
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// 00 to 18 are for general use. Be careful about straping pins GPIO0 and GPIO3 - these may be pulled-up or pulled-down on your board.
|
||||
if (gpio > 18 && gpio < 21) return false; // 19 + 20 = USB-JTAG. Not recommended for other uses.
|
||||
if (gpio > 21 && gpio < 33) return false; // 22 to 32: not connected + SPI FLASH
|
||||
//if (gpio > 32 && gpio < 38) return false; // 33 to 37: not available if using _octal_ SPI Flash or _octal_ PSRAM
|
||||
// 38 to 48 are for general use. Be careful about straping pins GPIO45 and GPIO46 - these may be pull-up or pulled-down on your board.
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
// strapping pins: 0, 45 & 46
|
||||
if (gpio > 21 && gpio < 33) return false; // 22 to 32: not connected + SPI FLASH
|
||||
// JTAG: GPIO39-42 are usually used for inline debugging
|
||||
// GPIO46 is input only and pulled down
|
||||
#else
|
||||
if (gpio > 5 && gpio < 12) return false; //SPI flash pins
|
||||
#endif
|
||||
|
||||
if (output) return digitalPinCanOutput(gpio);
|
||||
else return true;
|
||||
}
|
||||
#else
|
||||
if (gpio < 6) return true;
|
||||
if (gpio < 12) return false; //SPI flash pins
|
||||
if (gpio < 17) return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -219,11 +270,20 @@ PinOwner PinManagerClass::getPinOwner(byte gpio) {
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
#define MAX_LED_CHANNELS 6
|
||||
#else
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
#define MAX_LED_CHANNELS 8
|
||||
#else
|
||||
#define MAX_LED_CHANNELS 16
|
||||
#endif
|
||||
#endif
|
||||
byte PinManagerClass::allocateLedc(byte channels)
|
||||
{
|
||||
if (channels > 16 || channels == 0) return 255;
|
||||
if (channels > MAX_LED_CHANNELS || channels == 0) return 255;
|
||||
byte ca = 0;
|
||||
for (byte i = 0; i < 16; i++) {
|
||||
for (byte i = 0; i < MAX_LED_CHANNELS; i++) {
|
||||
byte by = i >> 3;
|
||||
byte bi = i - 8*by;
|
||||
if (bitRead(ledcAlloc[by], bi)) { //found occupied pin
|
||||
@@ -248,7 +308,7 @@ byte PinManagerClass::allocateLedc(byte channels)
|
||||
void PinManagerClass::deallocateLedc(byte pos, byte channels)
|
||||
{
|
||||
for (byte j = pos; j < pos + channels; j++) {
|
||||
if (j > 16) return;
|
||||
if (j > MAX_LED_CHANNELS) return;
|
||||
byte by = j >> 3;
|
||||
byte bi = j - 8*by;
|
||||
bitWrite(ledcAlloc[by], bi, false);
|
||||
|
||||
@@ -149,3 +149,19 @@ void handlePlaylist() {
|
||||
applyPreset(playlistEntries[playlistIndex].preset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void serializePlaylist(JsonObject sObj) {
|
||||
JsonObject playlist = sObj.createNestedObject(F("playlist"));
|
||||
JsonArray ps = playlist.createNestedArray("ps");
|
||||
JsonArray dur = playlist.createNestedArray("dur");
|
||||
JsonArray transition = playlist.createNestedArray(F("transition"));
|
||||
playlist[F("repeat")] = playlistRepeat;
|
||||
playlist["end"] = playlistEndPreset;
|
||||
playlist["r"] = playlistOptions & PL_OPTION_SHUFFLE;
|
||||
for (int i=0; i<playlistLen; i++) {
|
||||
ps.add(playlistEntries[i].preset);
|
||||
dur.add(playlistEntries[i].dur);
|
||||
transition.add(playlistEntries[i].tr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,73 +10,95 @@ static char *tmpRAMbuffer = nullptr;
|
||||
|
||||
static volatile byte presetToApply = 0;
|
||||
static volatile byte callModeToApply = 0;
|
||||
static volatile byte presetToSave = 0;
|
||||
static char quickLoad[3];
|
||||
static char saveName[33];
|
||||
static bool includeBri = true, segBounds = true, selectedOnly = false, playlistSave = false;;
|
||||
|
||||
bool applyPreset(byte index, byte callMode, bool fromJson)
|
||||
static const char *getName(bool persist = true) {
|
||||
return persist ? "/presets.json" : "/tmp.json";
|
||||
}
|
||||
|
||||
static void doSaveState() {
|
||||
bool persist = (presetToSave < 251);
|
||||
const char *filename = getName(persist);
|
||||
|
||||
if (!requestJSONBufferLock(10)) return; // will set fileDoc
|
||||
|
||||
JsonObject sObj = doc.to<JsonObject>();
|
||||
|
||||
DEBUG_PRINTLN(F("Serialize current state"));
|
||||
if (playlistSave) {
|
||||
serializePlaylist(sObj);
|
||||
if (includeBri) sObj["on"] = true;
|
||||
} else {
|
||||
serializeState(sObj, true, includeBri, segBounds, selectedOnly);
|
||||
}
|
||||
sObj["n"] = saveName;
|
||||
if (quickLoad[0]) sObj[F("ql")] = quickLoad;
|
||||
/*
|
||||
#ifdef WLED_DEBUG
|
||||
DEBUG_PRINTLN(F("Serialized preset"));
|
||||
serializeJson(doc,Serial);
|
||||
DEBUG_PRINTLN();
|
||||
#endif
|
||||
*/
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
if (!persist) {
|
||||
if (tmpRAMbuffer!=nullptr) free(tmpRAMbuffer);
|
||||
size_t len = measureJson(*fileDoc) + 1;
|
||||
DEBUG_PRINTLN(len);
|
||||
// if possible use SPI RAM on ESP32
|
||||
#ifdef WLED_USE_PSRAM
|
||||
if (psramFound())
|
||||
tmpRAMbuffer = (char*) ps_malloc(len);
|
||||
else
|
||||
#endif
|
||||
tmpRAMbuffer = (char*) malloc(len);
|
||||
if (tmpRAMbuffer!=nullptr) {
|
||||
serializeJson(*fileDoc, tmpRAMbuffer, len);
|
||||
} else {
|
||||
writeObjectToFileUsingId(filename, presetToSave, fileDoc);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
writeObjectToFileUsingId(filename, presetToSave, fileDoc);
|
||||
|
||||
if (persist) presetsModifiedTime = toki.second(); //unix time
|
||||
releaseJSONBufferLock();
|
||||
updateFSInfo();
|
||||
|
||||
// clean up
|
||||
presetToSave = 0;
|
||||
saveName[0] = '\0';
|
||||
quickLoad[0] = '\0';
|
||||
playlistSave = false;
|
||||
}
|
||||
|
||||
bool applyPreset(byte index, byte callMode)
|
||||
{
|
||||
DEBUG_PRINT(F("Request to apply preset: "));
|
||||
DEBUG_PRINTLN(index);
|
||||
presetToApply = index;
|
||||
callModeToApply = callMode;
|
||||
// the following is needed in case of HTTP JSON API call to return correct state to the caller
|
||||
// fromJson is true in case when deserializeState() was called with presetId==0
|
||||
if (fromJson) handlePresets(true); // force immediate processing
|
||||
return true;
|
||||
}
|
||||
|
||||
void handlePresets(bool force)
|
||||
void handlePresets()
|
||||
{
|
||||
if (presetToSave) {
|
||||
doSaveState();
|
||||
return;
|
||||
}
|
||||
|
||||
bool changePreset = false;
|
||||
uint8_t tmpPreset = presetToApply; // store temporary since deserializeState() may call applyPreset()
|
||||
uint8_t tmpMode = callModeToApply;
|
||||
|
||||
if (tmpPreset == 0 || (fileDoc && !force)) return; // JSON buffer already allocated and not force apply or no preset waiting
|
||||
if (tmpPreset == 0 || (fileDoc /*&& !force*/)) return; // JSON buffer already allocated and not force apply or no preset waiting
|
||||
|
||||
JsonObject fdo;
|
||||
const char *filename = tmpPreset < 255 ? "/presets.json" : "/tmp.json";
|
||||
|
||||
//crude way to determine if this was called by a network request
|
||||
uint8_t core = 1;
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
core = xPortGetCoreID();
|
||||
#endif
|
||||
//only allow use of fileDoc from the core responsible for network requests (AKA HTTP JSON API)
|
||||
//do not use active network request doc from preset called by main loop (playlist, schedule, ...)
|
||||
if (fileDoc && core && force && tmpPreset < 255) {
|
||||
DEBUG_PRINT(F("Force applying preset: "));
|
||||
DEBUG_PRINTLN(presetToApply);
|
||||
|
||||
presetToApply = 0; //clear request for preset
|
||||
callModeToApply = 0;
|
||||
|
||||
// this will overwrite doc with preset content but applyPreset() is the last in such case and content of doc is no longer needed
|
||||
errorFlag = readObjectFromFileUsingId(filename, tmpPreset, fileDoc) ? ERR_NONE : ERR_FS_PLOAD;
|
||||
|
||||
JsonObject fdo = fileDoc->as<JsonObject>();
|
||||
|
||||
//HTTP API commands
|
||||
const char* httpwin = fdo["win"];
|
||||
if (httpwin) {
|
||||
String apireq = "win"; // reduce flash string usage
|
||||
apireq += F("&IN&"); // internal call
|
||||
apireq += httpwin;
|
||||
handleSet(nullptr, apireq, false); // may call applyPreset() via PL=
|
||||
setValuesFromFirstSelectedSeg(); // fills legacy values
|
||||
changePreset = true;
|
||||
} else {
|
||||
if (!fdo["seg"].isNull()) unloadPlaylist(); // if preset contains "seg" we must unload playlist
|
||||
if (!fdo["seg"].isNull() || !fdo["on"].isNull() || !fdo["bri"].isNull() || !fdo["ps"].isNull() || !fdo[F("playlist")].isNull()) changePreset = true;
|
||||
fdo.remove("ps"); //remove load request for presets to prevent recursive crash
|
||||
|
||||
deserializeState(fdo, tmpMode, tmpPreset); // may call applyPreset() which will overwrite presetToApply
|
||||
}
|
||||
|
||||
if (!errorFlag && changePreset) presetCycCurr = currentPreset = tmpPreset;
|
||||
|
||||
colorUpdated(tmpMode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (force) return; // something went wrong with force option (most likely WS request), quit and wait for async load
|
||||
const char *filename = getName(tmpPreset < 255);
|
||||
|
||||
// allocate buffer
|
||||
if (!requestJSONBufferLock(9)) return; // will also assign fileDoc
|
||||
@@ -128,72 +150,53 @@ void handlePresets(bool force)
|
||||
}
|
||||
|
||||
//called from handleSet(PS=) [network callback (fileDoc==nullptr), IR (irrational), deserializeState, UDP] and deserializeState() [network callback (filedoc!=nullptr)]
|
||||
void savePreset(byte index, const char* pname, JsonObject saveobj)
|
||||
void savePreset(byte index, const char* pname, JsonObject sObj)
|
||||
{
|
||||
if (index == 0 || (index > 250 && index < 255)) return;
|
||||
char tmp[12];
|
||||
JsonObject sObj = saveobj;
|
||||
bool bufferAllocated = false;
|
||||
|
||||
bool persist = (index != 255);
|
||||
const char *filename = persist ? "/presets.json" : "/tmp.json";
|
||||
|
||||
if (!fileDoc) {
|
||||
// called from handleSet() HTTP API
|
||||
if (!requestJSONBufferLock(10)) return;
|
||||
sObj = fileDoc->to<JsonObject>();
|
||||
bufferAllocated = true;
|
||||
if (pname) strlcpy(saveName, pname, 33);
|
||||
else {
|
||||
if (sObj["n"].is<const char*>()) strlcpy(saveName, sObj["n"].as<const char*>(), 33);
|
||||
else sprintf_P(saveName, PSTR("Preset %d"), index);
|
||||
}
|
||||
if (sObj["n"].isNull() && pname == nullptr) {
|
||||
sprintf_P(tmp, PSTR("Preset %d"), index);
|
||||
sObj["n"] = tmp;
|
||||
} else if (pname) sObj["n"] = pname;
|
||||
|
||||
sObj.remove(F("psave"));
|
||||
sObj.remove(F("v"));
|
||||
DEBUG_PRINT(F("Saving preset (")); DEBUG_PRINT(index); DEBUG_PRINT(F(") ")); DEBUG_PRINTLN(saveName);
|
||||
|
||||
if (!sObj["o"]) {
|
||||
DEBUGFS_PRINTLN(F("Serialize current state"));
|
||||
if (sObj["ib"].isNull() && sObj["sb"].isNull()) serializeState(sObj, true);
|
||||
else serializeState(sObj, true, sObj["ib"], sObj["sb"]);
|
||||
if (persist) currentPreset = index;
|
||||
}
|
||||
sObj.remove("o");
|
||||
sObj.remove("ib");
|
||||
sObj.remove("sb");
|
||||
sObj.remove(F("sc"));
|
||||
presetToSave = index;
|
||||
playlistSave = false;
|
||||
if (sObj[F("ql")].is<const char*>()) strlcpy(quickLoad, sObj[F("ql")].as<const char*>(), 3); // only 2 chars for QL
|
||||
sObj.remove("v");
|
||||
sObj.remove("time");
|
||||
sObj.remove(F("error"));
|
||||
sObj.remove(F("time"));
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
if (index==255) {
|
||||
if (tmpRAMbuffer!=nullptr) free(tmpRAMbuffer);
|
||||
size_t len = measureJson(*fileDoc) + 1;
|
||||
DEBUG_PRINTLN(len);
|
||||
// if possible use SPI RAM on ESP32
|
||||
#ifdef WLED_USE_PSRAM
|
||||
if (psramFound())
|
||||
tmpRAMbuffer = (char*) ps_malloc(len);
|
||||
else
|
||||
#endif
|
||||
tmpRAMbuffer = (char*) malloc(len);
|
||||
if (tmpRAMbuffer!=nullptr) {
|
||||
serializeJson(*fileDoc, tmpRAMbuffer, len);
|
||||
sObj.remove(F("psave"));
|
||||
if (sObj["o"].isNull()) { // "o" marks a playlist or manually entered API
|
||||
includeBri = sObj["ib"].as<bool>() || index==255; // temporary preset needs brightness
|
||||
segBounds = sObj["sb"].as<bool>() || index==255; // temporary preset needs bounds
|
||||
selectedOnly = sObj[F("sc")].as<bool>();
|
||||
sObj.remove("ib");
|
||||
sObj.remove("sb");
|
||||
sObj.remove(F("sc"));
|
||||
} else {
|
||||
// this is a playlist or API
|
||||
sObj.remove("o");
|
||||
if (sObj[F("playlist")].isNull()) {
|
||||
presetToSave = 0; // we will save API immediately
|
||||
if (index < 251 && fileDoc) {
|
||||
if (sObj["n"].isNull()) sObj["n"] = saveName;
|
||||
writeObjectToFileUsingId(getName(index), index, fileDoc);
|
||||
presetsModifiedTime = toki.second(); //unix time
|
||||
updateFSInfo();
|
||||
}
|
||||
} else {
|
||||
writeObjectToFileUsingId(filename, index, fileDoc);
|
||||
// store playlist
|
||||
includeBri = true; // !sObj["on"].isNull();
|
||||
playlistSave = true;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
writeObjectToFileUsingId(filename, index, fileDoc);
|
||||
|
||||
if (persist) presetsModifiedTime = toki.second(); //unix time
|
||||
if (bufferAllocated) releaseJSONBufferLock();
|
||||
updateFSInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void deletePreset(byte index) {
|
||||
StaticJsonDocument<24> empty;
|
||||
writeObjectToFileUsingId("/presets.json", index, &empty);
|
||||
writeObjectToFileUsingId(getName(), index, &empty);
|
||||
presetsModifiedTime = toki.second(); //unix time
|
||||
updateFSInfo();
|
||||
}
|
||||
@@ -167,8 +167,12 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
int hw_btn_pin = request->arg(bt).toInt();
|
||||
if (pinManager.allocatePin(hw_btn_pin,false,PinOwner::Button)) {
|
||||
btnPin[i] = hw_btn_pin;
|
||||
pinMode(btnPin[i], INPUT_PULLUP);
|
||||
buttonType[i] = request->arg(be).toInt();
|
||||
#ifdef ESP32
|
||||
pinMode(btnPin[i], buttonType[i]==BTN_TYPE_PUSH_ACT_HIGH ? INPUT_PULLDOWN : INPUT_PULLUP);
|
||||
#else
|
||||
pinMode(btnPin[i], INPUT_PULLUP);
|
||||
#endif
|
||||
} else {
|
||||
btnPin[i] = -1;
|
||||
buttonType[i] = BTN_TYPE_NONE;
|
||||
@@ -478,8 +482,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
if (!requestJSONBufferLock(5)) return;
|
||||
|
||||
// global I2C & SPI pins
|
||||
int8_t hw_sda_pin = !request->arg(F("SDA")).length() ? -1 : max(-1,min(33,(int)request->arg(F("SDA")).toInt()));
|
||||
int8_t hw_scl_pin = !request->arg(F("SCL")).length() ? -1 : max(-1,min(33,(int)request->arg(F("SCL")).toInt()));
|
||||
int8_t hw_sda_pin = !request->arg(F("SDA")).length() ? -1 : (int)request->arg(F("SDA")).toInt();
|
||||
int8_t hw_scl_pin = !request->arg(F("SCL")).length() ? -1 : (int)request->arg(F("SCL")).toInt();
|
||||
#ifdef ESP8266
|
||||
// cannot change pins on ESP8266
|
||||
if (hw_sda_pin >= 0 && hw_sda_pin != HW_PIN_SDA) hw_sda_pin = HW_PIN_SDA;
|
||||
@@ -501,9 +505,9 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
i2c_sda = -1;
|
||||
i2c_scl = -1;
|
||||
}
|
||||
int8_t hw_mosi_pin = !request->arg(F("MOSI")).length() ? -1 : max(-1,min(33,(int)request->arg(F("MOSI")).toInt()));
|
||||
int8_t hw_miso_pin = !request->arg(F("MISO")).length() ? -1 : max(-1,min(33,(int)request->arg(F("MISO")).toInt()));
|
||||
int8_t hw_sclk_pin = !request->arg(F("SCLK")).length() ? -1 : max(-1,min(33,(int)request->arg(F("SCLK")).toInt()));
|
||||
int8_t hw_mosi_pin = !request->arg(F("MOSI")).length() ? -1 : (int)request->arg(F("MOSI")).toInt();
|
||||
int8_t hw_miso_pin = !request->arg(F("MISO")).length() ? -1 : (int)request->arg(F("MISO")).toInt();
|
||||
int8_t hw_sclk_pin = !request->arg(F("SCLK")).length() ? -1 : (int)request->arg(F("SCLK")).toInt();
|
||||
#ifdef ESP8266
|
||||
// cannot change pins on ESP8266
|
||||
if (hw_mosi_pin >= 0 && hw_mosi_pin != HW_PIN_DATASPI) hw_mosi_pin = HW_PIN_DATASPI;
|
||||
@@ -607,7 +611,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
}
|
||||
}
|
||||
usermods.readFromConfig(um); // force change of usermod parameters
|
||||
|
||||
DEBUG_PRINTLN(F("Done re-init usermods."));
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
|
||||
@@ -638,7 +642,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
#endif
|
||||
|
||||
lastEditTime = millis();
|
||||
if (subPage != 2 && !doReboot) serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init)
|
||||
if (subPage != 2 && !doReboot) doSerializeConfig = true; //serializeConfig(); //do not save if factory reset or LED settings (which are saved after LED re-init)
|
||||
if (subPage == 4) alexaInit();
|
||||
}
|
||||
|
||||
@@ -884,17 +888,10 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply)
|
||||
for (uint8_t i = 0; i < strip.getSegmentsNum(); i++) {
|
||||
Segment& seg = strip.getSegment(i);
|
||||
if (i != selectedSeg && (singleSegment || !seg.isActive() || !seg.isSelected())) continue; // skip non main segments if not applying to all
|
||||
if (fxModeChanged) {
|
||||
seg.startTransition(strip.getTransition());
|
||||
seg.mode = effectIn;
|
||||
// TODO: we should load defaults here as well
|
||||
}
|
||||
if (fxModeChanged) seg.setMode(effectIn, req.indexOf(F("FXD="))>0); // apply defaults if FXD= is specified
|
||||
if (speedChanged) seg.speed = speedIn;
|
||||
if (intensityChanged) seg.intensity = intensityIn;
|
||||
if (paletteChanged) {
|
||||
if (strip.paletteBlend) seg.startTransition(strip.getTransition());
|
||||
seg.palette = paletteIn;
|
||||
}
|
||||
if (paletteChanged) seg.setPalette(paletteIn);
|
||||
}
|
||||
|
||||
//set advanced overlay
|
||||
|
||||
@@ -110,8 +110,8 @@ void ESPAsyncE131::parsePacket(AsyncUDPPacket _packet) {
|
||||
if (protocol == P_ARTNET) {
|
||||
if (memcmp(sbuff->art_id, ESPAsyncE131::ART_ID, sizeof(sbuff->art_id)))
|
||||
error = true; //not "Art-Net"
|
||||
if (sbuff->art_opcode != ARTNET_OPCODE_OPDMX)
|
||||
error = true; //not a DMX packet
|
||||
if (sbuff->art_opcode != ARTNET_OPCODE_OPDMX && sbuff->art_opcode != ARTNET_OPCODE_OPPOLL)
|
||||
error = true; //not a DMX or poll packet
|
||||
} else { //E1.31 error handling
|
||||
if (htonl(sbuff->root_vector) != ESPAsyncE131::VECTOR_ROOT)
|
||||
error = true;
|
||||
|
||||
@@ -57,6 +57,8 @@ typedef struct ip_addr ip4_addr_t;
|
||||
#define DDP_TYPE_RGBW32 0x1A
|
||||
|
||||
#define ARTNET_OPCODE_OPDMX 0x5000
|
||||
#define ARTNET_OPCODE_OPPOLL 0x2000
|
||||
#define ARTNET_OPCODE_OPPOLLREPLY 0x2100
|
||||
|
||||
#define P_E131 0
|
||||
#define P_ARTNET 1
|
||||
@@ -154,6 +156,48 @@ typedef union {
|
||||
uint8_t raw[1458];
|
||||
} e131_packet_t;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t reply_id[8];
|
||||
uint16_t reply_opcode;
|
||||
uint8_t reply_ip[4];
|
||||
uint16_t reply_port;
|
||||
uint8_t reply_version_h;
|
||||
uint8_t reply_version_l;
|
||||
uint8_t reply_net_sw;
|
||||
uint8_t reply_sub_sw;
|
||||
uint8_t reply_oem_h;
|
||||
uint8_t reply_oem_l;
|
||||
uint8_t reply_ubea_ver;
|
||||
uint8_t reply_status_1;
|
||||
uint16_t reply_esta_man;
|
||||
uint8_t reply_short_name[18];
|
||||
uint8_t reply_long_name[64];
|
||||
uint8_t reply_node_report[64];
|
||||
uint8_t reply_num_ports_h;
|
||||
uint8_t reply_num_ports_l;
|
||||
uint8_t reply_port_types[4];
|
||||
uint8_t reply_good_input[4];
|
||||
uint8_t reply_good_output_a[4];
|
||||
uint8_t reply_sw_in[4];
|
||||
uint8_t reply_sw_out[4];
|
||||
uint8_t reply_sw_video;
|
||||
uint8_t reply_sw_macro;
|
||||
uint8_t reply_sw_remote;
|
||||
uint8_t reply_spare[3];
|
||||
uint8_t reply_style;
|
||||
uint8_t reply_mac[6];
|
||||
uint8_t reply_bind_ip[4];
|
||||
uint8_t reply_bind_index;
|
||||
uint8_t reply_status_2;
|
||||
uint8_t reply_good_output_b[4];
|
||||
uint8_t reply_status_3;
|
||||
uint8_t reply_filler[21];
|
||||
} __attribute__((packed));
|
||||
|
||||
uint8_t raw[239];
|
||||
} ArtPollReply;
|
||||
|
||||
// new packet callback
|
||||
typedef void (*e131_packet_callback_function) (e131_packet_t* p, IPAddress clientIP, byte protocol);
|
||||
|
||||
|
||||
@@ -43,6 +43,34 @@ IPAddress NetworkClass::gatewayIP()
|
||||
return INADDR_NONE;
|
||||
}
|
||||
|
||||
void NetworkClass::localMAC(uint8_t* MAC)
|
||||
{
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
// ETH.macAddress(MAC); // Does not work because of missing ETHClass:: in ETH.ccp
|
||||
|
||||
// Start work around
|
||||
String macString = ETH.macAddress();
|
||||
char macChar[18];
|
||||
char * octetEnd = macChar;
|
||||
|
||||
strlcpy(macChar, macString.c_str(), 18);
|
||||
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
MAC[i] = (uint8_t)strtol(octetEnd, &octetEnd, 16);
|
||||
octetEnd++;
|
||||
}
|
||||
// End work around
|
||||
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
if (MAC[i] != 0x00) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
WiFi.macAddress(MAC);
|
||||
return;
|
||||
}
|
||||
|
||||
bool NetworkClass::isConnected()
|
||||
{
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
IPAddress localIP();
|
||||
IPAddress subnetMask();
|
||||
IPAddress gatewayIP();
|
||||
void localMAC(uint8_t* MAC);
|
||||
bool isConnected();
|
||||
bool isEthernet();
|
||||
};
|
||||
|
||||
@@ -158,9 +158,9 @@ bool oappend(const char* txt)
|
||||
|
||||
void prepareHostname(char* hostname)
|
||||
{
|
||||
sprintf_P(hostname, "wled-%*s", 6, escapedMac.c_str() + 6);
|
||||
const char *pC = serverDescription;
|
||||
uint8_t pos = 5;
|
||||
|
||||
uint8_t pos = 5; // keep "wled-"
|
||||
while (*pC && pos < 24) { // while !null and not over length
|
||||
if (isalnum(*pC)) { // if the current char is alpha-numeric append it to the hostname
|
||||
hostname[pos] = *pC;
|
||||
@@ -169,18 +169,14 @@ void prepareHostname(char* hostname)
|
||||
hostname[pos] = '-';
|
||||
pos++;
|
||||
}
|
||||
// else do nothing - no leading hyphens and do not include hyphens for all other characters.
|
||||
pC++;
|
||||
}
|
||||
// if the hostname is left blank, use the mac address/default mdns name
|
||||
if (pos < 6) {
|
||||
sprintf(hostname + 5, "%*s", 6, escapedMac.c_str() + 6);
|
||||
} else { //last character must not be hyphen
|
||||
while (pos > 0 && hostname[pos -1] == '-') {
|
||||
hostname[pos -1] = 0;
|
||||
pos--;
|
||||
}
|
||||
}
|
||||
// else do nothing - no leading hyphens and do not include hyphens for all other characters.
|
||||
pC++;
|
||||
}
|
||||
//last character must not be hyphen
|
||||
if (pos > 5) {
|
||||
while (pos > 4 && hostname[pos -1] == '-') pos--;
|
||||
hostname[pos] = '\0'; // terminate string (leave at least "wled")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ void WLED::loop()
|
||||
|
||||
yield();
|
||||
|
||||
if (doSerializeConfig) serializeConfig();
|
||||
|
||||
if (doReboot && !doInitBusses) // if busses have to be inited & saved, wait until next iteration
|
||||
reset();
|
||||
|
||||
@@ -265,6 +267,9 @@ void WLED::setup()
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.setTimeout(50);
|
||||
#if defined(WLED_DEBUG) && (defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3))
|
||||
delay(2500); // allow CDC USB serial to initialise
|
||||
#endif
|
||||
DEBUG_PRINTLN();
|
||||
DEBUG_PRINT(F("---WLED "));
|
||||
DEBUG_PRINT(versionString);
|
||||
@@ -274,6 +279,31 @@ void WLED::setup()
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
DEBUG_PRINT(F("esp32 "));
|
||||
DEBUG_PRINTLN(ESP.getSdkVersion());
|
||||
#if defined(ESP_ARDUINO_VERSION)
|
||||
//DEBUG_PRINTF(F("arduino-esp32 0x%06x\n"), ESP_ARDUINO_VERSION);
|
||||
DEBUG_PRINTF("arduino-esp32 v%d.%d.%d\n", int(ESP_ARDUINO_VERSION_MAJOR), int(ESP_ARDUINO_VERSION_MINOR), int(ESP_ARDUINO_VERSION_PATCH)); // availeable since v2.0.0
|
||||
#else
|
||||
DEBUG_PRINTLN(F("arduino-esp32 v1.0.x\n")); // we can't say in more detail.
|
||||
#endif
|
||||
|
||||
DEBUG_PRINT(F("CPU: ")); DEBUG_PRINT(ESP.getChipModel());
|
||||
DEBUG_PRINT(F(" rev.")); DEBUG_PRINT(ESP.getChipRevision());
|
||||
DEBUG_PRINT(F(", ")); DEBUG_PRINT(ESP.getChipCores()); DEBUG_PRINT(F(" core(s)"));
|
||||
DEBUG_PRINT(F(", ")); DEBUG_PRINT(ESP.getCpuFreqMHz()); DEBUG_PRINTLN(F("MHz."));
|
||||
DEBUG_PRINT(F("FLASH: ")); DEBUG_PRINT((ESP.getFlashChipSize()/1024)/1024);
|
||||
DEBUG_PRINT(F("MB, Mode ")); DEBUG_PRINT(ESP.getFlashChipMode());
|
||||
#ifdef WLED_DEBUG
|
||||
switch (ESP.getFlashChipMode()) {
|
||||
// missing: Octal modes
|
||||
case FM_QIO: DEBUG_PRINT(F(" (QIO)")); break;
|
||||
case FM_QOUT: DEBUG_PRINT(F(" (QOUT)"));break;
|
||||
case FM_DIO: DEBUG_PRINT(F(" (DIO)")); break;
|
||||
case FM_DOUT: DEBUG_PRINT(F(" (DOUT)"));break;
|
||||
default: break;
|
||||
}
|
||||
#endif
|
||||
DEBUG_PRINT(F(", speed ")); DEBUG_PRINT(ESP.getFlashChipSpeed()/1000000);DEBUG_PRINTLN(F("MHz."));
|
||||
|
||||
#else
|
||||
DEBUG_PRINT(F("esp8266 "));
|
||||
DEBUG_PRINTLN(ESP.getCoreVersion());
|
||||
@@ -283,17 +313,29 @@ void WLED::setup()
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
|
||||
if (psramFound()) {
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// GPIO16/GPIO17 reserved for SPI RAM
|
||||
managed_pin_type pins[2] = { {16, true}, {17, true} };
|
||||
pinManager.allocateMultiplePins(pins, 2, PinOwner::SPI_RAM);
|
||||
}
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// S3: add GPIO 33-37 for "octal" PSRAM
|
||||
managed_pin_type pins[5] = { {33, true}, {34, true}, {35, true}, {36, true}, {37, true} };
|
||||
pinManager.allocateMultiplePins(pins, 5, PinOwner::SPI_RAM);
|
||||
#endif
|
||||
DEBUG_PRINT(F("Total PSRAM: ")); DEBUG_PRINT(ESP.getPsramSize()/1024); DEBUG_PRINTLN("kB");
|
||||
DEBUG_PRINT(F("Free PSRAM : ")); DEBUG_PRINT(ESP.getFreePsram()/1024); DEBUG_PRINTLN("kB");
|
||||
} else
|
||||
DEBUG_PRINTLN(F("No PSRAM found."));
|
||||
#endif
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM) && !defined(WLED_USE_PSRAM)
|
||||
DEBUG_PRINTLN(F("PSRAM not used."));
|
||||
#endif
|
||||
|
||||
//DEBUG_PRINT(F("LEDs inited. heap usage ~"));
|
||||
//DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap());
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
pinManager.allocatePin(1, true, PinOwner::DebugOut); // GPIO1 reserved for debug output
|
||||
pinManager.allocatePin(hardwareTX, true, PinOwner::DebugOut); // TX (GPIO1 on ESP32) reserved for debug output
|
||||
#endif
|
||||
#ifdef WLED_ENABLE_DMX //reserve GPIO2 as hardcoded DMX pin
|
||||
pinManager.allocatePin(2, true, PinOwner::DMX);
|
||||
@@ -320,6 +362,7 @@ void WLED::setup()
|
||||
#endif
|
||||
updateFSInfo();
|
||||
|
||||
strcpy_P(apSSID, PSTR("WLED-AP")); // otherwise it is empty on first boot until config is saved
|
||||
DEBUG_PRINTLN(F("Reading config"));
|
||||
deserializeConfigFromFS();
|
||||
|
||||
@@ -348,7 +391,7 @@ void WLED::setup()
|
||||
#ifdef WLED_ENABLE_ADALIGHT
|
||||
//Serial RX (Adalight, Improv, Serial JSON) only possible if GPIO3 unused
|
||||
//Serial TX (Debug, Improv, Serial JSON) only possible if GPIO1 unused
|
||||
if (!pinManager.isPinAllocated(3) && !pinManager.isPinAllocated(1)) {
|
||||
if (!pinManager.isPinAllocated(hardwareRX) && !pinManager.isPinAllocated(hardwareTX)) {
|
||||
Serial.println(F("Ada"));
|
||||
}
|
||||
#endif
|
||||
@@ -357,25 +400,16 @@ void WLED::setup()
|
||||
escapedMac = WiFi.macAddress();
|
||||
escapedMac.replace(":", "");
|
||||
escapedMac.toLowerCase();
|
||||
if (strcmp(cmDNS, "x") == 0) // fill in unique mdns default
|
||||
{
|
||||
strcpy_P(cmDNS, PSTR("wled-"));
|
||||
sprintf(cmDNS + 5, "%*s", 6, escapedMac.c_str() + 6);
|
||||
}
|
||||
if (mqttDeviceTopic[0] == 0) {
|
||||
strcpy_P(mqttDeviceTopic, PSTR("wled/"));
|
||||
sprintf(mqttDeviceTopic + 5, "%*s", 6, escapedMac.c_str() + 6);
|
||||
}
|
||||
if (mqttClientID[0] == 0) {
|
||||
strcpy_P(mqttClientID, PSTR("WLED-"));
|
||||
sprintf(mqttClientID + 5, "%*s", 6, escapedMac.c_str() + 6);
|
||||
}
|
||||
// fill in unique mdns default
|
||||
if (strcmp(cmDNS, "x") == 0) sprintf_P(cmDNS, PSTR("wled-%*s"), 6, escapedMac.c_str() + 6);
|
||||
if (mqttDeviceTopic[0] == 0) sprintf_P(mqttDeviceTopic, PSTR("wled/%*s"), 6, escapedMac.c_str() + 6);
|
||||
if (mqttClientID[0] == 0) sprintf_P(mqttClientID, PSTR("WLED-%*s"), 6, escapedMac.c_str() + 6);
|
||||
|
||||
#ifdef WLED_ENABLE_ADALIGHT
|
||||
if (Serial.available() > 0 && Serial.peek() == 'I') handleImprovPacket();
|
||||
#endif
|
||||
|
||||
strip.service();
|
||||
strip.service(); // why?
|
||||
|
||||
#ifndef WLED_DISABLE_OTA
|
||||
if (aOtaEnabled) {
|
||||
@@ -443,10 +477,10 @@ void WLED::initAP(bool resetAP)
|
||||
if (apBehavior == AP_BEHAVIOR_BUTTON_ONLY && !resetAP)
|
||||
return;
|
||||
|
||||
if (!apSSID[0] || resetAP)
|
||||
if (resetAP) {
|
||||
strcpy_P(apSSID, PSTR("WLED-AP"));
|
||||
if (resetAP)
|
||||
strcpy_P(apPass, PSTR(DEFAULT_AP_PASS));
|
||||
}
|
||||
DEBUG_PRINT(F("Opening access point "));
|
||||
DEBUG_PRINTLN(apSSID);
|
||||
WiFi.softAPConfig(IPAddress(4, 3, 2, 1), IPAddress(4, 3, 2, 1), IPAddress(255, 255, 255, 0));
|
||||
@@ -600,7 +634,7 @@ void WLED::initConnection()
|
||||
DEBUG_PRINTLN("...");
|
||||
|
||||
// convert the "serverDescription" into a valid DNS hostname (alphanumeric)
|
||||
char hostname[25] = "wled-";
|
||||
char hostname[25];
|
||||
prepareHostname(hostname);
|
||||
|
||||
#ifdef ESP8266
|
||||
@@ -800,7 +834,7 @@ void WLED::handleStatusLED()
|
||||
ledStatusType = 4;
|
||||
} else if (apActive) {
|
||||
c = RGBW32(0,0,255,0);
|
||||
ledStatusType = 2;
|
||||
ledStatusType = 1;
|
||||
}
|
||||
if (ledStatusType) {
|
||||
if (millis() - ledStatusLastMillis >= (1000/ledStatusType)) {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
// version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2209091
|
||||
#define VERSION 2210101
|
||||
|
||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||
//#define WLED_USE_MY_CONFIG
|
||||
@@ -27,7 +27,7 @@
|
||||
//#define WLED_DISABLE_ALEXA // saves 11kb
|
||||
//#define WLED_DISABLE_BLYNK // saves 6kb
|
||||
//#define WLED_DISABLE_HUESYNC // saves 4kb
|
||||
//#define WLED_DISABLE_INFRARED // saves 12kb
|
||||
//#define WLED_DISABLE_INFRARED // saves 12kb, there is no pin left for this on ESP8266-01
|
||||
#ifndef WLED_DISABLE_MQTT
|
||||
#define WLED_ENABLE_MQTT // saves 12kb
|
||||
#endif
|
||||
@@ -251,6 +251,16 @@ WLED_GLOBAL int8_t irPin _INIT(-1);
|
||||
WLED_GLOBAL int8_t irPin _INIT(IRPIN);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || (defined(RX) && defined(TX))
|
||||
// use RX/TX as set by the framework - these boards do _not_ have RX=3 and TX=1
|
||||
constexpr uint8_t hardwareRX = RX;
|
||||
constexpr uint8_t hardwareTX = TX;
|
||||
#else
|
||||
// use defaults for RX/TX
|
||||
constexpr uint8_t hardwareRX = 3;
|
||||
constexpr uint8_t hardwareTX = 1;
|
||||
#endif
|
||||
|
||||
//WLED_GLOBAL byte presetToApply _INIT(0);
|
||||
|
||||
WLED_GLOBAL char ntpServerName[33] _INIT("0.wled.pool.ntp.org"); // NTP server to use
|
||||
@@ -290,7 +300,7 @@ WLED_GLOBAL byte bootPreset _INIT(0); // save preset to load
|
||||
WLED_GLOBAL bool autoSegments _INIT(false);
|
||||
WLED_GLOBAL bool correctWB _INIT(false); // CCT color correction of RGB color
|
||||
WLED_GLOBAL bool cctFromRgb _INIT(false); // CCT is calculated from RGB instead of using seg.cct
|
||||
WLED_GLOBAL bool gammaCorrectCol _INIT(false); // use gamma correction on colors
|
||||
WLED_GLOBAL bool gammaCorrectCol _INIT(true ); // use gamma correction on colors
|
||||
WLED_GLOBAL bool gammaCorrectBri _INIT(false); // use gamma correction on brightness
|
||||
|
||||
WLED_GLOBAL byte col[] _INIT_N(({ 255, 160, 0, 0 })); // current RGB(W) primary color. col[] should be updated if you want to change the color.
|
||||
@@ -373,10 +383,10 @@ WLED_GLOBAL uint16_t e131Universe _INIT(1); // settings fo
|
||||
WLED_GLOBAL uint16_t e131Port _INIT(5568); // DMX in port. E1.31 default is 5568, Art-Net is 6454
|
||||
WLED_GLOBAL byte DMXMode _INIT(DMX_MODE_MULTIPLE_RGB); // DMX mode (s.a.)
|
||||
WLED_GLOBAL uint16_t DMXAddress _INIT(1); // DMX start address of fixture, a.k.a. first Channel [for E1.31 (sACN) protocol]
|
||||
WLED_GLOBAL byte DMXOldDimmer _INIT(0); // only update brightness on change
|
||||
WLED_GLOBAL byte e131LastSequenceNumber[E131_MAX_UNIVERSE_COUNT]; // to detect packet loss
|
||||
WLED_GLOBAL bool e131Multicast _INIT(false); // multicast or unicast
|
||||
WLED_GLOBAL bool e131SkipOutOfSequence _INIT(false); // freeze instead of flickering
|
||||
WLED_GLOBAL uint16_t pollReplyCount _INIT(0); // count number of replies for ArtPoll node report
|
||||
|
||||
WLED_GLOBAL bool mqttEnabled _INIT(false);
|
||||
WLED_GLOBAL char mqttDeviceTopic[33] _INIT(""); // main MQTT topic (individual per device, default is wled/mac)
|
||||
@@ -609,8 +619,9 @@ WLED_GLOBAL byte errorFlag _INIT(0);
|
||||
WLED_GLOBAL String messageHead, messageSub;
|
||||
WLED_GLOBAL byte optionType;
|
||||
|
||||
WLED_GLOBAL bool doReboot _INIT(false); // flag to initiate reboot from async handlers
|
||||
WLED_GLOBAL bool doPublishMqtt _INIT(false);
|
||||
WLED_GLOBAL bool doSerializeConfig _INIT(false); // flag to initiate saving of config
|
||||
WLED_GLOBAL bool doReboot _INIT(false); // flag to initiate reboot from async handlers
|
||||
WLED_GLOBAL bool doPublishMqtt _INIT(false);
|
||||
|
||||
// status led
|
||||
#if defined(STATUSLED)
|
||||
|
||||
@@ -20,23 +20,56 @@ enum class AdaState {
|
||||
};
|
||||
|
||||
uint16_t currentBaud = 1152; //default baudrate 115200 (divided by 100)
|
||||
bool continuousSendLED = false;
|
||||
uint32_t lastUpdate = 0;
|
||||
|
||||
void updateBaudRate(uint32_t rate){
|
||||
uint16_t rate100 = rate/100;
|
||||
if (rate100 == currentBaud || rate100 < 96) return;
|
||||
currentBaud = rate100;
|
||||
|
||||
if (!pinManager.isPinAllocated(1) || pinManager.getPinOwner(1) == PinOwner::DebugOut){
|
||||
if (!pinManager.isPinAllocated(hardwareTX) || pinManager.getPinOwner(hardwareTX) == PinOwner::DebugOut){
|
||||
Serial.print(F("Baud is now ")); Serial.println(rate);
|
||||
}
|
||||
|
||||
Serial.flush();
|
||||
Serial.begin(rate);
|
||||
}
|
||||
|
||||
|
||||
// RGB LED data return as JSON array. Slow, but easy to use on the other end.
|
||||
void sendJSON(){
|
||||
if (!pinManager.isPinAllocated(hardwareTX) || pinManager.getPinOwner(hardwareTX) == PinOwner::DebugOut) {
|
||||
uint16_t used = strip.getLengthTotal();
|
||||
Serial.write('[');
|
||||
for (uint16_t i=0; i<used; i++) {
|
||||
Serial.print(strip.getPixelColor(i));
|
||||
if (i != used-1) Serial.write(',');
|
||||
}
|
||||
Serial.println("]");
|
||||
}
|
||||
}
|
||||
|
||||
// RGB LED data returned as bytes in TPM2 format. Faster, and slightly less easy to use on the other end.
|
||||
void sendBytes(){
|
||||
if (!pinManager.isPinAllocated(hardwareTX) || pinManager.getPinOwner(hardwareTX) == PinOwner::DebugOut) {
|
||||
Serial.write(0xC9); Serial.write(0xDA);
|
||||
uint16_t used = strip.getLengthTotal();
|
||||
uint16_t len = used*3;
|
||||
Serial.write(highByte(len));
|
||||
Serial.write(lowByte(len));
|
||||
for (uint16_t i=0; i < used; i++) {
|
||||
uint32_t c = strip.getPixelColor(i);
|
||||
Serial.write(qadd8(W(c), R(c))); //R, add white channel to RGB channels as a simple RGBW -> RGB map
|
||||
Serial.write(qadd8(W(c), G(c))); //G
|
||||
Serial.write(qadd8(W(c), B(c))); //B
|
||||
}
|
||||
Serial.write(0x36); Serial.write('\n');
|
||||
}
|
||||
}
|
||||
|
||||
void handleSerial()
|
||||
{
|
||||
if (pinManager.isPinAllocated(3)) return;
|
||||
if (pinManager.isPinAllocated(hardwareRX)) return;
|
||||
|
||||
#ifdef WLED_ENABLE_ADALIGHT
|
||||
static auto state = AdaState::Header_A;
|
||||
@@ -70,32 +103,13 @@ void handleSerial()
|
||||
} else if (next == 0xB5) {updateBaudRate( 921600);
|
||||
} else if (next == 0xB6) {updateBaudRate(1000000);
|
||||
} else if (next == 0xB7) {updateBaudRate(1500000);
|
||||
|
||||
} else if (next == 'l') { //RGB(W) LED data return as JSON array. Slow, but easy to use on the other end.
|
||||
if (!pinManager.isPinAllocated(1) || pinManager.getPinOwner(1) == PinOwner::DebugOut){
|
||||
uint16_t used = strip.getLengthTotal();
|
||||
Serial.write('[');
|
||||
for (uint16_t i=0; i<used; i+=1) {
|
||||
Serial.print(strip.getPixelColor(i));
|
||||
if (i != used-1) Serial.write(',');
|
||||
}
|
||||
Serial.println("]");
|
||||
}
|
||||
} else if (next == 'L') { //RGB LED data returned as bytes in tpm2 format. Faster, and slightly less easy to use on the other end.
|
||||
if (!pinManager.isPinAllocated(1) || pinManager.getPinOwner(1) == PinOwner::DebugOut) {
|
||||
Serial.write(0xC9); Serial.write(0xDA);
|
||||
uint16_t used = strip.getLengthTotal();
|
||||
uint16_t len = used*3;
|
||||
Serial.write(highByte(len));
|
||||
Serial.write(lowByte(len));
|
||||
for (uint16_t i=0; i < used; i++) {
|
||||
uint32_t c = strip.getPixelColor(i);
|
||||
Serial.write(qadd8(W(c), R(c))); //R, add white channel to RGB channels as a simple RGBW -> RGB map
|
||||
Serial.write(qadd8(W(c), G(c))); //G
|
||||
Serial.write(qadd8(W(c), B(c))); //B
|
||||
}
|
||||
Serial.write(0x36); Serial.write('\n');
|
||||
}
|
||||
|
||||
} else if (next == 'l') {sendJSON(); // Send LED data as JSON Array
|
||||
} else if (next == 'L') {sendBytes(); // Send LED data as TPM2 Data Packet
|
||||
|
||||
} else if (next == 'o') {continuousSendLED = false; // Disable Continuous Serial Streaming
|
||||
} else if (next == 'O') {continuousSendLED = true; // Enable Continuous Serial Streaming
|
||||
|
||||
} else if (next == '{') { //JSON API
|
||||
bool verboseResponse = false;
|
||||
if (!requestJSONBufferLock(16)) return;
|
||||
@@ -107,7 +121,7 @@ void handleSerial()
|
||||
}
|
||||
verboseResponse = deserializeState(doc.as<JsonObject>());
|
||||
//only send response if TX pin is unused for other purposes
|
||||
if (verboseResponse && (!pinManager.isPinAllocated(1) || pinManager.getPinOwner(1) == PinOwner::DebugOut)) {
|
||||
if (verboseResponse && (!pinManager.isPinAllocated(hardwareTX) || pinManager.getPinOwner(hardwareTX) == PinOwner::DebugOut)) {
|
||||
doc.clear();
|
||||
JsonObject state = doc.createNestedObject("state");
|
||||
serializeState(state);
|
||||
@@ -177,7 +191,19 @@ void handleSerial()
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// All other received bytes will disable Continuous Serial Streaming
|
||||
if (continuousSendLED && next != 'O'){
|
||||
continuousSendLED = false;
|
||||
}
|
||||
|
||||
Serial.read(); //discard the byte
|
||||
}
|
||||
#endif
|
||||
|
||||
// If Continuous Serial Streaming is enabled, send new LED data as bytes
|
||||
if (continuousSendLED && (lastUpdate != strip.getLastShow())){
|
||||
sendBytes();
|
||||
lastUpdate = strip.getLastShow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ void initServer()
|
||||
if (!isConfig) {
|
||||
serveJson(request); return; //if JSON contains "v"
|
||||
} else {
|
||||
serializeConfig(); //Save new settings to FS
|
||||
doSerializeConfig = true; //serializeConfig(); //Save new settings to FS
|
||||
}
|
||||
}
|
||||
request->send(200, "application/json", F("{\"success\":true}"));
|
||||
|
||||
@@ -111,13 +111,18 @@ void sendDataWs(AsyncWebSocketClient * client)
|
||||
DEBUG_PRINTF("JSON buffer size: %u for WS request (%u).\n", doc.memoryUsage(), len);
|
||||
|
||||
size_t heap1 = ESP.getFreeHeap();
|
||||
buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes
|
||||
buffer = ws.makeBuffer(len); // will not allocate correct memory sometimes on ESP8266
|
||||
#ifdef ESP8266
|
||||
size_t heap2 = ESP.getFreeHeap();
|
||||
#else
|
||||
size_t heap2 = 0; // ESP32 variants do not have the same issue and will work without checking heap allocation
|
||||
#endif
|
||||
if (!buffer || heap1-heap2<len) {
|
||||
releaseJSONBufferLock();
|
||||
DEBUG_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();
|
||||
return; //out of memory
|
||||
}
|
||||
|
||||
|
||||
186
wled00/xml.cpp
186
wled00/xml.cpp
@@ -167,6 +167,112 @@ void fillUMPins(JsonObject &mods)
|
||||
}
|
||||
}
|
||||
|
||||
void appendGPIOinfo() {
|
||||
char nS[8];
|
||||
|
||||
oappend(SET_F("d.um_p=[-1")); // has to have 1 element
|
||||
if (i2c_sda > -1 && i2c_scl > -1) {
|
||||
oappend(","); oappend(itoa(i2c_sda,nS,10));
|
||||
oappend(","); oappend(itoa(i2c_scl,nS,10));
|
||||
}
|
||||
if (spi_mosi > -1 && spi_sclk > -1) {
|
||||
oappend(","); oappend(itoa(spi_mosi,nS,10));
|
||||
oappend(","); oappend(itoa(spi_sclk,nS,10));
|
||||
}
|
||||
// usermod pin reservations will become unnecessary when settings pages will read cfg.json directly
|
||||
if (requestJSONBufferLock(6)) {
|
||||
// if we can't allocate JSON buffer ignore usermod pins
|
||||
JsonObject mods = doc.createNestedObject(F("um"));
|
||||
usermods.addToConfig(mods);
|
||||
if (!mods.isNull()) fillUMPins(mods);
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
oappend(SET_F("];"));
|
||||
|
||||
// add reserved and usermod pins as d.um_p array
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
oappend(SET_F("d.rsvd=[22,23,24,25,26,27,28,29,30,31,32"));
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
oappend(SET_F("d.rsvd=[19,20,22,23,24,25,26,27,28,29,30,31,32")); // includes 19+20 for USB OTG (JTAG)
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
oappend(SET_F("d.rsvd=[11,12,13,14,15,16,17"));
|
||||
#elif defined(ESP32)
|
||||
oappend(SET_F("d.rsvd=[6,7,8,9,10,11,24,28,29,30,31"));
|
||||
#else
|
||||
oappend(SET_F("d.rsvd=[6,7,8,9,10,11"));
|
||||
#endif
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
oappend(SET_F(",2")); // DMX hardcoded pin
|
||||
#endif
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
oappend(SET_F(",")); oappend(itoa(hardwareTX,nS,10));// debug output (TX) pin
|
||||
#endif
|
||||
|
||||
//Note: Using pin 3 (RX) disables Adalight / Serial JSON
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32S3) && !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
if (psramFound()) oappend(SET_F(",16,17")); // GPIO16 & GPIO17 reserved for SPI RAM on ESP32 (not on S2, S3 or C3)
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
if (psramFound()) oappend(SET_F(",33,34,35,36,37")); // in use for "octal" PSRAM or "octal" FLASH -seems that octal PSRAM is very common on S3.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
|
||||
for (uint8_t p=0; p<WLED_ETH_RSVD_PINS_COUNT; p++) { oappend(","); oappend(itoa(esp32_nonconfigurable_ethernet_pins[p].pin,nS,10)); }
|
||||
if (ethernetBoards[ethernetType].eth_power>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_power,nS,10)); }
|
||||
if (ethernetBoards[ethernetType].eth_mdc>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdc,nS,10)); }
|
||||
if (ethernetBoards[ethernetType].eth_mdio>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdio,nS,10)); }
|
||||
switch (ethernetBoards[ethernetType].eth_clk_mode) {
|
||||
case ETH_CLOCK_GPIO0_IN:
|
||||
case ETH_CLOCK_GPIO0_OUT:
|
||||
oappend(SET_F(",0"));
|
||||
break;
|
||||
case ETH_CLOCK_GPIO16_OUT:
|
||||
oappend(SET_F(",16"));
|
||||
break;
|
||||
case ETH_CLOCK_GPIO17_OUT:
|
||||
oappend(SET_F(",17"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
oappend(SET_F("];"));
|
||||
|
||||
// add info for read-only GPIO
|
||||
oappend(SET_F("d.ro_gpio=["));
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
oappendi(46);
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
// none for S3
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
// none for C3
|
||||
#elif defined(ESP32)
|
||||
oappend(SET_F("34,35,36,37,38,39"));
|
||||
#else
|
||||
// none for ESP8266
|
||||
#endif
|
||||
oappend(SET_F("];"));
|
||||
|
||||
// add info about max. # of pins
|
||||
oappend(SET_F("d.max_gpio="));
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
oappendi(46);
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
oappendi(48);
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
oappendi(21);
|
||||
#elif defined(ESP32)
|
||||
oappendi(39);
|
||||
#else
|
||||
oappendi(16);
|
||||
#endif
|
||||
oappend(SET_F(";"));
|
||||
}
|
||||
|
||||
//get values for settings form in javascript
|
||||
void getSettingsJS(byte subPage, char* dest)
|
||||
@@ -257,72 +363,11 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
{
|
||||
char nS[8];
|
||||
|
||||
// Pin reservations will become unnecessary when settings pages will read cfg.json directly
|
||||
// add reserved and usermod pins as d.um_p array
|
||||
oappend(SET_F("d.um_p=[6,7,8,9,10,11"));
|
||||
|
||||
if (i2c_sda > -1 && i2c_scl > -1) {
|
||||
oappend(","); oappend(itoa(i2c_sda,nS,10));
|
||||
oappend(","); oappend(itoa(i2c_scl,nS,10));
|
||||
}
|
||||
if (spi_mosi > -1 && spi_sclk > -1) {
|
||||
oappend(","); oappend(itoa(spi_mosi,nS,10));
|
||||
oappend(","); oappend(itoa(spi_sclk,nS,10));
|
||||
}
|
||||
|
||||
if (requestJSONBufferLock(6)) {
|
||||
// if we can't allocate JSON buffer ignore usermod pins
|
||||
JsonObject mods = doc.createNestedObject(F("um"));
|
||||
usermods.addToConfig(mods);
|
||||
if (!mods.isNull()) fillUMPins(mods);
|
||||
releaseJSONBufferLock();
|
||||
}
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
oappend(SET_F(",2")); // DMX hardcoded pin
|
||||
#endif
|
||||
|
||||
#ifdef WLED_DEBUG
|
||||
oappend(SET_F(",1")); // debug output (TX) pin
|
||||
#endif
|
||||
|
||||
//Note: Using pin 3 (RX) disables Adalight / Serial JSON
|
||||
|
||||
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
|
||||
if (psramFound()) oappend(SET_F(",16,17")); // GPIO16 & GPIO17 reserved for SPI RAM
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
|
||||
for (uint8_t p=0; p<WLED_ETH_RSVD_PINS_COUNT; p++) { oappend(","); oappend(itoa(esp32_nonconfigurable_ethernet_pins[p].pin,nS,10)); }
|
||||
if (ethernetBoards[ethernetType].eth_power>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_power,nS,10)); }
|
||||
if (ethernetBoards[ethernetType].eth_mdc>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdc,nS,10)); }
|
||||
if (ethernetBoards[ethernetType].eth_mdio>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdio,nS,10)); }
|
||||
switch (ethernetBoards[ethernetType].eth_clk_mode) {
|
||||
case ETH_CLOCK_GPIO0_IN:
|
||||
case ETH_CLOCK_GPIO0_OUT:
|
||||
oappend(SET_F(",0"));
|
||||
break;
|
||||
case ETH_CLOCK_GPIO16_OUT:
|
||||
oappend(SET_F(",16"));
|
||||
break;
|
||||
case ETH_CLOCK_GPIO17_OUT:
|
||||
oappend(SET_F(",17"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
oappend(SET_F("];"));
|
||||
appendGPIOinfo();
|
||||
|
||||
// set limits
|
||||
oappend(SET_F("bLimits("));
|
||||
#if defined(ESP32) && defined(USERMOD_AUDIOREACTIVE)
|
||||
// requested by @softhack007 https://github.com/blazoncek/WLED/issues/33
|
||||
oappend(itoa(WLED_MAX_BUSSES-2,nS,10)); oappend(","); // prevent use of I2S buses if audio installed
|
||||
#else
|
||||
oappend(itoa(WLED_MAX_BUSSES,nS,10)); oappend(",");
|
||||
#endif
|
||||
oappend(itoa(MAX_LEDS_PER_BUS,nS,10)); oappend(",");
|
||||
oappend(itoa(MAX_LED_MEMORY,nS,10)); oappend(",");
|
||||
oappend(itoa(MAX_LEDS,nS,10));
|
||||
@@ -500,14 +545,14 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
char hueErrorString[25];
|
||||
switch (hueError)
|
||||
{
|
||||
case HUE_ERROR_INACTIVE : strcpy(hueErrorString,(char*)F("Inactive")); break;
|
||||
case HUE_ERROR_ACTIVE : strcpy(hueErrorString,(char*)F("Active")); break;
|
||||
case HUE_ERROR_UNAUTHORIZED : strcpy(hueErrorString,(char*)F("Unauthorized")); break;
|
||||
case HUE_ERROR_LIGHTID : strcpy(hueErrorString,(char*)F("Invalid light ID")); break;
|
||||
case HUE_ERROR_PUSHLINK : strcpy(hueErrorString,(char*)F("Link button not pressed")); break;
|
||||
case HUE_ERROR_JSON_PARSING : strcpy(hueErrorString,(char*)F("JSON parsing error")); break;
|
||||
case HUE_ERROR_TIMEOUT : strcpy(hueErrorString,(char*)F("Timeout")); break;
|
||||
default: sprintf(hueErrorString,(char*)F("Bridge Error %i"),hueError);
|
||||
case HUE_ERROR_INACTIVE : strcpy_P(hueErrorString,PSTR("Inactive")); break;
|
||||
case HUE_ERROR_ACTIVE : strcpy_P(hueErrorString,PSTR("Active")); break;
|
||||
case HUE_ERROR_UNAUTHORIZED : strcpy_P(hueErrorString,PSTR("Unauthorized")); break;
|
||||
case HUE_ERROR_LIGHTID : strcpy_P(hueErrorString,PSTR("Invalid light ID")); break;
|
||||
case HUE_ERROR_PUSHLINK : strcpy_P(hueErrorString,PSTR("Link button not pressed")); break;
|
||||
case HUE_ERROR_JSON_PARSING : strcpy_P(hueErrorString,PSTR("JSON parsing error")); break;
|
||||
case HUE_ERROR_TIMEOUT : strcpy_P(hueErrorString,PSTR("Timeout")); break;
|
||||
default: sprintf_P(hueErrorString,PSTR("Bridge Error %i"),hueError);
|
||||
}
|
||||
|
||||
sappends('m',SET_F("(\"sip\")[0]"),hueErrorString);
|
||||
@@ -630,6 +675,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
|
||||
if (subPage == 8) //usermods
|
||||
{
|
||||
appendGPIOinfo();
|
||||
oappend(SET_F("numM="));
|
||||
oappendi(usermods.getModCount());
|
||||
oappend(";");
|
||||
|
||||
Reference in New Issue
Block a user