Skip to content

Commit 82e72d5

Browse files
authored
fix(esp32): reboot out of a wedged network stack instead of needing a power cycle (#106)
Node 35 dropped off the fabric and stayed off for 27 min until it was power-cycled by hand. The chip was fine (bootreason power-on, RS-485 polling clean throughout) -- nothing in the firmware watches the network, since the only WDT-subscribed task talks to the A/C bus, not Wi-Fi. - net-loss reboot watchdog (15 min, ESPHome's reboot_timeout default). Liveness = associated AND link-local IPv6 valid; Matter is IPv6-only and esp-idf#5796 can kill the LL address while Wi-Fi/IPv4 stay healthy. Gated against firing mid-OTA. - force WIFI_PS_NONE (IDF defaults to modem sleep), falling back to MIN_MODEM while a BLE window is open for single-radio coex. - feed the task WDT per drained TX item: a full queue drain on a silent bus exceeded the 5 s timeout and could panic-reboot the node on its own. - CONFIG_HISENSE_RS485_HW_MODE (default off): opt-in native UART_MODE_RS485_HALF_DUPLEX DE handling, matching ESPHome's uart component, for bench comparison. Deployed and verified on node 35 as esp32 v1.1.12 (int 10112). AmebaZ2 version bumped to 1.3.28 because the shared driver changed (the new feed is #ifdef ESP_PLATFORM, so the AmebaZ2 binary is functionally unchanged). Assisted-by: AI
1 parent 33e05ee commit 82e72d5

7 files changed

Lines changed: 277 additions & 17 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.11")
5+
set(PROJECT_VER "1.1.12")
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/components/hisense_hal/hisense_hal.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,27 @@ void serial_baud(serial_t *obj, int baud)
8181
.source_clk = UART_SCLK_DEFAULT,
8282
};
8383
uart_param_config(HAL_UART_NUM, &cfg);
84+
#ifdef CONFIG_HISENSE_RS485_HW_MODE
85+
/* Opt-in hardware half-duplex (see CONFIG_HISENSE_RS485_HW_MODE's help text). The DE line
86+
* becomes this UART's RTS, and the peripheral asserts it exactly while the TX FIFO drains --
87+
* the same sequence ESPHome's uart component uses for every RS-485 device it supports:
88+
* uart_set_pin(num, tx, rx, flow_control_pin as RTS, UART_PIN_NO_CHANGE)
89+
* uart_set_mode(num, UART_MODE_RS485_HALF_DUPLEX)
90+
* PA_17 is the DE GPIO (PinNames.h remaps it to the ESP32 pin actually wired). */
91+
uart_set_pin(HAL_UART_NUM, obj->tx, obj->rx, PA_17, UART_PIN_NO_CHANGE);
92+
#else
8493
uart_set_pin(HAL_UART_NUM, obj->tx, obj->rx,
8594
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
95+
#endif
8696
if (!s_evt_q) {
8797
uart_driver_install(HAL_UART_NUM, HAL_UART_RX_BUF, 0,
8898
HAL_UART_EVT_QLEN, &s_evt_q, 0);
8999
}
100+
#ifdef CONFIG_HISENSE_RS485_HW_MODE
101+
/* Order matters: ESP-IDF requires uart_set_mode() AFTER uart_driver_install(), because the
102+
* install resets the RS485 config registers. ESPHome sequences it the same way and says so. */
103+
uart_set_mode(HAL_UART_NUM, UART_MODE_RS485_HALF_DUPLEX);
104+
#endif
90105
#ifdef HISENSE_HAL_UART_INTERNAL_LOOPBACK
91106
// DIAGNOSTIC ONLY: internally wire this UART's TX to its own RX inside the
92107
// chip, so a transmit must show up as a receive if the RX path is healthy.
@@ -145,11 +160,19 @@ void serial_putc(serial_t *obj, int c)
145160
uint8_t b = (uint8_t)c;
146161
uart_write_bytes(HAL_UART_NUM, &b, 1);
147162
g_hal_tx_bytes++;
163+
#ifndef CONFIG_HISENSE_RS485_HW_MODE
148164
// Block until the byte has actually left the wire. uart_write_bytes() only
149165
// queues to the TX FIFO; hisense_tx_raw() drops the DE line right after its
150166
// putc loop, so without this the tail of the frame would be cut off while DE
151167
// is already low (RS-485 half-duplex). At 9600 baud this is ~1ms/byte.
152168
uart_wait_tx_done(HAL_UART_NUM, pdMS_TO_TICKS(20));
169+
#else
170+
// Hardware RS-485 mode: the peripheral holds DE asserted until the FIFO and
171+
// shift register are empty, so there is nothing to race and no reason to
172+
// block here. Letting the bytes accumulate in the FIFO is also what makes
173+
// this a one-shot frame write (ESPHome's write_array) rather than 16
174+
// separate stop-and-wait transmissions.
175+
#endif
153176
}
154177

155178
void serial_free(serial_t *obj)

firmware/esp32-matter/main/Kconfig.projbuild

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,33 @@ config HISENSE_DEBUG_BUILD
2020
flavours. Anything that changes bus or Matter behaviour would make bench
2121
results meaningless for the shipped image.
2222

23+
config HISENSE_RS485_HW_MODE
24+
bool "RS-485: let the UART peripheral drive DE (hardware half-duplex)"
25+
default n
26+
help
27+
UNVALIDATED, BENCH-ONLY. Leave this OFF unless you are actively comparing
28+
the two paths on a debug node with a scope or a frame-error count.
29+
30+
Default (OFF) is the path this project validated on real hardware: the
31+
shared driver toggles the DE line itself as a plain GPIO, waits
32+
HISENSE_DE_SETTLE_MS before the first byte, and holds DE high for
33+
HISENSE_DE_DRAIN_MS after the last so the FIFO and shift register empty
34+
before the transceiver stops driving. Finding that DE existed at all cost
35+
a multi-day debug, so the default does not change.
36+
37+
When ON, DE is handed to the UART peripheral instead: the DE GPIO becomes
38+
UART RTS and the port is put into UART_MODE_RS485_HALF_DUPLEX, so the
39+
hardware asserts DE exactly while the TX FIFO drains and releases it on
40+
the same clock as the bit stream. The software toggle and both delays are
41+
compiled out, and serial_putc() stops waiting per byte.
42+
43+
This is what ESPHome's uart component does for every RS-485 device it
44+
supports (flow_control_pin -> uart_set_pin RTS -> uart_set_mode), so it is
45+
well proven in general -- just not yet against THIS A/C. It cannot desync
46+
the way a software toggle can, and it removes ~30 ms of dead time per
47+
frame.
48+
49+
AmebaZ2 is unaffected either way: it has no such peripheral mode and keeps
50+
the software path unconditionally.
51+
2352
endmenu

0 commit comments

Comments
 (0)