Graphe des révisions
981 Révisions
Auteur SHA1 Message Date
Valentin V. Bartenev de978414ce Fix: remove redundant stopRedirectServer() call from ensureWebServer()
When the "start ota" command was issued from the web panel, a race
condition could occur between the main loop thread and the HTTP server
thread:

- HTTP thread: prepareForOTAStart() -> stopRedirectServer() ->
  httpd_stop(_redirect_server) [blocking]
- Main thread: loop() -> ensureWebServer() -> stopRedirectServer() ->
  httpd_stop(_redirect_server) [double free!]

Because stopRedirectServer() sets _redirect_server = nullptr only after
httpd_stop() returns, both threads could pass the nullptr check
simultaneously, resulting in a double free and heap corruption:

  CORRUPT HEAP: Bad head at 0x3fcb1f24. Expected 0xabba1234 got 0x3fca5f34
  assert failed: multi_heap_free multi_heap_poisoning.c:259 (head != NULL)

The call in ensureWebServer() was added as a safety net in commit
4b83142b, but is redundant: prepareForOTAStart() already calls
stopRedirectServer() synchronously before startOTAUpdate() occupies
port 80. Removing it eliminates the race condition.

The bug only manifested when OTA was triggered from the web panel
(HTTP thread), not from radio (main thread), because in the latter
case both calls happen on the same thread and cannot race.
2026-05-08 01:38:09 +03:00
Valentin V. Bartenev efce5856d8 Web: refactor sendWhole loop to eliminate per-iteration branch
The previous while loop computed a ternary `chunk_len` on every
iteration to handle the final partial chunk as a special case, and
called vTaskDelay(1) after every chunk including the last one,
adding an unnecessary yield immediately before the terminating null
chunk.

Restructure the loop using the identity:

  last_size = ((len - 1) % kWebPageChunkSize) + 1

This gives `last_size` in [1, kWebPageChunkSize] for any non-zero `len`,
so the final partial chunk is always non-empty and can be sent after
the loop without a special case.  The for loop then iterates only over
full kWebPageChunkSize chunks, making every iteration identical and
branch-free, and vTaskDelay(1) is called only after full chunks.

Also add a `len == 0` guard to short-circuit immediately when there is
nothing to send, and remove the best-effort terminator send on the
error path since a failed send makes a follow-up send equally likely
to fail.
2026-05-06 23:11:26 +03:00
Valentin V. Bartenev 50c9daa8c2 Fix broken compilation by copy-paste typo in b4b301d 2026-05-06 23:10:51 +03:00
Valentin V. Bartenev 16066547fa Web: use sizeof instead of strlen for PROGMEM HTML page sizes
The HTML pages (login, app, stats) are string literals compiled into
PROGMEM. Their sizes are known at compile time, but the previous
sendProgmemChunked() called strlen() on every request to determine
the length, requiring a full scan of potentially tens of kilobytes of
PROGMEM data before the first byte was sent.

Replace sendProgmemChunked() with a sendWhole() function that accepts
an explicit length parameter, and introduce a sendProgmem() macro that
passes sizeof(mem) - 1 at the call site. For array literals this
resolves to a compile-time constant, eliminating the runtime strlen
scan entirely.

The rename from sendProgmemChunked() to sendWhole() also better reflects
what the function does — it sends the entire buffer in chunks — rather
than where the data comes from.
2026-05-06 22:33:00 +03:00
Valentin V. Bartenev 650d03abf3 Web: allow browser caching of static HTML pages
The login, app, and stats HTML pages are static assets compiled into
PROGMEM.  All dynamic content is fetched separately via JavaScript API
calls after the page loads — the HTML itself never changes between
requests within the same firmware version.

The "Cache-Control: no-store" header forced the browser to re-download
the full HTML on every visit, including a complete chunked transfer
from the ESP32.  Given that the ESP32 is slow and serves responses in
small chunks, this was an unnecessary repeated cost that added latency
before the page became interactive.

Removing no-store allows the browser to cache the HTML pages locally.
Subsequent requests are served from the browser cache instantly, with no
transfer from the ESP32 at all, leaving the server free to handle the
API requests that actually carry dynamic data.
2026-05-06 22:30:22 +03:00
Valentin V. Bartenev b4b301dbbf Web: prevent redirect server socket exhaustion from stale connections
The HTTP redirect server has max_open_sockets = 2.  A client that
connects and then disappears silently (network drop, browser crash,
mobile radio loss) leaves its socket occupying a slot indefinitely —
the server has no way to detect the loss until it tries to write to
the socket again.

Enable lru_purge_enable on the redirect server so that when both
slots are occupied by such stale connections, the least-recently-used
one is evicted automatically to make room for a new incoming
connection, rather than refusing it outright.

Add a "Connection: close" header to the 302 response as a complementary
measure.  For well-behaved clients this triggers an immediate TCP
teardown after the redirect is received, shrinking the window during
which a connection can turn into a zombie.  Together the two changes
provide defence in depth: "Connection: close" prevents stale connections
from forming in the first place; lru_purge_enable cleans them up when
they do.
2026-05-06 22:25:51 +03:00
Valentin V. Bartenev 94d2ddf507 Web: tune httpd config to reduce browser freezes on slow ESP32
The ESP32 processes HTTP requests sequentially on a single task.
Under load — generating chunked HTML pages, running commands, or
serving sequential stats API calls — it can be slow to send data
or loop back to accept the next request.  The previous 2-second
recv/send timeouts were too tight for this: the httpd layer would
drop connections mid-transfer, causing the browser to hang or
show incomplete pages.

Raise recv_wait_timeout and send_wait_timeout from 2 s to 10 s
on both the HTTPS and HTTP-redirect servers, giving the ESP32
enough headroom to finish generating and sending responses without
the transport layer tearing down the connection prematurely.

Set backlog_conn to 0 on both servers. With the previous backlog
of 2, incoming connections completed the TCP handshake and queued
in the kernel while the ESP32 was busy.  The browser saw the
connection as open but received no HTTP response, causing it to
freeze indefinitely.  With backlog 0 (lwIP minimum), connections
that cannot be immediately accepted are refused outright, giving
the browser a fast, recoverable error instead of a silent hang.

Enable lru_purge_enable on the HTTPS server.  Browsers hold
keep-alive connections open for reuse.  With max_open_sockets = 2,
both slots can be occupied by idle keep-alive connections from the
same session, blocking a new connection attempt entirely.  LRU
purge automatically closes the least-recently-used idle keep-alive
connection to make room, ensuring the single client can always
reconnect without a server restart.
2026-05-06 22:14:28 +03:00
Valentin V. Bartenev 9b2358416c Web: yield in sendProgmemChunked loop to prevent task WDT reset
When sending large PROGMEM content in chunks over HTTPS, the lwIP TCP/IP
task (tiT) could monopolize CPU 0 for an extended period without ever
yielding, starving the IDLE0 task and triggering the task watchdog timer.

Add vTaskDelay(1) at the end of each iteration in sendProgmemChunked()
to yield to the scheduler between chunks, allowing the IDLE task to reset
the watchdog and preventing spurious reboots during web panel page loads.
2026-05-06 22:02:57 +03:00
Valentin V. Bartenev 4d404f5301 Web: align page chunk size to MBEDTLS_SSL_OUT_CONTENT_LEN to fix WDT reboot
The hardcoded kWebPageChunkSize of 768 bytes caused the task watchdog
to trigger when serving the web panel over HTTPS.  Each call to
httpd_resp_send_chunk() results in a separate TLS record encryption
via mbedTLS, which on ESP32-S3 uses DMA-backed AES-GCM (esp_aes_process_dma).

The gdma_disconnect() call inside that path enters a critical section,
blocking the IDLE0 task.  With 768-byte chunks, a large page response
requires many such DMA operations in tight succession, starving the IDLE
task long enough to trip the watchdog.

Replacing the hardcoded value with MBEDTLS_SSL_OUT_CONTENT_LEN aligns the
chunk size to the TLS output record buffer, minimising the number of TLS
records (and thus DMA encryption operations) needed to send a full page,
and keeping the httpd task within the watchdog timeout.

Fixes: task_wdt abort in sendProgmemChunked() -> httpd_ssl_send() ->
       esp_aes_process_dma() -> gdma_disconnect() on ESP32-S3.
2026-05-06 21:58:57 +03:00
Valentin V. Bartenev e99cee2a1b Web: pin HTTPS and redirect server tasks to core 0 at low priority
Set task_priority = tskIDLE_PRIORITY + 2 and core_id = 0 for both the
HTTPS server and the HTTP-to-HTTPS redirect server. This keeps web
serving off core 1, which handles radio and application logic, reducing
interference with time-sensitive operations.
2026-05-05 21:00:13 +03:00
Valentin V. Bartenev dd90afba1d NetworkService: keep hasTimeSync() true for 24h after WiFi drops
_have_time_sync is reset to false whenever WiFi disconnects, even
though the ESP32 RTC continues to hold accurate time after a
successful SNTP sync. This caused hasTimeSync() to return false
during transient WiFi outages, unnecessarily tearing down MQTT
broker connections and suppressing packet publishing.

Introduce _last_time_sync to record the wall-clock time of the
most recent confirmed sync. Move hasTimeSync() out of the header
into NetworkService.cpp and extend its logic: in addition to the
existing _have_time_sync flag, return true if the system clock is
still sane (>= kMinSaneEpoch) and no more than kMaxOutOfSync (24h)
has elapsed since the last confirmed sync.

This makes the MQTT uplink resilient to brief WiFi dropouts without
requiring any changes to callers of hasTimeSync().

Also bump kMinSaneEpoch from 2025-01-01 to 2026-01-01.
2026-05-03 22:10:32 +03:00
Valentin V. Bartenev 80851b9c25 Use Russian NTP servers 2026-05-03 20:09:57 +03:00
Valentin V. Bartenev afd5fc2f98 Unify firmware version format with release builds. 2026-04-29 01:07:11 +03:00
Valentin V. Bartenev a99196c3ce Do not tokenize HW model in JSON payload.
Tokenization replaces dots and spaces with underscores
making it look ugly on the MeshCoreTel website.
2026-04-28 03:27:49 +03:00
Valentin V. Bartenev 68e951e204 Work around IDFv4 heap canary corruption on early connect failure.
When the MQTT WebSocket handshake fails before a connection is fully
established (e.g. "Sec-WebSocket-Accept not found"), the IDF v4
transport teardown path writes only 3 of the 4 bytes of the heap block
tail canary (expected 0xbaad5678, actual 0xbaad5600).  The subsequent
esp_mqtt_client_destroy() call frees that block, causing multi_heap_free
to detect the broken canary and abort:

  CORRUPT HEAP: Bad tail at 0x3fcb42a4. Expected 0xbaad5678 got 0xbaad5600
  assert failed: multi_heap_free multi_heap_poisoning.c:259 (head != NULL)

The fix is to check heap integrity with heap_caps_check_integrity_all(false)
between esp_mqtt_client_stop() and esp_mqtt_client_destroy().  If corruption
is detected, scan internal SRAM (0x3FC00000–0x3FD00000) for the truncated
canary pattern and restore it to the correct value before
destroy() runs.

The scan is a no-op when the heap is clean and is compiled out entirely
on IDF v5+, where the underlying bug does not exist.

This fixes crash-on-reconnect observed with ESP32-S3 + IDF v4 + WSS transport.
2026-04-28 03:01:37 +03:00
Valentin V. Bartenev 03c365124b Fix truncation of LWT message.
It's just too small to fit the whole JSON that is put there
in refreshBrokerState().
2026-04-28 02:11:59 +03:00
Valentin V. Bartenev 46b0df7a4b Removed EastMesh MQTT broker support.
It's intended for Eastern Australia repeaters only.  Using original EastMesh
firmware is prefered in this case.
2026-04-28 00:52:26 +03:00
Valentin V. Bartenev f1912ff184 Added support for MeshCoreTel broker. 2026-04-27 03:15:00 +03:00
Jared Dohrman 4b83142b9e fix: start ota is now consistent across command sources 2026-04-23 09:27:48 +10:00
Jared Dohrman 5c50295552 fix: hide battery voltage range text for PMU-backed web stats 2026-04-22 18:06:29 +10:00
Jared Dohrman 46f0496865 feat: add mqtt client version to repeater app and cli 2026-04-22 13:29:03 +10:00
Jared Dohrman b1a32fa56b fix: make repeater stats battery display board-aware 2026-04-22 10:57:04 +10:00
Jared Dohrman bbab7c95d4 refactor: remove legacy battery reporting toggle path 2026-04-22 09:57:51 +10:00
Jared Dohrman 63e9368c84 fix: improve stats archive rotation and satellites trend handling 2026-04-21 19:14:17 +10:00
Jared Dohrman 2ad3b134f2 fix: suspend web panel for OTA 2026-04-21 18:19:33 +10:00
Jared Dohrman 0ad50e5bd0 repeater-mqtt-eastmesh-v1.3.8 2026-04-21 18:07:04 +10:00
Jared Dohrman 746396aee9 fix: sd card shares the radio SPI object 2026-04-21 16:01:19 +10:00
Jared Dohrman eb1a934849 fix: set radio using , separator 2026-04-21 14:13:57 +10:00
Jared Dohrman 4ae00fcf46 fix: http use ctrl_port = 32769 2026-04-21 13:36:49 +10:00
Jared Dohrman ec924e0c0d fix: add SD card support for TBEAM_1W 2026-04-21 13:18:50 +10:00
Jared Dohrman bf57f342b3 fix: require configured mqtt iata before broker connect 2026-04-20 22:30:06 +10:00
Jared Dohrman bcce0071f1 feat: open full web CLI access and add panel redirect flow 2026-04-20 08:59:24 +10:00
Jared Dohrman 2646d7177e Merge upstream/dev into develop 2026-04-19 12:21:01 +10:00
Scott Powell 49b37d5622 * minor bounds fix 2026-04-18 21:32:41 +10:00
liamcottle cfe4b0b9a5 bleuart service stay registered first to prevent gatt cache issues on android when already paired 2026-04-18 14:43:47 +12:00
Jared Dohrman 663f493817 web: cache repeater title across app and stats pages 2026-04-18 11:17:49 +10:00
Liam CottleetGitHub 77d737beb9 Merge pull request #2323 from txkbaldlaw/updated-companion-dfu-from-mt
Add support for Companion BLE OTA updates on nRF devices
2026-04-18 12:17:16 +12:00
Jared Dohrman d00fc588d3 fix(web-panel): apply 4-color thresholds to largest-block memory meters 2026-04-17 21:05:47 +10:00
Jared Dohrman e05c9bc09c Merge upstream/dev into develop 2026-04-17 17:54:47 +10:00
Jared Dohrman 64402c7f5e fix: improve web stats startup and memory indicators 2026-04-17 17:40:32 +10:00
Scott Powell d7a3d41843 Merge branch 'default-scope' into dev 2026-04-17 16:30:19 +10:00
Scott Powell 91f3fa0bdf * CLI: 'region put ...' now defaults to flood allowed 2026-04-17 15:11:10 +10:00
Scott Powell 7cdb056cb3 * CLI: 'region default ...' now auto-creates the region 2026-04-17 15:02:04 +10:00
Jared Dohrman 05fcffc838 fix: start web stats capture from boot on >=4MB PSRAM boards 2026-04-17 14:42:51 +10:00
Scott Powell 77d02e844f * bug fix 2026-04-17 14:38:03 +10:00
Jared Dohrman d412e3e84a fix: improve repeater stats UX and persist ESP32 fallback clock 2026-04-17 12:17:35 +10:00
txkbaldlaw b898e7a04e Add DFU to BLE Stack 2026-04-16 16:04:30 -05:00
Jared Dohrman b2ea2f882d fix(stats): lazy-init web stats and degrade non-PSRAM boards to live-only 2026-04-16 18:09:07 +10:00
Jared Dohrman 49e0993f73 fix(web): default web stats off on non-PSRAM boards 2026-04-16 17:27:07 +10:00
Jared Dohrman ec5406dcc6 fix: tune /stats radio RSSI graph for LoRa signal ranges 2026-04-15 19:05:54 +10:00