The C6 autodetect picked the CUSTOM profile, but load_board_profile() has
no entry for it, so Rx, Tx and the button stayed at the placeholder 99.
The startup settings load then failed the GPIO check and fell back to an
empty settings object (blank Board Profile, Tx Mode and Bus ID), and every
Application settings save returned 400 until valid pins were sent together
with CUSTOM through the API.
Start a C6 without a profile as CUSTOM with pins for the Seeed XIAO
ESP32-C6 (the c6_xiao_4M build): Rx GPIO20 (D9), Tx GPIO19 (D8), button
GPIO9 (BOOT), no LED, no Dallas sensor.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WebLogService::operator<< trims with `log_messages_.size() >= limit_log_messages_`.
size() is unsigned, so with limit_log_messages_ == 0 the condition holds even for an
empty deque and pop_front() runs on it - undefined behaviour, in practice heap
corruption. On a PSRAM board the stored value survives a reboot, so the device crashes
again as soon as the first log message is written.
Nothing clamps the value on the way in: start() copies settings.weblog_buffer with no
lower bound, the PSRAM branch of operator<< passes it straight into
limit_log_messages_, and ArduinoJson's | in WebSettings::update() only substitutes the
default for a missing or type-incompatible key - a stored 0 is a valid uint16_t.
- operator<<: skip the trim loop on an empty deque
- start(): keep the existing default when the stored value is 0
- getSetValues(): a missing or zero max_messages keeps the current size instead of
storing 0
- WebSettings::update(): repair a stored 0, and derive the psram/no-psram default once
instead of twice
Refs #3237