Skip to content

Omit SNR/RSSI instead of publishing hardcoded 12.5/-65 when no radio data#13

Open
ACETyr wants to merge 1 commit into
agessaman:mqtt-bridge-implementationfrom
ACETyr:fix/mqtt-observer-null-snr-rssi
Open

Omit SNR/RSSI instead of publishing hardcoded 12.5/-65 when no radio data#13
ACETyr wants to merge 1 commit into
agessaman:mqtt-bridge-implementationfrom
ACETyr:fix/mqtt-observer-null-snr-rssi

Conversation

@ACETyr

@ACETyr ACETyr commented Jun 14, 2026

Copy link
Copy Markdown

What

When the MQTT observer has no real radio measurement for a packet, omit the SNR/RSSI fields
instead of publishing the hardcoded constants 12.5 / -65.

src/helpers/MQTTMessageBuilder.cpp:

  • buildPacketJSON() (the no-raw-data overload) previously passed literal 12.5f / -65 to
    buildPacketMessage() ("// SNR/RSSI - using reasonable default"). It now passes sentinels.
  • buildPacketMessage() emits the SNR/RSSI fields only when the value is real (sentinel <= -900
    → omit). Real measurements are always within sane radio ranges and serialize exactly as before.

Why

buildPacketJSONFromRaw() carries the real per-packet SNR/RSSI, but the publish path falls back to
buildPacketJSON() whenever fresh raw radio data isn't available (no raw_data passed and the
global _last_raw_* snapshot older than 1 s — MQTTBridge.cpp). Because the MQTT publish runs on a
separate task that drains the queue asynchronously, that 1 s window is missed often.

The result is fabricated values indistinguishable from real readings. Measured against the public
CoreScope aggregator (200-packet samples per observer): the exact (SNR=12.5, RSSI=-65) pair appears
on ~3–20% of packets for every observer running this bridge (and 0% on a non-bridge observer),
silently polluting network-wide RF analytics (avgSnr/avgRssi//analytics/rf). Confirmed
end-to-end on a controlled node by correlating the serial RX log (real radio values) to the published
MQTT JSON by packet hash: published 12.5/-65 exactly where the radio reported real, different
values.

Omitting is honest about the gap — consumers can detect a missing field, but cannot detect a
fabricated reading.

Testing

  • Compiles clean: pio run -e Heltec_v3_repeater_observer_mqtt → SUCCESS (ESP32-S3 image built).
  • Localized JSON-builder change; buildPacketJSONFromRaw() (the real-data path) is unaffected.

Note

This only stops the fabrication. Reducing how often the fallback fires (e.g. carrying the
per-packet SNR/RSSI into QueuedPacket at reception so the publish path doesn't depend on the 1 s
_last_raw_* window) is a separate, optional robustness improvement.

…data

buildPacketJSON() (the no-raw-data path) passed hardcoded 12.5f / -65 to
buildPacketMessage(), so every packet published without fresh raw radio data
carried fabricated SNR/RSSI indistinguishable from real readings. Measured at
~3-20% of packets per observer on the live network (sampled via the CoreScope
aggregator), which silently pollutes network-wide RF analytics.

buildPacketMessage() now omits the SNR/RSSI fields when passed sentinel values
(<= -900); real measurements are always within sane radio ranges and format as
before. Consumers see an explicit gap instead of a fabricated reading.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant