Commit gráf
3221 Commit-ok
Szerző SHA1 Üzenet Dátum
Valentin V. Bartenev 21e396366c Web: fix trend min/max range to include both rx and tx for packets series
The packets series is serialized as 3-element points [timestamp, rx, tx]
by buildSeriesJson() since b64d57f, unlike other series which use
[timestamp, value].

The min/max range display in renderTrendResult() was using p[1] for all
series, which for packets only captured the rx delta, ignoring tx.

Fix by selecting the mapper function based on the series key: for
"packets", sum p[1] + p[2] (rx + tx); for all other series, use p[1]
as before.  This matches the existing pattern already used in
drawSparkline() (line 1890).
2026-05-15 21:49:33 +03:00
Rastislav VysokyésGitHub d4c99dec65 Change MeshCore intro video link to The Comms Channel's MC intro playlist 2026-05-15 16:42:29 +02:00
Liam CottleésGitHub cf9bf1d7fe Merge pull request #2565 from willumpie82/ble-pin-code
Disable BLE_PIN_CODE for E290 companion USB environment
2026-05-15 23:17:43 +12:00
Willem Oldemans e1616ff29d Remove static BLE_PIN_CODE definition from Heltec E290_usb configuration 2026-05-15 13:09:59 +02:00
Willem Oldemans 973321d9b1 Disable BLE_PIN_CODE for E290 companion USB environment
Remove the hardcoded BLE_PIN_CODE build flag from the Heltec_E290_companion_ble
environment. The presence of BLE_PIN_CODE prevents the unit from operating as a
USB companion. Commenting it out allows dynamic PIN generation and enables USB
companion functionality.
2026-05-15 12:57:36 +02:00
Liam CottleésGitHub 6325a85336 Merge pull request #2493 from Rococo88/complex-wifi-reconnect
Refactor WiFi auto-reconnect to use non-blocking polling
2026-05-15 21:21:16 +12:00
ripplebizésGitHub 0db7715430 Merge pull request #2497 from oltaco/reduce-esp32-dram
Reduce dram0_0 usage on older ESP32 boards
2026-05-15 14:04:42 +10:00
ripplebizésGitHub f4be34a997 Merge pull request #2515 from Cisien/station-g3-esp32
Add support for Station G3 and its software configurable LNA/PA1
2026-05-15 13:58:42 +10:00
ripplebizésGitHub 181a54e718 Merge pull request #2532 from swaits/fix/trace-offset-widening
fix(mesh): widen TRACE offset to uint16 to avoid narrowing
2026-05-15 13:17:03 +10:00
Liam CottleésGitHub 555745700e Merge pull request #2543 from meshcore-dev/cmd-send-raw
new CMD_SEND_RAW_PACKET
2026-05-13 22:10:52 +12:00
Scott Powell c588540b1b * new CMD_SEND_RAW_PACKET 2026-05-13 13:28:56 +10:00
Liam CottleésGitHub 910b1bee5b Merge pull request #2541 from AI7NC/patch-2
Update cli_commands.md to include 'ver'
2026-05-13 14:43:10 +12:00
Valentin V. Bartenev 89a59d5a2e Web: enable TLS session tickets for faster session resumption
Rename arch/esp32/tls_cipher_restrict.c → arch/esp32/tls_server_config.c.
The file now handles two aspects of TLS server configuration via the
existing __wrap_mbedtls_ssl_config_defaults() intercept, so the old name
no longer described its full scope.

Add TLS session ticket support (RFC 5077).  Browsers can now resume
HTTPS sessions without a full RSA handshake, reducing connection latency
on repeated connections.

A static mbedtls_ssl_ticket_context is initialized once on first server
start using the ESP32 hardware RNG (esp_fill_random), then registered via
mbedtls_ssl_conf_session_tickets_cb().  The context must be static because
mbedTLS stores the pointer for the lifetime of the server, analogous to
kServerOnlyCipherSuites.  If mbedtls_ssl_ticket_setup() fails, the flag
remains unset and session tickets are silently skipped rather than
registering callbacks against an uninitialized context.

CONFIG_MBEDTLS_SSL_SESSION_TICKETS is enabled by default in the pre-built
arduino-esp32 framework, so no build system changes are required beyond
the filename update in platformio.ini.
2026-05-13 04:48:15 +03:00
AI7NCésGitHub 16cb6d518f Update cli_commands.md to include 'ver'
Include the 'ver' command for retrieving the firmware version
2026-05-12 12:42:33 -07:00
agessaman 19f950018c Add bulk region hierarchy command to CLI
Add a new command `region bulk` for defining region hierarchies in a single line. This command allows users to create multiple regions in a single message. Updated the documentation to include usage examples and detailed parameter descriptions.
2026-05-12 12:08:03 -07:00
Huw DuddyésGitHub 12a37a224a Merge pull request #2536 from recrof/patch-3
revert: "feat: Enable GPS on RAK 1W kit" (#2401)
2026-05-13 00:41:41 +10:00
Rastislav VysokyésGitHub 68363d9e4d revert: "feat: Enable GPS on RAK 1W kit" (#2401)
reverted changes to RAK_BOARD and PIN_GPS_EN. setting `RAK_BOARD` would cause radio to stop working and end with RadioLib error -707
2026-05-12 15:56:28 +02:00
Stephen Waits 09a27a2591 fix(mesh): widen TRACE offset to uint16 to avoid narrowing 2026-05-11 19:32:56 -06:00
liamcottle b0b87fd709 fix gps pins for lilygo t impulse plus 2026-05-12 10:36:19 +12:00
dex2codeésGitHub 09e6796fee Check COMMIT_HASH before retrieving git commit SHA
Add check for empty COMMIT_HASH before assignment.
2026-05-11 17:28:30 +03:00
Liam CottleésGitHub c4523f71a9 Merge pull request #2522 from liamcottle/board/lilygo-t-impulse-plus
Add support for LilyGo T-Impulse-Plus
2026-05-12 01:39:44 +12:00
liamcottle 2fdbfbdbf6 turn off 3.3v rail when powering off 2026-05-12 01:06:22 +12:00
Liam CottleésGitHub 96bbed225a Merge pull request #2520 from Quency-D/heltec-mesh-node-t1
add heltec-mesh-node-t1
2026-05-12 00:38:14 +12:00
Valentin V. BartenevésGitHub 89df9eb9a0 Update README.md with new features 2026-05-11 13:53:31 +03:00
liamcottle a49ee6ebe9 fix battery voltage reading 2026-05-11 22:17:53 +12:00
liamcottle 242c45f4a3 initial support for lilygo t impulse plus 2026-05-11 22:05:06 +12:00
Liam CottleésGitHub 3cdcb3ef84 Merge pull request #2519 from oltaco/customlfs-0.2.2
pin CustomLFS to version 0.2.2
2026-05-11 21:33:05 +12:00
Valentin V. Bartenev 34cadd3a8e Web: poll for device reboot after OTA upload
After a successful firmware upload the UI previously showed a static
"Done. Device is rebooting..." message with no indication of progress.

Replace the static message with a live polling loop.  A dot is appended
to the message every second as a visual progress indicator.  After a
4-second initial delay (to allow the device to start rebooting), the
loop fetches "/" to check whether the HTTPS server is back up.  On a
successful response the interval is cleared and the user is redirected
to the login page after 1.5 s (the session token is invalidated by the
reboot).  On a network error the fetch is retried every 4 seconds.
A guard value of 1000 prevents a second fetch from being started while
the first is still in flight.  If the device does not respond within
60 seconds the loop stops and an error message is shown.
2026-05-11 10:52:48 +03:00
Quency-D 6d3b71eed9 add heltec-mesh-node-t1 2026-05-11 15:11:07 +08:00
Valentin V. Bartenev ac33787de5 Web: replace ElegantOTA UI with native HTTPS firmware upload
OTA firmware updates previously required starting a separate plain-HTTP
AsyncWebServer on port 80 via ElegantOTA.  This meant stopping the
redirect server, spinning up a new server instance, and transmitting
the firmware binary unencrypted without authentication enforced.

Add a POST /api/ota endpoint directly to WebPanelServer.  The handler
authenticates via the existing X-Auth-Token session token, resets the
idle timeout via noteActivity(), and streams the request body in
4096-byte chunks into Update.write() (matching the ESP32 flash sector
size). Update.begin() is called with the exact Content-Length so the
library can verify partition space upfront.  Update.abort() is called
on any read or write error to leave the device on the current firmware.
On success the handler sends a 200 response and reboots after 1s.
The endpoint runs over the existing HTTPS server on port 443.

Replace the "Start OTA" button in the actions bar with an inline
"Firmware Update" card on the app page.  The card contains a .bin file
picker and an upload button.  Upload progress is tracked via
XMLHttpRequest's upload.onprogress event and shown with a progress bar.
A 401 response redirects to the login page; other failures re-enable
the file picker so the user can retry without reloading the page.

The old ElegantOTA infrastructure (ESP32Board::startOTAUpdate,
WebService::prepareForOTAStart, the /update redirect handler) is not
yet removed and remains accessible via the `start ota` CLI command.
2026-05-11 09:18:12 +03:00
Valentin V. Bartenev 3a81dcf69b Web: synchronize sparkline hover highlight across all trend charts
When hovering over a sparkline chart, the hover point is now drawn
simultaneously on all other trend charts at the same relative time
position. The tooltip, however, is shown only for the chart under
the cursor to avoid visual noise.

Implementation: store _updateHover and _points directly on each
canvas element after binding. On mousemove/touchstart, compute a
normalized position fraction (0..1) and broadcast it to all canvases
inside #statsTrends via querySelectorAll, translating the fraction
to each chart's own point index. On mouseleave/touchend, reset the
hover state on all canvases. The showTooltip parameter added to
updateHover controls whether the tooltip is updated, defaulting to
true to preserve existing behavior.
2026-05-11 07:16:01 +03:00
taco 716ba8ee5e pin CustomLFS to version 0.2.2
CustomLFS 0.2.2 adds support for the flash chip in LilyGo T-Echo Card and T-Echo Lite
2026-05-11 11:37:56 +10:00
Valentin V. Bartenev 5642259bee Web: reorder stats dashboard cards (Radio+Packets, Wi-Fi+Memory)
Swap Memory and Packets cards in the stats dashboard grid so that
Radio and Packets appear together in the middle row, and Wi-Fi and
Memory appear together in the lower row. This groups network-related
stats (Radio/Packets) and puts the less-frequently-needed Memory card
last, improving readability on both desktop and mobile layouts.
2026-05-11 04:08:37 +03:00
Valentin V. Bartenev 4dc5bdf8cf Web: rework MQTT IATA field
Replace the hardcoded 24-city <select id="mqttIata"> with an
<input list="mqttIataList"> + <datalist id="mqttIataList"> pair.
The datalist is populated at runtime from the same conf.json endpoint
used for radio presets, so the city list stays in sync with the API
without requiring firmware updates.

The <input> allows the user to type any IATA code directly, including
codes not present in the preset list. onfocus="this.select()" selects
all text on focus so the user can immediately type to replace the
current value without manually clearing it first.

Disable the Save button in HTML and enable it only when the field is
non-empty. The <input> event handler updates the button state as the
user types so Save is disabled immediately when the field is cleared.
refreshMqttIataWarning() also updates the button state after a Refresh
or Save, ensuring the button is re-enabled when the device value is
loaded.

The warning banner and inline warning are not updated while
the user is typing — the <input> event handler does not call
refreshMqttIataWarning(). The warning only reflects the device's
saved state, updated on Refresh and after a successful Save.

Switch the event listener from "change" to "input" so the Save
button responds to every keystroke rather than only on blur.
2026-05-11 04:00:41 +03:00
Valentin V. Bartenev fbab148382 Web: rework preset UX
Change the radio preset endpoint from https://api.meshcore.nz/api/v1/config
to https://api.vbart.ru/meshcore-firmware/v1/conf.json, which returns
a flat JSON array of city objects:
  [{"code":"BSK","name":"Biysk","timezone":"...","settings":{...}}, ...]

Adapt preset parsing to the new schema: pass entry.settings through
normalizeRadioConfig() at the map step so invalid or missing settings
fields are silently filtered out via filter(Boolean). radioSignature()
and formatRadioConfig() are simplified to operate directly on already-
normalized numbers, using JS default number-to-string to avoid trailing
zeros (62.5 instead of 62.500, 869 instead of 869.000).

Rework the preset UX:
- Remove the auto-matching logic that highlighted the current radio in
  the dropdown; many cities share identical radio settings so it is
  impossible to reliably determine which preset is active.
  The dropdown now starts at "Load from preset".
- When a preset is selected, preview it in the "Current Radio" field
  (relabelled "Selected radio preset") and show "Click Save to apply".
  If the selected preset matches the current radio settings, the Save
  button is not activated and the UI is left unchanged.
- After a successful apply, set radioSettingsChanged=true and show a
  persistent "reboot required" warning; relabel the field "New radio".
- Disable the Save button (with opacity + cursor feedback) until a
  preset is chosen; savebtn:hover suppressed while disabled.
- Reset the preset dropdown when the radio config is refreshed from
  the device.
- Update placeholder to "Loading..." to match the new display format.
- Remove now-unused "Preset" label row from the Radio Settings card.
2026-05-11 01:01:02 +03:00
Huw DuddyésGitHub 3eacc49489 Merge pull request #2503 from jirogit/fix/techo-lite-non-shell-usb
feat(techo-lite): add USB companion radio target for non-shell variant
2026-05-10 22:40:12 +10:00
Liam CottleésGitHub e7ea2fc563 Merge pull request #2516 from oltaco/disable-ds3231-probe
Add option to disable DS3231 RTC probe
2026-05-10 22:48:47 +12:00
taco e7e97ec438 add option to disable DS3231 probe 2026-05-10 20:29:47 +10:00
Huw DuddyésGitHub a3e1930176 Merge pull request #2511 from entr0p1/techo-lite-rf-fix
Corrected T-Echo Lite SX1262 RXEN and TXEN pins, TCXO voltage.
2026-05-10 19:55:20 +10:00
Valentin V. Bartenev 72fc497980 Slightly optimize CPU util tracking by branchless tick hook
Replace the two separate volatile counters (s_idle_ticks, s_busy_ticks)
with a two-element array s_ticks[2]:

  [0] = idle ticks   (xTaskGetCurrentTaskHandle() != s_idle_handle → false → 0)
  [1] = busy ticks   (xTaskGetCurrentTaskHandle() != s_idle_handle → true  → 1)

The tick hook becomes a single branchless statement:

  s_ticks[xTaskGetCurrentTaskHandle() != s_idle_handle]++;

This eliminates the conditional branch from the tick ISR at the cost
of a few additional instructions in IRAM.  On Xtensa LX7 the branch
is somewhat unpredictable — the system alternates between idle and
busy — so removing the misprediction penalty (~5–10 cycles/call)
outweighs the slightly larger code size.

_onSample() is updated to read s_ticks[0] (idle) and s_ticks[1]
(busy) accordingly. All other logic is unchanged.
2026-05-10 10:02:21 +03:00
Valentin V. Bartenev 482b31bc19 Slightly optimize CPU util tracking by defer float conversion
Previously _onSample() computed and stored a volatile float _sma_avg
on every sample (64 times/minute on core 0).  The getter simply returned
that pre-computed value.

This commit inverts the responsibility:

- _sma_sum is now volatile uint16_t — the raw integer sum is the
  cross-core shared state, written atomically by core 0 (single S16I
  instruction on Xtensa LX7) and read by core 1.

- The float conversion (uint16_t → float multiply) is moved into
  getCore0Util(), which is called rarely (once/minute for history,
  on-demand for web requests).  The multiply now happens on the core
  that actually needs the result.

- _sma_avg is removed entirely, saving 4 bytes and one volatile float
  store per sample from the hot path.

- The SMA update is rewritten as a single combined expression:
    const uint16_t last = _sma_buf[_sma_idx];
    _sma_buf[_sma_idx] = s8;
    _sma_sum = (_sma_sum - last) + s8;
  This produces a single write to _sma_sum instead of two (decrement
  then increment), which is cleaner when _sma_sum is volatile.

The cross-core contract is unchanged: core 0 writes _sma_sum once per
sample; core 1 reads it in getCore0Util().  A 16-bit aligned store on
Xtensa LX7 is a single instruction, so no spinlock is needed.
2026-05-10 07:09:03 +03:00
Valentin V. Bartenev a9273f02bb Redesign approach for CPU usage tracking
The previous implementation maintained three exponential moving averages
(1/5/15-minute) of CPU busy-fraction, sampled every 5 seconds. This had
two problems:

1. Wrong semantics: the metric was labelled "load_avg" (a Unix concept
   measuring run-queue depth), but the tracker actually measures CPU
   busy-time as a fraction of FreeRTOS ticks — i.e. utilization, not
   load.  The name was misleading.

2. Poor responsiveness: the 1-minute EMA (DECAY = exp(-5/60) ≈ 0.920)
   has a ~60-second time constant.  On a live web panel, this made the
   metric appear frozen, especially during short bursts of activity
   (e.g. an HTTP request from the panel itself).

This commit replaces the EMA with a 64-sample simple moving average
(SMA) over a compact uint8_t circular buffer. The sample interval is
60 s / 64 = 937,500 µs, so the window covers exactly 60 seconds:

Algorithm:
- Every 937,500 µs (= 60 s / 64), the FreeRTOS tick delta (busy vs
  idle ticks on core 0) is computed and stored as a uint8_t
  (0–255 = 0–100% utilization).
- The circular buffer holds 64 samples, spanning exactly 60 seconds
  (64 × 937,500 µs = 60,000,000 µs). The timer interval is derived
  directly from the window size: 60 000 000 / SMA_WINDOW µs.
- The index wraps with a bitwise AND (& 63) instead of modulo, since
  64 is a power of 2.
- The running sum is a uint16_t (max 64 × 255 = 16 320, fits easily).
- The result _sma_avg is a volatile float written atomically by the
  esp_timer task (core 0) and read from the main loop (core 1).
  No spinlock is needed: on Xtensa LX7, a 32-bit aligned float store
  is a single instruction.

The 60-second window smooths out short bursts (e.g. WiFi/HTTP spikes)
while reacting to sustained load changes within ~10–15 seconds.

Naming:
- The public API is now getCore0Util() returning a float in [0.0, 1.0].
  The name explicitly identifies which core is measured (core 0, which
  runs WiFi, MQTT, HTTP, and LwIP — see task_pinning.c).
- JSON keys: "load_avg" (array) → "core0_util" (scalar float, percent)
- HistorySample field: load_avg1_pct → core0_util_pct
- StatsHistory series key: "cpu_load" → "core0_util"
- Web panel label: "Load Avg" → "Core0 Util"

The history snapshot (once per minute) reads the same _sma_avg value,
which at that point represents the rolling average of the last 60
seconds — exactly one history interval.
2026-05-10 06:26:08 +03:00
Valentin V. Bartenev 5bf017be1b Web: normalize unit formatting across all HUD cards
Apply consistent number-unit spacing and helper usage:

- Add a space before "%" in all percentage labels (Battery,
  Signal Quality, TX Share, RX/TX Flood Share, RX Error Rate,
  Archive Free), matching the existing style of other units
  such as " dBm" and " dB".

- Replace raw String(x) + " s" with formatDuration() for RX Air,
  TX Air, and Total Air in the Radio card, matching the style
  already used for uptime and event age.
2026-05-10 00:09:08 +03:00
Valentin V. Bartenev 86bf8dcb5c Web: reorder Radio HUD air-time metrics (RX first, TX Share last)
Reorder the four air-time metrics in the Radio card:

  Before: TX Air  | RX Air
          TX Share | Total Air

  After:  RX Air    | TX Air
          Total Air | TX Share

Move RX Air to the primary (top-left) position — a repeater
receives before it retransmits, so RX is the primary signal.
Move TX Share after Total Air so the derived percentage follows
the raw counters it summarises.
2026-05-09 23:56:48 +03:00
Chris 75fb07fc2c Add support for Station G3 and its software configurable LNA and PA1 2026-05-09 13:53:20 -07:00
Valentin V. Bartenev d5fa2466d7 Web: show min/max range on trend cards
Add a compact "min X / max Y" label to the right of the current value
on each trend card in the stats panel.  The range is computed client-side
from the points array already present in the API response, so no backend
changes are required.

CSS: add .trend-info (flex:1 wrapper) and .trend-range (small muted
label) classes; make .trend-value a flex row with space-between so the
range label is pushed to the right edge.

JS: refactor formatTrendValue to use a module-level kTrendFmt lookup
table instead of a chain of if-statements, and add a withUnit parameter
(default true) so the same function can produce unit-free numbers for
the range display. Populate the new head-range-<key> span in
renderTrendResult after the existing axis-label block.
2026-05-09 23:50:59 +03:00
Valentin V. Bartenev eaf3c4c263 Stats: add CPU load trend series to history and web panel
Wire the 1-minute CPU load average into the stats history pipeline:

- Rename HistorySample::reserved to load_avg1_pct (uint8_t, 0-100);
  struct size unchanged.
- Populate load_avg1_pct in updateStatsHistory on ESP32 from
  _cpu_tracker.getLoadAvg1() * 100.
- Register "cpu_load" series in buildPointValue / seriesTitle /
  seriesUnit so buildSeriesJson serves it via
  /api/stats?series=cpu_load.
- Persist load_avg1_pct as a 29th CSV column in flushSummaryLog.
  parseSummaryLine accepts both 28-column (old, load_avg1_pct=0)
  and 29-column (new) formats for backward compatibility.
- Add "cpu_load" to the web panel trend card order with orange
  sparkline color (#e07b39), "N %" hover formatting, and a Y-axis
  floored at 0 with a 10% minimum ceiling.
2026-05-09 22:31:02 +03:00
Valentin V. Bartenev bbb07b65ca Web: stale stats indicator in Stats header
Add a blinking "refreshed >Xm ago" label aligned to the right of the
Stats page h2 header.  The label is hidden until stats are more than
1 minute old, then appears with a CSS color-pulse animation (red to
--text-muted) to draw attention. It hides immediately when a refresh
is initiated and reappears only after the next successful fetch.

- Add @keyframes blink-stale (color pulse red <-> --text-muted) and
  .stats-stale CSS class (13px, normal weight, 1.5s animation)
- Add statsLastUpdated span inside the Stats h2 (flex, right-aligned,
  initially hidden)
- statsLastFetchedAt: null until first successful fetch, reset to null
  at the start of each loadStatsPage call, set to Date.now() on success
- updateStatsLastUpdated() hides span when null or secs < 60, shows
  minutes/hours elapsed otherwise
- setInterval(updateStatsLastUpdated, 10000) runs only on /stats page
2026-05-09 21:23:40 +03:00
Valentin V. Bartenev 55f21997aa Web: show RX metrics before TX in Packets HUD card
For a mesh repeater, a packet must be received before it can be
retransmitted, so RX metrics are the primary signal. Reorder the
Packets card to reflect this:

- Swap meter order: RX Flood Share before TX Flood Share
- Swap metric order: Recv before Sent, RX Direct before TX Direct
- Reorder floodTx/directTx declarations to match the new order
2026-05-09 20:12:03 +03:00
Valentin V. Bartenev b64d57f6a4 Web: split "packets" series into separate RX/TX components
The "packets" trend series previously emitted a single combined
packet-count delta per interval. This change breaks it into two
separate values so the sparkline can show directional traffic.

Backend (StatsHistory::buildSeriesJson):
- Add a dedicated branch for series == "packets" in the second
  (JSON-emission) loop.
- Each point is now emitted as [uptime_secs, rx_delta, tx_delta]
  instead of [uptime_secs, total_delta].
- rx_delta / tx_delta are computed as non-wrapping uint32_t
  differences of packets_recv / packets_sent between consecutive
  samples, consistent with the existing buildPointValue logic.
- Buffer overflow guard updated from +24 to +40 bytes to cover
  the wider three-integer format.

Frontend (WebPanelServer / inline JS):
- drawSparkline: for "packets", compute Y scale and bar height
  from rx+tx total; render a stacked bar with TX on top (cyan,
  #06b6d4 / hover #22d3ee) and RX below (violet, #8b5cf6 /
  hover #a78bfa), proportioned by the rx:tx ratio.
- bindSparkHover tooltip: show "RX: N  TX: M" for "packets"
  instead of the generic formatTrendValue output.
- Remove the now-unused sparkStrokeColor early-return for
  "packets" (stroke color is not used in the bar path).
2026-05-09 19:58:36 +03:00