Skip to content

Add compile-time WiFi hard-off mode (WLED_FORCE_WIFI_OFF)#5394

Open
Stargazer2026 wants to merge 12 commits into
wled:mainfrom
Stargazer2026:main
Open

Add compile-time WiFi hard-off mode (WLED_FORCE_WIFI_OFF)#5394
Stargazer2026 wants to merge 12 commits into
wled:mainfrom
Stargazer2026:main

Conversation

@Stargazer2026
Copy link
Copy Markdown

@Stargazer2026 Stargazer2026 commented Feb 23, 2026

Motivation

  • Provide a compile-time "flight mode" option to completely disable the WiFi radio (WIFI_OFF) so builds for Ethernet-only or radio-restricted environments do not start STA or AP interfaces.
  • This saves 5kb for the resulting firmware.bin binary.

Description

  • Add a commented compile-time flag WLED_FORCE_WIFI_OFF to wled.h and document it in my_config_sample.h so users can enable it from my_config.h or build flags.
  • In wled.cpp, change startup networking to call WiFi.mode(WIFI_OFF) when WLED_FORCE_WIFI_OFF is defined instead of starting STA scanning and findWiFi(true).
  • Short-circuit initAP() to immediately return when WLED_FORCE_WIFI_OFF is defined so no softAP is created.
  • Make initConnection() early-exit when WLED_FORCE_WIFI_OFF is defined after calling WiFi.disconnect(true) and forcing WiFi.mode(WIFI_OFF), and add debug prints to indicate the forced-off state.

Summary by CodeRabbit

  • New Features

    • Optional build-time WiFi radio disable (requires Ethernet); disabled by default.
    • Emergency access-point activation via a configured momentary button remains available.
  • Bug Fixes / Behavior

    • Startup, scan, and reconnect behavior now respect the optional WiFi-off mode, preventing normal WiFi scans/connections when enabled.
  • Chores

    • Added build-time validation warnings to highlight incompatible or missing button/ethernet configurations.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 23, 2026

Walkthrough

This PR adds a WLED_FORCE_WIFI_OFF compile-time feature that hard-disables regular WiFi startup and scanning while preserving emergency AP recovery via button press. The feature requires Ethernet to be enabled and validates button configuration at build time. Runtime helpers gate startup, scans, and reconnect behavior when forced-off is active.

Changes

WiFi-off mode feature

Layer / File(s) Summary
Feature flag definition and build-time validation
wled00/wled.h, wled00/my_config_sample.h
Commented feature flag added to sample config and feature-disable section. Build-time checks require WLED_USE_ETHERNET and warn/error on button pin/type configuration that would prevent emergency AP long-press recovery.
WiFi-off helpers and startup integration
wled00/wled.cpp
Adds internal helpers (initRegularWiFiStartup, findWiFiIfEnabled, wifiScanRunning) that choose forced-off WIFI_OFF + AP button-only vs. regular STA startup and suppress scanning when forced-off. Startup now calls initRegularWiFiStartup().
Connection and reconnect flow guards
wled00/wled.cpp
initConnection() short-circuits when forced-off; handleConnection() uses wifiScanRunning() and findWiFiIfEnabled() to avoid scans and preserve selectedWiFi under forced-off mode.
WiFi and Ethernet disconnect event handling
wled00/network.cpp
Adds restartWiFiScanIfEnabled(requireMultiWiFi) to centralize rescan gating; STA and Ethernet disconnect handlers now call this helper instead of inline conditions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • wled/WLED#5194: Modifies initConnection() for WPA-Enterprise handling; overlaps connection-initialization code paths with this PR.
  • wled/WLED#4558: Changes WLED::initConnection() timing behavior; related to connection startup adjustments made here.

Suggested reviewers

  • netmindz
  • rapek412-prog
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a compile-time WiFi hard-off mode feature flag. It accurately reflects the primary objective without unnecessary detail.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
wled00/wled.cpp (1)

641-643: Consider adding a debug print for consistency with the other two WLED_FORCE_WIFI_OFF blocks.

initAP() silently returns, while setup() (line 515) and initConnection() (line 686) both emit a DEBUG_PRINTLN when forced off. This makes it harder to trace the suppression in debug logs.

✨ Proposed addition
 `#ifdef` WLED_FORCE_WIFI_OFF
+  DEBUG_PRINTLN(F("WLED_FORCE_WIFI_OFF: skipping AP init."));
   return;
 `#endif`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/wled.cpp` around lines 641 - 643, The initAP() function currently
returns silently when WLED_FORCE_WIFI_OFF is defined; add a DEBUG_PRINTLN call
consistent with the other blocks (setup() and initConnection()) so the
suppression is visible in logs. Locate the WLED_FORCE_WIFI_OFF guard inside
initAP() and insert a DEBUG_PRINTLN with a clear message (e.g.,
"WLED_FORCE_WIFI_OFF: AP init suppressed") immediately before the return so
behavior matches the other WLED_FORCE_WIFI_OFF blocks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@wled00/wled.cpp`:
- Around line 641-643: The initAP() function currently returns silently when
WLED_FORCE_WIFI_OFF is defined; add a DEBUG_PRINTLN call consistent with the
other blocks (setup() and initConnection()) so the suppression is visible in
logs. Locate the WLED_FORCE_WIFI_OFF guard inside initAP() and insert a
DEBUG_PRINTLN with a clear message (e.g., "WLED_FORCE_WIFI_OFF: AP init
suppressed") immediately before the return so behavior matches the other
WLED_FORCE_WIFI_OFF blocks.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83f45cd and f2e7d79.

📒 Files selected for processing (1)
  • wled00/wled.cpp

@netmindz netmindz added connectivity Issue regarding protocols, WiFi connection or availability of interfaces enhancement labels Feb 23, 2026
@softhack007
Copy link
Copy Markdown
Member

softhack007 commented Feb 23, 2026

If we add this PR, then there should be a HUGE warning that "no wifi" means "no wifi connection at all"

  • no way to configure ethernet via WLED-AP
  • if ethernet settings is are bad, the only recovery option is to flash wled via USB
  • if ethernet stop working (for example bug Conflict between Ethernet and PDM mic on main #5391, or users accidentally configure usermods to use ethernet pins) then there is no easy recovery (see above)
  • you can't use the "long button press" method to open an "emergency" access point (WLED-AP)

@softhack007
Copy link
Copy Markdown
Member

softhack007 commented Feb 23, 2026

(joke) maybe rename the build flag to WLED_FORCE_WIFI_OFF_AND_SHOOT_YOURSELF_IN_THE_FOOT 😉

I'm not saying NO to this PR - there might be special use cases where its helpful.

But I would prefer a less "hard off" approach, maybe by adding a "wifi mode" user setting next to "AP mode", and keep at least "AP mode by button" operational for emergency cases.

@DedeHai
Copy link
Copy Markdown
Collaborator

DedeHai commented Feb 23, 2026

can you please elaborate on the advantages?

@Stargazer2026
Copy link
Copy Markdown
Author

Thank you for reviewing this.

The intention of this PR is not to change default behavior. WiFi remains enabled unless explicitly excluded via a compile time flag.

The primary motivation is deterministic Ethernet only deployments. In certain installations, especially fixed architectural or PoE based setups, it is desirable to completely exclude the WiFi stack at build time rather than simply not configuring it.

This approach has several technical advantages:

  1. Reduced attack surface, the WiFi stack is not activated at all
  2. Deterministic network behavior, no AP fallback and no unintended RF activity
  3. Slight reduction in firmware size (measured delta: 5 kB)
  4. No runtime configuration complexity

The decision to implement this as a compile time exclusion instead of a runtime toggle was intentional.

A runtime option would require:

  • additional UI configuration
  • persistence handling
  • conditional logic in multiple code paths
  • interaction with AP fallback logic
  • expanded testing surface

By contrast, this implementation:

  • introduces no additional runtime branches
  • does not extend the configuration UI
  • does not increase support surface
  • keeps the code delta small and isolated
  • leaves default builds completely unchanged

The feature is strictly opt in for specialized builds and does not affect existing users or standard firmware releases.

If needed, I am happy to provide exact firmware size comparisons or adjust naming and placement of the build flag to better match existing conventions.

@DedeHai
Copy link
Copy Markdown
Collaborator

DedeHai commented Feb 24, 2026

The only point I am getting is the "radio restricted area" thing, but that is very niche. All other points can be addressed in a different way and the firmware size reduction is not really helpful unless you pack it full of usermods.
On the other hand this make it really unflexible and if anything in your network changes you need to recompile - so I agree with @softhack007 that the AP fallback should still be there and if it is, the rest does not really make sense.

@Stargazer2026
Copy link
Copy Markdown
Author

Thanks for the clarification.

I understand that this is a niche use case. I am not trying to position it as something broadly needed for typical installations.

My main point is that the implementation cost for the project is extremely low:

  • 19 lines of code
  • isolated behind a compile-time flag
  • no changes to default builds
  • no runtime branches added
  • no UI changes
  • no impact on the existing AP fallback logic in standard firmware

This does not introduce additional behavioral complexity in the normal code path. If the flag is not set, the firmware behaves exactly as before.

Regarding flexibility: yes, a recompile is required if the network topology changes. But since this is a compile-time exclusion, it is explicitly targeting fixed-purpose deployments where that trade-off is intentional.

In other words, this is not meant as a user-facing feature but as a specialization hook for constrained or regulated environments.

Given the very small and well-contained delta, I would argue that the long-term maintenance overhead should be close to zero, especially compared to runtime configuration features.

If you still feel this increases project surface unnecessarily, I completely understand. I just wanted to clarify that the implementation was intentionally minimal and non-invasive.

@DedeHai
Copy link
Copy Markdown
Collaborator

DedeHai commented Feb 24, 2026

In other words, this is not meant as a user-facing feature but as a specialization hook for constrained or regulated environments.

I do understand your intend and I can see its usefulness for some cases, however my concern is this:
many users do "random stuff without understanding consequences" and if they think this feature is something they want but it is more of a "know what you do" thing there will be complaints and questions on why its not working right. Just one example: it assumes you have DHCP. If you don't it wont even show up in the network without compiling that in too.

edit:
a solution would be to have this flag produce a build error with this big fat warning included and saying that this build error line must be deleted, therefore forcing a user to aknowledge and bear the consequences. It also hinders online compilers from using it, making sure only advanced users get access to this flag.

edit2:
this still wont keep users from posting bins and using those though.

Comment thread wled00/wled.h Outdated
#endif

// You can choose some of these features to disable:
//#define WLED_FORCE_WIFI_OFF // saves 5kb, hard-disable WiFi radio entirely (WIFI_OFF). AP/STA will not start.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its better to not add the flag here. It will be too tempting for "cool lets try this" users to brick their boards.

For documentation, i'd prefer a PR in WLED-Docs and describe the new flag as an expert option.

Comment thread wled00/my_config_sample.h Outdated
#define CLIENT_PASS "Your_Password"
*/

//#define WLED_FORCE_WIFI_OFF // Hard disable WiFi radio (WIFI_OFF): no STA and no AP.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove the flag here, too (see my comment on wled.h)

@softhack007
Copy link
Copy Markdown
Member

softhack007 commented Feb 24, 2026

A few use case questions that came to my mind:

  • if you take a freshly wiped board (esptool erase_flash), then install WLED compiled with -D WLED_USE_ETHERNET -D WLED_FORCE_WIFI_OFF, how can people configure ethernet?
    Ethernet type, static/dynamic IP, gateway, DNS server, etc?
  • similar scenario, but with a fully configured ethernet: someone presses button #0 for longer than 10 seconds => factory reset => device lost.
  • Radio-restricted environments (security?): -D WLED_FORCE_WIFI_OFF should also include -D WLED_DISABLE_OTA and -D WLED_DISABLE_ESPNOW, right?
    OTA would be the easiest way to re-enable wifi radio if i want to break out of the restricted zone.
  • Should we add be a compile time #error when -D WLED_FORCE_WIFI_OFF is used without -D WLED_USE_ETHERNET ?

@softhack007
Copy link
Copy Markdown
Member

softhack007 commented Feb 24, 2026

@Stargazer2026 please don't get me wrong - I'm not trying to corner your PR. This is just an attempt to find out what could be the consequences of having WLED without wifi option. See it as a free "safety analysis", that can be used to improve the safety and availability in non-normal cases.

Actually, this kind of analysis is part of my "day time job", so don't take my questions personally :-)

@netmindz
Copy link
Copy Markdown
Member

I can completely understand that outside of the home setting, that to have devices that might run on ethernet 99% of the time, the suddenly swap to AP mode in an office, nightclub etc just because someone rebooted the router is a valid concern, so having the option to select ethernet only makes sense.

I'm less clear on the use case of when you would use WLED in a setting where you couldn't (legally?) have an active radio and if this change would be robust enough to meet that compliance 100% - it's certainly a feature I wouldn't want the team to take responsibility for ensuring every release never activates the radio at all

@softhack007
Copy link
Copy Markdown
Member

softhack007 commented Feb 24, 2026

it's certainly a feature I wouldn't want the team to take responsibility for ensuring every release never activates the radio at all

I agree, especially we know that many "non-expert admins" will simply use OTA to refresh their device with the latest software. Like "I trust HomeAssistent to pick the right update for me". After that point, the WLED device will start offering its WLED-AP against all "radio off" assumptions.

As a counter-measure, removing OTA and ESP-NOW could work. But if hard "radio off" requirements need to be met, it also means to put the board into a hard-to-break enclosure with intruder alert, to prevent updating WLED by UART pins.

@Stargazer2026
Copy link
Copy Markdown
Author

First of all, thank you all for the detailed analysis! I genuinely appreciate the “safety review” perspective. These are exactly the right questions to ask :-)

Let me clarify one important point:

This feature is not intended as a regulatory compliance guarantee and should not be marketed or understood as “certified radio-off firmware”. It is simply a compile-time exclusion of the WiFi stack for controlled, fixed-purpose builds where the integrator fully owns the deployment.

I completely agree that default WLED behavior must remain flexible, recoverable and safe for normal users.

Given the concerns raised, I would propose tightening the scope further:

  • Require -D WLED_USE_ETHERNET together with -D WLED_FORCE_WIFI_OFF
  • Add a compile-time #error if WLED_FORCE_WIFI_OFF is used without Ethernet
  • Add a prominent compile-time warning message explaining the consequences
  • Clearly document this as an expert-only option in WLED-Docs
  • Explicitly state that OTA, ESP-NOW and recovery expectations change under this flag

Regarding responsibility:
The integrator who compiles with this flag accepts that recovery requires physical access (UART, rebuild, etc). This is intentional and aligned with the “expert-only” nature of the option.

From an implementation perspective, the delta remains small, isolated and fully opt-in. Default builds and OTA users remain completely unaffected.

If the concern is mainly about inexperienced users experimenting with it, I am fully in favor of making access intentionally inconvenient (for example via forced acknowledgement in code), so it does not become a “cool let’s try this” toggle.

My goal here is not to expand WLED’s general feature surface, but to provide a safe specialization hook for advanced deployments without affecting mainstream users.

Happy to adjust the PR accordingly.

@softhack007
Copy link
Copy Markdown
Member

softhack007 commented Feb 24, 2026

Thanks for your response :-) I think "make access inconvenient" is the way to go 👍

My goal here is not to expand WLED’s general feature surface, but to provide a safe specialization hook for advanced deployments without affecting mainstream users.

What if we could have both? If we lower our expectations on "radio off", we could still allow "WLEP-AP by button" for dummy user recovery. The 5Kb firmware shrink is nothing we absolutely need, so keeping the AP code active but enforcing "AP_BEHAVIOR_BUTTON_ONLY" could be an option.

Not saving 5Kb flash is really a small price, compared to a few "stuck users" who need help to get out of their "no Wi-Fi" problems, especially if the "inaccessible" board is sealed in epoxy and fixed to the roof ;-)

@Stargazer2026
Copy link
Copy Markdown
Author

Thanks again for the constructive feedback. I’ve reworked the implementation based on the discussion.

Updated Behavior

When WLED_FORCE_WIFI_OFF is enabled, WLED now follows a soft-off WiFi model:

  • Regular STA startup is disabled
  • AP fallback behavior is disabled
  • WiFi remains OFF during normal operation
  • The long-press emergency AP via button 0 remains available

In other words, WiFi is no longer part of normal network behavior, but intentional recovery via button-triggered AP is still possible.

This addresses the main concern about “stuck” devices while still preventing unintended RF activity in Ethernet-first deployments.

Compile-Time Safeguards

To make this feature safer and clearly expert-only, I added the following safeguards:

  • #error if WLED_FORCE_WIFI_OFF is used without WLED_USE_ETHERNET
  • #warning if BTNPIN < 0 (no hardware button configured)
  • #warning if BTNTYPE is not momentary by default

This shifts prerequisite validation to compile time, where it is most useful for this type of feature.

Scope Clarification

This is not intended to provide regulatory “radio-off compliance”.
It is an expert build option for controlled Ethernet deployments.

Default firmware behavior remains completely unchanged.

The implementation remains small and isolated:

  • No impact on standard builds
  • No change to default AP behavior
  • Emergency recovery path preserved
  • No additional runtime configuration surface

If the naming should better reflect the soft-off semantics instead of “FORCE_WIFI_OFF”, I’m happy to adjust that as well.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
wled00/wled.cpp (1)

929-934: Avoid misleading scan log when scans are compiled out.

Line 930 logs “scan initiated” even though findWiFi(true) is omitted under WLED_FORCE_WIFI_OFF. Consider making the log conditional or updating the message.

💡 Optional tweak
     if (scanDone && multiWiFi.size() > 1) {
-      DEBUG_PRINTLN(F("WiFi scan initiated on disconnect."));
-#ifndef WLED_FORCE_WIFI_OFF
-      findWiFi(true); // reinit scan
-#endif
+#ifndef WLED_FORCE_WIFI_OFF
+      DEBUG_PRINTLN(F("WiFi scan initiated on disconnect."));
+      findWiFi(true); // reinit scan
+#else
+      DEBUG_PRINTLN(F("WiFi scan suppressed (WLED_FORCE_WIFI_OFF)."));
+#endif
       scanDone = false;
       return;         // try to connect in next iteration
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/wled.cpp` around lines 929 - 934, The log message "WiFi scan initiated
on disconnect." can be misleading when scans are compiled out under
WLED_FORCE_WIFI_OFF; update the condition around the DEBUG_PRINTLN or change the
message so it only appears when findWiFi(true) will actually be called.
Concretely, move or wrap the DEBUG_PRINTLN(F("WiFi scan initiated on
disconnect.")) so it is executed only when WLED_FORCE_WIFI_OFF is not defined
(alongside the call to findWiFi(true)), or alter the text to say "WiFi scan
skipped (scanning disabled) on disconnect" when WLED_FORCE_WIFI_OFF is defined;
adjust the block that uses scanDone, multiWiFi, and findWiFi accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@wled00/wled.cpp`:
- Around line 929-934: The log message "WiFi scan initiated on disconnect." can
be misleading when scans are compiled out under WLED_FORCE_WIFI_OFF; update the
condition around the DEBUG_PRINTLN or change the message so it only appears when
findWiFi(true) will actually be called. Concretely, move or wrap the
DEBUG_PRINTLN(F("WiFi scan initiated on disconnect.")) so it is executed only
when WLED_FORCE_WIFI_OFF is not defined (alongside the call to findWiFi(true)),
or alter the text to say "WiFi scan skipped (scanning disabled) on disconnect"
when WLED_FORCE_WIFI_OFF is defined; adjust the block that uses scanDone,
multiWiFi, and findWiFi accordingly.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ad958f and 700849b.

📒 Files selected for processing (1)
  • wled00/wled.cpp

Comment thread wled00/wled.h
#error "WLED_FORCE_WIFI_OFF requires WLED_USE_ETHERNET."
#endif
#if (BTNPIN < 0)
#warning "WLED_FORCE_WIFI_OFF: BTNPIN is disabled. Emergency AP long-press on button 0 will not be available unless configured in cfg.json."
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both button defines should be mendatory IMHO

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. How do you feel about a change as outlined below?

It moves the default definitions of BTNPIN and BTNTYPE below the checks for the WLED_FORCE_WIFI_OFF use case. This results in a hard enforcement that the button is explicitly configured by the user in the intended way. It also changes from warning to error in case it's missing.

diff --git a/wled00/wled.h b/wled00/wled.h
index 207848d2671fda94e14b671d2d61fb30c7e178e8..7e29a4b231df4fe9baeb652fa4ee85c38a432b0f 100644
--- a/wled00/wled.h
+++ b/wled00/wled.h
@@ -263,69 +263,75 @@ using PSRAMDynamicJsonDocument = BasicJsonDocument<PSRAM_Allocator>;
   #define _INIT(x)
   #define _INIT_N(x)
   #define _INIT_PROGMEM(x)
 #else
   #define WLED_GLOBAL
   #define _INIT(x) = x
   //needed to ignore commas in array definitions
   #define UNPACK( ... ) __VA_ARGS__
   #define _INIT_N(x) UNPACK x
   #define _INIT_PROGMEM(x) PROGMEM = x
 #endif
 
 #define STRINGIFY(X) #X
 #define TOSTRING(X) STRINGIFY(X)
 
 #define WLED_CODENAME "Niji"
 
 // AP and OTA default passwords (for maximum security change them!)
 WLED_GLOBAL char apPass[65]  _INIT(WLED_AP_PASS);
 #ifdef WLED_OTA_PASS
 WLED_GLOBAL char otaPass[33] _INIT(WLED_OTA_PASS);
 #else
 WLED_GLOBAL char otaPass[33] _INIT(DEFAULT_OTA_PASS);
 #endif
 
-// Hardware and pin config
-#ifndef BTNPIN
-  #define BTNPIN 0
-#endif
-#ifndef BTNTYPE
-  #define BTNTYPE BTN_TYPE_PUSH
-#endif
-
 #ifdef WLED_FORCE_WIFI_OFF
   #ifndef WLED_USE_ETHERNET
     #error "WLED_FORCE_WIFI_OFF requires WLED_USE_ETHERNET."
   #endif
+  #ifndef BTNPIN
+    #error "WLED_FORCE_WIFI_OFF requires BTNPIN to be explicitly defined."
+  #endif
+  #ifndef BTNTYPE
+    #error "WLED_FORCE_WIFI_OFF requires BTNTYPE to be explicitly defined."
+  #endif
   #if (BTNPIN < 0)
-    #warning "WLED_FORCE_WIFI_OFF: BTNPIN is disabled. Emergency AP long-press on button 0 will not be available unless configured in cfg.json."
+    #error "WLED_FORCE_WIFI_OFF requires BTNPIN >= 0 so emergency AP long-press can be triggered on button 0."
   #endif
   #if !((BTNTYPE == BTN_TYPE_PUSH) || (BTNTYPE == BTN_TYPE_PUSH_ACT_HIGH) || (BTNTYPE == BTN_TYPE_TOUCH))
-    #warning "WLED_FORCE_WIFI_OFF: BTNTYPE is not momentary by default. Emergency AP long-press on button 0 requires a momentary button type."
+    #error "WLED_FORCE_WIFI_OFF requires a momentary BTNTYPE (BTN_TYPE_PUSH / BTN_TYPE_PUSH_ACT_HIGH / BTN_TYPE_TOUCH)."
   #endif
 #endif
+
+// Hardware and pin config
+#ifndef BTNPIN
+  #define BTNPIN 0
+#endif
+#ifndef BTNTYPE
+  #define BTNTYPE BTN_TYPE_PUSH
+#endif
 #ifndef RLYPIN
 WLED_GLOBAL int8_t rlyPin _INIT(-1);
 #else
 WLED_GLOBAL int8_t rlyPin _INIT(RLYPIN);
 #endif
 //Relay mode (1 = active high, 0 = active low, flipped in cfg.json)
 #ifndef RLYMDE
 WLED_GLOBAL bool rlyMde _INIT(true);
 #else
 WLED_GLOBAL bool rlyMde _INIT(RLYMDE);
 #endif
 //Use open drain (floating pin) when relay should be off
 #ifndef RLYODRAIN
 WLED_GLOBAL bool rlyOpenDrain _INIT(false);
 #else
 WLED_GLOBAL bool rlyOpenDrain _INIT(RLYODRAIN);
 #endif
 #ifndef IRPIN
   #define IRPIN -1
 #endif
 #ifndef IRTYPE
   #define IRTYPE 0
 #endif
 
 #if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || (defined(RX) && defined(TX))

@Stargazer2026
Copy link
Copy Markdown
Author

I refactored the implementation now to use less #ifdef blocks. Please have a look at commit d14643b and let me know if you prefer this implementation or the former implementation, with more explicit (inline) #ifdef blocks.

@Stargazer2026

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

@DedeHai
Copy link
Copy Markdown
Collaborator

DedeHai commented Feb 26, 2026

how do the two versions compare regarding code size?

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@Stargazer2026
Copy link
Copy Markdown
Author

how do the two versions compare regarding code size?

They're almost identical:

| Code Base / Build Target | WIFI ON | WIFI OFF |
|--------------------------|---------|----------|
| Before Refactoring       | 1301777 | 1297105  |
| After Refactoring        | 1301789 | 1297077  |

@Stargazer2026
Copy link
Copy Markdown
Author

Just a quick follow-up in case anything is still open on my side.

If there are remaining concerns, adjustments, or preferred refinements to the current approach, I’m happy to work on them.

Otherwise I’ll wait for further feedback :-)

@softhack007 softhack007 added this to the 16.0.0 beta milestone Mar 9, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
wled00/FX.cpp (1)

6915-6949: Extract the onset-tuning literals into named constants.

The detector currently bakes several calibration values directly into the math (96.0f, 1.12f, 4.0f, 3.2f, 0.18f, decay bounds). Hoisting them into constexpr names would make future retuning much easier without changing behavior.

♻️ Example refactor
+  constexpr float kEnergyFloor = 96.0f;
+  constexpr float kFluxBaselineAlpha = 0.08f;
+  constexpr float kFluxBaselineScale = 1.12f;
+  constexpr float kFluxBaselineOffset = 4.0f;
+  constexpr float kOnsetGain = 3.2f;
+  constexpr float kBaseDriveScale = 0.18f;
+
   float sensitivity = 0.45f + (float)SEGMENT.intensity / 128.0f; // slider 2
-  float onsetNorm   = (flux * 255.0f) / (energy + 96.0f);        // robust against AGC level shifts
-  data->fluxAvg += 0.08f * (onsetNorm - data->fluxAvg);
-  float onsetOnly  = fmaxf(0.0f, onsetNorm - (data->fluxAvg * 1.12f + 4.0f));
-  float onsetDrive = fminf(255.0f, onsetOnly * sensitivity * 3.2f);
-  float baseDrive  = fminf(255.0f, (energy / 12.0f) * 0.18f);     // secondary fill component only
+  float onsetNorm   = (flux * 255.0f) / (energy + kEnergyFloor);  // robust against AGC level shifts
+  data->fluxAvg += kFluxBaselineAlpha * (onsetNorm - data->fluxAvg);
+  float onsetOnly  = fmaxf(0.0f, onsetNorm - (data->fluxAvg * kFluxBaselineScale + kFluxBaselineOffset));
+  float onsetDrive = fminf(255.0f, onsetOnly * sensitivity * kOnsetGain);
+  float baseDrive  = fminf(255.0f, (energy / 12.0f) * kBaseDriveScale); // secondary fill component only
Based on learnings, added/modified WLED code should replace hardcoded numeric literals with meaningful constants when those constants improve maintainability.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@wled00/FX.cpp` around lines 6915 - 6949, Summary: Replace hardcoded
onset-tuning literals with named constexpr constants to improve maintainability.
Fix: add a small block of constexpr float definitions near the top of the FX.cpp
scope (or above the spectral flux code) with meaningful names (e.g.,
kEnergyOffset = 96.0f, kFluxAvgGain = 0.08f is already used but add
kFluxAvgFactor if desired, kFluxAvgMultiplier = 1.12f, kOnsetOffset = 4.0f,
kOnsetDriveScale = 3.2f, kBaseDriveScale = 0.18f, kSensitivityBase = 0.45f,
kSensitivityDiv = 128.0f, kDecayMin = 1.4f, kDecayMax = 16.0f) and replace the
numeric literals in the spectral flux block (references: data->prevFft loop,
onsetNorm computation, data->fluxAvg update, onsetOnly calculation, onsetDrive,
baseDrive, and the mapf call using SEGMENT.speed) so the behavior does not
change but tuning values are centralized and named.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@wled00/FX.cpp`:
- Around line 6915-6949: Summary: Replace hardcoded onset-tuning literals with
named constexpr constants to improve maintainability. Fix: add a small block of
constexpr float definitions near the top of the FX.cpp scope (or above the
spectral flux code) with meaningful names (e.g., kEnergyOffset = 96.0f,
kFluxAvgGain = 0.08f is already used but add kFluxAvgFactor if desired,
kFluxAvgMultiplier = 1.12f, kOnsetOffset = 4.0f, kOnsetDriveScale = 3.2f,
kBaseDriveScale = 0.18f, kSensitivityBase = 0.45f, kSensitivityDiv = 128.0f,
kDecayMin = 1.4f, kDecayMax = 16.0f) and replace the numeric literals in the
spectral flux block (references: data->prevFft loop, onsetNorm computation,
data->fluxAvg update, onsetOnly calculation, onsetDrive, baseDrive, and the mapf
call using SEGMENT.speed) so the behavior does not change but tuning values are
centralized and named.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 469a8745-66ed-48dd-b82e-1a9ae4b4f8dc

📥 Commits

Reviewing files that changed from the base of the PR and between ef88e49 and 4428f00.

📒 Files selected for processing (2)
  • wled00/FX.cpp
  • wled00/FX.h

Comment thread wled00/FX.cpp Outdated


/////////////////////////////
// * MIDNOISE ONSET //
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please put this in a new PR

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, that's what happens when you use "main" as source for a PR. Sorry for that.

I removed the accidental commit that got looped into this PR.

@softhack007

This comment was marked as outdated.

@coderabbitai

This comment was marked as outdated.

@softhack007 softhack007 added the AI Partly generated by an AI. Make sure that the contributor fully understands the code! label May 29, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
wled00/wled.h (1)

32-32: ⚡ Quick win

Rename this feature flag to the standard WLED_DISABLE_* / WLED_ENABLE_* form.

WLED_FORCE_WIFI_OFF breaks the project's feature-toggle convention, so it adds a one-off compile-time API to maintain across headers, docs, and build flags. Please rename it consistently here and in the sample config/docs before merge.

As per coding guidelines "Use WLED-specific feature flags: WLED_DISABLE_* and WLED_ENABLE_* for feature toggling".

Also applies to: 296-306

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/wled.h` at line 32, The feature flag WLED_FORCE_WIFI_OFF breaks the
project's naming convention; rename it to follow the WLED_DISABLE_* pattern
(e.g., WLED_DISABLE_WIFI_OFF or WLED_DISABLE_WIFI) everywhere: update the define
in wled.h (replace WLED_FORCE_WIFI_OFF), update all conditional checks
(`#ifdef/`#ifndef/defined(...) uses) that reference WLED_FORCE_WIFI_OFF, and
update sample config/docs and any build flags to use the new name; also apply
the same renaming pattern to other non-conforming flags referenced around lines
296-306 so all feature toggles use WLED_DISABLE_* / WLED_ENABLE_* consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@wled00/wled.cpp`:
- Around line 55-59: The helper wifiScanRunning currently treats any negative
WiFi.scanComplete() value as “still running”; change it to return true only when
the status equals WIFI_SCAN_RUNNING so failed scans (WIFI_SCAN_FAILED) aren’t
considered running. Update the return in wifiScanRunning(bool wifiConfigured) to
check WiFi.scanComplete() == WIFI_SCAN_RUNNING and keep the existing
regularWiFiEnabled(), wifiConfigured and multiWiFi.size() > 1 checks so
findWiFi(), restartWiFiScanIfEnabled() and handleConnection() won’t be blocked
by a failed async scan.

---

Nitpick comments:
In `@wled00/wled.h`:
- Line 32: The feature flag WLED_FORCE_WIFI_OFF breaks the project's naming
convention; rename it to follow the WLED_DISABLE_* pattern (e.g.,
WLED_DISABLE_WIFI_OFF or WLED_DISABLE_WIFI) everywhere: update the define in
wled.h (replace WLED_FORCE_WIFI_OFF), update all conditional checks
(`#ifdef/`#ifndef/defined(...) uses) that reference WLED_FORCE_WIFI_OFF, and
update sample config/docs and any build flags to use the new name; also apply
the same renaming pattern to other non-conforming flags referenced around lines
296-306 so all feature toggles use WLED_DISABLE_* / WLED_ENABLE_* consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 43591438-01d2-4c9d-902a-1c0d2b57800c

📥 Commits

Reviewing files that changed from the base of the PR and between 555d0cf and 0558bda.

📒 Files selected for processing (4)
  • wled00/my_config_sample.h
  • wled00/network.cpp
  • wled00/wled.cpp
  • wled00/wled.h

Comment thread wled00/wled.cpp
Comment on lines +55 to +59
bool wifiScanRunning(bool wifiConfigured)
{
if (!regularWiFiEnabled()) return false;
return wifiConfigured && multiWiFi.size() > 1 && WiFi.scanComplete() < 0;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Treat only WIFI_SCAN_RUNNING as “scan still running” here.

findWiFi() and the new restartWiFiScanIfEnabled() both treat WIFI_SCAN_FAILED as recoverable, but this helper still returns true for any negative status. In a multi-SSID setup, one failed async scan will keep handleConnection() returning early and can stall reconnect indefinitely.

Suggested fix
 bool wifiScanRunning(bool wifiConfigured)
 {
   if (!regularWiFiEnabled()) return false;
-  return wifiConfigured && multiWiFi.size() > 1 && WiFi.scanComplete() < 0;
+  const int scanStatus = WiFi.scanComplete();
+  return wifiConfigured && multiWiFi.size() > 1 && scanStatus == WIFI_SCAN_RUNNING;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@wled00/wled.cpp` around lines 55 - 59, The helper wifiScanRunning currently
treats any negative WiFi.scanComplete() value as “still running”; change it to
return true only when the status equals WIFI_SCAN_RUNNING so failed scans
(WIFI_SCAN_FAILED) aren’t considered running. Update the return in
wifiScanRunning(bool wifiConfigured) to check WiFi.scanComplete() ==
WIFI_SCAN_RUNNING and keep the existing regularWiFiEnabled(), wifiConfigured and
multiWiFi.size() > 1 checks so findWiFi(), restartWiFiScanIfEnabled() and
handleConnection() won’t be blocked by a failed async scan.

@softhack007
Copy link
Copy Markdown
Member

softhack007 commented May 29, 2026

WLED_FORCE_WIFI_OFF breaks the project's feature-toggle convention, so it adds a one-off compile-time API to maintain across headers, docs, and build flags. Please rename it consistently here and in the sample config/docs before merge.

I agree with the bunny here - maybe better to rename the feature flag to WLED_DISABLE_WIFI.

Copy link
Copy Markdown
Member

@willmmiles willmmiles left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for submitting this. My colleagues' concerns about safety are not ill-founded, but I also agree that that there are valuable use cases for being able to isolate the Wifi network interface from the rest of the system. ESP32-P4 support is the top of that list - we still need V5 and ESP-IDF support but we can make some steps like this to get ready.

I concur that we should be using WLED_DISABLE_WIFI as the flag name.

This dovetails a bit with #5650 .

At the limit, I think there's a bigger "network management" refactor waiting in the wings -- both with the "dependencies/network" name collision with V5, as well as issues like #4703 which points out that the two interfaces can't be treated as truly independent in some hardware configurations. The particular goal of treating wifi client support as a compile-time option would definitely be cleaner in that wider context.

Comment thread wled00/network.cpp

namespace {

bool restartWiFiScanIfEnabled(bool requireMultiWiFi)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Factoring the common logic out to a function is a good idea, but conditionals in function names are an antipattern.

In a feature-toggle case like this, it's better to #ifdef gate the call sites instead. Blanking out function bodies makes it hard to reason about what's actually happening when reading at the call sites.

Comment thread wled00/wled.cpp
Comment on lines +19 to +26
bool regularWiFiEnabled()
{
#ifdef WLED_FORCE_WIFI_OFF
return false;
#else
return true;
#endif
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you must expose this state as a variable, use a static constexpr variable to communicate clearly that it is a compile-time constant.

Comment thread wled00/wled.cpp
Comment on lines +28 to +39
void initRegularWiFiStartup()
{
if (!regularWiFiEnabled()) {
DEBUG_PRINTLN(F("WLED_FORCE_WIFI_OFF: disabling regular WiFi startup (AP button emergency remains available)."));
apBehavior = AP_BEHAVIOR_BUTTON_ONLY;
WiFi.mode(WIFI_OFF);
return;
}

WiFi.mode(WIFI_STA); // enable scanning
findWiFi(true); // start scanning for available WiFi-s
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for restartWifiScan: gate the feature at the call site, not inside the implementing function. If a function is named initRegularWifiStartup(), then it should init the wifi any time it is called, not just sometimes.

Comment thread wled00/wled.cpp
if (regularWiFiEnabled()) return false;

DEBUG_PRINTLN(F("WLED_FORCE_WIFI_OFF active. WiFi stays OFF unless AP emergency mode is opened by button."));
apBehavior = AP_BEHAVIOR_BUTTON_ONLY;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not appropriate to mysteriously overwrite system config values in the main loop. (I'm not saying the code we have is clean: just that we shouldn't repeat existing mistakes.)

For handling the configuration value, instead set an appropriate bootup default and dike out the reader sites that set it (cfg.cpp, set.cpp).

Comment thread wled00/wled.cpp

bool handleForceWiFiOffConnection()
{
if (regularWiFiEnabled()) return false;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This call path is unnecessarily convoluted.

  • First, we unconditionally call this function - expressly named as being handling a specific build-time configuration value, regardless of its setting
  • Then we call some other function to test the build time value
  • ...and return, cancelling out the call, if it's not set.

This needlessly obfuscates what's being done. Please instead #ifdef the call at the call site, so the function (a) do exactly what it says, and (b) clearly get called only when needed.

Comment thread wled00/wled.cpp

bool wifiScanRunning(bool wifiConfigured)
{
if (!regularWiFiEnabled()) return false;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this check necessary?

Comment thread wled00/wled.cpp
return wifiConfigured && multiWiFi.size() > 1 && WiFi.scanComplete() < 0;
}

int findWiFiIfEnabled(bool doScan = false)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conditional in function name, again.

Comment thread wled00/wled.cpp
void WLED::initConnection()
{
DEBUG_PRINTF_P(PSTR("initConnection() called @ %lus.\n"), millis()/1000);
if (handleForceWiFiOffConnection()) return;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The websocket line below is important for Ethernet too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI Partly generated by an AI. Make sure that the contributor fully understands the code! connectivity Issue regarding protocols, WiFi connection or availability of interfaces enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants