- MQTT broker TLS. Transport TLS (
mqtts, CA bundle, cert validation) is not implemented onmqtt_publisher.py(plain TCP only). Until then use plain port 1883 or a LAN broker without TLS.
Meshtastic mesh participant release on main (merge feat/v0.7.6). Edge-only, pure Python, no concentrator recompile. Upgrade: Settings → Updates → Stable, or the full SSH block in docs/COMMON-ERRORS.md (git fetch, checkout main, pull, scripts/install.sh, restart). Required this release: new cryptography dependency for PKI and an updated meshpoint.service unit (RAK V2 reset fix). Pull-only upgrades can miss both. Witness-tested on RAK V2. Settings → Updates RC picker now points at v0.7.7 on feat/v0.7.7.
- PKI (2.5+ clients). X25519 keypair in
data/keys.yaml,public_keyin NodeInfo, AES-CCM encrypt/decrypt for DMs when peers advertise keys. Meshtastic apps show a closed lock instead of Shared Key-only mode. - DM routing ACKs. Inbound
want_ackTEXT to our node_id triggers a routing ACK on the same channel. - Periodic telemetry and position TX.
device_metricsand POSITION broadcasts when configured; position source and privacy are separate from the Meshradar registration pin (see GPS below). - Traceroute replies. Answer unicast traceroute requests with preserved inbound route/SNR, populated
route_back/snr_back, andrequest_idso the app does not show? dBon direct hops. - Telemetry request response. Answer unicast
TELEMETRYprobes (Signal quality /local_stats) with matching variant,request_id,Telemetry.time, andLocalStats.noise_floor. - PKI + channel reply encryption. Unicast replies (routing ACK, traceroute, telemetry) use PKI only when the inbound packet has
channel_hash == 0. Channel-based requests stay on channel AES even when the peer pubkey is known. - Relay vs inbound replies. Skip relay for unicast packets addressed to our node; run inbound auto-responders before relay evaluation so replies are not delayed behind relay airtime on the SX1302.
- Public channel sender names. Channel TEXT from other nodes shows the resolved node name in Messages and the packet feed, not the literal "Broadcast" label (#38 sender-name regression).
- Map Direct/Relayed filters. Node map markers stay in sync with the Direct and Relayed filter pills.
- Apply finish reliability. Dashboard Apply uses a detached
apply_finish.shsopip installandpost_update.shcomplete before restart; fixes crash loops when new dependencies land on RC branches. - Messages startup fix. Resolves
MessageNameResolvercrash on boot when the message store initializes before the node roster is ready. - MeshCore offline copy. When Native TX is disabled, Configuration and Radio explain that USB capture can still work while the companion card shows
transmit_disabledinstead of a generic disconnect.
- Location split: Meshradar pin vs mesh POSITION. Registered coordinates in
device.latitude/longitudeare always sent to Meshradar upstream and are not overwritten by live gpsd fixes. Meshtastic POSITION on the LoRa mesh is configured under Configuration → GPS → Mesh position broadcasts (registered pin vs live GPS, with approximate/precise/hidden privacy). MQTTlocation_precisionremains independent. - Message display names. Outbound and inbound chat bubbles resolve sender names from the live node roster instead of stale or cross-protocol fallbacks.
- Unknown
local.yamlkeys. Config loader logs a singleWARNINGlisting keys it could not apply (typos, mistyped sections) instead of failing silently (#63).
- RAK Hotspot V2 reset robustness. Systemd
ExecStartPre/ExecStopPostconcentrator reset uses+prefix so root-owned reset runs reliably on sensitive RAK7248 carriers (#62). - WisMesh Node experimental channel. Settings → Updates adds an optional WisMesh Node (RAK6421 HAT) track on
feat/wismesh-hat(not for standard SX1302 gateways). Seedocs/WISMESH-NODE.md.
Patch release on main. Edge-only. Upgrade: Settings → Updates → Stable, or git pull on main plus systemctl restart meshpoint. No concentrator or HAL changes.
- Lightweight apply finish. Settings → Updates runs
git fetch/ checkout / reset, then a detachedscripts/apply_finish.shthat stops the service, runspip install -r requirements.txt,post_update.sh, and restarts. Avoids crash loops when the next release adds new Python dependencies before the service boots the new code. - Live update progress. Apply and rollback streams show command output in a terminal panel; step labels match the backend (
upgradeinstead of staleinstall.sh/restart servicekeys). - SSH upgrade path.
install.shon upgrade (IS_UPGRADE=1) refreshes the venv before apt/HAL work so manual upgrades get the same pip-first safety net.
Companion polish, live GPS, and local dashboard UX. Edge-only, pure Python, no concentrator recompile. Upgrade: git pull on main (or Settings → Updates → Stable) plus scripts/install.sh when crossing from older releases so gpsd packages and sudoers stay in sync. Settings → Updates RC picker now points at v0.7.6 on feat/v0.7.6.
- Live GPS via
gpsd. Pluggablelocation.source(static|gpsd|uart). Configuration → GPS ships a skyplot (az/el/SNR, constellations, fix lamp, DOP, last fix).install.shinstalls gpsd + USB hotplug config idempotently. Live fixes update device coordinates and the local map marker. UART path remains a placeholder for RAK onboard GPS. - MeshCore USB skips u-blox GPS.
UsbPortClassifierexcludes VID0x1546from MeshCore serial probing so a GPS stick and Heltec companion can coexist.
- Companion set-name from the dashboard.
PUT /api/config/meshcore/companion-name, editable name on Configuration → MeshCore, optional re-apply fromlocal.yamlafter USB reconnect. - Channel keys (extends v0.7.4 editors). Slot 0 = Public (locked); user channels on slots 1–7; hashtag channels with empty key map to the 16-byte zero secret; Messages send/RX use the same slot index as Configuration.
- Zero-key length fix. Empty hashtag saves previously wrote 64 hex digits and blocked later Save Channels; legacy yaml normalizes on read/save.
- Custom preset on Configuration → Radio. Restore Custom chip with SF/BW/CR inputs when modem params do not match a named preset.
- MQTT Home Assistant state topics. Retained publishes on
meshpoint/{node_id}/telemetryandmeshpoint/{node_id}/positionwhen HA discovery is enabled.
- Map: remember zoom/view across reload; node popup Last heard line; MeshCore nodes render as diamond markers (Meshtastic stays circles).
- Node grid: sort (last heard / signal / hops / name) and filter (all / direct / relayed), persisted in localStorage.
- Favorite nodes: star on cards and drawer, amber map border, Favorites only filter.
- Load average on the system stats row.
GET /api/device/metricsreturns[1m, 5m, 15m]from/proc/loadavg; new Load Avg dashboard card. PR #61 merged tomainafter v0.7.4; ships in this release (no separate patch version). - Stats CPU temperature honors Settings → Meshpoint °F/°C preference.
- Terminal: quick-command insert no longer steals focus from the shell.
Major dashboard release on main (merge 56d4f7c). Builds on v0.7.3 auth: every page and API call stays behind the login cookie. Edge-only, pure Python, no concentrator recompile. Upgrade: use the idempotent block in README.md and docs/COMMON-ERRORS.md (git fetch, checkout main, pull, scripts/install.sh, systemctl restart) so jumps from v0.6.x or v0.7.2 pick up venv deps, stale .so cleanup, and sudoers. No new Python packages beyond v0.7.3 (bcrypt, PyJWT). After upgrade on main, Settings → Updates defaults the channel picker to Release candidate (v0.7.5) for early testers.
- New sidebar IA. Persistent nav for Dashboard, Messages, Stats, Radio (read-only RF telemetry), Terminal, Configuration (Identity / Radio / Channels / MeshCore / Transmit / MQTT / GPS), and Settings (Updates / Auth / Meshpoint). FLIP accent bar,
g+letter shortcuts, tablet click-outside-to-collapse, and a larger framed Meshpoint logo with a websocket status pip. - Top bar chrome. Connection lamp, device identity, live radio summary chip, build stamp, and sign-out control stay visible on every route.
- Polish layer (Sprints A–D). Sidebar noise-floor sparkline and reconnect storyboard; live browser-tab title; per-page init checklist and route fade-ins; Ctrl+K command palette and
?keymap overlay; optional sound engine; high-contrast and sunlight themes; terminal ASCII splash and "since you last looked" delta line. - Responsive fixes. Stats section scrolls on tablet; map keeps zero page-level horizontal scroll; KPI strip scrolls inside its card; packets feed height is bounded so live traffic no longer buries the map; phone landscape keeps map + node panel visible; mobile drawer scrolls end-to-end with
100dvhand safe-area padding so Sign Out clears the iOS Safari toolbar. - Sidebar badges. Radio shows a live NodeInfo TX countdown; Messages counts unread DMs only (not channel chatter), seeds from the server, and clears when you open a conversation.
- Node list online dot. Green/grey indicator now uses a 2-hour "recently heard" window (Meshtastic-style) instead of 15 minutes, so nodes at "18m ago" are not mislabeled offline while the timestamp still looks fresh. UTC-safe parsing for SQLite timestamps without a
Zsuffix.
- Settings > Auth. Change admin password (rotates JWT secret and forces re-login), sign out everywhere (bumps session version), enable/configure viewer read-only login, tune failed-login lockout attempts and cooldown, and adjust session lifetime from the dashboard.
- Audit trail. Admin actions append JSON lines to
data/admin_audit.jsonl(config saves, auth changes, dangerous invokes, terminal commands, update apply). Sensitive fields are redacted.
- Configuration editors. Identity (names + pinned node ID), Radio (region, preset, MHz/slot via Meshtastic firmware formula, hop limit), Channels (PSK table with per-channel delete (#38)), MeshCore (USB source, channel keys, Send Advert, Refresh contacts), Transmit (TX power, duty, native TX enable, relay limits), MQTT (broker, topic root, region segment, encryption, publish allowlist, JSON mirror, HA discovery, location precision), Advanced (upstream Meshradar URL/key/reconnect, device placement, storage paths, radio-advanced, MeshCore USB tuning), and GPS (placement UI;
PUT /api/config/gpsstill deferred). Top-level Radio tab is observational only; all edits live under Configuration. - MQTT API wired.
PUT /api/config/mqttand enrichedGET /api/configmap dashboard fields tolocal.yaml. Service restart required for the publisher to reconnect. - Hierarchical MQTT topic paths (#35):
topic_rootandregionsegment combine per the Meshtastic spec (<topic_root>/<region>/2/e/<channel>/<gateway>) with live preview in Configuration → MQTT. Avoids the double-region footgun (msh/US/FL/US/...). - Preset save hot-reload. Saving a modem preset updates in-memory config immediately; observational Radio tab and top-bar preset readout refresh on the next poll without a hard browser refresh.
- Web terminal. Browser-based shell (xterm.js) with Connect/Disconnect, command guide drawer (
?), search overlay, and admin-only access. Commands are audited; dangerous invocations use a confirm modal (typed confirmation removed in favor of a simpler Confirm/Cancel flow). - In-dashboard updates. Settings > Updates lists installed version, git branch, and last check; Check for updates reports commits behind the selected channel; Apply runs fetch/checkout/install/restart with streamed progress. Release channel picker: Stable (main), Release candidate (v0.7.5) (
feat/v0.7.5), and custom branch. Gateways onmainat v0.7.4+ default to the v0.7.5 RC in the picker. Rollback restores a prior SHA after apply (watchdog auto-rollback is follow-up work). - Settings > Meshpoint (formerly "Dangerous"). Confirm modal before restart service, clear local database, wipe phantom nodes, force NodeInfo broadcast, or in-process concentrator restart. Service restart uses a detached
systemctlhandoff so the API no longer reports failure while the process is exiting.GET/PUTtransmit config correctly round-trips nested relay settings.
- MeshCore channel configuration (#53): dashboard editors for companion channel keys, synced from the USB path.
- Faster peer discovery via
NEW_CONTACTevents (#55). - Friendly repeater names instead of pubkey placeholders (#54): wider advert name aliases, placeholder cleanup migration, and throttled contact-list enrichment from the USB companion.
- MeshCore nodes on the local map (#51): advertisement and position packets write lat/lon into the node table so MeshCore contacts appear on the dashboard map with RSSI/SNR on adverts, not only DMs.
- Contact roster at startup. Deferred ~20s retry after USB connect logs the full peer list and syncs friendly names into SQLite when the first fetch returns zero rows.
get_contacts()robustness. Tolerates mixed-type companion payloads without crashing the sync path.
- Native onboard relay (experimental). Meshtastic packets can be re-broadcast through the onboard SX1302 with identity preserved (
hop_limitdecrements, sender and ciphertext unchanged). Decoder now retainsraw_app_payloadso the relay path is not silently empty. See docs/CONFIGURATION.md#smart-relay. - Noise floor. Sidebar telemetry uses a rolling minimum of
rssi - snr(fixes endless "calibrating" on rural single-neighbour links). Optional SX1302 spectral scan whenradio.sx1261_spi_pathis set (off by default on RAK/SenseCap: SX1261 is not on a Pi-visible SPI bus). UI tooltips describe whether the readout is packet-derived or spectral-scan sourced.
- Top bar protocol chips. Meshtastic and MeshCore grouped chips with connection dots (no separate ONLINE/OFFLINE text). MeshCore shows companion name, frequency, and primary channel when configured.
- Node drawer metrics charts.
GET /api/nodes/{id}/metrics_historyfor battery, voltage, channel/air util, temperature, and RSSI over 1H / 6H / 24H / All. - Display unit preferences. Settings > Meshpoint: browser-local °F/°C and miles-feet vs km-m for node cards, drawer, and packet feed.
- Node card temperature. Telemetry stored in Celsius from Meshtastic; dashboard converts for display instead of mislabeling Celsius values as °F.
- Messages empty-state copy. Plain instructions (pick a conversation, use All/MT/MC filters) instead of internal jargon.
- Sidebar scroll and accent bar. Nav column scrolls when Configuration submenus expand; green route indicator tracks the active nav item, not the bottom of the sidebar column.
- GPS configuration page crash. Fixed template-literal typo in the GPS card that broke the page on load.
- Top bar MeshCore offline state. Companion chip shows amber when the API or WebSocket path is down, not only when USB is unplugged.
- Terminal copy shortcut. Ctrl+Shift+C uses
preventDefaultin the terminal pane so browser copy works reliably. - Updates rollback persistence. Pre-update SHA captured with
sudo giton the Pi and stored underdata/update_rollback.json; rollback button stays usable after Apply + reload. - Check for updates commit counts.
git rev-listallowed inconfig/sudoers-meshpointwithgit log --onelinefallback whenrev-listis denied. data/ownership on service start. systemdExecStartPrechownsdata/for themeshpointservice user so rollback and audit files remain writable after upgrade.
- MQTT broker TLS (
mqtts), gpsd save API, watchdog auto-rollback on failed Apply, MeshCore companion rename from the dashboard (planned for v0.7.5).
- New routes for auth config, terminal PTY, update apply, MQTT/upstream/device config, meshcore contact enrichment, spectral scan, and admin audit. Release channel registry advances RC to
feat/v0.7.5onmainat v0.7.4+. Test suite 700+ passing. Optional LAN smoke:scripts/smoke_v074_api.pywhenMESHPOINT_PASSWORDis set.
Hotfix on top of v0.7.3 the same day. Reported by Willard on Discord ~3h after release: dashboard stuck on "Reconnecting..." with no data after upgrading. Two compounding bugs in the new auth path; a stale browser tab against an auth-required server is enough to trigger both.
- WS auth close frame now actually reaches the browser.
src/api/server.pywas callingawait websocket.close(code=4401)beforeawait websocket.accept(), which causes Starlette to fail the WebSocket handshake with HTTP 403 instead of completing the handshake and sending a close frame. Browsers translate that to close code1006(Abnormal Closure) on the JS side, and ourfrontend/js/websocket_client.jsonly special-cases4401for the redirect-to-/login path -- so unauthenticated WS connections fell through to the generic reconnect loop and stuck forever. Fix:accept()first, thenclose(code=WS_AUTH_CLOSE_CODE). Validated end-to-end on .141 (cookie cleared, dashboard refreshed → bounces to /login as intended). - Dashboard root (
/) now redirects unauthenticated requests to/login. TheStaticFiles(directory=..., html=True)mount on/was servingindex.htmlto everyone with no auth check, so a stale browser tab could load the new SPA JS and immediately fight the now-auth-required/ws. New explicit@app.get("/")route registered ahead of the static mount: 302s unauthenticated requests to/login(or/setupif no admin password is set yet), servesindex.htmlfor valid sessions. Static asset paths (/css,/js,/assets, etc.) still fall through to the existing mount. - Client defense in depth.
frontend/js/websocket_client.jsnow tracks whether the socket reached theopenstate. Ifonclosefires without a prioronopen, the handshake failed before the close frame could be delivered. The client probes an auth-gated endpoint (/api/device/status); the global 401 interceptor inapp.jsthen handles the redirect if it's an auth-shaped failure, while real network blips fall through to the existing reconnect schedule. Belt-and-suspenders coverage so a future server-side regression in the close-code path can't strand users again. - Internal: new
tests/test_dashboard_root_route.py(unauthenticated GET/→ 302 to/login; GET/with no admin password yet → 302 to/setup; GET/with valid cookie → 200 + index.html). Newtests/test_websocket_auth_close_code.pyasserts the WS handshake completes and the close frame's code is exactly4401for both no-cookie and bad-cookie cases (regression for theaccept()-before-close()requirement). 403 tests passing, ruff clean.
Local-dashboard authentication, dashboard branding polish, and the second-leg phantom-node leak fix. Auth lands as a hard requirement on every Meshpoint: existing devices upgrading from v0.7.2 will be prompted to set an admin password the first time the dashboard is opened after the upgrade. Pure-Python where it counts; install.sh re-run picks up two new dependencies (bcrypt, PyJWT).
- Local dashboard authentication. First-visit redirects to
/setup, where you set an admin password (bcrypt-hashed, never stored in plaintext, never logged). Subsequent visits land on/login. Sessions are stateless JWTs in an HttpOnly + SameSite=Lax cookie; the JWT secret is auto-generated on the device and persisted tolocal.yamlonly when/setupcompletes (a fresh-SD install with no admin password yet leaveslocal.yamluntouched, so the existing setup wizard's "Existing config found" detection still works). All/api/*routes, the dashboard pages, and the/wsWebSocket are now behindDepends(require_auth); unauthenticated calls return 401 (HTTP) or close code 4401 (WebSocket) and the dashboard JS auto-redirects to/login?next=.... Failed-login lockout (web_auth.lockout_attempts, default 5;web_auth.lockout_cooldown_minutes, default 5) is per-username, in-memory, and surfaces a live countdown on the login page via theRetry-Afterheader. Optional viewer role for read-only access viaweb_auth.viewer_password_hash+web_auth.allow_read_only: true. meshpoint reset-passwordrecovery. New CLI command for the "I forgot the dashboard password" path. Hashes the new password, rotatesweb_auth.jwt_secret, bumpsweb_auth.session_version, and writes everything tolocal.yamlin one operation: every existing browser session is invalidated and the new credentials work immediately. Run via SSH (sudo /opt/meshpoint/venv/bin/python -m src.cli.main reset-password <new-password>); requires no service restart.- Sign-out in the topbar. Door-out icon at the far right of the dashboard header. Clicking it POSTs
/api/auth/logout, the cookie is cleared, and the browser redirects to/login. Hover tints accent-cyan; safe under network failure (still redirects, the global 401 interceptor catches any lingering authenticated call). - Auth pages get the radar treatment.
/setupand/loginship a slowly rotating cyan sweep over a deep-navy radar disc with a live identity strip (device name, firmware version, online dot) so you can confirm you're talking to the right Meshpoint before entering credentials. Same--bg-primary/ Inter / JetBrains Mono palette as the dashboard, singleauth-BEM prefix, full reduced-motion support. The radar's blip layer is intentionally unwired in v0.7.3: blips are reserved for real concentrator RX events once a deliberately-public scrubbed feed lands in v0.7.4, rather than ship cosmetic randomness today. - Dashboard branding. Topbar now carries the actual Meshpoint logo (40px, rounded-tile gradient mark) where the placeholder trigram glyph was, plus a 256x256 favicon used on
/,/setup, and/login. iOS home-screen icon (apple-touch-icon) keeps the rounded-tile mark for nicer bookmark rendering. Establishesfrontend/assets/as the canonical asset folder. - Phantom-node leak: drop
STAT_NO_CRCand unknown-status packets at the HAL boundary. v0.7.2 closed theSTAT_CRC_BADleg of the leak butSTAT_NO_CRCpackets still flowed into the decoder, where the random bytes after the LoRa header parsed as a "valid" Meshtastic packet and produced a phantom node row in the local SQLite (one packet, no name, no role, never heard again). Fleet diagnostics on a high-traffic Meshpoint (nopemesh, v0.7.2 baseline) measured 4 NO_CRC packets per 30 minutes alongside 108 CRC_BAD and 72 CRC_OK, with the resulting rows accumulating into a 92%-phantom local node table (~72k of ~78k total nodes).SX1302Wrapper.receive()now dropsSTAT_NO_CRCwith a counted WARNING (RX NO_CRC if=N sf? bw=? rssi=? snr=? size=? (total NO_CRC: N)) and additionally drops any packet whose status is neitherCRC_OK,CRC_BAD, norNO_CRCso that future HAL revisions introducing a new status code cannot silently re-open the leak. Newno_crc_countandunknown_status_countproperties on the wrapper for observability. - Defense in depth: drop Meshtastic headers with
hop_limit > hop_start. A Meshtastic packet originates withhop_limit == hop_startand decrementshop_limitat each relay whilehop_startstays fixed, sohop_limit > hop_startis mathematically impossible for an honestly-originated packet.MeshtasticDecoder._parse_header()now returnsNonefor that combination so the corrupted bytes never reach the storage layer. Caught two of the five fresh phantoms on the kmax test Pi and four of four on nopemesh in fleet diagnostics, independent of the wrapper-level status filter. Zero false-positive risk by construction. - Hardware-validated three ways. Fresh-SD install (Meshpoint-MNTD-RAKV2 .49) exercises the full
install.shpath withbcrypt+PyJWTdeps and the bootstrap →/setup→local.yaml-creation flow end to end. Upgrade from v0.7.2 (Sensecap M1) confirms the existinglocal.yamlis preserved untouched on service start, and theweb_authblock is appended atomically when the user completes/setup. RAK V2 .141 confirms the upgrade-on-top-of-RC path. All three flows green; no phantom rows observed on the upgraded high-traffic device since the no-crc fix landed. - Internal: new
requirements.txtdepsbcrypt>=4.2.0andPyJWT>=2.10.0. Newsrc/api/auth/package:password_hasher,jwt_session,lockout_tracker,auth_service,auth_bootstrap,dependencies,ws_guard. New routessrc/api/routes/auth_routes.pyandsrc/api/routes/identity_routes.py. New CLIsrc/cli/reset_password_command.py. New frontendfrontend/auth/{setup,login}.html,frontend/css/auth.css,frontend/js/auth.js,frontend/js/signout_controller.js. New tests:test_password_hasher,test_jwt_session,test_lockout_tracker,test_auth_service,test_auth_dependencies,test_auth_routes,test_auth_page_serving,test_auth_bootstrap,test_identity_route,test_protected_router_wiring,test_reset_password_command, plus the no-crc test additions intest_sx1302_wrapper.pyand the newtest_meshtastic_decoder_header_validity.py.tests/test_relay_node_header.pydefaultflagsbyte moved from0x03(hop_limit=3, hop_start=0, structurally impossible) to0x63(hop_limit=3, hop_start=3, valid direct packet) so existing relay-node tests pass under the new validity check. 401 tests passing, ruff clean, bandit clean.
Two-fix bundle on top of v0.7.1. One small UX feature for hop-chain debugging, one quiet-but-important correctness fix that was inflating node counts on the cloud catalog and producing intermittent garbled-but-readable text on the local mesh. Both ride together because they touch the same RX path. Pure-Python, no recompile needed.
- Drop
STAT_CRC_BADpackets at the HAL boundary instead of forwarding them to the decoder.SX1302Wrapper.receive()was logging the diagnostic WARNING for CRC-failed packets but still appending them to the returned packet list, withcrc_ok=Falseset on theConcentratorPacket. No downstream code (concentrator_source,packet_router, decoders) ever read thecrc_okfield, so RF-corrupted bytes flowed intoMeshtasticDecoder.decode()where the source-ID was extracted directly from the corrupted header. Three observable downstream symptoms produced by this: (1) phantom node IDs registered in the local SQLite node table and propagated up to the cloud DynamoDB node catalog, where a single bit-flip in the source-ID field creates a new "node" sharing all-but-one bit with a real source ID; (2) false ENCRYPTED packet attribution when the channel-hash byte was corrupted and stopped matching the LongFast hash; (3) garbled-but-readable text when AES-CTR XORed corrupted ciphertext with the keystream, producing mostly-correct plaintext with a few mangled characters. Hardware-validated on RAK V2: 14 historical phantoms in the local DB matched the bit-flip fingerprint of legitimate neighbors (7d8b98a9,a0dd8936, etc.), and zero new phantoms have entered the database since the fix. Cloud-sideactive_nodes_24hcount is expected to drop sharply over the 24-48h after fleet rollout. Closes #34. - Relay-node visibility on the dashboard. Surfaces the Meshtastic header
relay_nodebyte (the lowest byte of the last relay node's ID) through the decoder →Packetmodel → SQLite schema (with idempotentALTER TABLEmigration for existing installs) → WebSocket payload → dashboard packet feed. Source cells in the packet feed now read!source ↝ !relaywhenever the packet was relayed, with full short-ID resolution when the relay byte matches a known node in the local registry. Clicking a relayed packet draws a line on the map between the source marker and the relay marker so you can trace the hop chain visually. Direct (non-relayed) packets render as before. Real-world utility: tracing a hop chain back to a rooftop node and confirming its ERP from the RSSI/SNR pattern. hop_limiton outbound TX now honorsTransmitConfiginstead of being hardcoded to 3. Two paths (send_text_messageand the NodeInfo broadcaster) were ignoring the configured hop limit and using a hardcoded3regardless of what was set inlocal.yaml. Behavior is unchanged for installs running the default (still 3); fixes the silent "I sethop_limitin my yaml and it didn't take" gap. The dashboard's per-packet HOPS column (hop_used / hop_limit) now reflects the actual configured ceiling.- Internal: new
tests/test_sx1302_wrapper.pycovers the CRC_BAD drop contract (syntheticSTAT_CRC_BADinput is dropped,crc_bad_countincrements, the WARNING fires, the decoder is not reached). Newtests/test_relay_node_header.pycoversrelay_nodeheader byte parsing andPacket.relay_nodepopulation.tests/test_database_migration.pyextended for thepackets.relay_nodeALTER TABLE path. 297 tests passing, ruff clean.
Polish bundle on top of the v0.7.0 source-publication release. Edge-only, no cloud changes. Touches radio tab UX, branding, and a handful of small upgrade-path papercuts. Pure-Python, no recompile needed.
- Radio tab redesign. Reworked the Radio tab with an SDR-console aesthetic: status lamps, readout cards, an analog-style duty-cycle gauge, a new NodeInfo Broadcast card, and a sticky restart banner that floats at the top while you scroll instead of getting buried at the bottom of the page. Channels table behavior is unchanged.
- NodeInfo broadcast is now configurable from the dashboard. New card on the Radio tab shows live telemetry (next broadcast countdown, last-sent timestamp, current interval, status lamp), exposes preset chips (
Off / 5m / 30m / 1h / 3h / 6h / 12h / 24h) plus a free-form 5-1440 minute input, and aSend Nowbutton that fires an immediate NodeInfo packet without waiting for the next scheduled tick.interval_minutes: 0pauses periodic broadcasts; non-broadcast TX (DMs, replies) is unaffected. New telemetry keys (last_sent_at,next_due_at,running) onGET /api/config/nodeinfo. NewPOST /api/config/nodeinfo/sendendpoint. - Interval changes hot-reload without a service restart. Saving a new NodeInfo interval immediately wakes the broadcast loop and re-anchors the next-due time, including during the initial 60-second startup delay window. Pausing (
interval=0) cleanly idles the loop; resuming fires the next broadcast right away if one was already overdue. Onlystartup_delay_secondschanges still require a restart, and the UI says so. - Pending-changes cue on Save buttons. When the displayed NodeInfo interval differs from the saved value, an amber notification dot pulses at the top-right of the Save button so unsaved work is hard to miss. Clears automatically on save or page refresh.
- Save NodeInfo card auto-refreshes after a broadcast fires. Previously the countdown got stuck on "broadcasting..." until you reloaded the page.
- Send Advert button on the MeshCore Companion card now actually works. Previously it POSTed to the text-message endpoint with empty body, got rejected by the empty-text validation, and surfaced "Advert failed" with nothing in the logs. Added a dedicated
POST /api/messages/advertendpoint that callsMeshCoreTxClient.send_advert()directly. Reported by iceice400. - Branding consistency pass. All user-facing prose and log lines now read "Meshpoint" and "Meshradar" (one word, capital M) instead of "Mesh Point" and "Mesh Radar". Most importantly, the default Meshtastic NodeInfo
long_namebroadcast over RF now readsMeshpoint, so the device shows up correctly onmeshmap.net, the Meshtastic phone app, and neighbor MQTT envelopes. Other surfaces touched: dashboard header, browser tab title, FastAPI auto-docs, CLI prose (meshpoint setup,meshpoint status,wizard_meshcore), installer prose, systemd unit descriptions, and module docstrings. Code identifiers (CLI command name, Python module names, YAML keys) are unchanged: branding rule applies to prose only. - Duty cycle default now auto-derives from your region. Previously hardcoded to
1.0(the EU 1% etiquette ceiling) regardless of where you were. Newresolve_max_duty_percent()readsradio.regionand applies a conservative regional default (US: 10%, EU 868: 1%, ANZ: 10%, IN: 1%, KR: 1%, SG 923: 10%) unlessrelay.max_duty_percentis explicitly set inlocal.yaml. Source surfaced in the Radio tab duty gauge asregion_defaultvsuser_override. Seedocs/RADIO-CONFIG-EXPLAINED.mdfor how to override. - Mobile responsive polish. All four dashboard tabs (Dashboard, Stats, Messages, Radio) render cleanly on phone-width viewports. Validated with the official Playwright MCP at iPhone 14 Pro and Galaxy S24 viewports.
- Header
Meshradarbrand link. The "Meshradar" portion of the dashboard header is now a link tomeshradar.io(opens in new tab). The "Meshpoint" portion stays plain text. Requested by Parker WEST. - Setup wizard preserves your existing coordinates. Example coordinates in the location prompt now show neutral NYC values (
40.7128, -74.0060) instead of a developer-specific location. Existingdevice.latitude/device.longitudeinlocal.yamlare still preserved as the prompt default, so re-runningmeshpoint setupdoes not overwrite them. install.shupgrade-aware banner. When run on a Meshpoint that already has an existing install (detected viaconfig/local.yamlpresence ormeshpoint.serviceenabled), the closing banner now reads "Meshpoint upgrade to vX.Y.Z complete: restart the service" instead of the spurious "Reboot to apply SPI/UART changes" message that was misleading users on every v0.7.0+ upgrade. Fresh installs still see the full first-run flow.- MQTT topic clarification in
default.yaml. Added inline comments explaining thatmqtt.topic_rootandmqtt.regionare concatenated to form the full Meshtastic spec topic (<topic_root>/<region>/2/e/<channel>/<gateway>), and thatmqtt.regionis independent ofradio.region. Avoids the double-region footgun (msh/US/FL/US/...) where users assumetopic_rootis the complete prefix. - FastAPI app version follows
__version__. The auto-generated/docsSwagger header was hardcoded to0.1.0since v0.1.x. Now reads fromsrc.version.__version__so it matches the running release. - Internal: new
tests/test_messages_advert_route.py(5 tests, FastAPI TestClient pattern),tests/test_nodeinfo_broadcaster.py::TestNodeInfoBroadcasterHotReload(10 tests covering hot-reload, pause, resume, startup-delay interruption),tests/test_duty_cycle_resolver.py(region resolution + override semantics). 254 tests passing, ruff clean.
Distribution architecture change: the eleven core SX1302/MeshCore modules are now shipped as Python source files in src/{hal,capture,decode,transmit}/ instead of pre-compiled .cpython-*.so binaries. Behavior is identical to v0.6.8; the change is purely about distribution format. Closes issue #32.
- Source published. All eleven modules (HAL wrapper, channel-plan builder, GPS reader, concentrator capture source, SX1262 SPI source, AES-CTR crypto service, Meshtastic and Meshcore decoders, portnum handlers, packet router, Meshtastic packet builder) ship as plain
.pyfiles under the existing AGPL-3.0 license. Auditability and portability to non-aarch64 hardware become trivial. - Upgrade path uses
install.sh.scripts/install.shnow removes any.cpython-*.soleft behind by previous installs before the venv is set up. Aftergit pull, runsudo /opt/meshpoint/scripts/install.shfollowed bysudo systemctl restart meshpoint. Agit pullalone is not sufficient on existing v0.6.x devices: Python's import machinery would prefer the stale binary over the new source. - Boot-time stale-
.sodetection. A startup WARN fires (and lists the offending files) if compiled binaries somehow re-appear insrc/. Surfaces inmeshpoint logsso you can fix the install before behavior freezes at v0.6.x. - RX diagnostic logging. Every CRC_BAD packet on the SX1302 concentrator now logs a WARNING with the IF chain, SF, BW, RSSI, SNR, size, and a running CRC_BAD counter. Useful for diagnosing rapid-fire packet loss caused by overlapping LoRa transmissions on the same demodulator. Per-packet RX traces are also available via the new
MESHPOINT_DEBUG_RX=1environment variable (off by default). - Internal: retired the Cython build pipeline that produced the per-release
.cpython-*.soartifacts since it's no longer needed.
Pure-Python follow-up to v0.6.7. No core module recompile required: just git pull + systemctl restart. Fixes two user-visible regressions surfaced after v0.6.7 shipped, plus the long-standing PRIVATE_HW labeling on community maps.
- Auto-derived Node ID is now persisted to
local.yamlon first boot. v0.6.7 added stable Meshtastic identity but only displayed the derived value on the dashboard if you happened to also save the Radio settings page; until then the API kept returningnode_id_hex: ""and the field rendered blank. Reported by Parker WEST. The Meshpoint now writes the derived value totransmit.node_idautomatically the first time it falls back to thedevice_idderivation, then treats it as a normal pinned config value on every subsequent restart. Hint text on the Radio tab tracks the source ("Pinned in local.yaml. Edit to override." vs "Random fallback (no device ID configured).") so you can tell at a glance where the value came from. End-to-end validated on RAK V2 with a fresh derive → persist → reload cycle. - Hardware model now reports as
PORTDUINO(37) instead ofPRIVATE_HW(255). Reported by holmebrian. Other Meshpoints, MQTT gateways, andmeshmap.netwere displaying every Meshpoint as the generic "private hardware" label even though Meshtastic has had aPORTDUINOenum value for Linux-based nodes since 2.4. NewHW_MODEL_PORTDUINOconstant alongside the existingHW_MODEL_PRIVATE_HW, threaded throughNodeInfoBroadcasteras the default. Verified on a witness Meshtastic phone after the broadcast cycle (60 s after restart, then every 30 min). Existing nodes will pick this up automatically on their next NodeInfo decode. - Local Stats tab "Network" section now actually renders. The Hardware Models donut on the local dashboard was hidden for everyone, even though the underlying SQLite query was returning data (143 of 458 nodes had a populated
hardware_modelcolumn on the test RAK). Two bugs: (1) the section was section-level hidden until roles had data, but the deferred edge decoder bug filters role 0 (=CLIENT, the most common role) out at decode time, so roles is effectively always empty on v0.6.x; (2) theHW_NAMESlookup table on the frontend had drifted from the upstream MeshtasticHardwareModelenum, so any model that DID render was getting the wrong label. Fixed both: each chart now hides itself independently, the section appears as long as either has data, andHW_NAMESis regenerated frommesh.proto(covers 0..129 plus 255). The Device Roles chart will start populating once the v0.7.0 core module bundle ships the deferred decoder fix. - Internal: new
node_id_sourceproperty onTxService("config" / "derived" / "random") for API + dashboard introspection. Newpersist_derived_node_idconstructor flag for test isolation. Eight new tests covering source-tracking and the auto-persist path (success, no-op when pinned, no-op when random, swallowed PermissionError). Two new tests onNodeInfoBroadcasterfor the PORTDUINO default + override.
Stable Meshtastic identity, NodeInfo broadcasts, and a clutch of small reliability fixes. Core module recompile required. Fixes Meshtastic DMs sent from a Meshpoint never arriving at recipients, even though the dashboard showed "Sent". Reported by Max_Plastix.
- Stable
source_node_idper Meshpoint. Previously the Meshtastic node ID was randomly chosen on every service restart unless the user manually settransmit.node_idinlocal.yamlor via the dashboard radio tab. Recipients ended up seeing a brand new "ghost" Meshpoint each restart and never built a stable contact, so direct messages had nowhere to thread to. Resolution priority is now (1)transmit.node_idin config, (2) deterministic SHA-256 derivation from the provisioneddevice.device_idUUID (stable across reboots), (3) cryptographically random fallback with a startup WARN if neither is set. Reserved IDs (0x00000000,0xFFFFFFFF) are explicitly skipped. Existing manually-set node IDs are preserved. - Periodic NodeInfo broadcasts. New
NodeInfoBroadcasteradvertises the Meshpoint's identity (long name, short name, node ID, hardware modelPRIVATE_HW) on the mesh 60 seconds after startup and every 30 minutes after that. This is what makes recipient nodes (T-Beam, Heltec, etc.) form a contact for your Meshpoint so they can route DMs back to it. Samesource_node_idis used for both NodeInfo and outbound DMs/text so recipients see one consistent identity. - Setup wizard surfaces the resolved identity. The
meshpoint setupDevice step now prints the device ID, derived node ID, long name, and short name with their origin (existing configvsauto-generated) so you can see exactly what will be advertised on the mesh before saving. - Setup wizard preflight check.
meshpoint setupnow verifies write permission toconfig/local.yamland the existence of theconfig/directory before asking any of the eight questions, so it bails immediately with an actionable message instead of failing 60 seconds in after you've filled in the whole form. Hit by holmebrian during initial setup. - Wizard config preservation (carried over from disk). Untouched sections of
local.yaml(e.g.meshcore_usb,mqtt) are now preserved when re-runningmeshpoint setup, instead of getting wiped out by the wizard overlay. New_deep_mergehelper handles nested merges. 13 unit tests cover the merge semantics. - Relay marked experimental, log noise tamed. Relay TX has never worked end-to-end (see ROADMAP.md). When
relay.enabled: trueyou now get a one-line WARN banner at startup making this explicit. The per-packetRelay TX: no payload availablewarning now fires only once per process and drops to DEBUG for every subsequent skip, so logs stay readable while the v0.7.0 relay completion is in flight. - Cross-protocol sender-name leak in DMs fixed. Meshtastic inbound DMs were showing arbitrary MeshCore contact names ("Guzii_RedV4" leaking into a Meshtastic conversation, etc.) because the unscoped fallback in
_save_and_notifygrabbed the first availablemc:%node row regardless of the inbound packet's protocol, then persisted that wrong name back to the Meshtastic node row so it stuck across reconnects. Each fallback is now scoped to its own protocol, and a parallel Meshtastic source-id lookup now runs for inbound MT direct messages (mirroring the existing broadcast path). Found mid-validation while testing the v0.6.7 NodeInfo fix. - Auto-cleanup of pre-v0.6.7 contamination. New idempotent startup migration in
DatabaseManagerrepairs Meshtastic node rows whoselong_namewas overwritten by a MeshCore contact name in earlier versions. Affected rows have theirlong_namereset to NULL on first restart of v0.6.7; the next NodeInfo broadcast from the real node repopulates the correct name automatically. Migration is a no-op on clean databases. (Previously-stored corrupted message rows in themessagestable are not auto-repaired since they're an immutable per-message snapshot; delete the affected conversation if the historical naming bothers you.) docs/COMMON-ERRORS.mdgains entries for "Meshtastic DM shows Sent but recipient never gets it" (now fixed in v0.6.7) and "Two Meshpoints with the same node ID breaking the mesh" (only happens if youddclone an SD card without re-runningscripts/provision.py).docs/RADIO-CONFIG-EXPLAINED.mddocuments the three identity sources (dashboard / wizard / yaml), their resolution priority, and the fact that identity changes require a service restart.- Internal: new tests for
_resolve_node_id(8 cases),NodeInfoBroadcaster(8 cases),build_nodeinforound-trip through the decoder (8 cases, private repo), wizard preflight (4 cases), and the relay no-payload dedup (4 cases). 32 new tests total, all green.
MeshCore reliability patch. Small follow-up to v0.6.5 cleaning up rough edges around the MeshCore USB companion. No edge concentrator changes, no cloud changes.
- Companion connects cleanly on
systemctl restart meshpoint. ESP32-S3 boards (Heltec V3/V4 etc.) need 6-10 seconds to be USB-ready after a reboot, but the underlying meshcore library was giving up after 5. Bumped the handshake window so cold connects work the first time instead of needing a manual USB unplug. - Background reconnect with DTR soft-reset. When the initial handshake does miss anyway, the source now schedules a background reconnect with exponential backoff and pulses DTR low to soft-reset the chip on the second attempt onwards. Recovers in 30-50 seconds without user intervention. On boards where DTR is wired to RESET (the common case for ESP32 dev boards) this is a real hardware reset; on others it's a harmless no-op.
- Health check tuning. The MeshCore health check (in place since March) was sometimes treating slow but healthy responses as a dead connection and triggering a full reconnect cycle. We caught it on the RAK during this round of testing: every 2-3 minutes the source would tear down and rebuild, costing 15-20 seconds of MeshCore RX downtime each time. Whether this was happening on other Meshpoints in production is unknown; it was never surfaced as a user-visible symptom. The health check now passes a proper command timeout, skips the active probe when inbound events have arrived recently (proof of life), and tolerates a single transient miss before declaring the connection dead.
- Dashboard radio tab now shows real values. The MeshCore Companion card was stuck on
Name: Unknown / Frequency: ? MHz / SF: SF? / TX Power: ? dBmfor everyone. Dashboard was reading from the wrong source. It now reads from the same place themeshpoint meshcore-radioCLI does, which has always shown correct values. - Smarter
meshpoint meshcore-radioCLI. Now prompts for a full Pi reboot after applying new radio settings instead of doing a service restart that races the still-recovering USB CDC stack. Reboot is the reliable path; restarting the service mid-USB-enumeration leaves MeshCore in a half-connected state where messages don't flow. - Heltec V4 ACM-shift fix. The companion would temporarily move from
/dev/ttyACM0to/dev/ttyACM1during the post-config reboot, get pinned into yourlocal.yaml, then become unreachable after the next Pi reboot when the kernel re-assigned it back to/dev/ttyACM0. The CLI now switches your config toauto_detect: truewhenever it sees the port shift, so the companion is found wherever it lands across reboots. docs/COMMON-ERRORS.mdgains entries for the MeshCore handshake-failed log message and spurious health-check reconnects.- Demoted
No MeshCore USB device foundfrom WARNING to INFO with friendlier wording (it's an expected state if the source is enabled but no companion is currently plugged in, not an error). - Internal: fixed deprecated
asyncio.get_event_loop()pattern intests/test_message_repository.pyso the suite remains compatible with newer test files usingIsolatedAsyncioTestCase.
- Network watchdog reliability fix: the watchdog no longer triggers an infinite reboot loop on networks where the gateway blocks ICMP. Gateway pings now fall back to
8.8.8.8before a check is counted as a failure, and auto-reboot is disabled by default (REBOOT_THRESHOLD = 0). Stage 1 recovery (interface restart at 3 consecutive failures) is unchanged. To re-enable automatic reboots, editscripts/network_watchdog.pyand setREBOOT_THRESHOLDback to6. Startup banner now logs the active thresholds so you can confirm the policy at a glance. Thanks to first-time contributor @dotchance for catching this and shipping the fix. (#27) - Support documentation expansion: new
docs/FAQ.md,docs/HARDWARE-MATRIX.md,docs/COMMON-ERRORS.md,docs/RADIO-CONFIG-EXPLAINED.md, anddocs/MQTT-AND-MESHRADAR.md. README "Support and documentation" section reorganized into Setup / When-something-goes-wrong / Project groups. - SX1302 minimum bandwidth documented:
docs/HARDWARE-MATRIX.mdanddocs/RADIO-CONFIG-EXPLAINED.mdnow explain that the SX1302 concentrator cannot tune below 125 kHz, which is why MeshCore (62.5 kHz) requires a USB companion radio for RX.
- Meshtastic broadcast sender names: received messages on public channels (LongFast, etc.) now show the sending node's long name, short name, or hex ID. Previously the UI showed the conversation key (
broadcast:meshtastic:0) in place of the sender because the backend never resolved the source node for Meshtastic broadcast text packets. The v0.6.2 sender-name fix only covered MeshCore; this finishes the job for Meshtastic. (#19) - Defensive frontend filter: chat UI no longer renders strings starting with
broadcast:as a sender label if they ever slip through.
- TX channel hash fix: messages sent from the dashboard were going out with hash 0x02 (invisible to the mesh) instead of the correct 0x08. The primary channel name defaulted to blank, producing the wrong hash. Now defaults to "LongFast" matching Meshtastic firmware. (#21)
- Primary channel editable: channel 0 can now be renamed and saved from the Radio settings page. Previously edits reverted on refresh. (#13)
- Channel display cleanup: Radio settings shows the actual channel name (e.g. "LongFast") instead of "Primary (LongFast)".
- MQTT channel name fix: MQTT topics now use the actual channel name (LongFast, MediumFast, ShortFast, etc.) instead of
chXXhashes. NewChannelResolvermaps all 8 standard Meshtastic presets and supports user-configured channel keys. (#20) - Chat sender names: received messages now show the sender's node name or hex ID. Previously there was no way to tell who sent what. (#19)
- Chat day dividers: messages from different days are separated by date labels (Today, Yesterday, or the date) in the chat window.
- Espressif USB udev rule: installer adds a udev rule so Heltec V3/V4 and T-Beam ESP32-S3 USB serial devices are accessible to the meshpoint service user without manual group changes. (#12)
- Local stats dashboard: new Stats tab on the local dashboard with 12 live Chart.js charts: protocol split, packet types, RSSI distribution, signal quality, direct vs relayed, active nodes, device roles, hardware models, relay decisions, rejection reasons, and traffic timeline. All generated locally, no cloud needed.
- Enriched heartbeat: edge accumulates per-packet stats in memory and sends a batched summary to Meshradar in each heartbeat instead of the cloud processing every individual packet. Same data, significantly fewer backend operations. Savings scale with fleet size.
- Local topology layer: map tab gains a "Topology Links" toggle showing lines between nodes with RSSI/SNR tooltips.
- Farthest direct tracking: tracks the farthest direct (0-hop) node heard, with distance and signal strength, visible on the stats page.
- Relay rejection tracking: relay engine now records why packets are rejected (duplicate, rate limited, type filtered, signal bounds), visible in local stats.
- Native mesh messaging: send and receive Meshtastic messages from the browser. Broadcast to LongFast, talk on custom channels, DM individual nodes. MeshCore messaging via USB companion. SX1302 transmits with correct sync word and encryption.
- Chat UI: conversations organized by channel and contact. Signal info on every received bubble. Duplicate badge for relayed messages. History persisted locally.
- Radio config from dashboard: region, modem preset, frequency override, TX power, duty cycle, custom channels with PSKs, and TX toggle. All configurable from the Radio tab without SSH.
- Node discovery: live node cards with name, ID, protocol, hardware model, signal, battery, last seen. Detail drawer with signal history. DM from node card.
- Dashboard overhaul: messaging tab, node cards grid, radio settings page, frequency and SF columns in packet feed.
- CLI operational report:
meshpoint reportcommand with full-screen terminal dashboard: RX stats, traffic breakdown, signal averages, system metrics, health status. - Setup wizard improvements: unique random Meshtastic node ID per device (no collisions), MeshCore companion as its own wizard step.
- MQTT hotfix: shipped missing MQTT runtime files (publisher, formatter, pipeline wiring) that were absent from v0.5.4. MQTT config and docs were present but the code was not, so
mqtt.enabled: truehad no effect. Update and restart to activate MQTT publishing.
- MQTT gateway: dual-protocol MQTT publishing for Meshtastic (protobuf ServiceEnvelope) and MeshCore (JSON). Publishes to community maps (meshmap.net, NHmesh.live) and Home Assistant. Two-gate privacy model: MQTT is off by default, and only public channel traffic is published unless you explicitly allowlist a private channel. Each Meshpoint gets a unique node-format gateway ID that integrates natively with the Meshtastic ecosystem, appearing on meshmap.net, Liam Cottle's map, and other community tools. Optional JSON mirror for HA/Node-RED, auto-discovery sensor configs, and configurable location precision.
- Packet type filter (cloud): filter the Meshradar cloud packet feed by type (traceroute, position, text, etc.) and protocol (Meshtastic/MeshCore). Dropdown filters in the packets tab header.
- Setup wizard MQTT step:
meshpoint setupnow includes an MQTT opt-in prompt with broker selection and HA integration toggle.
- Multi-key decryption: packets on private Meshtastic channels now decrypt when channel keys are configured in
local.yaml. Previously only the default key was tried. (#5) - Heartbeat optimization: reduced upstream heartbeat interval for lower cloud costs.
- Core module binary fix: v0.5.1 shipped updated source but stale compiled
.sofiles. This release includes the correctly compiled binaries.
- Non-LongFast preset fix:
ConcentratorChannelPlan.from_radio_config()no longer ignores spreading factor and bandwidth when using the region's default frequency. EU_868 MediumFast (SF9/BW250), ShortFast, and other presets now work correctly. Previously, any preset at the default frequency was silently overridden to LongFast (SF11/BW250). (#4)
- Multi-region frequency support: 6 Meshtastic regions (US, EU_868, ANZ, IN, KR, SG_923) with auto-tuning concentrator and setup wizard region selector.
- Preset tuning: service channel SF and BW are configurable via
local.yaml. Supports MediumFast, ShortFast, ShortTurbo: not just LongFast. - Frequency override: set
frequency_mhzinlocal.yamlto tune to a non-default slot within your region. - Full portnum decoding: position speed/heading/altitude, power metrics, routing errors, NEIGHBORINFO, TRACEROUTE payloads.
meshpoint meshcore-radioCLI: switch MeshCore companion frequency without re-running the full wizard. Presets (US/EU/ANZ) or custom entry.- Startup banner accuracy: boot log shows the actual radio config, not just the region default.
- Config stability: empty YAML sections no longer crash the service on startup.
- Real-time packet streaming: cloud dashboard receives packets instantly via WebSocket. Live animated lines trace packets from source nodes to your Meshpoint on the map.
- Cloud map overhaul: marker clustering, signal heatmap layer, topology lines from neighborinfo data, and a live Recent Packets ticker panel.
- SenseCap M1 support: auto-detects SenseCap M1 carrier board via I2C probe during setup. Flash an SD card and go.
- 14 Meshtastic portnums decoded: TEXT, POSITION, NODEINFO, TELEMETRY, ROUTING, ADMIN, WAYPOINT, DETECTION_SENSOR, PAXCOUNTER, STORE_FORWARD, RANGE_TEST, TRACEROUTE, NEIGHBORINFO, MAP_REPORT, plus encrypted packet tracking.
- Device role extraction: node table shows CLIENT, ROUTER, REPEATER, TRACKER, SENSOR, and other roles from NodeInfo packets.
- Smart relay engine: deduplication, token-bucket rate limiting, hop/type/signal filtering, independent SX1262 TX path.
- Live dashboard UX: color-coded packet feed, decoded payload contents, 24h active node counts, version-based update indicator, and enlarged map view.
- Cloud dashboard tabs: tabbed layout with fleet view, interactive map controls, device-scoped filters, unified packet cards with signal strength bars, and public activity stream for visitors.
- MeshCore USB capture: new capture source for USB-connected MeshCore companion nodes. Auto-detects the device, configures radio frequency via the setup wizard (US/EU/ANZ presets or custom), with auto-reconnect and health monitoring. Startup banner shows all active sources.
- Custom frequency tuning: configurable SX1302 channel plan via
local.yaml. Validated on live hardware with LongFast (SF11/BW250). Dual-protocol HAL patch for simultaneous Meshtastic and MeshCore sync words.