Files
meshcore-stats/docs/firmware-responses.md
Jorijn Schrijvershof a3015e2209 feat: add telemetry collection for companion and repeater nodes (#24)
Add environmental telemetry collection (temperature, humidity, barometric
pressure, voltage) from both the repeater node (over LoRa) and companion
node (local serial). Telemetry is stored in the same EAV metrics table
with `telemetry.` prefix.

Key changes:
- Add TELEMETRY_ENABLED feature flag (defaults to OFF)
- Add telemetry-specific timeout/retry settings
- Create shared telemetry.py module with extract_lpp_from_payload()
  and extract_telemetry_metrics() helpers
- Handle MeshCore API dict payload format: {'pubkey_pre': '...', 'lpp': [...]}
- Repeater: store status metrics BEFORE attempting telemetry (LoRa reliability)
- Companion: merge telemetry into single DB write (serial is reliable)
- Telemetry failures do NOT affect circuit breaker state

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 09:53:15 +01:00

5.7 KiB

MeshCore Firmware Response Reference

This document captures the actual response structures from MeshCore firmware commands. Use this as a reference when updating collectors or adding new metrics.

Last verified: 2025-12-29


Companion Node

The companion node is queried via USB serial using multiple commands.

get_stats_core()

Core system statistics.

{
    'battery_mv': 3895,      # Battery voltage in millivolts
    'uptime_secs': 126,      # Uptime in seconds
    'errors': 0,             # Error count
    'queue_len': 0           # Message queue length
}

get_stats_packets()

Packet counters (cumulative since boot).

{
    'recv': 20,              # Total packets received
    'sent': 0,               # Total packets sent
    'flood_tx': 0,           # Flood packets transmitted
    'direct_tx': 0,          # Direct packets transmitted
    'flood_rx': 20,          # Flood packets received
    'direct_rx': 0           # Direct packets received
}

get_stats_radio()

Radio/RF statistics.

{
    'noise_floor': -113,     # Noise floor in dBm
    'last_rssi': -123,       # RSSI of last received packet (dBm)
    'last_snr': -8.5,        # SNR of last received packet (dB)
    'tx_air_secs': 0,        # Cumulative TX airtime in seconds
    'rx_air_secs': 11        # Cumulative RX airtime in seconds
}

get_bat()

Battery/storage info (note: not the main battery source for metrics).

{
    'level': 4436,           # Battery level (unclear unit)
    'used_kb': 256,          # Used storage in KB
    'total_kb': 1404         # Total storage in KB
}

get_contacts()

Returns a dict keyed by public key. Count via len(payload).


Repeater Node

The repeater is queried over LoRa using the binary protocol command req_status_sync().

req_status_sync(contact)

Returns a single dict with all status fields.

{
    'bat': 4047,             # Battery voltage in millivolts
    'uptime': 1441998,       # Uptime in seconds
    'last_rssi': -63,        # RSSI of last received packet (dBm)
    'last_snr': 12.5,        # SNR of last received packet (dB)
    'noise_floor': -118,     # Noise floor in dBm
    'tx_queue_len': 0,       # TX queue depth
    'nb_recv': 221311,       # Total packets received (counter)
    'nb_sent': 93993,        # Total packets sent (counter)
    'airtime': 64461,        # TX airtime in seconds (counter)
    'rx_airtime': 146626,    # RX airtime in seconds (counter)
    'flood_dups': 59799,     # Duplicate flood packets (counter)
    'direct_dups': 8,        # Duplicate direct packets (counter)
    'sent_flood': 92207,     # Flood packets transmitted (counter)
    'recv_flood': 216960,    # Flood packets received (counter)
    'sent_direct': 1786,     # Direct packets transmitted (counter)
    'recv_direct': 4328      # Direct packets received (counter)
}

Telemetry Data

Environmental telemetry is requested via req_telemetry_sync(contact) and returns Cayenne LPP formatted sensor data. This requires TELEMETRY_ENABLED=1 and a sensor board attached to the repeater.

Payload Format

Both req_telemetry_sync() and get_self_telemetry() return a dict containing the LPP data list and a public key prefix:

{
    'pubkey_pre': 'a5c14f5244d6',
    'lpp': [
        {'channel': 0, 'type': 'temperature', 'value': 23.5},
        {'channel': 0, 'type': 'humidity', 'value': 45.2},
    ]
}

The extract_lpp_from_payload() helper in src/meshmon/telemetry.py handles extracting the lpp list from this wrapper format.

req_telemetry_sync(contact)

Returns sensor readings from a remote node in Cayenne LPP format:

[
    {'channel': 0, 'type': 'temperature', 'value': 23.5},
    {'channel': 0, 'type': 'humidity', 'value': 45.2},
    {'channel': 0, 'type': 'barometer', 'value': 1013.25},
    {'channel': 1, 'type': 'gps', 'value': {'latitude': 51.5, 'longitude': -0.1, 'altitude': 10}},
]

Common sensor types:

Type Unit Description
temperature Celsius Temperature reading
humidity % Relative humidity
barometer hPa/mbar Barometric pressure
voltage V Voltage reading
gps compound GPS with latitude, longitude, altitude

Stored as:

  • telemetry.temperature.0 - Temperature on channel 0
  • telemetry.humidity.0 - Humidity on channel 0
  • telemetry.gps.1.latitude - GPS latitude on channel 1

Notes:

  • Requires environmental sensor board (BME280, BME680, etc.) on repeater
  • Channel number distinguishes multiple sensors of the same type
  • Not all repeaters have environmental sensors attached
  • Telemetry collection does not affect circuit breaker state
  • Telemetry failures are logged as warnings and do not block status collection

get_self_telemetry()

Returns self telemetry from the companion node's attached sensors. Same Cayenne LPP format as req_telemetry_sync().

[
    {'channel': 0, 'type': 'temperature', 'value': 23.5},
    {'channel': 0, 'type': 'humidity', 'value': 45.2},
]

Notes:

  • Requires environmental sensor board attached to companion
  • Returns empty list if no sensors attached
  • Uses same format as repeater telemetry

Derived Metrics

These are computed at query time, not stored:

Metric Source Computation
bat_pct bat or battery_mv voltage_to_percentage(mv / 1000) using 18650 Li-ion discharge curve

Notes

  • Counters reset to 0 on device reboot
  • Millivolts to volts: Divide by 1000 (e.g., bat: 40474.047V)
  • Repeater fields come from a single req_status_sync() call
  • Companion fields are spread across multiple get_stats_*() calls