Thermal + WiFi reliability changes - #39
Open
webdevbrian wants to merge 2 commits into
Open
Conversation
esp_wifi_set_max_tx_power() was never called anywhere in the firmware, so the radio always ran at the PHY default of 20 dBm - despite WiFiTxPower_t storing a 13 dBm default in NVS and exposing it through get_config. Both getWiFiTxPowerConfig() and setWiFiTxPower() had zero callers. Add WiFiManager::ApplyTxPower(), called after esp_wifi_start() on the station paths; calling it before start is silently ignored by the driver. AP mode is left at full power since it is the setup-fallback hotspot where range matters. Switch scan_method to WIFI_ALL_CHANNEL_SCAN. The existing sort_method = WIFI_CONNECT_AP_BY_SIGNAL was a no-op under WIFI_FAST_SCAN, which ends at the first SSID match - on a mesh network that means associating with whichever node answers first regardless of signal strength. The all-channel scan costs ~2-3s per attempt, so raise the retry budget (3 -> 5, plus a 300ms backoff) and the connect timeouts (8s/10s -> 18s) to match. Without that the old windows would expire mid-retry and fall through to AP-mode fallback sooner than before the change. Drop the XIAO ESP32S3 to 160 MHz. CONFIG_ESP32S3_DEFAULT_CPU_FREQ_* is a legacy alias and switchBoardType.py merges base and board configs by exact key, so the 240 symbols have to be explicitly disabled rather than just omitted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
setupCameraPinout() picked the XCLK frequency with `#if CONFIG_GENERAL_INCLUDE_UVC_MODE`, so any build with UVC support compiled in ran the sensor at CAMERA_USB_XCLK_FREQ even when the device was actually running in WiFi mode. CAMERA_WIFI_XCLK_FREQ was therefore dead on those builds, and boards paid the higher sensor clock - and the extra heat - for a mode they were not in. Check the stored device mode instead. Mode is loaded before the camera is set up, and switching modes already requires a reboot, so it is settled by then. Also log the selected frequency, since the value that ends up in use was previously impossible to confirm from the outside. Measured on a XIAO ESP32S3 Sense (OV3660) in WiFi mode: XCLK drops 23MHz -> 16.5MHz. Frame corruption warnings from cam_hal also decreased - 2x NO-SOI plus 4x FB-OVF before, consistently 1x NO-SOI after, across repeated boots. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Thermal + WiFi reliability changes
The following changes have been proven to locally drop temperatures (camera OV3660 and ESP monitored) from 155F+ to 110F, still with amazing performance and reliability with no visual degradation in tracking under same real-world scenarios locally
Summary
Changes aimed at reducing heat and making WiFi association deterministic on mesh networks.
Two of these are bugs where an existing setting silently did nothing:
esp_wifi_set_max_tx_power()is never called anywhere in the firmware, so every board has always transmitted at the PHY default of 20 dBm — even thoughWiFiTxPower_talready loads a 13 dBm default from NVS, saves it, and exposes it throughget_config. BothgetWiFiTxPowerConfig()andsetWiFiTxPower()had zero callers.CONFIG_CAMERA_WIFI_XCLK_FREQis dead on any build with UVC compiled in, because the camera clock was selected with#ifat compile time rather than from the mode the device actually runs in.components/wifiManager/wifiManager/wifiManager.cppcomponents/wifiManager/wifiManager/wifiManager.hppcomponents/CameraManager/CameraManager/CameraManager.cppboards/seed_studio/xiao_esp32s3Changes
1. Apply the configured WiFi TX power
Adds
WiFiManager::ApplyTxPower(), called after eachesp_wifi_start()on the station paths. It must come after start — calling it earlier is silently ignored by the driver. The value is clamped to the driver's valid 8–84 range (0.25 dBm units) and the applied result is read back and logged.20 dBm → 13 dBm is a 7 dB cut: roughly 5× less radiated power and a substantial reduction in PA dissipation.
AP mode is deliberately left at full power — it's the setup-fallback hotspot where range matters, and it doesn't run in steady state.
2.
WIFI_FAST_SCAN→WIFI_ALL_CHANNEL_SCANSetCredentials()setssort_method = WIFI_CONNECT_AP_BY_SIGNALwith the comment "Connect to strongest signal" — butscan_methodwasWIFI_FAST_SCAN. Per the IDF header:Fast scan stops at the first SSID match, so there's no scan list left to sort and
sort_methodis a no-op. On mesh/repeater networks the device associates with whichever node answers the probe first, which varies between boots. On my test network the same SSID was visible at −50 dBm and −84 dBm; landing on the far node makes association unreliable.Costs ~1–2 s more at boot.
3. Retry budget and timeouts
EXAMPLE_ESP_MAXIMUM_RETRY3 → 5WIFI_RETRY_BACKOFF_MS= 300 ms between attempts (previously all retries could fire inside a second, which isn't a real attempt)Important
Changes 2 and 3 are coupled — please don't merge one without the other. An all-channel scan costs ~2–3 s per attempt, so keeping the old 8 s/10 s windows would expire mid-retry and fall through to AP-mode fallback sooner than before the change.
4. Select camera XCLK from the runtime mode, not the compile flag
setupCameraPinout()chose the sensor clock like this:Because that's
#ifrather than a runtime check, any build with UVC support compiled in ran the sensor at the USB clock even while the device was in WiFi mode.CONFIG_CAMERA_WIFI_XCLK_FREQwas effectively unreachable on those builds, and boards paid the higher sensor clock — and the resulting heat — for a mode they weren't in.Now checks the stored device mode. Mode is loaded before the camera is set up, and switching modes already requires a reboot, so it's settled by then. The selected frequency is also logged, since previously there was no way to confirm from outside what the sensor was actually running at.
5. XIAO ESP32S3: CPU 240 MHz → 160 MHz
Scoped to
boards/seed_studio/xiao_esp32s3, so only that board's artifact changes.Worth flagging for anyone editing board files later — the obvious one-line edit does not work:
That still builds at 240 MHz, because:
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_*is a legacy alias; the authoritative symbols areCONFIG_ESP_DEFAULT_CPU_FREQ_MHZ*, and setting the alias doesn't propagate.switchBoardType.pymerges with{**base, **board}— a dict merge by exact key...._240and..._160are different keys, so setting_160=yleavesbase_defaults'_240=yin place.Both
_240symbols therefore have to be explicitly=n. That's the six lines in the diff, with a comment in the board file explaining why.Testing
Verified on Seeed XIAO ESP32S3 Sense (OV3660), ESP-IDF v5.4.4, against a mesh network (two APs, same SSID, −50 dBm and −84 dBm).
WiFi
Boot log confirms TX power now applies and the device still associates fine at reduced power:
Reset-to-connected soak, 8/8 cycles connected, ~9 s each with no variance:
Camera XCLK
In WiFi mode the sensor clock drops from 23 MHz to 16.5 MHz:
I built a control image without this change and compared
cam_halwarnings on the same hardware across repeated boots:NO-SOIFB-OVFSo frame corruption decreases — the lower pixel clock leaves more DMA headroom, which is why the frame-buffer overflows disappear entirely.
CPU frequency
Verified against the compiled output rather than
sdkconfig, sinceconfgenhas the final say:Review notes
Behaviour change for existing users (TX power). Anyone with a stored
txpowervalue will now actually get it, defaulting to 13 dBm instead of the current effective 20 dBm. That's the intent, but it is a change in shipped behaviour. The value is already runtime-configurable — only the plumbing is new. Happy to change the default if 13 dBm is considered too aggressive.Behaviour change for OV2640 boards (camera XCLK). On boards with UVC compiled in, WiFi mode now runs the sensor at
CAMERA_WIFI_XCLK_FREQ(16.5 MHz by default) instead of the USB value. That's what the config was always meant to do, but it may reduce achievable framerate on OV2640 hardware. I only have an OV3660 to test on. If a lower clock proves too slow for some board, the right fix is raising that board'sCAMERA_WIFI_XCLK_FREQrather than reverting to the compile-time behaviour.Only hardware-tested on
esp32s3. These changes compile-affect all 12 boards in the CI matrix, including the threeesp32targets, which I have no hardware for. Relying on CI for those.No baseline for change 2. I did not capture a failure rate with
WIFI_FAST_SCANbefore making the change, so this isn't proven to fix a specific field report. The defect is confirmed against the IDF header; the improvement is reasoned rather than measured. Post-change is 8/8 as above.Not included
HIL harness defaults produce phantom failures.
tests/.env.exampleshipsSWITCH_MODE_REBOOT_TIME=5; USB re-enumeration alone measured 2.8 s, and the board isn't responsive for several seconds after that because the serial task runs at priority 1 and gets starved during WiFi association. Compounding it,OpenIrisDeviceManager.get_device()silently returns a stale dead handle when the port changes instead of raising or retrying, so one timing miss cascades. Raising the default to 8 took a run from 43 failed / 17 passed to 60 passed / 1 skipped with no firmware change.