Skip to content

Commit fd4d75f

Browse files
authored
Merge branch 'wled:main' into main
2 parents ab883a3 + e6716fe commit fd4d75f

File tree

10 files changed

+181
-51
lines changed

10 files changed

+181
-51
lines changed

.github/workflows/usermods.yml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: Usermod CI
2+
3+
on:
4+
push:
5+
paths:
6+
- usermods/**
7+
- .github/workflows/usermods.yml
8+
9+
jobs:
10+
11+
get_usermod_envs:
12+
name: Gather Usermods
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: actions/setup-python@v5
17+
with:
18+
python-version: '3.12'
19+
cache: 'pip'
20+
- name: Install PlatformIO
21+
run: pip install -r requirements.txt
22+
- name: Get default environments
23+
id: envs
24+
run: |
25+
echo "usermods=$(find usermods/ -name library.json | xargs dirname | xargs -n 1 basename | jq -R | grep -v PWM_fan | grep -v BME68X_v2| grep -v pixels_dice_tray | jq --slurp -c)" >> $GITHUB_OUTPUT
26+
outputs:
27+
usermods: ${{ steps.envs.outputs.usermods }}
28+
29+
30+
build:
31+
name: Build Enviornments
32+
runs-on: ubuntu-latest
33+
needs: get_usermod_envs
34+
strategy:
35+
fail-fast: false
36+
matrix:
37+
usermod: ${{ fromJSON(needs.get_usermod_envs.outputs.usermods) }}
38+
environment: [usermods_esp32, usermods_esp32c3, usermods_esp32s2, usermods_esp32s3]
39+
steps:
40+
- uses: actions/checkout@v4
41+
- name: Set up Node.js
42+
uses: actions/setup-node@v4
43+
with:
44+
node-version-file: '.nvmrc'
45+
cache: 'npm'
46+
- run: npm ci
47+
- name: Cache PlatformIO
48+
uses: actions/cache@v4
49+
with:
50+
path: |
51+
~/.platformio/.cache
52+
~/.buildcache
53+
build_output
54+
key: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-${{ hashFiles('wled00/**', 'usermods/**') }}
55+
restore-keys: pio-${{ runner.os }}-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'pio-scripts/output_bins.py') }}-
56+
- name: Set up Python
57+
uses: actions/setup-python@v5
58+
with:
59+
python-version: '3.12'
60+
cache: 'pip'
61+
- name: Install PlatformIO
62+
run: pip install -r requirements.txt
63+
- name: Add usermods environment
64+
run: |
65+
cp -v usermods/platformio_override.usermods.ini platformio_override.ini
66+
echo >> platformio_override.ini
67+
echo "custom_usermods = ${{ matrix.usermod }}" >> platformio_override.ini
68+
cat platformio_override.ini
69+
70+
- name: Build firmware
71+
run: pio run -e ${{ matrix.environment }}

usermods/Si7021_MQTT_HA/library.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
"build": { "libArchive": false },
44
"dependencies": {
55
"finitespace/BME280":"3.0.0",
6-
"adafruit/Adafruit Si7021 Library" : "1.5.3"
6+
"adafruit/Adafruit Si7021 Library" : "1.5.3",
7+
"SPI":"*",
8+
"adafruit/Adafruit BusIO": "1.17.1"
79
}
810
}

usermods/Si7021_MQTT_HA/readme.md

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,8 @@ SDA_PIN = 4;
4949

5050
## Software
5151

52-
Add to `build_flags` in platformio.ini:
52+
Add `Si7021_MQTT_HA` to custom_usermods
5353

54-
```
55-
-D USERMOD_SI7021_MQTT_HA
56-
```
57-
58-
Add to `lib_deps` in platformio.ini:
59-
60-
```
61-
adafruit/Adafruit Si7021 Library @ 1.4.0
62-
BME280@~3.0.0
63-
```
6454

6555
# Credits
6656

usermods/buzzer/buzzer.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@
55

66
#define USERMOD_ID_BUZZER 900
77
#ifndef USERMOD_BUZZER_PIN
8+
#ifdef GPIO_NUM_32
89
#define USERMOD_BUZZER_PIN GPIO_NUM_32
10+
#else
11+
#define USERMOD_BUZZER_PIN 21
12+
#endif
913
#endif
1014

1115
/*
1216
* Usermods allow you to add own functionality to WLED more easily
1317
* See: https://github.com/wled-dev/WLED/wiki/Add-own-functionality
1418
*
15-
* Using a usermod:
16-
* 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
17-
* 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
1819
*/
1920

2021
class BuzzerUsermod : public Usermod {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[platformio]
2+
default_envs = usermods_esp32, usermods_esp32c3, usermods_esp32s2, usermods_esp32s3
3+
4+
[env:usermods_esp32]
5+
extends = env:esp32dev_V4
6+
custom_usermods = ${usermods.custom_usermods}
7+
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat
8+
9+
10+
[env:usermods_esp32c3]
11+
extends = env:esp32c3dev
12+
board = esp32-c3-devkitm-1
13+
custom_usermods = ${usermods.custom_usermods}
14+
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat
15+
16+
17+
[env:usermods_esp32s2]
18+
extends = env:lolin_s2_mini
19+
custom_usermods = ${usermods.custom_usermods}
20+
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat
21+
22+
23+
[env:usermods_esp32s3]
24+
extends = env:esp32s3dev_16MB_opi
25+
custom_usermods = ${usermods.custom_usermods}
26+
board_build.partitions = ${esp32.extreme_partitions} ; We're gonna need a bigger boat
27+
28+
29+
30+
[usermods]
31+
# Added in CI

wled00/FX.cpp

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6091,9 +6091,7 @@ uint16_t mode_2Dscrollingtext(void) {
60916091
}
60926092

60936093
char text[WLED_MAX_SEGNAME_LEN+1] = {'\0'};
6094-
if (SEGMENT.name) for (size_t i=0,j=0; i<strlen(SEGMENT.name); i++) if (SEGMENT.name[i]>31 && SEGMENT.name[i]<128) text[j++] = SEGMENT.name[i];
6095-
const bool zero = strchr(text, '0') != nullptr;
6096-
6094+
size_t result_pos = 0;
60976095
char sec[5];
60986096
int AmPmHour = hour(localTime);
60996097
bool isitAM = true;
@@ -6105,27 +6103,62 @@ uint16_t mode_2Dscrollingtext(void) {
61056103
sprintf_P(sec, PSTR(":%02d"), second(localTime));
61066104
}
61076105

6108-
if (!strlen(text)) { // fallback if empty segment name: display date and time
6106+
size_t len = 0;
6107+
if (SEGMENT.name) len = strlen(SEGMENT.name); // note: SEGMENT.name is limited to WLED_MAX_SEGNAME_LEN
6108+
if (len == 0) { // fallback if empty segment name: display date and time
61096109
sprintf_P(text, PSTR("%s %d, %d %d:%02d%s"), monthShortStr(month(localTime)), day(localTime), year(localTime), AmPmHour, minute(localTime), sec);
61106110
} else {
6111-
if (text[0] == '#') for (auto &c : text) c = std::toupper(c);
6112-
if (!strncmp_P(text,PSTR("#DATE"),5)) sprintf_P(text, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime));
6113-
else if (!strncmp_P(text,PSTR("#DDMM"),5)) sprintf_P(text, zero?PSTR("%02d.%02d") :PSTR("%d.%d"), day(localTime), month(localTime));
6114-
else if (!strncmp_P(text,PSTR("#MMDD"),5)) sprintf_P(text, zero?PSTR("%02d/%02d") :PSTR("%d/%d"), month(localTime), day(localTime));
6115-
else if (!strncmp_P(text,PSTR("#TIME"),5)) sprintf_P(text, zero?PSTR("%02d:%02d%s") :PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
6116-
else if (!strncmp_P(text,PSTR("#HHMM"),5)) sprintf_P(text, zero?PSTR("%02d:%02d") :PSTR("%d:%02d"), AmPmHour, minute(localTime));
6117-
else if (!strncmp_P(text,PSTR("#HH"),3)) sprintf (text, zero? ("%02d") : ("%d"), AmPmHour);
6118-
else if (!strncmp_P(text,PSTR("#MM"),3)) sprintf (text, zero? ("%02d") : ("%d"), minute(localTime));
6119-
else if (!strncmp_P(text,PSTR("#SS"),3)) sprintf (text, ("%02d") , second(localTime));
6120-
else if (!strncmp_P(text,PSTR("#DD"),3)) sprintf (text, zero? ("%02d") : ("%d"), day(localTime));
6121-
else if (!strncmp_P(text,PSTR("#DAY"),4)) sprintf (text, ("%s") , dayShortStr(day(localTime)));
6122-
else if (!strncmp_P(text,PSTR("#DDDD"),5)) sprintf (text, ("%s") , dayStr(day(localTime)));
6123-
else if (!strncmp_P(text,PSTR("#DAYL"),5)) sprintf (text, ("%s") , dayStr(day(localTime)));
6124-
else if (!strncmp_P(text,PSTR("#MO"),3)) sprintf (text, zero? ("%02d") : ("%d"), month(localTime));
6125-
else if (!strncmp_P(text,PSTR("#MON"),4)) sprintf (text, ("%s") , monthShortStr(month(localTime)));
6126-
else if (!strncmp_P(text,PSTR("#MMMM"),5)) sprintf (text, ("%s") , monthStr(month(localTime)));
6127-
else if (!strncmp_P(text,PSTR("#YY"),3)) sprintf (text, ("%02d") , year(localTime)%100);
6128-
else if (!strncmp_P(text,PSTR("#YYYY"),5)) sprintf_P(text, zero?PSTR("%04d") : ("%d"), year(localTime));
6111+
size_t i = 0;
6112+
while (i < len) {
6113+
if (SEGMENT.name[i] == '#') {
6114+
char token[7]; // copy up to 6 chars + null terminator
6115+
bool zero = false; // a 0 suffix means display leading zeros
6116+
size_t j = 0;
6117+
while (j < 6 && i + j < len) {
6118+
token[j] = std::toupper(SEGMENT.name[i + j]);
6119+
if(token[j] == '0')
6120+
zero = true; // 0 suffix found. Note: there is an edge case where a '0' could be part of a trailing text and not the token, handling it is not worth the effort
6121+
j++;
6122+
}
6123+
token[j] = '\0';
6124+
int advance = 5; // number of chars to advance in 'text' after processing the token
6125+
6126+
// Process token
6127+
char temp[32];
6128+
if (!strncmp_P(token,PSTR("#DATE"),5)) sprintf_P(temp, zero?PSTR("%02d.%02d.%04d"):PSTR("%d.%d.%d"), day(localTime), month(localTime), year(localTime));
6129+
else if (!strncmp_P(token,PSTR("#DDMM"),5)) sprintf_P(temp, zero?PSTR("%02d.%02d") :PSTR("%d.%d"), day(localTime), month(localTime));
6130+
else if (!strncmp_P(token,PSTR("#MMDD"),5)) sprintf_P(temp, zero?PSTR("%02d/%02d") :PSTR("%d/%d"), month(localTime), day(localTime));
6131+
else if (!strncmp_P(token,PSTR("#TIME"),5)) sprintf_P(temp, zero?PSTR("%02d:%02d%s") :PSTR("%2d:%02d%s"), AmPmHour, minute(localTime), sec);
6132+
else if (!strncmp_P(token,PSTR("#HHMM"),5)) sprintf_P(temp, zero?PSTR("%02d:%02d") :PSTR("%d:%02d"), AmPmHour, minute(localTime));
6133+
else if (!strncmp_P(token,PSTR("#YYYY"),5)) sprintf_P(temp, PSTR("%04d") , year(localTime));
6134+
else if (!strncmp_P(token,PSTR("#MONL"),5)) sprintf (temp, ("%s") , monthStr(month(localTime)));
6135+
else if (!strncmp_P(token,PSTR("#DDDD"),5)) sprintf (temp, ("%s") , dayStr(weekday(localTime)));
6136+
else if (!strncmp_P(token,PSTR("#YY"),3)) { sprintf (temp, ("%02d") , year(localTime)%100); advance = 3; }
6137+
else if (!strncmp_P(token,PSTR("#HH"),3)) { sprintf (temp, zero? ("%02d") : ("%d"), AmPmHour); advance = 3; }
6138+
else if (!strncmp_P(token,PSTR("#MM"),3)) { sprintf (temp, zero? ("%02d") : ("%d"), minute(localTime)); advance = 3; }
6139+
else if (!strncmp_P(token,PSTR("#SS"),3)) { sprintf (temp, zero? ("%02d") : ("%d"), second(localTime)); advance = 3; }
6140+
else if (!strncmp_P(token,PSTR("#MON"),4)) { sprintf (temp, ("%s") , monthShortStr(month(localTime))); advance = 4; }
6141+
else if (!strncmp_P(token,PSTR("#MO"),3)) { sprintf (temp, zero? ("%02d") : ("%d"), month(localTime)); advance = 3; }
6142+
else if (!strncmp_P(token,PSTR("#DAY"),4)) { sprintf (temp, ("%s") , dayShortStr(weekday(localTime))); advance = 4; }
6143+
else if (!strncmp_P(token,PSTR("#DD"),3)) { sprintf (temp, zero? ("%02d") : ("%d"), day(localTime)); advance = 3; }
6144+
else { temp[0] = '#'; temp[1] = '\0'; zero = false; advance = 1; } // Unknown token, just copy the #
6145+
6146+
if(zero) advance++; // skip the '0' suffix
6147+
size_t temp_len = strlen(temp);
6148+
if (result_pos + temp_len < WLED_MAX_SEGNAME_LEN) {
6149+
strcpy(text + result_pos, temp);
6150+
result_pos += temp_len;
6151+
}
6152+
6153+
i += advance;
6154+
}
6155+
else {
6156+
if (result_pos < WLED_MAX_SEGNAME_LEN) {
6157+
text[result_pos++] = SEGMENT.name[i++]; // no token, just copy char
6158+
} else
6159+
break; // buffer full
6160+
}
6161+
}
61296162
}
61306163

61316164
const int numberOfLetters = strlen(text);

wled00/cfg.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -775,15 +775,15 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
775775

776776
static const char s_cfg_json[] PROGMEM = "/cfg.json";
777777

778-
void deserializeConfigFromFS() {
778+
bool deserializeConfigFromFS() {
779779
[[maybe_unused]] bool success = deserializeConfigSec();
780780
#ifdef WLED_ADD_EEPROM_SUPPORT
781781
if (!success) { //if file does not exist, try reading from EEPROM
782782
deEEPSettings();
783783
}
784784
#endif
785785

786-
if (!requestJSONBufferLock(1)) return;
786+
if (!requestJSONBufferLock(1)) return false;
787787

788788
DEBUG_PRINTLN(F("Reading settings from /cfg.json..."));
789789

@@ -795,7 +795,7 @@ void deserializeConfigFromFS() {
795795
bool needsSave = deserializeConfig(root, true);
796796
releaseJSONBufferLock();
797797

798-
if (needsSave) serializeConfigToFS(); // usermods required new parameters
798+
return needsSave;
799799
}
800800

801801
void serializeConfigToFS() {

wled00/fcn_declare.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ void IRAM_ATTR touchButtonISR();
2525

2626
//cfg.cpp
2727
bool deserializeConfig(JsonObject doc, bool fromFS = false);
28-
void deserializeConfigFromFS();
28+
bool deserializeConfigFromFS();
2929
bool deserializeConfigSec();
3030
void serializeConfig(JsonObject doc);
3131
void serializeConfigToFS();

wled00/pin_manager.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ bool PinManager::allocateMultiplePins(const managed_pin_type * mptArray, byte ar
100100
// as this can greatly simplify configuration arrays
101101
continue;
102102
}
103-
if (!isPinOk(gpio, mptArray[i].isOutput)) {
103+
// allow any GPIO for Ethernet (compile time assigned)
104+
if (!(isPinOk(gpio, mptArray[i].isOutput) || tag==PinOwner::Ethernet)) {
104105
DEBUG_PRINTF_P(PSTR("PIN ALLOC: FAIL Invalid pin attempted to be allocated: GPIO %d as %s\n."), gpio, mptArray[i].isOutput ? PSTR("output"): PSTR("input"));
105106
shouldFail = true;
106107
}
@@ -228,19 +229,18 @@ bool PinManager::isPinOk(byte gpio, bool output)
228229
#else
229230

230231
if ((strncmp_P(PSTR("ESP32-U4WDH"), ESP.getChipModel(), 11) == 0) || // this is the correct identifier, but....
231-
(strncmp_P(PSTR("ESP32-PICO-D2"), ESP.getChipModel(), 13) == 0)) { // https://github.com/espressif/arduino-esp32/issues/10683
232+
(strncmp_P(PSTR("ESP32-PICO-D"), ESP.getChipModel(), 12) == 0)) { // https://github.com/espressif/arduino-esp32/issues/10683
232233
// this chip has 4 MB of internal Flash and different packaging, so available pins are different!
233-
if (((gpio > 5) && (gpio < 9)) || (gpio == 11))
234-
return false;
234+
if ((gpio > 5 && gpio < 9) || gpio == 11) return false; // U4WDH/PICO-D2 & PICO-D4: GPIO 6, 7, 8, 11 are used for SPI flash; 9 & 10 are free
235+
if (gpio == 16 || gpio == 17) return false; // U4WDH/PICO-D?: GPIO 16 and 17 are used for PSRAM
236+
} else if (strncmp_P(PSTR("ESP32-PICO-V3"), ESP.getChipModel(), 13) == 0) {
237+
if (gpio == 6 || gpio == 11) return false; // PICO-V3: uses GPIO 6 and 11 for flash
238+
if (strstr_P(ESP.getChipModel(), PSTR("V3-02")) != nullptr && (gpio == 9 || gpio == 10)) return false; // PICO-V3-02: uses GPIO 9 and 10 for PSRAM; 7, 8 are free
235239
} else {
236240
// for classic ESP32 (non-mini) modules, these are the SPI flash pins
237241
if (gpio > 5 && gpio < 12) return false; //SPI flash pins
238242
}
239-
240-
if (((strncmp_P(PSTR("ESP32-PICO"), ESP.getChipModel(), 10) == 0) ||
241-
(strncmp_P(PSTR("ESP32-U4WDH"), ESP.getChipModel(), 11) == 0))
242-
&& (gpio == 16 || gpio == 17)) return false; // PICO-D4/U4WDH: gpio16+17 are in use for onboard SPI FLASH
243-
if (gpio == 16 || gpio == 17) return !psramFound(); //PSRAM pins on ESP32 (these are IO)
243+
if (gpio == 16 || gpio == 17) return !psramFound(); // PSRAM pins on ESP32-D0WDR2-V3 (these are IO)
244244
#endif
245245
if (output) return digitalPinCanOutput(gpio);
246246
else return true;

wled00/wled.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ void WLED::setup()
423423
multiWiFi.push_back(WiFiConfig(CLIENT_SSID,CLIENT_PASS)); // initialise vector with default WiFi
424424

425425
DEBUG_PRINTLN(F("Reading config"));
426-
deserializeConfigFromFS();
426+
bool needsCfgSave = deserializeConfigFromFS();
427427
DEBUG_PRINTF_P(PSTR("heap %u\n"), ESP.getFreeHeap());
428428

429429
#if defined(STATUSLED) && STATUSLED>=0
@@ -443,6 +443,8 @@ void WLED::setup()
443443
UsermodManager::setup();
444444
DEBUG_PRINTF_P(PSTR("heap %u\n"), ESP.getFreeHeap());
445445

446+
if (needsCfgSave) serializeConfigToFS(); // usermods required new parameters; need to wait for strip to be initialised #4752
447+
446448
if (strcmp(multiWiFi[0].clientSSID, DEFAULT_CLIENT_SSID) == 0)
447449
showWelcomePage = true;
448450
WiFi.persistent(false);

0 commit comments

Comments
 (0)