Meshtastic

From RevSpace
Jump to navigation Jump to search
Project Meshtastic
Meshtastic.png
Experiments with Meshtastic
Status In progress
Contact bertrik
Last Update 2025-03-26

1. Intro

The plan is to use Meshtastic to transfer citizen science measurement data.

Stuff to figure out:

  • network coverage: meshnet.nl map
  • do nodes forward packets that do not belong to their "own" network? -> appears to be so!

My node: https://db.meshnet.nl/da639b54.html

Done:

  • I know the basics of meshtastic
  • Understand how the encryption works, how keys are constructed, how nonce/salt is constructed
  • Can pick up messages sent through the network and shared via MQTT back into a local application and decrypt them

TODO

  • create a particulate matter sensor that sends meshtastic
    • figure out the LoRa settings for a LoRa transceiver
    • actually build it and test it out
  • write a backend/plugin for my sensor-data-bridge

2. Design

Overall design:

  • citizen science nodes send data in meshtastic-compatible format, so packets can be routed accross the meshtastic network
  • data is sent from the node typically every 5 minutes
  • data is broadcasted inside the network, until it reaches a node with a MQTT backend connection, typically within a maximum of 3 hops.
  • data arriving at the MQTT server is picked up by a backend application, which can then process it further
  • we use a more-or-less citizen-science-data specific channel, so we don't interfere with other meshtastic traffic

Protocol design:

  • Use a dedicated channel (longfast? + custom key)
  • Use standard network concepts from meshtastic as much as possible to comply with meshtastic expectations for smooth operation
  • Instead of the MeshPacket structure, we directly use the citizen science payload, so no wrapping in a protobuf with portnr + payload
  • Citizen science payload has its own custom encoding
  • A checksum (16-bit/32-bit?) at the end of the payload allows us to verify that it really is a citizen science data packet after decryption -> do we really need this if we already have a channel?
  • Each packet already has a semi-unique packet id, so we can identify duplicates at the backend

Backend:

  • The backend application listens on a topic on the de-facto central MQTT server for the netherlands, used by most meshtastic nodes, which is mqtt.meshnet.nl
  • Decoding works as follows:
    • Decrypt with the pre-shared-key (this always work but might result in garbage)
    • Attempt to decode according to the the protobuf portnum+payload (meshtastic 'Data')
    • Check the port number
    • Check and remove the 16-bit checksum in the payload
  • If all of the steps above check out, consider it to be a valid packet
  • Check in a local cache if this is a duplicate packet and if so, ignore it
  • process citizen science data payload: there is no such thing as TTN attributes, so any data required for further forwarding need to be kept locally (e.g. login credentials for opensense / sensor.community)

Problems:

  • Although any packet with a valid network header will be propagated through the meshtastic network, it will probably not be forwarded to MQTT if it cannot be encoded!

For example, encoded using an unknown key, or unfamiliar packet structure. A node cannot inspect the packet structure, unless it uses a known key. Conclusion is that we cannot use a private key to distinguish it from default LongFast traffic and expect it to be MQTT forwarded!

2.1. Packet structure

On the radio level:

[ lora preamble | ... | <payload> ] <to be documented>

Within the radio payload:

[ meshtastic 16-byte header | citizen science payload | CRC ]
                            |<--meshtastic encrypted part-->]

3. Protocol

See https://meshtastic.org/docs/overview/mesh-algo/

Quick links:

4. Hardware

Nice antenna? https://nl.aliexpress.com/item/1005007301116616.html

5. MQTT

In the netherlands, data is typically sent to the 'boreft' MQTT server, for example

 mosquitto_sub -h mqtt.meshnet.nl -u boreft -P meshboreft -t "#" -v

Examples of typical data:

 msh/7460-7463/2/stat/!da5857c0 online
 msh/EU_868/NL/2/e/LongFast/!eb66115c �%]�g(=���gx�� H5��Aw=]�gE��H`���������LongFast␦

Topics with data on MQTT have the following structure:

 msh/REGION/2/e/CHANNELNAME/USERID

5.1. Sending messages

Requirements for sending mqtt downlinks:

  • the meshtastic node needs to have a channel named "mqtt" (exactly), see https://github.com/meshtastic/firmware/blob/master/src/mqtt/MQTT.cpp#L354
  • the meshtastic node has JSON be enabled in its MQTT settings
  • -> the meshtastic node listens on topic: "ROOT/2/json/mqtt/+", where ROOT = "msh/gouda" in my case
  • -> the mqtt publisher sends to topic: 'msh/gouda/2/json/mqtt/!da639b54' for example
  • example payload:
    {"from": 3663960916, "type": "sendtext", "payload": "Test"}'

5.2. Example data

Examples of data as decoded from MQTT using the meshtastic python service wrapper:

packet {
  from: 2732702784
  to: 4294967295
  decoded {
    portnum: POSITION_APP
    payload: "\r\224\234\024\037\025\303\266\233\002\030\n\270\001 "
  }
  id: 663882246
  rx_time: 1741511999
  rx_snr: -18
  hop_limit: 2
  rx_rssi: -128
  hop_start: 3
}
channel_id: "LongFast"
gateway_id: "!da544e50"

Packet with encrypted data:

packet {
  from: 1128181476
  to: 4294967295
  channel: 8
  encrypted: "\007\355{o\340e\352\221\204\3112\365h\304[0\321&\351^{]\264\334\373\320\313>\213\2635\023\345'"
  id: 4272151039
  rx_time: 1741512321
  rx_snr: 5.75
  hop_limit: 4
  rx_rssi: -83
  hop_start: 5
}
channel_id: "LongFast"
gateway_id: "!da5c87d4"