- ESP32Board: общий механизм adcMultiplierBase()/adcMultiplier(); setAdcMultiplier
возвращает false только там, где батарея измеряется не через ADC
- платы со своей формулой переведены на общий механизм: Heltec E213/E290/T190,
heltec_tracker_v2, Heltec v2, ThinkNode M2/M5; Station G2/G3 и T-Beam сообщают,
что подстройка не поддерживается
- мастер настройки: поле ADC стартует от действующего значения платы
(getAdcMultiplier) и работает с тремя знаками (x.xxx), например 5.420;
0.000 возвращает заводское значение платы
- Meshadventurer: добавлена поддержка множителя (раньше делитель был зашит числом 2
и set adc.multiplier отвечал Error: unsupported)
- экран загрузки: meshcore.io, версия с датой и posadmesh.ru; строка < Repeater > убрана
Проверено сборкой: heltec_v4_repeater_mqtt, Meshadventurer_sx1262_repeater_mqtt
- ответ CLI показывался строкой поверх экрана и сливался с содержимым, особенно
в верхней части (например OK - reboot to apply на экране частоты)
- теперь это модальное окно: рамка, свой фон, перенос текста и кнопка OK;
закрывается коротким нажатием, пока открыто — экран под ним не меняется
- окно показывается сразу (сброс таймера отрисовки), при закрытии экран
перерисовывается немедленно
- setToast/renderToast заменены на showPopup/renderPopup
- документация: posadmesh-docs/device-setup.md (раздел «Всплывающие сообщения»
и макет экрана), README — журнал изменений
Проверено сборкой: heltec_v4_repeater_mqtt, Meshadventurer_sx1262_repeater_mqtt,
Generic_E22_sx1262_repeater_mqtt (без дисплея)
- applyRadio() отправлял команду всегда, в том числе при выходе из меню, если
в этой сессии не открывали ни один радиопункт: рабочая копия (_radio_*) оставалась
нулевой, устройство отвечало Error, invalid radio params
- добавлен признак _radio_dirty: команда уходит только после реальной правки
частоты, полосы, SF или CR; при успешном применении признак сбрасывается
- перед отправкой значения перечитываются из настроек узла (loadRadio), а при
каждом входе в мастер рабочая копия обновляется (_radio_loaded = false)
- README: запись в журнале изменений
Проверено сборкой: heltec_v4_repeater_mqtt, Meshadventurer_sx1262_repeater_mqtt,
Generic_E22_sx1262_repeater_mqtt (без дисплея)
- UITask: новые пункты меню Battery и ADC
Battery — напряжение, процент заряда, полоска и текущий множитель ADC; короткое
нажатие показывает «сырое» значение АЦП в милливольтах, длинное/двойное — выход в меню
ADC — поразрядная правка множителя в формате x.xx (как полоса), под цифрами
показывается измеренное напряжение, поэтому калибровать можно прямо на экране
- процент заряда берётся у платы (getBatteryPercent), а если плата его не считает —
линейно по getBatteryMinMilliVolts()/getBatteryMaxMilliVolts()
- множитель применяется командой set adc.multiplier, значение 0.00 возвращает
заводской расчёт платы; проверка и сохранение остаются в CommonCLI
- поразрядный редактор SCREEN_DIGITS теперь обслуживает и полосу, и ADC
- документация: posadmesh-docs/device-setup.md (таблица меню, раздел «Батарея и
калибровка ADC», два новых макета экранов), README — журнал изменений
Проверено сборкой: heltec_v4_repeater_mqtt, Meshadventurer_sx1262_repeater_mqtt,
Generic_E22_sx1262_repeater_mqtt (без дисплея — код исключается препроцессором)
Разрешённые конфликты (12 файлов, из них 4 companion взяты из upstream целиком):
- examples/companion_radio/*: взят upstream, companion в форке не собирается
- examples/simple_repeater/MyMesh.cpp: убрана наша интеграция вентилятора
(setFanPostTxHoldMs/setFanMode) — upstream 1.18.0 сам грузит настройки вентилятора
через attachDynamicPrefs, а его CLI (get fan, set fan auto|on|off, set fan.lo/hi)
доступен через board.handleCommand из CommonCLI; удалён наш дублирующий блок
get fan/set fan. Оставлена наша сигнатура format*StatsReply(char*, size_t)
- examples/simple_repeater/MyMesh.h: оставлены наши WebSensorSnapshot/BlacklistEntry/
ChanNameFilter и getCLI() (нужен мастеру настройки), добавлен upstream isHiddenNode();
FIRMWARE_VERSION по умолчанию — v1.18.0
- src/helpers/CommonCLI.h: оставлены наши FanPrefs (совместимость NVS) и добавлен
upstream DynamicConfigSerializer custom (getCustom() нужен attachDynamicPrefs)
- src/helpers/ESP32Board.h: время восстанавливается сначала из NVS (наша надстройка,
переживает отключение питания), затем из RTC-памяти upstream, иначе опорная дата;
deep sleep по-прежнему не трогаем
- src/helpers/sensors/EnvironmentSensorManager.cpp: взята логика upstream
(detected[], один драйвер на тип), поверх возвращено определение адреса платой
(boardDetectedI2CSensorAddress)
- variants/lilygo_tbeam_1w/*: взят upstream (задача вентилятора, NTC, гистерезис,
CLI и сохранение настроек), наши правки перенесены как делегирование его API
- README.md: оставлен наш
- экраны Frequency и Bandwidth: единица измерения в конце строки значения
(868.731 MHz, 062.50 kHz), внизу отдельный пункт Save & exit
- после правки цифр пользователь переходит на Save & exit и удерживает кнопку:
значения применяются, экран возвращается в предыдущее меню
- документация: страница posadmesh-docs/device-setup.md дополнена схемой управления
и изображениями всех экранов (12 картинок в posadmesh-docs/_assets/wizard/)
- вики Gitea: страницы Home и Device-Setup с теми же иллюстрациями
- README: ссылка на вики, схема управления и запись в журнале изменений
- радиопресет заменён на четыре отдельных пункта меню: Frequency, Bandwidth, CR, SF
- частота: целые МГц шагом +-1, кГц правятся по разрядам (принцип как у координат)
- полоса: поразрядный ввод значения с точкой (формат 062.50)
- CR и SF: шаг +-1 с ограничением 5-8 и 5-12
- применение любого из четырёх пунктов отправляет set radio <частота>,<полоса>,<sf>,<cr>
целиком, остальные значения сохраняются; внизу меню остаётся Save & exit
- документация device-setup.md обновлена, README: раздел и журнал изменений
- проверено сборкой: heltec_v4_repeater_mqtt, Meshadventurer_sx1262_repeater_mqtt,
Generic_E22_sx1262_repeater_mqtt (без дисплея)
- добавлен локальный мастер настройки репитера: examples/simple_repeater/UITask.*
(меню на кнопке, редактор координат, применение значений через CommonCLI)
- MyMesh: публичный доступ к CommonCLI для UI (getCLI), main.cpp передаёт его в UITask
- мастер работает на любой цели с дисплеем и кнопкой и исключается препроцессором
там, где их нет; WiFi-код теперь под #if defined(ESP32)
- новая страница документации posadmesh-docs/device-setup.md, ссылки в index.md и mkdocs.yml
- README: раздел «Настройка с дисплея и кнопки» и запись в журнале изменений
- проверено сборкой: heltec_v4_repeater_mqtt, Meshadventurer_sx1262_repeater_mqtt,
Generic_E22_sx1262_repeater_mqtt (без дисплея), RAK_3112_repeater_mqtt
The serial command buffers must stay NUL-terminated within their
bounds: if they ever aren't, strlen() can return >= sizeof(command)
and the read loop would index past the buffer. Additionally, a full
buffer now becomes a completed line (end-of-line marker placed
inside the buffer, NUL terminator kept) instead of overwriting the
terminator and silently corrupting the buffer for the next pass.
Applies to the serial CLI readers of the repeater, room server,
sensor and secure chat examples, and to the CLI rescue reader of
the companion example.
Both Clyde North observers stopped answering over WiFi after days of
uptime while LoRa and MQTT kept working. `memory` showed heap_min ~1KB
and a largest internal block of 19KB: the HTTPS listener was alive but
no mbedTLS handshake (~40KB contiguous) could be allocated.
The trigger was the Aug 9 lru_purge change: with a 2-socket pool, every
browser page-load burst evicted a live session and forced a fresh TLS
handshake, and the repeated 40KB alloc/free cycles fragmented internal
RAM. Before that change the pool simply jammed, so nothing churned.
- Send `Connection: close` and trigger a session close on one-shot
responses (pages, favicon, redirect, login, 401s) so page-load bursts
release their sockets immediately. The authenticated /api/* polling
connection keeps keep-alive so it does not pay a handshake per poll.
- Gate WebPanelServer::start() on internal heap headroom (56KB free /
32KB largest), retrying every 15s instead of every loop tick.
- Self-heal: when the panel is idle and the largest internal block drops
below 24KB, stop and re-create the server to return its pools.
- Count the MQTT teardown path that deliberately abandons a client on a
heap-integrity failure.
- Expose the above as `heals:`/`deferred:` in `get web.status` and
`leaked:` in `get mqtt.status`.
- Fix the stats page Channel/Gateway cells showing `--`: the /api/stats
summary JSON never carried channel, gateway health, or the watchdog
count (only the `get wifi.status` string did).
- Add eastmesh-tools/web-heap-check.sh to read heap/service health over
the API and optionally stress the panel with browser-style bursts.
- Release notes 2026.8.3 for both observer tracks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MAX_CLIENTS is defined in both src/helpers/ClientACL.h and examples/simple_repeater/MyMesh.h. Appears it was centralised in the former header file some time ago. Both are included in some places and, depending on which order they're in, either value can win.
This change drops the duplicate entry from the repeater firmware and bumps the central limit up to 32 (per the original repeater firmware value).
Changes:
- Remove MAX_CLIENTS from repeater MyMesh.h
- Increase MAX_CLIENTS limit in ClientACL.h to 32
Both Clyde North observers sat WL_CONNECTED on an AP that kept beaconing
after its bridge to the wired LAN died: association up, good RSSI, stale
DHCP lease, but ARP-invisible and both MQTT brokers stuck in backoff.
The firmware equated "associated" with "online", so neither node ever
re-scanned and the outage held until a manual reassociation.
Add a connectivity watchdog to NetworkService: while connected, ARP-probe
the gateway every 30s (posted to the lwIP tcpip thread, safe on both the
IDF4 and IDF5 cores). If the gateway stays silent for 3 minutes, clear
the channel hint and force a full disconnect/rescan so the node can roam
to a healthy AP, backing off exponentially (up to 48min) while the
outage persists. Nodes without a gateway skip the probe.
Add a `wifi reconnect` repeater command that triggers the same forced
reassociation on demand, and report `gw:ok|lost wd:<count>` in
`get wifi.status`. Teach the web panel parser the new fields (also fixes
the IP metric rendering as "x.x.x.x channel:n") and show Gateway and
Channel in the Wi-Fi card.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Integrates upstream v1.17.0. Key adaptations:
- companion + repeater NodePrefs moved to upstream's new ConfigSerializer
(prefs.json) format; EastMesh fields (companion wifi, repeater fan and
MQTT bridge peer) appended as serializer groups/keys
- legacy binary prefs loaders retain EastMesh tail fields, so existing
devices migrate their settings to the new format
- companion main.cpp adopts upstream InterfaceManager while keeping
runtime WiFi credentials from prefs
- kept EastMesh release-tag versioning in setup-build-environment action
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>