Skip to content

fix(mqtt): coarsen public position uplinks#10290

Open
Komzpa wants to merge 4 commits into
meshtastic:developfrom
Komzpa:fix/public-mqtt-position-precision
Open

fix(mqtt): coarsen public position uplinks#10290
Komzpa wants to merge 4 commits into
meshtastic:developfrom
Komzpa:fix/public-mqtt-position-precision

Conversation

@Komzpa
Copy link
Copy Markdown
Contributor

@Komzpa Komzpa commented Apr 24, 2026

Summary

  • dual-publish decoded POSITION_APP packets to the default public MQTT server
  • publish a generated coarse packet first with precision_bits = 15, then publish the original packet unchanged
  • apply the same ordering to JSON MQTT output when JSON is enabled

Why

The public MQTT broker currently filters precise position packets. Publishing the coarse packet first lets current public consumers receive a location update, while publishing the original second preserves current behavior for private/custom brokers and for any future public-broker policy that accepts more precise positions.

Notes

  • Only decoded POSITION_APP packets sent to the default public MQTT server are duplicated/coarsened.
  • Encrypted uplinks remain unchanged.
  • The generated coarse packet keeps the same mesh metadata but masks latitude/longitude to the public precision bucket.

Tests

  • Added/updated test_mqtt coverage for protobuf dual publish, JSON dual publish, and encrypted-position behavior.
  • Built heltec-v3 successfully with PlatformIO.

@github-actions github-actions Bot added needs-review Needs human review bugfix Pull request that fixes bugs labels Apr 24, 2026
@Komzpa Komzpa force-pushed the fix/public-mqtt-position-precision branch 2 times, most recently from 701f04b to 8ac2d6d Compare April 24, 2026 23:28
@Komzpa Komzpa marked this pull request as ready for review April 25, 2026 02:21
@Komzpa Komzpa force-pushed the fix/public-mqtt-position-precision branch from 8ac2d6d to 8633c41 Compare May 14, 2026 02:21
@Komzpa Komzpa force-pushed the fix/public-mqtt-position-precision branch from 8633c41 to 6106339 Compare May 14, 2026 02:26
@thebentern thebentern requested a review from Copilot May 14, 2026 11:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Coarsens public-MQTT POSITION_APP uplinks to address the public broker's filtering of precise positions. When the configured MQTT server is the default public one, decoded position packets are dual-published: first a generated copy with coordinates bucketed to ≤15 precision bits (and a fresh packet id), then the original packet unchanged. JSON output mirrors the same ordering. Encrypted protobuf uplinks are intentionally left as-is. When the offline queue has only one slot left, only the imprecise copy is queued so the public-usable variant survives eviction.

Changes:

  • New getPublicMqttPositionPrecision() and makePublicMqttPositionPacket() helpers in src/mqtt/MQTT.cpp that build a coarsened POSITION_APP copy.
  • MQTT::onSend refactored around a publishPacket lambda that handles a single (proto, json) publish; it's now invoked twice for public-server position packets, with a queue-pressure carve-out.
  • New test_mqtt cases covering imprecise-then-original ordering for protobuf and JSON, coarser-source preservation, no-coordinates skipping, encrypted-stays-encrypted, and offline-queue-near-full behavior; the mock MQTT server's command parser was rewritten to handle multi-packet writes and varint remaining-length.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
src/mqtt/MQTT.cpp Adds the public-position coarsening helpers and reworks onSend to dual-publish (coarse + original) for default-public-broker decoded position packets, with queue-pressure handling.
test/test_mqtt/MQTT.cpp Adds position helpers, drainQueue helper, multi-packet/varint-length parsing in the mock MQTT server, JSON-topic capture, and new test cases for the dual-publish behavior.

Comment thread src/mqtt/MQTT.cpp Outdated
Comment on lines +848 to +856
const meshtastic_MeshPacket *p;
if (moduleConfig.mqtt.encryption_enabled) {
p = &mp_encrypted;
LOG_DEBUG("encrypted message");
} else if (mp_decoded.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
p = &mp_decoded;
if (isConfiguredForDefaultServer && !isMqttServerAddressPrivate &&
makePublicMqttPositionPacket(mqttPositionPacket, mp_decoded))
publicMqttPositionPacket = &mqttPositionPacket;
Comment thread src/mqtt/MQTT.cpp
position.precision_bits = precision;

mqttPacket = sourcePacket;
mqttPacket.id = generatePacketId();
Comment thread src/mqtt/MQTT.cpp Outdated
Comment on lines +64 to +67
// Public MQTT map reports currently accept precision bits up to 15.
// Forwarded LoRa positions use that most precise public value as a cap so
// an already-coarser source packet keeps its original privacy boundary.
return 15;
Comment thread src/mqtt/MQTT.cpp Outdated
Comment on lines +62 to +98
uint32_t getPublicMqttPositionPrecision()
{
// Public MQTT map reports currently accept precision bits up to 15.
// Forwarded LoRa positions use that most precise public value as a cap so
// an already-coarser source packet keeps its original privacy boundary.
return 15;
}

bool makePublicMqttPositionPacket(meshtastic_MeshPacket &mqttPacket, const meshtastic_MeshPacket &sourcePacket)
{
if (sourcePacket.which_payload_variant != meshtastic_MeshPacket_decoded_tag ||
sourcePacket.decoded.portnum != meshtastic_PortNum_POSITION_APP)
return false;

meshtastic_Position position = meshtastic_Position_init_default;
if (!pb_decode_from_bytes(sourcePacket.decoded.payload.bytes, sourcePacket.decoded.payload.size, &meshtastic_Position_msg,
&position))
return false;
if (!position.has_latitude_i || !position.has_longitude_i)
return false;

uint32_t precision = getPublicMqttPositionPrecision();
if (position.precision_bits > 0 && position.precision_bits < precision)
precision = position.precision_bits;
position.latitude_i = position.latitude_i & (UINT32_MAX << (32 - precision));
position.longitude_i = position.longitude_i & (UINT32_MAX << (32 - precision));
position.latitude_i += (1 << (31 - precision));
position.longitude_i += (1 << (31 - precision));
position.precision_bits = precision;

mqttPacket = sourcePacket;
mqttPacket.id = generatePacketId();
mqttPacket.decoded.payload.size =
pb_encode_to_bytes(mqttPacket.decoded.payload.bytes, sizeof(mqttPacket.decoded.payload.bytes), &meshtastic_Position_msg,
&position);
return mqttPacket.decoded.payload.size > 0;
}
Comment thread src/mqtt/MQTT.cpp Outdated
Comment on lines +94 to +97
mqttPacket.decoded.payload.size =
pb_encode_to_bytes(mqttPacket.decoded.payload.bytes, sizeof(mqttPacket.decoded.payload.bytes), &meshtastic_Position_msg,
&position);
return mqttPacket.decoded.payload.size > 0;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Pull request that fixes bugs needs-review Needs human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants