Skip to content

fix(wifi): apply the Wi-Fi country after the driver is initialized - #3088

Open
0x03c1 wants to merge 2 commits into
BruceDevices:devfrom
0x03c1:fix/wifi-country-after-init
Open

0x03c1 wants to merge 2 commits into
BruceDevices:devfrom
0x03c1:fix/wifi-country-after-init

Conversation

@0x03c1

@0x03c1 0x03c1 commented Nov 13, 2026 •

Copy link
Copy Markdown

Proposed Changes

setup() sets the Wi-Fi country (US, channels 1–14, manual policy) and the max TX power right after before-wifi-init. At that point the Wi-Fi driver hasn't been initialized yet: Bruce brings Wi-Fi up on demand. Both calls return ESP_ERR_WIFI_NOT_INIT, and since the return codes are ignored, this goes unnoticed. The driver then keeps its default country 01 (world safe, channels 1–11).

Result: features that walk channels 1–14 (deauther, karma, sniffers, sdr, wifi_atks…) fail on channels 12–14, because esp_wifi_set_channel(12..14) returns ESP_ERR_INVALID_ARG.

Wi-Fi is brought up in two ways:

  1. Through the Arduino core, with WiFi.mode() in many places. The core posts ARDUINO_EVENT_WIFI_READY right after esp_wifi_init(), so a handler registered in setup() applies the country every time the driver is (re)initialized.
  2. With esp_wifi_init() called directly, which never fires that event. This happens in sniffer.cpp, pwngrid.cpp, spam.cpp, sdr.cpp, channel_analyzer.cpp and jam_detect.cpp. Each of them now calls applyWifiCountry() right after esp_wifi_init(). Thanks to the Copilot review for pointing out this path.

Changes:

  • applyWifiCountry() added to core/wifi/wifi_common.{h,cpp}. The country config is unchanged from what setup() already defined.
  • setup() registers it for ARDUINO_EVENT_WIFI_READY instead of calling it too early.
  • One applyWifiCountry() call after each of the six direct esp_wifi_init() calls.
  • The early esp_wifi_set_max_tx_power(80) is removed:
    • It never took effect.
    • The driver already defaults to 78 (19.5 dBm), measured below.
    • esp_wifi_set_max_tx_power() only works after esp_wifi_start(). Re-applying it on every start could override modules that set their own TX power, such as sdr.cpp.

Why the direct path sometimes looked fine: the Arduino core initializes Wi-Fi with persistent storage, which is the default and is never disabled in Bruce. Once any WiFi.mode() path had applied the country, it was saved to NVS, and later direct esp_wifi_init() calls loaded it from there. On a clean NVS (fresh flash, NVS erased, or when the first Wi-Fi feature used is one of those six modules), they ran with 01.

Types of Changes

Bugfix.

Verification

Measured on an M5StickC Plus2 (m5stack-cplus2) with a temporary diagnostic task, which is not part of this PR. After Wi-Fi came up, the task printed the active country and the result of esp_wifi_set_channel(13).

Current dev, wifi on over serial:

boot: set_max_tx_power=ESP_ERR_WIFI_NOT_INIT set_country=ESP_ERR_WIFI_NOT_INIT
wifi up: country cc=01 schan=1 nchan=11 policy=0 | max_tx=78 (qdBm)
  set_channel(12..14)=ESP_ERR_INVALID_ARG   (promiscuous mode)

Direct esp_wifi_init() path (serial sniffer command, NVS partition erased before each run):

Build Result
WIFI_READY handler only (first commit) cc=01 nchan=11, set_channel(13)=ESP_ERR_INVALID_ARG
This PR (both commits) cc=US nchan=14, set_channel(13)=ESP_OK

Arduino path with this PR (serial wifi on, NVS erased): cc=US nchan=14, set_channel(13)=ESP_OK once it fell back to AP mode. Across repeated wifi on/wifi off cycles, the country was re-applied on every init and channels 11–14 were accepted.

Testing

  • pio run -e m5stack-cplus2 builds. The added lines follow .clang-format.
  • Tested on hardware as described above. The final build (no diagnostics) was also flashed: it boots, wifi on/wifi off and sniffer work, with no esp_wifi_set_country errors and no crashes.
  • Other environments were not built locally. The change only uses WiFi.onEvent(), ARDUINO_EVENT_WIFI_READY and esp_wifi_set_country(); main.cpp already called the last one for every board.

Linked Issues

None found.

User-Facing Change

Wi-Fi features that scan or hop channels now actually reach channels 12-14. The Wi-Fi country set at boot was never applied, which limited the radio to channels 1-11.

Further Comments

This only makes the existing, intended country config (US, 1–14, manual) take effect. Regional rules differ (channel 14, and in some regions 12–13, aren't allowed everywhere). If maintainers would rather make the country configurable, applyWifiCountry() is now the single place to do it.

setup() called esp_wifi_set_country() and esp_wifi_set_max_tx_power()
before Wi-Fi was ever initialized, so both returned ESP_ERR_WIFI_NOT_INIT
(return codes were ignored) and the driver kept its default "01" country
(channels 1-11). Every module that walks channels 1-14 (deauther, karma,
sniffers, sdr...) silently failed on 12-14: esp_wifi_set_channel()
returned ESP_ERR_INVALID_ARG.

Wi-Fi is brought up on demand via WiFi.mode() in many places, so set the
country from an ARDUINO_EVENT_WIFI_READY handler, which runs every time
the driver is (re)initialized.

The early esp_wifi_set_max_tx_power(80) is dropped: it never took effect,
the driver already defaults to 78 (19.5 dBm), and re-applying it on every
start could override modules that set their own TX power (sdr.cpp).
Copilot AI balanced review requested due to automatic review settings November 13, 2026 17:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Direct ESP-IDF Wi-Fi initialization bypasses the callback that applies country settings, leaving channels 12–14 unavailable when sniffer, pwngrid, or SDR initializes Wi-Fi first.

1 open issue
What changed in this PR

This PR moves Wi-Fi country configuration out of early startup and into the Arduino driver-ready callback, aiming to enable channels 12–14 for Wi-Fi features.

Changes:

  • Applies the existing country settings on ARDUINO_EVENT_WIFI_READY.
  • Logs country-configuration failures.
  • Removes the ineffective early transmit-power setting.
File Description
src/​main.cpp Adds and registers applyWifiCountry(), replacing premature Wi-Fi configuration calls.

Review effort: Balanced

Comment thread src/main.cpp Outdated
// Wi-Fi is initialized on demand (WiFi.mode() all over the code), so at this point the driver
// doesn't exist yet and esp_wifi_set_country() would fail with ESP_ERR_WIFI_NOT_INIT.
// Apply the country every time the driver is (re)initialized instead.
WiFi.onEvent(applyWifiCountry, ARDUINO_EVENT_WIFI_READY);

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.

Confirmed on hardware and fixed in aaee325. With the NVS partition erased, the sniffer path (direct esp_wifi_init()) came up with cc=01 nchan=11 and set_channel(13)=ESP_ERR_INVALID_ARG on the previous commit. With applyWifiCountry() now called right after each direct esp_wifi_init() (sniffer, pwngrid, spam, sdr, channel_analyzer, jam_detect), it comes up with cc=US nchan=14 and set_channel(13)=ESP_OK. Before, it only looked fine when an earlier WiFi.mode() had persisted the country to NVS. Details are in the PR description.

…irectly

sniffer, pwngrid, spam, sdr, channel_analyzer and jam_detect bring Wi-Fi
up with esp_wifi_init() instead of the Arduino core, so
ARDUINO_EVENT_WIFI_READY never fires for them. They only got the right
country when an earlier WiFi.mode() had persisted it to NVS; on a clean
NVS they ran with the default "01" country and channels 12-14 failed.

Move the setup into applyWifiCountry() in wifi_common and call it right
after each direct esp_wifi_init(), keeping the WIFI_READY handler for the
Arduino path.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants