Skip to content

v2.9.0: honest traffic stats and remote admin

Choose a tag to compare

@maxg10 maxg10 released this 10 Oct 18:10
· 19 commits to main since this release

Two things in this release. Stats used to answer one question badly: how
much traffic is there?
It counted four kinds of packet, kept three days of them,
and called the result "total packets". On a real mesh (Pimesh1, Luboń) the node's
own radio decoded about 16,000 unique frames a day while Stats showed about 6,000.
2.9.0 makes that number honest and keeps it long enough to see a trend.

And remote admin: read and change another node's settings over the mesh,
straight from its popup on the map.

Traffic History

A new card at the top of Stats, with 24h / 7d / 30d / 1y ranges:

  • Logged vs heard. Bars show what MeshPulse decoded and stored (RF and MQTT
    apart); the line shows what the radio actually received, from the node's own
    firmware counters with duplicates removed. The gap between them is traffic
    MeshPulse cannot decode.
  • Channel utilisation, average and peak, duplicate and bad-CRC rates, and
    the packet type mix, with range totals on top and a CSV export.
  • Hours with no data stay gaps instead of turning into quiet hours, and days with
    fewer than 24 recorded hours are drawn lighter and starred, so a short bar is
    not mistaken for a quiet day.

History that survives

Every complete hour is folded into a small rollup table that is never pruned
(about 9,000 rows a year). The first start after the update backfills it from
whatever raw data is still there. Raw packet history is now kept 30 days
instead of 3, configurable as stats_retention_days in config.json (1 to 365).

Every packet counts

Until now only NODEINFO, POSITION, TELEMETRY and TEXT reached the stats database.
Every other port the radio hands over is now counted too, including packets on
channels this node has no key for (ENCRYPTED) and ports the Meshtastic library
does not know (PORT_<n>). Expect your Stats totals to jump: the mesh did
not get busier, the counter got honest.

Remote admin

A 🛠 Admin button in a Meshtastic node's popup (and 🛠 in the No GPS list) opens
a window that talks to that node over the mesh, through the radio MeshPulse is
already connected to. No port juggling, no CLI.

  • Read 15 sections: owner, firmware, device, LoRa, position, power, network,
    display, Bluetooth, security, MQTT, telemetry, neighbor info, store & forward,
    range test. A console shows every step and its round trip, typically 1 to 2 s
    per section on a direct neighbour.
  • Write: ✏️ Edit turns a section into a form built from the firmware's own
    protobuf definitions. Review shows old → new for every changed field, Apply
    sends it, and 20 s later MeshPulse reads the section back and marks each field
    ✓ or ✗ (so you see it when the firmware quietly clamps a value). Reboot is a
    two-click button.
  • Off by default. Writes are armed from Config > 🛠 Remote Admin for 15,
    30, 60 or 120 minutes and switch off by themselves; remote_admin_write: true
    in config.json enables them permanently. Every write, reboot, arm and disarm
    is recorded with the client address and listed on that tab.
  • Guard rails. Fields that can cut a node off the mesh are locked, with the
    reason shown: LoRa radio parameters, Security, Network, Bluetooth, MQTT, device
    role, licensed mode, power saving.
  • Requirements. The target node must list this node's public key as an
    Admin Key (Security settings), and both must run firmware with PKC (2.5+).
    MeshPulse now keeps every public key it hears (node_keys in stats.db) and
    hands a key back to its own radio when the radio has dropped it from a full
    NodeDB, so admin keeps working on a busy mesh.

Fixes

  • TELEMETRY and TEXT were logged with via_mqtt hardcoded to false and no hops or
    SNR, so MQTT telemetry counted as radio traffic and could raise "too frequent
    telemetry" anomalies against nodes that are not even in range.
  • A packet that arrived with its hop limit used up lost its hop count: protobuf
    leaves zero-valued fields out, so hopLimit was simply missing.
  • install.sh reported "web server NOT FOUND" when run over ssh, because lighttpd
    lives in /usr/sbin.

Performance

Measured on a Raspberry Pi 2 before shipping. With 30 days of traffic, one query
of the Stats summary took 4.95 s, every minute, holding the lock that packet
logging waits on; a new index brings it to 0.47 s. Pruning now deletes in small
chunks, so lowering the retention cannot freeze packet logging. The history card
reads only the rollup: 18 to 40 ms per request on the same Pi.

Updating

cd ~/meshpulse && git pull origin main && ./install.sh && sudo systemctl restart meshpulse

Docker

docker compose pull && docker compose up -d

Fresh Install

git clone https://github.com/maxg10/meshpulse.git
cd meshpulse
./install.sh