Skip to content

Commit 20d156c

Browse files
authored
feat(esp32): #83 brownout defense-in-depth (PHY TX backoff + connectivity log) (#94)
Two firmware levers for the node 35 brownout crash-loop at phy_init. Neither touches the initial phy_init calibration PEAK that trips the reset (only the bulk cap / custom PCB #11 does) -- these are belt-and-suspenders. 1. CONFIG_ESP_PHY_REDUCE_TX_POWER=y (sdkconfig.defaults, grouped with the brownout block). IDF-native: esp_phy_reduce_tx_power() forces the lowest PHY TX power on the boot immediately after ESP_RST_BROWNOUT (phy_init.c gates it on exactly that reset reason), trimming current on the retry boot. Post-brownout backoff, NOT a peak cut. 2. Wi-Fi connectivity-transition logger (app_main.cpp). Originally scoped as a runtime reconnect backoff via ConnectivityMgr().SetWiFiStationReconnectInterval, but esp-matter's ESP32 ConnectivityManagerImpl DECLARES _SetWiFiStationReconnect- Interval without defining it (only _Get links; the interval is a fixed compile- time 5s), so that call does not link on this platform. CHIP already auto- reconnects on its 5s timer, and the field "never reconnects" symptom is the brownout reboot loop (pre-CHIP) anyway. So this ships as an observer that timestamps Lost/Established transitions -- lets a real Wi-Fi drop be told apart from a brownout reboot when triaging a node. Observer-only; touches no CHIP state. Bumps PROJECT_VER 1.1.7 -> 1.1.8 (int 10108); sdkconfig NUMBER/STRING synced. Verified: builds + links clean (debug flavour, 15% flash free). Not yet flashed to node 35. See #83. Assisted-by: AI
1 parent fbc5565 commit 20d156c

3 files changed

Lines changed: 44 additions & 3 deletions

File tree

firmware/esp32-matter/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# . ~/esp/esp-idf-v5.5.4/export.sh && . $ESP_MATTER_PATH/export.sh
33
# idf.py set-target esp32 && idf.py build flash monitor
44
cmake_minimum_required(VERSION 3.16)
5-
set(PROJECT_VER "1.1.7")
5+
set(PROJECT_VER "1.1.8")
66

77
# Unified versioning (issue #77): the Matter softwareVersion INT is DERIVED from PROJECT_VER --
88
# MAJOR*10000+MINOR*100+PATCH -> a readable, strictly-monotonic uint32. This keeps the human

firmware/esp32-matter/main/app_main.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,32 @@ class HisenseOTARequestorDriver : public chip::DeviceLayer::ExtendedOTARequestor
971971
};
972972
static HisenseOTARequestorDriver s_hisense_ota_driver;
973973

974+
/* #83 defense-in-depth: Wi-Fi connectivity-transition diagnostics. CHIP's ESP32 ConnectivityManager
975+
* already owns Wi-Fi reconnect and auto-re-arms on a FIXED CHIP_DEVICE_CONFIG_WIFI_STATION_RECONNECT_
976+
* INTERVAL (5 s) timer (DriveStationState), so the "re-arm after a genuine association loss" half of
977+
* #83 is already covered. A runtime exponential backoff would be nicer, but esp-matter's ESP32
978+
* platform DECLARES ConnectivityManagerImpl::_SetWiFiStationReconnectInterval WITHOUT defining it
979+
* (only _Get links; the member is set once at init), so ConnectivityMgr().SetWiFiStationReconnect-
980+
* Interval() does not link on this platform -- and patching the SDK for a marginal win is not worth
981+
* it. Note too: the field "never reconnects until power-cycle" symptom is the brownout REBOOT loop
982+
* (the device resets at phy_init before CHIP even runs), which no reconnect logic can fix -- only the
983+
* bulk cap / custom PCB (#11) does. So this handler stays purely OBSERVATIONAL: it timestamps
984+
* Lost/Established transitions on the hisense_ac log so a real Wi-Fi drop can be told apart from a
985+
* brownout reboot when triaging a field node. Runs on the CHIP event loop (PostEventOrDie ->
986+
* DispatchEvent) with the stack lock already held; reads the event only, touches no CHIP state. */
987+
static void wifi_connectivity_log_handler(const chip::DeviceLayer::ChipDeviceEvent *event, intptr_t)
988+
{
989+
if (event->Type != chip::DeviceLayer::DeviceEventType::kWiFiConnectivityChange) return;
990+
991+
const auto result = event->WiFiConnectivityChange.Result;
992+
const unsigned up_s = (unsigned) (esp_timer_get_time() / 1000000);
993+
if (result == chip::DeviceLayer::kConnectivity_Established) {
994+
ESP_LOGW(TAG, "Wi-Fi connectivity: ESTABLISHED (uptime %us)", up_s);
995+
} else if (result == chip::DeviceLayer::kConnectivity_Lost) {
996+
ESP_LOGW(TAG, "Wi-Fi connectivity: LOST (uptime %us) -- CHIP auto-reconnects on its ~5s timer", up_s);
997+
}
998+
}
999+
9741000
static void https_ota_task(void *arg)
9751001
{
9761002
ESP_LOGW(TAG, "HTTPS-OTA: fetching %s", HISENSE_OTA_URL);
@@ -1520,6 +1546,13 @@ extern "C" void app_main()
15201546
esp_matter_ota_requestor_set_config(ota_cfg);
15211547
}
15221548

1549+
// #83 defense-in-depth: log Wi-Fi connectivity transitions. Registers an observer for CHIP's
1550+
// own connectivity-change event so a field node's Lost/Established transitions are timestamped
1551+
// (tells a real Wi-Fi drop apart from a brownout reboot). Observer only -- CHIP's DriveStation-
1552+
// State owns the actual 5s auto-reconnect (its interval setter is unimplemented on esp-matter
1553+
// ESP32, so no runtime backoff). Safe to register any time after esp_matter::start().
1554+
chip::DeviceLayer::PlatformMgr().AddEventHandler(wifi_connectivity_log_handler, 0);
1555+
15231556
// HA entity labels (UserLabel key "ha_entitylabel") -> distinguishable same-type entities.
15241557
// start() has returned (Server::Init done, endpoints exist); take the stack lock since we
15251558
// run on a different task than the Matter event loop. Read live by the UserLabel cluster.

firmware/esp32-matter/sdkconfig.defaults

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ CONFIG_CUSTOM_DEVICE_INFO_PROVIDER=y
4646
# so it (and the fallback NUMBER) MUST stay equal to PROJECT_VER or the device reports a stale
4747
# softwareVersionString. Edit PROJECT_VER in CMakeLists.txt, then update both lines below.
4848
# esp32-lint.sh enforces this equality (fails the commit/CI if they drift).
49-
CONFIG_DEVICE_SOFTWARE_VERSION_NUMBER=10107
50-
CONFIG_DEVICE_SOFTWARE_VERSION_STRING="1.1.7"
49+
CONFIG_DEVICE_SOFTWARE_VERSION_NUMBER=10108
50+
CONFIG_DEVICE_SOFTWARE_VERSION_STRING="1.1.8"
5151
# --- OTA hardening (faster + reliable on marginal Wi-Fi) ---
5252
# Delta OTA: ship a diff (tens of KB) instead of the full ~1.5MB image over BDX.
5353
CONFIG_ENABLE_DELTA_OTA=y
@@ -75,6 +75,14 @@ CONFIG_ESP_BROWNOUT_DET=y
7575
CONFIG_ESP_BROWNOUT_DET_LVL_SEL_0=y
7676
CONFIG_ESP_BROWNOUT_DET_LVL=0
7777

78+
# Reduce Wi-Fi TX power on the boot that FOLLOWS a brownout reset (#83). IDF-native, no code:
79+
# esp_phy_reduce_tx_power() forces the lowest PHY TX power before register_chipv7_phy on the boot
80+
# immediately after esp_reset_reason()==ESP_RST_BROWNOUT, trimming a little current on the retry
81+
# boot so a marginal rail is likelier to clear re-association. HONEST SCOPE: this is a POST-brownout
82+
# backoff, NOT a cut to the initial phy_init calibration PEAK that trips the reset (esp-idf#1308) --
83+
# only the bulk cap / custom PCB (#11) fixes the peak. Belt-and-suspenders, not a hardware substitute.
84+
CONFIG_ESP_PHY_REDUCE_TX_POWER=y
85+
7886
# Task watchdog PANIC (#12): turn a hung task into a reboot (which re-runs the Wi-Fi/Matter/RS-485
7987
# bring-up) instead of a warning + a wedged node (Matter + A/C both dark) until a power cycle --
8088
# exactly the "recover without a reflash" case #12 is about. The RS-485 bus task explicitly

0 commit comments

Comments
 (0)