Hardware
- 2x Seeed Studio XIAO ESP32S3 & Wio-SX1262 Kit
-
2x HELTEC 868-915MHz Antenna (HT-LoRaIPEX1)
-
Server running mosquitto
- Android phone with meshtastic app
-
Meshtastic web, on Mac running helium
-
USB-C cable from phone
- Super high speed USB-C cable from projector
Goal
- Get a position packet from one radio to another, in a private channel, or in a private message.
- Upload that position packet to a local MQTT server
- Save the position and the ID of the first radio into a Postgres DB
Notes
- The dev board USB connectors seem to be very sensitive to cable quality and movement. The high quality cable was much better. The cheaper phone data cable was less stable than connecting over Wi-Fi
- Meshtastic web UI is buggy, will probably use the CLI or the phone app from now on.
Method
The two radios have been named BORT and BONK.
BORTis being used as a "base" node -- it is set to roleCLIENT, and has the standard LongFast primary channel enabled. It has MQTT uplink enabled. The root MQTT topic was set tolive-tracking, but this was unnecessary. It has only been noted here to make the examples later make sense. JSON output enabled withing the MQTT module.- It also has a second channel enabled, named
private. The key for this channel was generated on this device. BONKis set up (to the best of my knowledge) as a tracking node. Its role isTRACKER, and its primary channel is theprivatechannel setup onBONK. MQTT uplink was disabled on this device.- Has a shared secret key
- Has location enabled
- Location configured to be shared every minute
- For this test, the phone location was shared with the device, as it does not have a GPS chipset.
Result
BONK and BORT in same room
- Success (0 hops)
- Success
- Success
BONK indoors, BORT on balcony (wifi router is indoors)
- Success (0 hops)
- Success
- Success
BONK in neighbourhood, BORT on balcony
- Success (2 hops)
- Success
- Success
BONK on bike ride, BORT on balcony
- Partial success (2 hops)
- Success
- Success
Output
Two kinds of messages within the private channel made it to the MQTT broker. Less interesting was a message with binary encoding:
live-tracking/2/e/private/!d45aa270
�y1���%�r�j(x�� H5��މ=�r�jE�@Hx���private !d45aa270
And more usefully, a JSON-encoded message:
live-tracking/2/json/private/!d45aa270 {"channel":1,"from":2904406684,"hop_start":3,"hops_away":0,"id":2313080000,"payload":{"altitude":303,"latitude_i":494515737,"longitude_i":110622513,"precision_bits":32,"time":1788506792},"sender":"!d45aa270","snr":5.75,"timestamp":1788506792,"to":4294967295,"type":"position"}
Useful information:
- The node name/ID is in the topic. Useful for identifying users later on.
- The channel name is in the topic as well. This could be used to identify routes.
- Most obviously, the lng, lat, timestamp and altitude are present within the payload
- We can also filter messages by their type. We are only interested in
"type": "position"for now
None of the messages were visible in the output of a local store-and-forward server which should be within range. This does need further testing.
Future work
The most obvious next steps are a range test, and a way to forward the packets into the DB in a more organised way.
Packet structure and persistence
- Ditch the topic prefix (
live-tracking) - Use the first part of the route ID as the channel name (the user would have to know this, so it might make sense to lower-case the whole thing, in order to improve ease of input on a phone)
- For example,
2521EE10-F136-4BA4-AD0E-21E17E6E0C43would become2521ee10. - When reading from the topic, we could then add rows into the DB using a query like
USING (SELECT id FROM user_routes WHERE id::string ILIKE "%<TOPIC>%") as routeand get the route id back from that. - Check whether id collision is more or less likely when using the start or end of the uuid
- Research whether the packets can be encrypted in a way where the user would also be able to decrypt them, but the server cannot. Failing this, encrypt the packets before writing to the DB, so that a DB breach does not leak users' historical locations (they should also be deleted either on live-track session end, or after a predetermined period of time, whichever is shorter)
- RLS
- Is it possible to use the user saved in postgres as the MQTT user?
Querying the DB for packets
SELECT payload #> '{payload,latitude_i}', payload #> '{payload,longitude_i}' as lng, payload #> '{payload,altitude}' as alt FROM public.mqtt_messages
WHERE payload ->> 'type' = 'position'
AND topic ILIKE '%private%'
ORDER BY "createdAt" DESC