Framework for upcoming variant-specific PRs that add LED feedback during boot. The hook gives users visual cues that the device is busy and
shouldn't be interacted with until startup completes.
Extract the inline cursor-walk in handleRegionCmd into file-local
helpers (skipSpaces, rtrimSpaces, takeToken, splitNameJump,
processRegionDefSegment), grouped immediately above the consumer.
Behavior is identical; addresses PR #2540 review feedback on
readability.
Tighten the region def docs: collapse five Note callouts into three
grouped paragraphs (Behavior / Existing regions / Limits), add a
case-sensitivity caveat plus an error example, note the cursor reset
between split commands, and use generic placeholder names.
The error_rate graph showed all zeros after ~65535 receive errors had
accumulated. Root cause was a two-part bug:
1. In MyMesh::updateStatsHistory(), recv_errors was clamped to 0xFFFF
via min<uint32_t>(..., 0xFFFF). Once the counter reached 65535,
every subsequent sample stored the same constant value, making the
per-interval delta always zero and therefore error_rate always zero.
2. HistorySample::recv_errors was declared as uint16_t while the
underlying counter (n_recv_errors) is uint32_t. parseSummaryLine()
also truncated the restored value back to uint16_t.
Fix:
- Remove the clamp in MyMesh::updateStatsHistory(); assign
getPacketsRecvErrors() (uint32_t) directly.
- Change HistorySample::recv_errors from uint16_t to uint32_t,
moving it next to the other uint32_t fields.
- Update both parseSummaryLine() code paths to cast recv_errors to
uint32_t instead of uint16_t.
The archive format is unchanged: recv_errors is written as %u and
read back into an unsigned local, which is correct for a 32-bit
value on ESP32.
Call sntp_servermode_dhcp(1) before WiFi.begin() so that lwIP's DHCP
client passes any NTP server address received in DHCP Option 42 to the
SNTP client (slot 0).
Replace configTzTime() with explicit setenv/tzset + sntp_setservername
+ sntp_init() to avoid overwriting the DHCP-provided server. Static
fallback servers (0.ru.pool.ntp.org, ntp.ix.ru, ntp21.vniiftri.ru) are
placed in slots 1-2 when DHCP provides a server, or slots 0-2 otherwise.
This relies on CONFIG_LWIP_DHCP_GET_NTP_SRV=y, which is already enabled
in the pre-built Arduino ESP32 core (framework-arduinoespressif32).
Add debug logging of the initialised NTP server list, guarded by
WIFI_DEBUG_LOGGING.
Adds a hidden `ℹ` button (meshcoretelProfileBtn) in the Public Key
field of the Info panel. After initApp() finishes loading the
"Loading info..." section, the public key value is read from the
DOM and validated against /^[0-9A-F]{64}$/. If it passes, an onclick
handler is attached that opens https://meshcoretel.ru/<PUBLIC_KEY>
in a new tab, and the button is made visible. The button remains
hidden if the key fails to load or does not match the expected 64-char
uppercase hex pattern, so the UI degrades gracefully on any firmware
that does not return a valid public key.
The container div is changed from fieldline (2-column grid: 1fr auto)
to inline-actions (3-column grid: minmax(0,1fr) auto auto) to accommodate
the second action button without wrapping.
renderNeighbourId in the /stats neighbours table now checks for the
"full_id" field emitted by appendJsonNeighbours(). If the value is a
valid 64-character hex string (the node's full 32-byte public key),
the short ID is wrapped in an anchor tag pointing to
https://meshcoretel.ru/<FULL_ID_UPPERCASE> (target="_blank",
rel="noopener noreferrer"). Falls back to plain short ID when
"full_id" is absent or does not match the expected pattern.
web-panel.md:
- Replace Start OTA nav item with dedicated Firmware Update section
- Update Info panel: Version/Client Version → Hardware/Firmware
- Add auto-refresh toggle to /stats element list
- Expand Trends list: add error_rate, core0_util, mcu_temp,
note RX/TX stacked rendering of packets series
- Update sequential trend loading order accordingly
- Rewrite OTA subsection to describe native HTTPS upload flow
api.md:
- Add error_rate and core0_util to supported series list
boards.md:
- Add DIY_ESP32S3_N16R8_E22_Back2back to repeater_mqtt table
local-builds.md:
- Add DIY_ESP32S3_N16R8_E22_Back2back_repeater_mqtt
- Add Ebyte_EoRa-S3_Repeater_mqtt
- Add ThinkNode_M2_Repeater_mqtt
- Add ThinkNode_M5_Repeater_mqtt
The hover index in syncHoverByX was computed as:
Math.round(x / width * (n - 1))
which linearly maps the full canvas width [0, width] to indices
[0, n-1]. For bar charts (packets, gps_satellites) the bars are
drawn in a plot area with plotLeft=4 and plotRight=width-4 margins,
so bar centers are at plotLeft + slotWidth*i + slotWidth/2. This
caused the first and last bars to require the cursor to be at the
very edge of the canvas to highlight correctly, while center bars
appeared fine.
For bar chart keys, use a slot-based formula instead:
Math.floor((x - plotLeft) / slotWidth)
which maps the cursor to whichever slot it falls in, matching the
actual bar layout. Line chart keys retain the original formula.
When hovering over a sparkline, the same X fraction was applied to
all other canvases to compute the highlighted point index. Because
different series can have different numbers of valid samples (some
values are filtered out), the same fraction mapped to different
indices and therefore different timestamps on each graph.
Fix by reading the uptime_secs timestamp (point[0]) of the hovered
point on the source canvas and finding the closest-timestamp point
in each other series, instead of using the fractional position.
Also de-duplicate the near-identical onmousemove/ontouchstart and
onmouseleave/ontouchend handlers into syncHoverByX() and
syncHoverClear() helpers, reducing the block from 34 lines to 16.
restoreSummaryLog() seeds the ring buffer with one sample from the
previous session (summary.latest). The next live sample has reset
counters and uptime starting from zero, so the delta across that
boundary is meaningless.
For the packets series this produced a zero point (same symptom as the
first-sample zero fixed in 1018404b, but at the restore boundary instead).
For error_rate it produced an inflated value because the counter difference
wraps around.
Fix by skipping any sample whose uptime_secs does not exceed the
previous sample's uptime_secs. In buildPointValue this returns false;
in the buildSeriesJson packets loop it skips emission and does not
advance previous.
Also: move have_previous = false into the else branch (the packets
branch does not use it), drop the now-redundant >= guards in favour of
direct uint32_t subtraction, and replace a C-style cast with
static_cast<int> for consistency.