Compare commits

..

29 Commits

Author SHA1 Message Date
Blaž Kristan
a0e81da8c5 WLED 0.15.0-b6 release (#4180)
* modified Improv chip & version handling
* Update build and changelog
2024-10-14 20:13:59 +02:00
Will Miles
85a7c3c60d Merge pull request #4189 from dosipod/0_15
Extra comma in the network password fix
2024-10-13 14:41:01 -04:00
AlDIY
01e07ca0bc Update xml.cpp 2024-10-13 20:34:18 +03:00
Frank
1468ee5fde Merge pull request #4188 from LuisFadini/0_15_brt_timezone
Added BRT timezone
2024-10-13 11:01:05 +02:00
Blaž Kristan
49f044ecde Better fix for #4154 2024-10-13 10:43:56 +02:00
Luis
37f32ab197 Added BRT timezone 2024-10-12 10:56:40 -03:00
maxi4329
a60231ba59 Fixed the positioning of the "Download the latest binary" button (#4184)
* fixed the positioning of the download button

* fixed space after "Download the latest binary:" disapering after building

* fixed typo

---------

Co-authored-by: maxi4329 <maxi4329>
2024-10-09 22:10:59 +02:00
Blaž Kristan
c8dafede6d Merge pull request #4183 from PaoloTK/autosegment_outputs_flag
Add WLED_AUTOSEGMENTS compile flag
2024-10-09 22:00:12 +02:00
Blaž Kristan
7deea9eb75 Minor button & rover CSS tweak. 2024-10-07 17:52:36 +02:00
Blaž Kristan
5e9a46d54d Fix for #4154 2024-10-07 17:15:35 +02:00
PaoloTK
488974dd3e change flag 2024-10-07 10:39:45 +02:00
PaoloTK
5975b9125f add autosegment outputs compile flag 2024-10-06 22:56:30 +02:00
Blaž Kristan
407477dc68 Fix for #4168
- set min value to 0 for disabled ABL
2024-10-06 15:42:58 +02:00
Blaz Kristan
1b0ce9a123 Fix for #4179 2024-10-05 15:00:58 +02:00
Blaž Kristan
ba636b17a0 Merge pull request #4175 from Xevel/fix_polybus_canshow
fix Polybus canShow
2024-10-03 16:19:44 +02:00
Blaž Kristan
2a07eb84f6 Merge pull request #4174 from Xevel/0_15
Fixed Improv rejecting all properly formatted packets
2024-10-03 16:07:17 +02:00
Nicolas Saugnier
949b9fb10e Fixed Polybus.canShow always returning true on ESP32 2024-10-03 15:21:39 +02:00
Nicolas Saugnier
ae1b6af0d4 Indent formatting... 2024-10-03 11:07:58 +02:00
Nicolas Saugnier
dd27504d30 Fixed Improv rejecting all properly formatted packets. 2024-10-03 11:04:47 +02:00
Blaz Kristan
c30a08cfc5 Merge branch '0_15' of https://github.com/aircoookie/WLED into 0_15 2024-10-02 20:16:41 +02:00
Blaz Kristan
a4c49aa35e Fix for #4005 2024-10-02 20:15:58 +02:00
Frank
402fba734a bugfix for holes in 2D DNA Spiral
Holes were visible at height > 32. Root cause: "lerp8x8" seems to be inaccurate --> replaced by a simple linear calculation.
2024-10-02 16:34:36 +02:00
Frank
262af0678f colored burst effect bugfix (swapped XY dimensions)
fixing a bug where width and height got swapped (visible on non-square panels)
2024-09-30 18:35:14 +02:00
Frank
3765d558b6 akemi bugfix fix
map2 --> map
2024-09-30 18:26:00 +02:00
Frank
4ed8ded502 Akemi bugfix for panel width > 32
due to a math accident, Akemi did not show proper GEQ bands in its hands when width>32
2024-09-30 17:44:38 +02:00
Frank
7fa25ca7ae pio update - flash size of non-standard boards
* adding missing flash size flags that were lost between 0.14 and 0.15
   (necessary if you don't flash using esptool)
* adding env:esp32dev_16M for 16MB flash (serg74 esp32-16M, twilightlord esp32 16M)
2024-09-30 11:25:58 +02:00
Frank
d3c401ed4e wu_pixel small optimization
5% faster
2024-09-29 19:29:12 +02:00
Blaz Kristan
10d8cfde85 Fix FX filter bug 2024-09-29 13:00:07 +02:00
Frank
6f221852a2 partition file for 512Kb Filesystem, 1.7MB Program
the missing link between 256KB (very small FS) and 700KB (only 100KB extra program)
2024-09-28 18:25:16 +02:00
30 changed files with 255 additions and 651 deletions

View File

@@ -1,9 +1,26 @@
## WLED changelog
#### Build 2410140
- WLED 0.15.0-b6 release
- Added BRT timezone (#4188 by @LuisFadini)
- Fixed the positioning of the "Download the latest binary" button (#4184 by @maxi4329)
- Add WLED_AUTOSEGMENTS compile flag (#4183 by @PaoloTK)
- New 512kB FS parition map for 4MB devices
- Internal API change: Static PinManager & UsermodManager
- Change in Improv chip ID and version generation
- Various optimisations, bugfixes and enhancements (#4005, #4174 & #4175 by @Xevel, #4180, #4168, #4154, #4189 by @dosipod)
#### Build 2409170
- UI: Introduce common.js in settings pages (size optimisation)
- Add the ability to toggle the reception of palette synchronizations (#4137 by @felddy)
- Usermod/FX: Temperature usermod added Temperature effect (example usermod effect by @blazoncek)
- Fix AsyncWebServer version pin
#### Build 2409140
- Configure different kinds of busses at compile (#4107 by @PaoloTK)
- BREAKING: removes LEDPIN and DEFAULT_LED_TYPE compile overrides
- Fetch LED types from Bus classes (dynamic UI) (#4129 by @netmindz, @blazoncek, @dedehai)
- Temperature usermod: update OneWire to 2.3.8 (#4131 by @iammattcoleman)
#### Build 2409100
- WLED 0.15.0-b5 release

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "wled",
"version": "0.15.0-b5",
"version": "0.15.0-b6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wled",
"version": "0.15.0-b5",
"version": "0.15.0-b6",
"license": "ISC",
"dependencies": {
"clean-css": "^5.3.3",

View File

@@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.15.0-b5",
"version": "0.15.0-b6",
"description": "Tools for WLED project",
"main": "tools/cdata.js",
"directories": {

View File

@@ -141,7 +141,6 @@ lib_deps =
makuna/NeoPixelBus @ 2.8.0
#https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1
https://github.com/Aircoookie/arduino-crypto.git
# for I2C interface
;Wire
# ESP-NOW library
@@ -431,7 +430,26 @@ lib_deps = ${esp32_idf_V4.lib_deps}
${esp32.AR_lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.large_partitions}
board_upload.flash_size = 8MB
board_upload.maximum_size = 8388608
; board_build.f_flash = 80000000L
; board_build.flash_mode = qio
[env:esp32dev_16M]
board = esp32dev
platform = ${esp32_idf_V4.platform}
platform_packages = ${esp32_idf_V4.platform_packages}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags} ${esp32_idf_V4.build_flags} -D WLED_RELEASE_NAME=ESP32_16M #-D WLED_DISABLE_BROWNOUT_DET
${esp32.AR_build_flags}
lib_deps = ${esp32_idf_V4.lib_deps}
${esp32.AR_lib_deps}
monitor_filters = esp32_exception_decoder
board_build.partitions = ${esp32.extreme_partitions}
board_upload.flash_size = 16MB
board_upload.maximum_size = 16777216
board_build.f_flash = 80000000L
board_build.flash_mode = dio
;[env:esp32dev_audioreactive]
;board = esp32dev
@@ -509,6 +527,8 @@ build_flags = ${common.build_flags} ${esp32s3.build_flags} -D WLED_RELEASE_NAME=
lib_deps = ${esp32s3.lib_deps}
${esp32.AR_lib_deps}
board_build.partitions = ${esp32.extreme_partitions}
board_upload.flash_size = 16MB
board_upload.maximum_size = 16777216
board_build.f_flash = 80000000L
board_build.flash_mode = qio
monitor_filters = esp32_exception_decoder

View File

@@ -359,6 +359,8 @@ upload_speed = 115200
lib_deps = ${esp32c3.lib_deps}
board_build.partitions = tools/WLED_ESP32_2MB_noOTA.csv
board_build.flash_mode = dio
board_upload.flash_size = 2MB
board_upload.maximum_size = 2097152
[env:wemos_shield_esp32]
board = esp32dev

View File

@@ -10,9 +10,6 @@
</p>
> [!CAUTION]
> This branch is actively used for research purposes. **Please do not push** to it.
# Welcome to my project WLED! ✨
A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102!

View File

@@ -0,0 +1,7 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x1B0000,
app1, app, ota_1, 0x1C0000,0x1B0000,
spiffs, data, spiffs, 0x370000,0x80000,
coredump, data, coredump,,64K
1 # Name, Type, SubType, Offset, Size, Flags
2 nvs, data, nvs, 0x9000, 0x5000,
3 otadata, data, ota, 0xe000, 0x2000,
4 app0, app, ota_0, 0x10000, 0x1B0000,
5 app1, app, ota_1, 0x1C0000,0x1B0000,
6 spiffs, data, spiffs, 0x370000,0x80000,
7 coredump, data, coredump,,64K

View File

@@ -4031,7 +4031,7 @@ uint16_t mode_pacifica()
// Increment the four "color index start" counters, one for each wave layer.
// Each is incremented at a different speed, and the speeds vary over time.
unsigned sCIStart1 = SEGENV.aux0, sCIStart2 = SEGENV.aux1, sCIStart3 = SEGENV.step, sCIStart4 = SEGENV.step >> 16;
unsigned sCIStart1 = SEGENV.aux0, sCIStart2 = SEGENV.aux1, sCIStart3 = SEGENV.step & 0xFFFF, sCIStart4 = (SEGENV.step >> 16);
uint32_t deltams = (FRAMETIME >> 2) + ((FRAMETIME * SEGMENT.speed) >> 7);
uint64_t deltat = (strip.now >> 2) + ((strip.now * SEGMENT.speed) >> 7);
strip.now = deltat;
@@ -4046,7 +4046,7 @@ uint16_t mode_pacifica()
sCIStart3 -= (deltams1 * beatsin88(501,5,7));
sCIStart4 -= (deltams2 * beatsin88(257,4,6));
SEGENV.aux0 = sCIStart1; SEGENV.aux1 = sCIStart2;
SEGENV.step = sCIStart4; SEGENV.step = (SEGENV.step << 16) + sCIStart3;
SEGENV.step = (sCIStart4 << 16) | (sCIStart3 & 0xFFFF);
// Clear out the LED array to a dim background blue-green
//SEGMENT.fill(132618);
@@ -4077,7 +4077,7 @@ uint16_t mode_pacifica()
c.green = scale8(c.green, 200);
c |= CRGB( 2, 5, 7);
SEGMENT.setPixelColor(i, c.red, c.green, c.blue);
SEGMENT.setPixelColor(i, c);
}
strip.now = nowOld;
@@ -4931,8 +4931,8 @@ uint16_t mode_2DColoredBursts() { // By: ldirko https://editor.so
SEGMENT.fadeToBlackBy(40);
for (size_t i = 0; i < numLines; i++) {
byte x1 = beatsin8(2 + SEGMENT.speed/16, 0, (cols - 1));
byte x2 = beatsin8(1 + SEGMENT.speed/16, 0, (cols - 1));
byte y1 = beatsin8(5 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 24);
byte x2 = beatsin8(1 + SEGMENT.speed/16, 0, (rows - 1));
byte y1 = beatsin8(5 + SEGMENT.speed/16, 0, (cols - 1), 0, i * 24);
byte y2 = beatsin8(3 + SEGMENT.speed/16, 0, (rows - 1), 0, i * 48 + 64);
CRGB color = ColorFromPalette(SEGPALETTE, i * 255 / numLines + (SEGENV.aux0&0xFF), 255, LINEARBLEND);
@@ -5010,9 +5010,11 @@ uint16_t mode_2DDNASpiral() { // By: ldirko https://editor.soulma
// draw a gradient line between x and x1
x = x / 2; x1 = x1 / 2;
unsigned steps = abs8(x - x1) + 1;
bool positive = (x1 >= x); // direction of drawing
for (size_t k = 1; k <= steps; k++) {
unsigned rate = k * 255 / steps;
unsigned dx = lerp8by8(x, x1, rate);
//unsigned dx = lerp8by8(x, x1, rate);
unsigned dx = positive? (x + k-1) : (x - k+1); // behaves the same as "lerp8by8" but does not create holes
//SEGMENT.setPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND).nscale8_video(rate));
SEGMENT.addPixelColorXY(dx, i, ColorFromPalette(SEGPALETTE, hue, 255, LINEARBLEND)); // use setPixelColorXY for different look
SEGMENT.fadePixelColorXY(dx, i, rate);
@@ -7528,8 +7530,9 @@ uint16_t mode_2DAkemi(void) {
//add geq left and right
if (um_data && fftResult) {
for (int x=0; x < cols/8; x++) {
unsigned band = x * cols/8;
int xMax = cols/8;
for (int x=0; x < xMax; x++) {
unsigned band = map(x, 0, max(xMax,4), 0, 15); // map 0..cols/8 to 16 GEQ bands
band = constrain(band, 0, 15);
int barHeight = map(fftResult[band], 0, 255, 0, 17*rows/32);
CRGB color = CRGB(SEGMENT.color_from_palette((band * 35), false, PALETTE_SOLID_WRAP, 0));

View File

@@ -720,7 +720,11 @@ class WS2812FX { // 96 bytes
#ifndef WLED_DISABLE_2D
panels(1),
#endif
#ifdef WLED_AUTOSEGMENTS
autoSegments(true),
#else
autoSegments(false),
#endif
correctWB(false),
cctFromRgb(false),
// semi-private (just obscured) used in effect functions through macros

View File

@@ -704,11 +704,14 @@ void Segment::wu_pixel(uint32_t x, uint32_t y, CRGB c) { //awesome wu_pixel
WU_WEIGHT(ix, yy), WU_WEIGHT(xx, yy)};
// multiply the intensities by the colour, and saturating-add them to the pixels
for (int i = 0; i < 4; i++) {
CRGB led = getPixelColorXY((x >> 8) + (i & 1), (y >> 8) + ((i >> 1) & 1));
int wu_x = (x >> 8) + (i & 1); // precalculate x
int wu_y = (y >> 8) + ((i >> 1) & 1); // precalculate y
CRGB led = getPixelColorXY(wu_x, wu_y);
CRGB oldLed = led;
led.r = qadd8(led.r, c.r * wu[i] >> 8);
led.g = qadd8(led.g, c.g * wu[i] >> 8);
led.b = qadd8(led.b, c.b * wu[i] >> 8);
setPixelColorXY(int((x >> 8) + (i & 1)), int((y >> 8) + ((i >> 1) & 1)), led);
if (led != oldLed) setPixelColorXY(wu_x, wu_y, led); // don't repaint if same color
}
}
#undef WU_WEIGHT

View File

@@ -766,47 +766,47 @@ class PolyBus {
#endif
#ifdef ARDUINO_ARCH_ESP32
// RMT buses
case I_32_RN_NEO_3: (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break;
case I_32_RN_NEO_4: (static_cast<B_32_RN_NEO_4*>(busPtr))->CanShow(); break;
case I_32_RN_400_3: (static_cast<B_32_RN_400_3*>(busPtr))->CanShow(); break;
case I_32_RN_TM1_4: (static_cast<B_32_RN_TM1_4*>(busPtr))->CanShow(); break;
case I_32_RN_TM2_3: (static_cast<B_32_RN_TM2_3*>(busPtr))->CanShow(); break;
case I_32_RN_UCS_3: (static_cast<B_32_RN_UCS_3*>(busPtr))->CanShow(); break;
case I_32_RN_UCS_4: (static_cast<B_32_RN_UCS_4*>(busPtr))->CanShow(); break;
case I_32_RN_APA106_3: (static_cast<B_32_RN_APA106_3*>(busPtr))->CanShow(); break;
case I_32_RN_FW6_5: (static_cast<B_32_RN_FW6_5*>(busPtr))->CanShow(); break;
case I_32_RN_2805_5: (static_cast<B_32_RN_2805_5*>(busPtr))->CanShow(); break;
case I_32_RN_TM1914_3: (static_cast<B_32_RN_TM1914_3*>(busPtr))->CanShow(); break;
case I_32_RN_SM16825_5: (static_cast<B_32_RN_SM16825_5*>(busPtr))->CanShow(); break;
case I_32_RN_NEO_3: return (static_cast<B_32_RN_NEO_3*>(busPtr))->CanShow(); break;
case I_32_RN_NEO_4: return (static_cast<B_32_RN_NEO_4*>(busPtr))->CanShow(); break;
case I_32_RN_400_3: return (static_cast<B_32_RN_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;
case I_32_RN_UCS_3: return (static_cast<B_32_RN_UCS_3*>(busPtr))->CanShow(); break;
case I_32_RN_UCS_4: return (static_cast<B_32_RN_UCS_4*>(busPtr))->CanShow(); break;
case I_32_RN_APA106_3: return (static_cast<B_32_RN_APA106_3*>(busPtr))->CanShow(); break;
case I_32_RN_FW6_5: return (static_cast<B_32_RN_FW6_5*>(busPtr))->CanShow(); break;
case I_32_RN_2805_5: return (static_cast<B_32_RN_2805_5*>(busPtr))->CanShow(); break;
case I_32_RN_TM1914_3: return (static_cast<B_32_RN_TM1914_3*>(busPtr))->CanShow(); break;
case I_32_RN_SM16825_5: return (static_cast<B_32_RN_SM16825_5*>(busPtr))->CanShow(); break;
// I2S1 bus or paralell buses
#ifndef WLED_NO_I2S1_PIXELBUS
case I_32_I1_NEO_3: if (useParallelI2S) (static_cast<B_32_I1_NEO_3P*>(busPtr))->CanShow(); else (static_cast<B_32_I1_NEO_3*>(busPtr))->CanShow(); break;
case I_32_I1_NEO_4: if (useParallelI2S) (static_cast<B_32_I1_NEO_4P*>(busPtr))->CanShow(); else (static_cast<B_32_I1_NEO_4*>(busPtr))->CanShow(); break;
case I_32_I1_400_3: if (useParallelI2S) (static_cast<B_32_I1_400_3P*>(busPtr))->CanShow(); else (static_cast<B_32_I1_400_3*>(busPtr))->CanShow(); break;
case I_32_I1_TM1_4: if (useParallelI2S) (static_cast<B_32_I1_TM1_4P*>(busPtr))->CanShow(); else (static_cast<B_32_I1_TM1_4*>(busPtr))->CanShow(); break;
case I_32_I1_TM2_3: if (useParallelI2S) (static_cast<B_32_I1_TM2_3P*>(busPtr))->CanShow(); else (static_cast<B_32_I1_TM2_3*>(busPtr))->CanShow(); break;
case I_32_I1_UCS_3: if (useParallelI2S) (static_cast<B_32_I1_UCS_3P*>(busPtr))->CanShow(); else (static_cast<B_32_I1_UCS_3*>(busPtr))->CanShow(); break;
case I_32_I1_UCS_4: if (useParallelI2S) (static_cast<B_32_I1_UCS_4P*>(busPtr))->CanShow(); else (static_cast<B_32_I1_UCS_4*>(busPtr))->CanShow(); break;
case I_32_I1_APA106_3: if (useParallelI2S) (static_cast<B_32_I1_APA106_3P*>(busPtr))->CanShow(); else (static_cast<B_32_I1_APA106_3*>(busPtr))->CanShow(); break;
case I_32_I1_FW6_5: if (useParallelI2S) (static_cast<B_32_I1_FW6_5P*>(busPtr))->CanShow(); else (static_cast<B_32_I1_FW6_5*>(busPtr))->CanShow(); break;
case I_32_I1_2805_5: if (useParallelI2S) (static_cast<B_32_I1_2805_5P*>(busPtr))->CanShow(); else (static_cast<B_32_I1_2805_5*>(busPtr))->CanShow(); break;
case I_32_I1_TM1914_3: if (useParallelI2S) (static_cast<B_32_I1_TM1914_3P*>(busPtr))->CanShow(); else (static_cast<B_32_I1_TM1914_3*>(busPtr))->CanShow(); break;
case I_32_I1_SM16825_5: if (useParallelI2S) (static_cast<B_32_I1_SM16825_5P*>(busPtr))->CanShow(); else (static_cast<B_32_I1_SM16825_5*>(busPtr))->CanShow(); break;
case I_32_I1_NEO_3: if (useParallelI2S) return (static_cast<B_32_I1_NEO_3P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_NEO_3*>(busPtr))->CanShow(); break;
case I_32_I1_NEO_4: if (useParallelI2S) return (static_cast<B_32_I1_NEO_4P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_NEO_4*>(busPtr))->CanShow(); break;
case I_32_I1_400_3: if (useParallelI2S) return (static_cast<B_32_I1_400_3P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_400_3*>(busPtr))->CanShow(); break;
case I_32_I1_TM1_4: if (useParallelI2S) return (static_cast<B_32_I1_TM1_4P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_TM1_4*>(busPtr))->CanShow(); break;
case I_32_I1_TM2_3: if (useParallelI2S) return (static_cast<B_32_I1_TM2_3P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_TM2_3*>(busPtr))->CanShow(); break;
case I_32_I1_UCS_3: if (useParallelI2S) return (static_cast<B_32_I1_UCS_3P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_UCS_3*>(busPtr))->CanShow(); break;
case I_32_I1_UCS_4: if (useParallelI2S) return (static_cast<B_32_I1_UCS_4P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_UCS_4*>(busPtr))->CanShow(); break;
case I_32_I1_APA106_3: if (useParallelI2S) return (static_cast<B_32_I1_APA106_3P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_APA106_3*>(busPtr))->CanShow(); break;
case I_32_I1_FW6_5: if (useParallelI2S) return (static_cast<B_32_I1_FW6_5P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_FW6_5*>(busPtr))->CanShow(); break;
case I_32_I1_2805_5: if (useParallelI2S) return (static_cast<B_32_I1_2805_5P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_2805_5*>(busPtr))->CanShow(); break;
case I_32_I1_TM1914_3: if (useParallelI2S) return (static_cast<B_32_I1_TM1914_3P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_TM1914_3*>(busPtr))->CanShow(); break;
case I_32_I1_SM16825_5: if (useParallelI2S) return (static_cast<B_32_I1_SM16825_5P*>(busPtr))->CanShow(); else return (static_cast<B_32_I1_SM16825_5*>(busPtr))->CanShow(); break;
#endif
// I2S0 bus
#ifndef WLED_NO_I2S0_PIXELBUS
case I_32_I0_NEO_3: (static_cast<B_32_I0_NEO_3*>(busPtr))->CanShow(); break;
case I_32_I0_NEO_4: (static_cast<B_32_I0_NEO_4*>(busPtr))->CanShow(); break;
case I_32_I0_400_3: (static_cast<B_32_I0_400_3*>(busPtr))->CanShow(); break;
case I_32_I0_TM1_4: (static_cast<B_32_I0_TM1_4*>(busPtr))->CanShow(); break;
case I_32_I0_TM2_3: (static_cast<B_32_I0_TM2_3*>(busPtr))->CanShow(); break;
case I_32_I0_UCS_3: (static_cast<B_32_I0_UCS_3*>(busPtr))->CanShow(); break;
case I_32_I0_UCS_4: (static_cast<B_32_I0_UCS_4*>(busPtr))->CanShow(); break;
case I_32_I0_APA106_3: (static_cast<B_32_I0_APA106_3*>(busPtr))->CanShow(); break;
case I_32_I0_FW6_5: (static_cast<B_32_I0_FW6_5*>(busPtr))->CanShow(); break;
case I_32_I0_2805_5: (static_cast<B_32_I0_2805_5*>(busPtr))->CanShow(); break;
case I_32_I0_TM1914_3: (static_cast<B_32_I0_TM1914_3*>(busPtr))->CanShow(); break;
case I_32_I0_SM16825_5: (static_cast<B_32_I0_SM16825_5*>(busPtr))->CanShow(); break;
case I_32_I0_NEO_3: return (static_cast<B_32_I0_NEO_3*>(busPtr))->CanShow(); break;
case I_32_I0_NEO_4: return (static_cast<B_32_I0_NEO_4*>(busPtr))->CanShow(); break;
case I_32_I0_400_3: return (static_cast<B_32_I0_400_3*>(busPtr))->CanShow(); break;
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;
case I_32_I0_UCS_3: return (static_cast<B_32_I0_UCS_3*>(busPtr))->CanShow(); break;
case I_32_I0_UCS_4: return (static_cast<B_32_I0_UCS_4*>(busPtr))->CanShow(); break;
case I_32_I0_APA106_3: return (static_cast<B_32_I0_APA106_3*>(busPtr))->CanShow(); break;
case I_32_I0_FW6_5: return (static_cast<B_32_I0_FW6_5*>(busPtr))->CanShow(); break;
case I_32_I0_2805_5: return (static_cast<B_32_I0_2805_5*>(busPtr))->CanShow(); break;
case I_32_I0_TM1914_3: return (static_cast<B_32_I0_TM1914_3*>(busPtr))->CanShow(); break;
case I_32_I0_SM16825_5: return (static_cast<B_32_I0_SM16825_5*>(busPtr))->CanShow(); break;
#endif
#endif
case I_HS_DOT_3: return (static_cast<B_HS_DOT_3*>(busPtr))->CanShow(); break;

View File

@@ -421,8 +421,6 @@
#define SEG_CAPABILITY_W 0x02
#define SEG_CAPABILITY_CCT 0x04
#define SESSION_ID_SIZE 16
// WLED Error modes
#define ERR_NONE 0 // All good :)
#define ERR_DENIED 1 // Permission denied
@@ -440,11 +438,6 @@
#define ERR_OVERTEMP 30 // An attached temperature sensor has measured above threshold temperature (not implemented)
#define ERR_OVERCURRENT 31 // An attached current sensor has measured a current above the threshold (not implemented)
#define ERR_UNDERVOLT 32 // An attached voltmeter has measured a voltage below the threshold (not implemented)
#define ERR_NONCE 40 // Invalid nonce
#define ERR_REPLAY 41 // Replay attack detected
#define ERR_HMAC 42 // HMAC verification failed
#define ERR_HMAC_MISS 43 // HMAC missing
#define ERR_HMAC_GEN 44 // HMAC handling error
// Timer mode types
#define NL_MODE_SET 0 //After nightlight time elapsed, set to target brightness

View File

@@ -1,320 +0,0 @@
#include <Crypto.h>
#include "wled.h"
#define HMAC_KEY_SIZE 32
#define MAX_SESSION_IDS 8
void printByteArray(const byte* arr, size_t len) {
for (size_t i = 0; i < len; i++) {
Serial.print(arr[i], HEX);
}
Serial.println();
}
struct Nonce {
byte sessionId[SESSION_ID_SIZE];
uint32_t counter;
};
Nonce knownSessions[MAX_SESSION_IDS] = {};
void moveToFirst(uint32_t i) {
if (i >= MAX_SESSION_IDS) return;
Nonce tmp = knownSessions[i];
for (int j = i; j > 0; j--) {
knownSessions[j] = knownSessions[j - 1];
}
knownSessions[0] = tmp;
}
uint8_t verifyNonce(const byte* sid, uint32_t counter) {
Serial.println(F("check sid"));
printByteArray(sid, SESSION_ID_SIZE);
uint32_t sum = 0;
for (size_t i = 0; i < SESSION_ID_SIZE; i++) {
sum += sid[i];
}
if (sum == 0) { // all-zero session ID is invalid as it is used for uninitialized entries
return ERR_NONCE;
}
for (int i = 0; i < MAX_SESSION_IDS; i++) {
if (memcmp(knownSessions[i].sessionId, sid, SESSION_ID_SIZE) == 0) {
Serial.print(F("Session ID matches e"));
Serial.println(i);
if (counter <= knownSessions[i].counter) {
Serial.println(F("Retransmission detected!"));
return ERR_REPLAY;
}
knownSessions[i].counter = counter;
// nonce good, move this entry to the first position of knownSessions
moveToFirst(i);
return ERR_NONE;
}
}
Serial.println(F("Unknown session ID!"));
return ERR_NONCE;
}
void addSessionId(byte* sid) {
RNG::fill(sid, SESSION_ID_SIZE);
// first, try to find a completely unused slot
for (int i = 0; i < MAX_SESSION_IDS; i++) {
// this is not perfect, but it is extremely unlikely that the first 32 bit of a random session ID are all zeroes
if ((uint32_t)(knownSessions[i].sessionId) == 0 && knownSessions[i].counter == 0) {
memcpy(knownSessions[i].sessionId, sid, SESSION_ID_SIZE);
moveToFirst(i);
return;
}
}
// next, find oldest slot that has counter 0 (not used before)
// but leave the two most recent slots alone
for (int i = MAX_SESSION_IDS - 1; i > 1; i--) {
if (knownSessions[i].counter == 0) {
memcpy(knownSessions[i].sessionId, sid, SESSION_ID_SIZE);
moveToFirst(i);
return;
}
}
// if all else fails, overwrite the oldest slot
memcpy(knownSessions[MAX_SESSION_IDS - 1].sessionId, sid, SESSION_ID_SIZE);
moveToFirst(MAX_SESSION_IDS - 1);
}
void hexStringToByteArray(const char* hexString, unsigned char* byteArray, size_t byteArraySize) {
size_t lenStr = strlen(hexString);
if (lenStr < 2 * byteArraySize) byteArraySize = lenStr / 2;
for (size_t i = 0; i < byteArraySize; i++) {
char c[3] = {hexString[2 * i], hexString[2 * i + 1], '\0'}; // Get two characters
byteArray[i] = (unsigned char)strtoul(c, NULL, 16); // Convert to byte
}
}
// requires hexString to be at least 2 * byteLen + 1 characters long
char* byteArrayToHexString(char* hexString, const byte* byteArray, size_t byteLen) {
for (size_t i = 0; i < byteLen; ++i) {
// Convert each byte to a two-character hex string
sprintf(&hexString[i * 2], "%02x", byteArray[i]);
}
// Null-terminate the string
hexString[byteLen * 2] = '\0';
return hexString;
}
void hmacSign(const byte* message, size_t msgLen, const char* pskHex, byte* signature) {
size_t len = strlen(pskHex) / 2; // This will drop the last character if the string has an odd length
if (len > HMAC_KEY_SIZE) {
Serial.println(F("PSK too long!"));
return;
}
unsigned char pskByteArray[len];
hexStringToByteArray(pskHex, pskByteArray, len);
SHA256HMAC hmac(pskByteArray, len);
hmac.doUpdate(message, msgLen);
hmac.doFinal(signature);
}
bool hmacVerify(const byte* message, size_t msgLen, const char* pskHex, const byte* signature) {
byte sigCalculated[SHA256HMAC_SIZE];
hmacSign(message, msgLen, pskHex, sigCalculated);
//Serial.print(F("Calculated: "));
//printByteArray(sigCalculated, SHA256HMAC_SIZE);
if (memcmp(sigCalculated, signature, SHA256HMAC_SIZE) != 0) {
Serial.println(F("HMAC verification failed!"));
Serial.print(F("Expected: "));
printByteArray(signature, SHA256HMAC_SIZE);
return false;
}
Serial.println(F("HMAC verification successful!"));
return true;
}
#define WLED_HMAC_TEST_PW "guessihadthekeyafterall"
#define WLED_HMAC_TEST_PSK "a6f8488da62c5888d7f640276676e78da8639faf0495110b43e226b35ac37a4c"
uint8_t verifyHmacFromJsonString0Term(byte* jsonStr, size_t len) {
// Zero-terminate the JSON string (replace the last character, usually '}', with a null terminator temporarily)
byte lastChar = jsonStr[len-1];
jsonStr[len-1] = '\0';
uint8_t result = verifyHmacFromJsonStr((const char*)jsonStr, len);
jsonStr[len-1] = lastChar;
return result;
}
uint8_t verifyHmacFromJsonStr(const char* jsonStr, uint32_t maxLen) {
// Extract the signature from the JSON string
size_t jsonLen = strlen(jsonStr);
Serial.print(F("Length: "));
Serial.println(jsonLen);
if (jsonLen > maxLen) { // memory safety
Serial.print(F("JSON string too long!"));
Serial.print(F(", max: "));
Serial.println(maxLen);
return ERR_HMAC_GEN;
}
Serial.print(F("Received JSON: "));
Serial.println(jsonStr);
const char* macPos = strstr(jsonStr, "\"mac\":\"");
if (macPos == nullptr) {
Serial.println(F("No MAC found in JSON."));
return ERR_HMAC_MISS;
}
StaticJsonDocument<128> macDoc;
DeserializationError error = deserializeJson(macDoc, macPos +6);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.c_str());
return ERR_HMAC_GEN;
}
const char* mac = macDoc.as<const char*>();
if (mac == nullptr) {
Serial.println(F("Failed MAC JSON."));
return ERR_HMAC_GEN;
}
Serial.print(F("Received MAC: "));
Serial.println(mac);
// extract the message object from the JSON string
char* msgPos = strstr(jsonStr, "\"msg\":");
char* objStart = strchr(msgPos + 6, '{');
if (objStart == nullptr) {
Serial.println(F("Couldn't find msg object start."));
return ERR_HMAC_GEN;
}
size_t maxObjLen = jsonLen - (objStart - jsonStr);
Serial.print(F("Max object length: ")); Serial.println(maxObjLen);
int32_t objDepth = 0;
char* objEnd = nullptr;
for (size_t i = 0; i < maxObjLen; i++) {
Serial.write(objStart[i]);
if (objStart[i] == '{') objDepth++;
if (objStart[i] == '}') objDepth--;
if (objDepth == 0) {
Serial.print(F("Found msg object end: "));
Serial.println(i);
objEnd = objStart + i;
break;
}
}
if (objEnd == nullptr) {
Serial.println(F("Couldn't find msg object end."));
return ERR_HMAC_GEN;
}
// get nonce (note: the nonce implementation uses "nc" for the key instead of "n" to avoid conflicts with segment names)
const char* noncePos = strstr(objStart, "\"nc\":");
if (noncePos == nullptr || noncePos > objEnd) {
// note that it is critical to check that the nonce is within the "msg" object and thus authenticated
Serial.println(F("No nonce found in msg."));
return ERR_HMAC_GEN;
}
// Convert the MAC from hex string to byte array
size_t len = strlen(mac) / 2; // This will drop the last character if the string has an odd length
if (len != SHA256HMAC_SIZE) {
Serial.println(F("Received MAC not expected size!"));
return ERR_HMAC_GEN;
}
unsigned char macByteArray[len];
hexStringToByteArray(mac, macByteArray, len);
// Calculate the HMAC of the message object
if (!hmacVerify((const byte*)objStart, objEnd - objStart + 1, WLED_HMAC_TEST_PSK, macByteArray)) {
return ERR_HMAC;
}
// Nonce verification (Replay attack prevention)
{
StaticJsonDocument<128> nonceDoc;
DeserializationError error = deserializeJson(nonceDoc, noncePos +5);
if (error) {
Serial.print(F("deser nc failed: "));
Serial.println(error.c_str());
return ERR_HMAC_GEN;
}
JsonObject nonceObj = nonceDoc.as<JsonObject>();
if (nonceObj.isNull()) {
Serial.println(F("Failed nonce JSON."));
return ERR_HMAC_GEN;
}
const char* sessionId = nonceObj["sid"];
if (sessionId == nullptr) {
Serial.println(F("No session ID found in nonce."));
return ERR_HMAC_GEN;
}
uint32_t counter = nonceObj["c"] | 0;
if (counter == 0) {
Serial.println(F("No counter found in nonce."));
return ERR_HMAC_GEN;
}
if (counter > UINT32_MAX - 100) {
Serial.println(F("Counter too large."));
return ERR_NONCE;
}
byte sidBytes[SESSION_ID_SIZE] = {};
hexStringToByteArray(sessionId, sidBytes, SESSION_ID_SIZE);
uint8_t nonceResult = verifyNonce(sidBytes, counter);
return nonceResult ? nonceResult : ERR_NONE;
}
}
bool hmacTest() {
Serial.println(F("Testing HMAC..."));
unsigned long start = millis();
const char message[] = "Hello, World!";
const char psk[] = "d0c0ffeedeadbeef";
byte mac[SHA256HMAC_SIZE];
hmacSign((const byte*)message, strlen(message), psk, mac);
Serial.print(F("Took "));
Serial.print(millis() - start);
Serial.println(F("ms to sign message."));
Serial.print(F("MAC: "));
printByteArray(mac, SHA256HMAC_SIZE);
start = millis();
bool result = hmacVerify((const byte*)message, strlen(message), psk, mac);
Serial.print(F("Took "));
Serial.print(millis() - start);
Serial.println(F("ms to verify MAC."));
return result;
}
void printDuration(unsigned long start) {
unsigned long end = millis();
Serial.print(F("Took "));
Serial.print(end - start);
Serial.println(F(" ms."));
yield();
}
#define HMAC_BENCH_ITERATIONS 100
void hmacBenchmark(const char* message) {
Serial.print(F("Starting HMAC benchmark with message length:"));
Serial.println(strlen(message));
Serial.println(F("100 iterations signing message."));
unsigned long start = millis();
byte mac[SHA256HMAC_SIZE];
for (int i = 0; i < HMAC_BENCH_ITERATIONS; i++) {
hmacSign((const byte*)message, strlen(message), WLED_HMAC_TEST_PSK, mac);
}
printDuration(start);
Serial.println(F("100 iterations verifying message."));
start = millis();
for (int i = 0; i < HMAC_BENCH_ITERATIONS; i++) {
hmacVerify((const byte*)message, strlen(message), WLED_HMAC_TEST_PSK, mac);
}
printDuration(start);
}

View File

@@ -35,6 +35,7 @@
--sgp: "block";
--bmt: 0;
--sti: 42px;
--stp: 42px;
}
html {
@@ -143,7 +144,7 @@ button {
}
.huge {
font-size: 42px;
font-size: 60px !important;
}
.segt, .plentry TABLE {
@@ -468,7 +469,7 @@ button {
padding: 4px 2px;
position: relative;
opacity: 1;
transition: opacity .5s linear, height .25s, transform .25s;
transition: opacity .25s linear, height .2s, transform .2s;
}
.filter {
@@ -583,6 +584,10 @@ button {
z-index: 3;
}
#rover .ibtn {
margin: 5px;
}
#ndlt {
margin: 12px 0;
}
@@ -623,7 +628,7 @@ button {
padding-bottom: 8px;
}
.infobtn {
#info .ibtn {
margin: 5px;
}
@@ -847,7 +852,7 @@ input[type=range]::-moz-range-thumb {
width: 135px;
}
#nodes .infobtn {
#nodes .ibtn {
margin: 0;
}
@@ -1335,10 +1340,12 @@ TD .checkmark, TD .radiomark {
top: 42px;
}
#fxlist .lstI.selected,
#pallist .lstI.selected {
#fxlist .lstI.selected {
top: calc(var(--sti) + 42px);
}
#pallist .lstI.selected {
top: calc(var(--stp) + 42px);
}
dialog::backdrop {
backdrop-filter: blur(10px);
@@ -1353,10 +1360,12 @@ dialog {
color: var(--c-f);
}
#fxlist .lstI.sticky,
#pallist .lstI.sticky {
#fxlist .lstI.sticky {
top: var(--sti);
}
#pallist .lstI.sticky {
top: var(--stp);
}
/* list item content */
.lstIcontent {
@@ -1519,7 +1528,7 @@ dialog {
#info table .btn, #nodes table .btn {
width: 200px;
}
#info .infobtn, #nodes .infobtn {
#info .ibtn, #nodes .ibtn {
width: 145px;
}
#info div, #nodes div, #nodes a.btn {

View File

@@ -304,10 +304,10 @@
</div>
<div id="kv">Loading...</div><br>
<div>
<button class="btn infobtn" onclick="requestJson()">Refresh</button>
<button class="btn infobtn" onclick="toggleNodes()">Instance List</button>
<button class="btn infobtn" onclick="window.open(getURL('/update'),'_self');">Update WLED</button>
<button class="btn infobtn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button>
<button class="btn ibtn" onclick="requestJson()">Refresh</button>
<button class="btn ibtn" onclick="toggleNodes()">Instance List</button>
<button class="btn ibtn" onclick="window.open(getURL('/update'),'_self');">Update WLED</button>
<button class="btn ibtn" id="resetbtn" onclick="cnfReset()">Reboot WLED</button>
</div>
<br>
<span class="h">Made with&#32;<span id="heart">&#10084;&#xFE0E;</span>&#32;by&#32;<a href="https://github.com/Aircoookie/" target="_blank">Aircoookie</a>&#32;and the&#32;<a href="https://wled.discourse.group/" target="_blank">WLED community</a></span>
@@ -318,7 +318,7 @@
<div id="ndlt">WLED instances</div>
<div id="kn">Loading...</div>
<div style="position:sticky;bottom:0;">
<button class="btn infobtn" onclick="loadNodes()">Refresh</button>
<button class="btn ibtn" onclick="loadNodes()">Refresh</button>
</div>
</div>
@@ -331,8 +331,8 @@
<div id="lv">?</div><br><br>
To use built-in effects, use an override button below.<br>
You can return to realtime mode by pressing the star in the top left corner.<br>
<button class="btn" onclick="setLor(1)">Override once</button>
<button class="btn" onclick="setLor(2)">Override until reboot</button><br>
<button class="btn ibtn" onclick="setLor(1)">Override once</button>
<button class="btn ibtn" onclick="setLor(2)">Override until reboot</button><br>
<span class="h">For best performance, it is recommended to turn off the streaming source when not in use.</span>
</div>

View File

@@ -214,10 +214,6 @@ function loadSkinCSS(cId)
}
}
var useSRA = false;
var sraWindow = null;
var sraOrigin = '';
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
@@ -247,13 +243,6 @@ function onLoad()
var sett = localStorage.getItem('wledUiCfg');
if (sett) cfg = mergeDeep(cfg, JSON.parse(sett));
if (window.opener) {
// can't get opener origin due to cross-origin browser policy
//var openerOrigin = window.opener.location.origin;
//console.log("WLED-UI opener origin: " + openerOrigin);
window.opener.postMessage('{"wled-ui":"onload"}', '*'); //openerOrigin);
}
tooltip();
resetPUtil();
initFilters();
@@ -312,28 +301,6 @@ function onLoad()
});
}
function handleWindowMessageEvent(event) {
console.log(`Received message: ${event.data}`);
console.log(`origin: ${event.origin}`);
try {
var json = JSON.parse(event.data)
} catch (e) {
console.log(`Error parsing JSON: ${e}`);
return;
}
if (json['wled-rc'] === 'ready') {
useSRA = true;
sraWindow = event.source;
sraOrigin = event.origin;
} else if (json['wled-rc'] === 'hmac') {
console.log(`Received HMAC: ${json['mac']}`);
// Pass the message containing the HMAC to the ESP
requestJson(json);
}
}
onmessage = (event) => { handleWindowMessageEvent(event) };
function updateTablinks(tabI)
{
var tablinks = gEBCN("tablinks");
@@ -713,12 +680,6 @@ function parseInfo(i) {
} else {
gId("filter2D").classList.remove('hide');
}
if (useSRA && i.sid) {
if (sraWindow) {
sraWindow.postMessage(JSON.stringify({"wled-ui":"sid","sid":i.sid}), sraOrigin);
}
}
// if (i.noaudio) {
// gId("filterVol").classList.add("hide");
// gId("filterFreq").classList.add("hide");
@@ -1443,23 +1404,6 @@ function makeWS() {
if (isInfo) populateInfo(i);
} else
i = lastinfo;
if (json.error) {
if (json.error == 42) {
showToast('HMAC verification failed! Please make sure you used the right password!', true);
return;
} else if (json.error == 43) {
showToast("This light's control is password protected. Please access it through rc.wled.me", true);
return;
} else if (json.error == 41) {
showToast('Replayed message detected!', true);
return;
} else if (json.error == 40) {
showToast('Invalid nonce', true);
return;
}
showToast(json.error, true);
return;
}
var s = json.state ? json.state : json;
displayRover(i, s);
readState(s);
@@ -1759,12 +1703,6 @@ function requestJson(command=null)
if (req.length > 500 && lastinfo && lastinfo.arch == "esp8266") useWs = false; // esp8266 can only handle 500 bytes
};
if (command && useSRA && !command['mac']) { // secure remote access integration, need to get HMAC from rc.wled.me
// if we already have a command including a MAC, we are good to go
sraWindow.postMessage(JSON.stringify({"wled-ui":"hmac-req", "msg":command}), sraOrigin);
return; // TODO need a sort of pending indicator
}
if (useWs) {
ws.send(req?req:'{"v":true}');
return;
@@ -2890,7 +2828,12 @@ function search(field, listId = null) {
// restore default preset sorting if no search term is entered
if (!search) {
if (listId === 'pcont') { populatePresets(); return; }
if (listId === 'pallist') { populatePalettes(); return; }
if (listId === 'pallist') {
let id = parseInt(d.querySelector('#pallist input[name="palette"]:checked').value); // preserve selected palette
populatePalettes();
updateSelectedPalette(id);
return;
}
}
// clear filter if searching in fxlist
@@ -2949,18 +2892,25 @@ function initFilters() {
function filterFocus(e) {
const f = gId("filters");
if (e.type === "focus") f.classList.remove('fade'); // immediately show (still has transition)
// compute sticky top (with delay for transition)
setTimeout(() => {
const sti = parseInt(getComputedStyle(d.documentElement).getPropertyValue('--sti')) + (e.type === "focus" ? 1 : -1) * f.offsetHeight;
sCol('--sti', sti + "px");
}, 252);
const c = !!f.querySelectorAll("input[type=checkbox]:checked").length;
const h = f.offsetHeight;
const sti = parseInt(getComputedStyle(d.documentElement).getPropertyValue('--sti'));
if (e.type === "focus") {
// compute sticky top (with delay for transition)
if (!h) setTimeout(() => {
sCol('--sti', (sti+f.offsetHeight) + "px"); // has an unpleasant consequence on palette offset
}, 255);
f.classList.remove('fade'); // immediately show (still has transition)
}
if (e.type === "blur") {
setTimeout(() => {
if (e.target === document.activeElement && document.hasFocus()) return;
// do not hide if filter is active
if (gId("filters").querySelectorAll("input[type=checkbox]:checked").length) return;
f.classList.add('fade');
if (!c) {
// compute sticky top
sCol('--sti', (sti-h) + "px"); // has an unpleasant consequence on palette offset
f.classList.add('fade');
}
}, 255); // wait with hiding
}
}
@@ -2973,7 +2923,7 @@ function filterFx() {
gId("fxlist").querySelectorAll('.lstI').forEach((listItem,i) => {
const listItemName = listItem.querySelector('.lstIname').innerText;
let hide = false;
gId("filters").querySelectorAll("input[type=checkbox]").forEach((e) => { if (e.checked && !listItemName.includes(e.dataset.flt)) hide = true; });
gId("filters").querySelectorAll("input[type=checkbox]").forEach((e) => { if (e.checked && !listItemName.includes(e.dataset.flt)) hide = i>0 /*true*/; });
listItem.style.display = hide ? 'none' : '';
});
}

View File

@@ -67,7 +67,7 @@
} catch (e) {}
if (ws && ws.readyState === WebSocket.OPEN) {
//console.info("Peek uses top WS");
ws.send('{"lv":true}');
ws.send("{'lv':true}");
} else {
//console.info("Peek WS opening");
let l = window.location;
@@ -80,7 +80,7 @@
ws = new WebSocket(url+"/ws");
ws.onopen = function () {
//console.info("Peek WS open");
ws.send('{"lv":true}');
ws.send("{'lv':true}");
}
}
ws.binaryType = "arraybuffer";

View File

@@ -31,7 +31,7 @@
ws = top.window.ws;
} catch (e) {}
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send('{"lv":true}');
ws.send("{'lv':true}");
} else {
let l = window.location;
let pathn = l.pathname;
@@ -42,7 +42,7 @@
}
ws = new WebSocket(url+"/ws");
ws.onopen = ()=>{
ws.send('{"lv":true}');
ws.send("{'lv':true}");
}
}
ws.binaryType = "arraybuffer";

View File

@@ -119,7 +119,12 @@
var en = d.Sf.ABL.checked;
gId('abl').style.display = (en) ? 'inline':'none';
gId('psu2').style.display = (en) ? 'inline':'none';
if (!en) d.Sf.PPL.checked = false;
if (!en) {
// limiter disabled
d.Sf.PPL.checked = false;
// d.Sf.querySelectorAll("#mLC select[name^=LAsel]").forEach((e)=>{e.selectedIndex = 0;}); // select default LED mA
// d.Sf.querySelectorAll("#mLC input[name^=LA]").forEach((e)=>{e.min = 0; e.value = 0;}); // set min & value to 0
}
UI();
}
// enable per port limiter and calculate current
@@ -132,46 +137,51 @@
d.Sf.MA.min = abl && !ppl ? 250 : 0;
gId("psuMA").style.display = ppl ? 'none' : 'inline';
gId("ppldis").style.display = ppl ? 'inline' : 'none';
// set PPL minimum value and clear actual PPL limit if ABL disabled
// set PPL minimum value and clear actual PPL limit if ABL is disabled
d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,x)=>{
var n = String.fromCharCode((x<10?48:55)+x);
gId("PSU"+n).style.display = ppl ? "inline" : "none";
const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
const c = parseInt(d.Sf["LC"+n].value); //get LED count
i.min = ppl && !(isVir(t) || isAna(t)) ? 250 : 0;
if (!abl || isVir(t) || isAna(t)) i.value = 0;
i.min = ppl && isDig(t) ? 250 : 0;
if (!abl || !isDig(t)) i.value = 0;
else if (ppl) sumMA += parseInt(i.value,10);
else if (sDI) i.value = Math.round(parseInt(d.Sf.MA.value,10)*c/sDI);
});
if (ppl) d.Sf.MA.value = sumMA; // populate UI ABL value if PPL used
}
// enable and update LED Amps
function enLA(s,n)
{
const abl = d.Sf.ABL.checked;
const t = parseInt(d.Sf["LT"+n].value); // LED type SELECT
gId('LAdis'+n).style.display = s.selectedIndex==5 ? "inline" : "none";
if (s.value!=="0") d.Sf["LA"+n].value = s.value;
d.Sf["LA"+n].min = (isVir(t) || isAna(t)) ? 0 : 1;
gId('LAdis'+n).style.display = s.selectedIndex==5 ? "inline" : "none"; // show/hide custom mA field
if (s.value!=="0") d.Sf["LA"+n].value = s.value; // set value from select object
d.Sf["LA"+n].min = (!isDig(t) || !abl) ? 0 : 1; // set minimum value for validation
}
function setABL()
{
d.Sf.ABL.checked = parseInt(d.Sf.MA.value) > 0;
let en = parseInt(d.Sf.MA.value) > 0;
// check if ABL is enabled (max mA entered per output)
d.Sf.querySelectorAll("#mLC input[name^=MA]").forEach((i,n)=>{
if (parseInt(i.value) > 0) d.Sf.ABL.checked = true;
if (parseInt(i.value) > 0) en = true;
});
d.Sf.ABL.checked = en;
// select appropriate LED current
d.Sf.querySelectorAll("#mLC select[name^=LAsel]").forEach((sel,x)=>{
sel.value = 0; // set custom
var n = String.fromCharCode((x<10?48:55)+x);
switch (parseInt(d.Sf["LA"+n].value)) {
case 0: break; // disable ABL
case 15: sel.value = 15; break;
case 30: sel.value = 30; break;
case 35: sel.value = 35; break;
case 55: sel.value = 55; break;
case 255: sel.value = 255; break;
}
enLA(sel,n);
if (en)
switch (parseInt(d.Sf["LA"+n].value)) {
case 0: break; // disable ABL
case 15: sel.value = 15; break;
case 30: sel.value = 30; break;
case 35: sel.value = 35; break;
case 55: sel.value = 55; break;
case 255: sel.value = 255; break;
}
else sel.value = 0;
enLA(sel,n); // configure individual limiter
});
enABL();
gId('m1').innerHTML = maxM;
@@ -202,7 +212,7 @@
let gRGBW = false, memu = 0;
let busMA = 0;
let sLC = 0, sPC = 0, sDI = 0, maxLC = 0;
const ablEN = d.Sf.ABL.checked;
const abl = d.Sf.ABL.checked;
maxB = oMaxB; // TODO make sure we start with all possible buses
let setPinConfig = (n,t) => {
let p0d = "GPIO:";
@@ -249,12 +259,12 @@
var t = parseInt(s.value);
memu += getMem(t, n); // calc memory
setPinConfig(n,t);
gId("abl"+n).style.display = (!ablEN || isVir(t) || isAna(t)) ? "none" : "inline";
if (change) {
gId("abl"+n).style.display = (!abl || !isDig(t)) ? "none" : "inline"; // show/hide individual ABL settings
if (change) { // did we change LED type?
gId("rf"+n).checked = (gId("rf"+n).checked || t == 31); // LEDs require data in off state (mandatory for TM1814)
if (isAna(t)) d.Sf["LC"+n].value = 1; // for sanity change analog count just to 1 LED
d.Sf["LA"+n].min = (isVir(t) || isAna(t)) ? 0 : 1;
d.Sf["MA"+n].min = (isVir(t) || isAna(t)) ? 0 : 250;
d.Sf["LA"+n].min = (!isDig(t) || !abl) ? 0 : 1; // set minimum value for LED mA
d.Sf["MA"+n].min = (!isDig(t)) ? 0 : 250; // set minimum value for PSU mA
}
gId("rf"+n).onclick = mustR(t) ? (()=>{return false}) : (()=>{}); // prevent change change of "Refresh" checkmark when mandatory
gRGBW |= hasW(t); // RGBW checkbox
@@ -294,7 +304,7 @@
if (s+c > sLC) sLC = s+c; //update total count
if (c > maxLC) maxLC = c; //max per output
if (!isVir(t)) sPC += c; //virtual out busses do not count towards physical LEDs
if (!(isVir(t) || isAna(t))) {
if (isDig(t)) {
sDI += c; // summarize digital LED count
let maPL = parseInt(d.Sf["LA"+n].value);
if (maPL == 255) maPL = 12;

View File

@@ -156,6 +156,7 @@
<option value="20">AKST/AKDT (Anchorage)</option>
<option value="21">MX-CST</option>
<option value="22">PKT (Pakistan)</option>
<option value="23">BRT (Brasília)</option>
</select><br>
UTC offset: <input name="UO" type="number" min="-65500" max="65500" required> seconds (max. 18 hours)<br>
Current local time is <span class="times">unknown</span>.<br>

View File

@@ -17,7 +17,8 @@
<h2>WLED Software Update</h2>
<form method='POST' action='./update' id='uf' enctype='multipart/form-data' onsubmit="U()">
Installed version: <span class="sip">##VERSION##</span><br>
Download the latest binary: <a href="https://github.com/Aircoookie/WLED/releases" target="_blank">
Download the latest binary:&nbsp;<a href="https://github.com/Aircoookie/WLED/releases" target="_blank"
style="vertical-align: text-bottom; display: inline-flex;">
<img src="https://img.shields.io/github/release/Aircoookie/WLED.svg?style=flat-square"></a><br>
<input type='file' name='update' required><br> <!--should have accept='.bin', but it prevents file upload from android app-->
<button type="submit">Update!</button><br>

View File

@@ -95,16 +95,6 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
uint16_t approximateKelvinFromRGB(uint32_t rgb);
void setRandomColor(byte* rgb);
//crypto.cpp
void addSessionId(byte* sid);
char* byteArrayToHexString(char* hexString, const byte* byteArray, size_t byteLen);
void hmacSign(const byte* message, size_t msgLen, const char* pskHex, byte* signature);
bool hmacVerify(const byte* message, size_t msgLen, const char* pskHex, const byte* signature);
uint8_t verifyHmacFromJsonStr(const char* jsonStr, uint32_t maxLen);
uint8_t verifyHmacFromJsonString0Term(byte* jsonStr, size_t len);
bool hmacTest();
void hmacBenchmark(const char* message);
//dmx.cpp
void initDMX();
void handleDMX();
@@ -462,7 +452,7 @@ void serveSettingsJS(AsyncWebServerRequest* request);
//ws.cpp
void handleWs();
void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len);
void sendDataWs(AsyncWebSocketClient * client = nullptr, bool initialConnection = false);
void sendDataWs(AsyncWebSocketClient * client = nullptr);
//xml.cpp
void XML_response(Print& dest);

View File

@@ -123,6 +123,7 @@ void handleImprovPacket() {
}
checksum += next;
checksum &= 0xFF;
packetByte++;
}
}
@@ -193,24 +194,22 @@ void sendImprovIPRPCResult(ImprovRPCType type) {
}
void sendImprovInfoResponse() {
const char* bString =
#ifdef ESP8266
"esp8266"
#elif CONFIG_IDF_TARGET_ESP32C3
"esp32-c3"
#elif CONFIG_IDF_TARGET_ESP32S2
"esp32-s2"
#elif CONFIG_IDF_TARGET_ESP32S3
"esp32-s3";
#else // ESP32
"esp32";
#endif
;
char bString[32];
#ifdef ESP8266
strcpy(bString, "esp8266");
#else // ESP32
strncpy(bString, ESP.getChipModel(), 31);
#if CONFIG_IDF_TARGET_ESP32
bString[5] = '\0'; // disregard chip revision for classic ESP32
#else
bString[31] = '\0'; // just in case
#endif
strlwr(bString);
#endif
//Use serverDescription if it has been changed from the default "WLED", else mDNS name
bool useMdnsName = (strcmp(serverDescription, "WLED") == 0 && strlen(cmDNS) > 0);
char vString[20];
sprintf_P(vString, PSTR("0.15.0-b5/%i"), VERSION);
char vString[32];
sprintf_P(vString, PSTR("%s/%i"), versionString, VERSION);
const char *str[4] = {"WLED", vString, bString, useMdnsName ? cmDNS : serverDescription};
sendImprovRPCResult(ImprovRPCType::Request_Info, 4, str);

View File

@@ -454,21 +454,25 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
handleSet(nullptr, apireq, false); // may set stateChanged
}
// applying preset (2 cases: a) API call includes all preset values ("pd"), b) API only specifies preset ID ("ps"))
// Applying preset from JSON API has 2 cases: a) "pd" AKA "preset direct" and b) "ps" AKA "preset select"
// a) "preset direct" can only be an integer value representing preset ID. "preset direct" assumes JSON API contains the rest of preset content (i.e. from UI call)
// "preset direct" JSON can contain "ps" API (i.e. call from UI to cycle presets) in such case stateChanged has to be false (i.e. no "win" or "seg" API)
// b) "preset select" can be cycling ("1~5~""), random ("r" or "1~5r"), ID, etc. value allowed from JSON API. This type of call assumes no state changing content in API call
byte presetToRestore = 0;
// a) already applied preset content (requires "seg" or "win" but will ignore the rest)
if (!root[F("pd")].isNull() && stateChanged) {
// a) already applied preset content (requires "seg" or "win" but will ignore the rest)
currentPreset = root[F("pd")] | currentPreset;
if (root["win"].isNull()) presetCycCurr = currentPreset; // otherwise it was set in handleSet() [set.cpp]
if (root["win"].isNull()) presetCycCurr = currentPreset; // otherwise presetCycCurr was set in handleSet() [set.cpp]
presetToRestore = currentPreset; // stateUpdated() will clear the preset, so we need to restore it after
DEBUG_PRINTF_P(PSTR("Preset direct: %d\n"), currentPreset);
} else if (!root["ps"].isNull()) {
ps = presetCycCurr;
if (root["win"].isNull() && getVal(root["ps"], &ps, 0, 0) && ps > 0 && ps < 251 && ps != currentPreset) {
// we have "ps" call (i.e. from button or external API call) or "pd" that includes "ps" (i.e. from UI call)
if (root["win"].isNull() && getVal(root["ps"], &presetCycCurr, 0, 0) && presetCycCurr > 0 && presetCycCurr < 251 && presetCycCurr != currentPreset) {
DEBUG_PRINTF_P(PSTR("Preset select: %d\n"), presetCycCurr);
// b) preset ID only or preset that does not change state (use embedded cycling limits if they exist in getVal())
presetCycCurr = ps;
applyPreset(ps, callMode); // async load from file system (only preset ID was specified)
applyPreset(presetCycCurr, callMode); // async load from file system (only preset ID was specified)
return stateResponse;
}
} else presetCycCurr = currentPreset; // restore presetCycCurr
}
JsonObject playlist = root[F("playlist")];

View File

@@ -36,8 +36,9 @@ Timezone* tz;
#define TZ_ANCHORAGE 20
#define TZ_MX_CENTRAL 21
#define TZ_PAKISTAN 22
#define TZ_BRASILIA 23
#define TZ_COUNT 23
#define TZ_COUNT 24
#define TZ_INIT 255
byte tzCurrent = TZ_INIT; //uninitialized
@@ -135,6 +136,10 @@ static const std::pair<TimeChangeRule, TimeChangeRule> TZ_TABLE[] PROGMEM = {
/* TZ_PAKISTAN */ {
{Last, Sun, Mar, 1, 300}, //Pakistan Standard Time = UTC + 5 hours
{Last, Sun, Mar, 1, 300}
},
/* TZ_BRASILIA */ {
{Last, Sun, Mar, 1, -180}, //Brasília Standard Time = UTC - 3 hours
{Last, Sun, Mar, 1, -180}
}
};

View File

@@ -219,6 +219,7 @@ void WLED::loop()
busConfigs[i] = nullptr;
}
strip.finalizeInit(); // also loads default ledmap if present
BusManager::setBrightness(bri); // fix re-initialised bus' brightness #4005
if (aligned) strip.makeAutoSegments();
else strip.fixInvalidSegments();
doSerializeConfig = true;
@@ -359,13 +360,6 @@ void WLED::setup()
#if !defined(WLED_DEBUG) && defined(ARDUINO_ARCH_ESP32) && !defined(WLED_DEBUG_HOST) && ARDUINO_USB_CDC_ON_BOOT
Serial.setDebugOutput(false); // switch off kernel messages when using USBCDC
#endif
{
//hmacTest();
//const char testMsg[] = "WLED HMAC test!!";
//hmacBenchmark(testMsg);
//const char longMsg[] = "LoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIpsumLoremIps";
//hmacBenchmark(longMsg);
}
DEBUG_PRINTLN();
DEBUG_PRINTF_P(PSTR("---WLED %s %u INIT---\n"), versionString, VERSION);
DEBUG_PRINTLN();

View File

@@ -3,12 +3,12 @@
/*
Main sketch, global variable declarations
@title WLED project sketch
@version 0.15.0-b5
@version 0.15.0-b6
@author Christian Schwinne
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2409170
#define VERSION 2410140
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG
@@ -316,8 +316,6 @@ WLED_GLOBAL bool rlyOpenDrain _INIT(RLYODRAIN);
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
// WiFi CONFIG (all these can be changed via web UI, no need to set them here)

View File

@@ -287,50 +287,18 @@ void initServer()
bool verboseResponse = false;
bool isConfig = false;
Serial.println("JSON request");
Serial.println((const char*)request->_tempObject);
if (!verifyHmacFromJsonString0Term((byte*)request->_tempObject, request->contentLength())) {
//releaseJSONBufferLock();
serveJsonError(request, 401, ERR_DENIED);
return;
}
if (!requestJSONBufferLock(14)) {
serveJsonError(request, 503, ERR_NOBUF);
return;
}
DeserializationError error = deserializeJson(*pDoc, (uint8_t*)(request->_tempObject));
// // if enabled, calculate HMAC and verify it
// Serial.println(F("HMAC verification"));
// Serial.write((const char*)request->_tempObject, request->contentLength());
// // actually we need to verify the HMAC of the nested "msg" object
// if (strlen((const char*)request->_tempObject) > request->contentLength()) {
// Serial.println(F("HMAC verification failed: content is not null-terminated"));
// releaseJSONBufferLock();
// serveJsonError(request, 400, ERR_JSON);
// return;
// }
// // find the "msg" object in JSON
// char * msgPtr = strstr((const char*)request->_tempObject, "\"msg\":");
// if (msgPtr == NULL) {
// Serial.println(F("HMAC verification failed: no \"msg\" object found"));
// releaseJSONBufferLock();
// serveJsonError(request, 400, ERR_JSON);
// return;
// }
// char * objStart = strchr(msgPtr, '{');
JsonObject root = pDoc->as<JsonObject>();
if (error || root.isNull()) {
releaseJSONBufferLock();
serveJsonError(request, 400, ERR_JSON);
return;
}
// old 4-digit pin logic for settings authentication (no transport encryption)
if (root.containsKey("pin")) checkSettingsPIN(root["pin"].as<const char*>());
const String& url = request->url();
@@ -343,11 +311,7 @@ void initServer()
DEBUG_PRINTLN();
#endif
*/
if (root.containsKey("msg")) {
verboseResponse = deserializeState(root["msg"]);
} else {
verboseResponse = deserializeState(root);
}
verboseResponse = deserializeState(root);
} else {
if (!correctPIN && strlen(settingsPIN)>0) {
releaseJSONBufferLock();

View File

@@ -11,28 +11,12 @@ unsigned long wsLastLiveTime = 0;
#define WS_LIVE_INTERVAL 40
void sendWsError(AsyncWebSocketClient * client, uint8_t error)
{
if (!ws.count()) return;
char errorStr[16];
strcpy_P(errorStr, PSTR("{\"error\":"));
strcpy(errorStr + 9, itoa(error, errorStr + 9, 10));
strcat(errorStr + 10, "}");
if (client) {
client->text(errorStr); // ERR_NOBUF
} else {
ws.textAll(errorStr); // ERR_NOBUF
}
}
void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len)
{
if(type == WS_EVT_CONNECT){
//client connected
DEBUG_PRINTLN(F("WS client connected."));
sendDataWs(client, true);
sendDataWs(client);
} else if(type == WS_EVT_DISCONNECT){
//client disconnected
if (client->id() == wsLiveClientId) wsLiveClientId = 0;
@@ -52,48 +36,27 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
}
bool verboseResponse = false;
Serial.print(F("WS message: "));
Serial.write(data, len);
Serial.println();
if (len < 11 && memcmp(data, "{\"v\":true}", 10) == 0) {
// if the received value is just "{"v":true}", send only to this client
verboseResponse = true;
Serial.println(F("Simple state query."));
} else if (len < 13 && memcmp(data, "{\"lv\":", 6) == 0) {
wsLiveClientId = data[6] == 't' ? client->id() : 0;
} else {
// check HMAC, must do before parsing JSON as that modifies "data" to store strings
uint8_t hmacVerificationResult = verifyHmacFromJsonString0Term(data, len);
if (hmacVerificationResult != ERR_NONE) {
sendWsError(client, hmacVerificationResult);
return;
}
if (!requestJSONBufferLock(11)) {
sendWsError(client, 3); // ERR_NOBUF
return;
}
Serial.print(F("deser input: "));
Serial.write(data, len);
Serial.println();
DeserializationError error = deserializeJson(*pDoc, data, len);
JsonObject root = pDoc->as<JsonObject>();
if (error || root.isNull()) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.c_str());
//Serial.println(F("WS JSON parse F!"));
sendWsError(client, 9); // ERR_JSON
releaseJSONBufferLock();
return;
}
verboseResponse = deserializeState(root["msg"]);
releaseJSONBufferLock();
if (!requestJSONBufferLock(11)) {
client->text(F("{\"error\":3}")); // ERR_NOBUF
return;
}
DeserializationError error = deserializeJson(*pDoc, data, len);
JsonObject root = pDoc->as<JsonObject>();
if (error || root.isNull()) {
releaseJSONBufferLock();
return;
}
if (root["v"] && root.size() == 1) {
//if the received value is just "{"v":true}", send only to this client
verboseResponse = true;
} else if (root.containsKey("lv")) {
wsLiveClientId = root["lv"] ? client->id() : 0;
} else {
verboseResponse = deserializeState(root);
}
releaseJSONBufferLock();
if (!interfaceUpdateCallMode) { // individual client response only needed if no WS broadcast soon
if (verboseResponse) {
sendDataWs(client);
@@ -119,7 +82,7 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
if((info->index + len) == info->len){
if(info->final){
if(info->message_opcode == WS_TEXT) {
sendWsError(client, 9); // ERR_JSON we do not handle split packets right now
client->text(F("{\"error\":9}")); // ERR_JSON we do not handle split packets right now
}
}
}
@@ -138,7 +101,7 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
}
}
void sendDataWs(AsyncWebSocketClient * client, bool initialConnection)
void sendDataWs(AsyncWebSocketClient * client)
{
if (!ws.count()) return;
@@ -157,16 +120,6 @@ void sendDataWs(AsyncWebSocketClient * client, bool initialConnection)
JsonObject info = pDoc->createNestedObject("info");
serializeInfo(info);
if (initialConnection) {
char sid[SESSION_ID_SIZE*2+1] = {};
byte sidBytes[SESSION_ID_SIZE] = {};
addSessionId(sidBytes);
byteArrayToHexString(sid, sidBytes, SESSION_ID_SIZE);
Serial.print(F("New session ID: "));
Serial.println(sid);
info["sid"] = sid;
}
size_t len = measureJson(*pDoc);
DEBUG_PRINTF_P(PSTR("JSON buffer size: %u for WS request (%u).\n"), pDoc->memoryUsage(), len);

View File

@@ -172,7 +172,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
char fpass[l+1]; //fill password field with ***
fpass[l] = 0;
memset(fpass,'*',l);
settingsScript.printf_P(PSTR("addWiFi(\"%s\",\",%s\",0x%X,0x%X,0x%X);"),
settingsScript.printf_P(PSTR("addWiFi(\"%s\",\"%s\",0x%X,0x%X,0x%X);"),
multiWiFi[n].clientSSID,
fpass,
(uint32_t) multiWiFi[n].staticIP, // explicit cast required as this is a struct