meshtastic/docs/software/integrations/mqtt/nodered.mdx
rcarteraz 66ffda7702
Some checks are pending
CI / quality (push) Waiting to run
CI / build (push) Waiting to run
compress the images (#1538)
2024-10-28 18:10:46 -07:00

1007 lines
27 KiB
Plaintext

---
id: nodered
title: Node-RED
sidebar_label: Node-RED
sidebar_position: 3
---
## Using MQTT with Node-RED
Node-RED is a free cross-platform programming tool for wiring together hardware, APIs, and online services developed originally by IBM for IOT. It is widely used for home automation by many non-professional programmers and runs well on Pi's. Node-RED has many plug-in modules written by the community.
I will use this platform as a practical example on how to interface with the MQTT features of Meshtastic. Everything can be done from GUI's without using command line.
### Enabling MQTT
Use http://client.meshtastic.org/ , the python CLI, or an Apple or Android app to connect to your device and adjust these settings.
1. Settings--> Radio Config--> Network
- On the node that will act as the gateway between the mesh and MQTT enable a network connection (i.e. Wifi, Ethernet).
- Save
2. Settings--> Module Config--> MQTT config
- Configure the MQTT gateway's network configuration.
- Verify Encryption Enabled is OFF.
- (optional) Turn JSON Output Enabled ON.
- Save
3. Channel Editor
- Go to Channel Editor and enable Uplink and Downlink on the channels you wish to publish to MQTT.
- Save
### Using Node-RED with Meshtastic
There are three common approaches:
1. Using JSON-encoded messages
2. Using protobuf-encoded messages with the Meshtastic decode node
3. Using protobuf-encoded messages with a protobuf decode node and the Meshtastic protobuf definitions
The JSON output only publishes the following subset of the messages on a Meshtastic network:
- Text Message
- Telemetry
- Device Metrics
- Environment Metrics
- Power Metrics
- Node Info
- Position
- Waypoint
- Neighbor Info
> JSON is intended to be a convenience for consuming data in other applications like Home Assistant.
> Protobufs are mesh native.
#### 1. Using JSON-encoded messages
:::note
JSON is not supported on the nRF52 platform.
:::
Make sure that option _JSON Output Enabled_ is set in MQTT module options and you have a channel called "mqtt".
Below is a valid JSON envelope for information sent by MQTT to a device for broadcast onto the mesh. The `to` field is optional and can be omitted for broadcast. The `channel` field is also optional and can be omitted to send to the primary channel.
```json
{
"from":<node number of the transmitter>,
"to": <node number of the receiver for a DM (optional)>,
"channel": <channel index (optional)>,
"type":"sendtext",
"payload": text or a json object go here
}
```
#### 2. Using protobuf-encoded messages with the Meshtastic decode node
Install Node-Red plug-in:
https://flows.nodered.org/node/@meshtastic/node-red-contrib-meshtastic
More info is in the plug-in source repository.
There is an example flow using this mechanism available
https://github.com/scruplelesswizard/meshtastic-node-red
#### 3. Using protobuf-encoded messages with a protobuf decode node and the Meshtastic protobuf definitions
If you don't want to depend on JSON decoding on the device, you can decode the protobuf messages off-device. To do that you will need to get the .proto files from https://github.com/meshtastic/protobufs. They function as a schema and are required for decoding in Node-RED. Save the files where the node-RED application can access them and note the file path of the "mqtt.proto" file.
Install Node-RED plug-ins to your Node-RED application for an embedded MQTT server and a protobuf decoder.
https://flows.nodered.org/node/node-red-contrib-aedes
https://flows.nodered.org/node/node-red-contrib-protobuf
Drag, drop, and wire the nodes like this. For this example, I ran Node-RED on a Windows machine. Note that file paths might be specified differently on different platforms. MQTT server wild cards are usually the same. A "+" is a single level wildcard for a specific topic level. A "#" is a multiple level wildcard that can be used at the end of a topic filter. The debug messages shown are what happens when the inject button sends a JSON message with a topic designed to be picked up by the specified Meshtastic device and then having it rebroadcast the message.
[<img src="/documents/mqtt/NodeRedTwo.webp" style={{zoom:'50%'}} />](/documents/mqtt/NodeRedTwo.webp)
[<img src="/documents/mqtt/NodeRedThree.webp" style={{zoom:'50%'}} />](/documents/mqtt/NodeRedThree.webp)
[<img src="/documents/mqtt/NR_nodes.webp" style={{zoom:'50%'}} />](/documents/mqtt/NR_nodes.webp)
The aedes broker must be set up on the same flow as the other nodes. By activating the Publish debug node, you can see all the published messages.
[<img src="/documents/mqtt/Broker1.webp" style={{zoom:'50%'}} />](/documents/mqtt/Broker1.webp)
Receiving a json mqtt message is very simple.
[<img src="/documents/mqtt/Consume.webp" style={{zoom:'50%'}} />](/documents/mqtt/Consume.webp)
Injecting a json message to be sent by a device is also very simple. You do need the correct envelope.
[<img src="/documents/mqtt/Inject.webp" style={{zoom:'50%'}} />](/documents/mqtt/Inject.webp)
Forwarding a text message from one device, through a broker, to another broker/device/channel would look like this.
[<img src="/documents/mqtt/Forward.webp" style={{zoom:'50%'}} />](/documents/mqtt/Forward.webp)
If you want to decode text and position messages without json, it gets complicated:
[<img src="/documents/mqtt/DecodeNewest.webp" style={{zoom:'50%'}} />](/documents/mqtt/DecodeNewest.webp)
If you are interested in my flow for this it is here:
```json
[
{
"id": "10fe1b2e9cb3feb2",
"type": "decode",
"z": "23dbb1ee.bc2e8e",
"name": "decode Protobuf",
"protofile": "a0d4288141f6a629",
"protoType": "ServiceEnvelope",
"x": 295.5,
"y": 285,
"wires": [["d3e396cf4f0a9608", "d08865b41a69d85d", "6f592d47b6a2eac4"]]
},
{
"id": "40c9ee66fe7a34cb",
"type": "function",
"z": "23dbb1ee.bc2e8e",
"name": "function get the message as string from TEXT_MESSAGE_APP",
"func": "msg.payload = msg.payload.packet.decoded.payload;\n\nlet bufferObj = Buffer.from(msg.payload, \"base64\");\nlet decodedString = bufferObj.toString(\"utf8\");\nmsg.payload = decodedString;\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 410.5,
"y": 450,
"wires": [["553374591214eaca"]]
},
{
"id": "553374591214eaca",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "text message out",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 762.5,
"y": 449,
"wires": []
},
{
"id": "c6afbb9f1665b162",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "channelId",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 785.5,
"y": 257,
"wires": []
},
{
"id": "607ef387d5701985",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "gatewayId",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 792.5,
"y": 293,
"wires": []
},
{
"id": "d3e396cf4f0a9608",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "entire payload",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 296.5,
"y": 247,
"wires": []
},
{
"id": "2339b328bb9bb1d8",
"type": "comment",
"z": "23dbb1ee.bc2e8e",
"name": "Decode all cleartext text and position messages sent by Meshtastic devices into JSON without relying on JSON conversion on the device.",
"info": "",
"x": 515.5,
"y": 214,
"wires": []
},
{
"id": "408d796d997bb832",
"type": "function",
"z": "23dbb1ee.bc2e8e",
"name": "function get the nested payload as base64",
"func": "msg.payload = msg.payload.packet.decoded.payload;\n\nlet bufferObj = Buffer.from(msg.payload, \"base64\");\n//let decodedString = bufferObj.toString(\"hex\");\nmsg.payload = bufferObj;\nmsg.topic=\"\";\n//if you don't zero out the protubufTopic it will try to\n//decode it as part of the mqtt service envelope instead\n//of two-stage decoding\nmsg.protobufType=null;\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 349,
"y": 552,
"wires": [["9435a3c605efedb4", "1ed6f96c8214d7b3"]]
},
{
"id": "61995c9f8e8266b3",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "portnum",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 784.5,
"y": 330,
"wires": []
},
{
"id": "9435a3c605efedb4",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "nested payload",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 281.5,
"y": 603,
"wires": []
},
{
"id": "b832775d386f7ac9",
"type": "mqtt in",
"z": "23dbb1ee.bc2e8e",
"name": "",
"topic": "msh/+/c/#",
"qos": "2",
"datatype": "buffer",
"broker": "37cadac381653b1e",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 117.5,
"y": 286,
"wires": [["10fe1b2e9cb3feb2"]]
},
{
"id": "d08865b41a69d85d",
"type": "switch",
"z": "23dbb1ee.bc2e8e",
"name": "switch manual decoding nested message based on portum",
"property": "payload.packet.decoded.portnum",
"propertyType": "msg",
"rules": [
{ "t": "eq", "v": "TEXT_MESSAGE_APP", "vt": "str" },
{ "t": "eq", "v": "POSITION_APP", "vt": "str" }
],
"checkall": "true",
"repair": false,
"outputs": 2,
"x": 281.5,
"y": 505,
"wires": [["40c9ee66fe7a34cb"], ["408d796d997bb832"]]
},
{
"id": "8abb1bb458af2c4f",
"type": "change",
"z": "23dbb1ee.bc2e8e",
"name": "",
"rules": [
{
"t": "set",
"p": "gatewayId",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1021.5,
"y": 288,
"wires": [[]]
},
{
"id": "1ced0be28eeef0d3",
"type": "change",
"z": "23dbb1ee.bc2e8e",
"name": "",
"rules": [
{
"t": "set",
"p": "latitude",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1026.5,
"y": 407,
"wires": [[]]
},
{
"id": "313fd3cfe6d91850",
"type": "change",
"z": "23dbb1ee.bc2e8e",
"name": "",
"rules": [
{
"t": "set",
"p": "longitude",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1036.5,
"y": 450,
"wires": [["d02e53cdfb565da6"]]
},
{
"id": "33dd43e3c05f826c",
"type": "geofence",
"z": "23dbb1ee.bc2e8e",
"name": "geofence",
"mode": "circle",
"inside": "true",
"rad": 69174.91569647488,
"points": [],
"centre": {
"latitude": 40.16287050252407,
"longitude": -86.60385131835938
},
"floor": "",
"ceiling": "",
"worldmap": true,
"outputs": 2,
"x": 1202.5,
"y": 595,
"wires": [[], ["4d01eb8f1b31f039"]]
},
{
"id": "d02e53cdfb565da6",
"type": "function",
"z": "23dbb1ee.bc2e8e",
"name": "trigger function to send a mapping point",
"func": "let lat = parseFloat(flow.get(\"latitude\"));\nlet lon = parseFloat(flow.get(\"longitude\"));\nlat=lat * 0.0000001;\nlon=lon * 0.0000001;\nlet name = flow.get(\"from\")\n\nmsg={\"payload\":{\"name\":name,\n \"lat\":lat,\n \"lon\":lon,\n \"action\":\"send\",\n \"icon\": \"car\",\n \"label\":name\n }}\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1181.5,
"y": 520,
"wires": [["33dd43e3c05f826c", "4d01eb8f1b31f039"]]
},
{
"id": "4d01eb8f1b31f039",
"type": "worldmap",
"z": "23dbb1ee.bc2e8e",
"name": "",
"lat": "40",
"lon": "-86",
"zoom": "7",
"layer": "OSMG",
"cluster": "",
"maxage": "",
"usermenu": "show",
"layers": "show",
"panit": "false",
"panlock": "false",
"zoomlock": "false",
"hiderightclick": "false",
"coords": "none",
"showgrid": "false",
"showruler": "false",
"allowFileDrop": "false",
"path": "/worldmap",
"overlist": "DR,CO,RA,DN,HM",
"maplist": "OSMG,OSMC,EsriC,EsriS,EsriT,EsriDG,UKOS",
"mapname": "",
"mapurl": "",
"mapopt": "",
"mapwms": false,
"x": 1206.5,
"y": 675,
"wires": []
},
{
"id": "1ed6f96c8214d7b3",
"type": "decode",
"z": "23dbb1ee.bc2e8e",
"name": "decode Position Protobuf",
"protofile": "dbab6472b07929a0",
"protoType": "Position",
"x": 667.5,
"y": 548,
"wires": [["db1933ba36503bd9", "dad9f487318f21d9"]]
},
{
"id": "db1933ba36503bd9",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "Position decoded",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 673.5,
"y": 607,
"wires": []
},
{
"id": "dad9f487318f21d9",
"type": "function",
"z": "23dbb1ee.bc2e8e",
"name": "Split",
"func": "var lat = { payload: msg.payload.latitudeI };\nvar lon = { payload: msg.payload.longitudeI };\nvar alt = { payload: msg.payload.altitude };\nvar tm = { payload: msg.payload.time };\n\nreturn [lat,lon,alt,tm];",
"outputs": 4,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 875.5,
"y": 549,
"wires": [
["1ced0be28eeef0d3", "8bb97f802662976c"],
["313fd3cfe6d91850", "c8e135f3e542bb1b"],
["602fb2020680280c"],
["ed424ae3d45dd2ac"]
]
},
{
"id": "8bb97f802662976c",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "lat",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1017.5,
"y": 583,
"wires": []
},
{
"id": "c8e135f3e542bb1b",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "lon",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1018.5,
"y": 618,
"wires": []
},
{
"id": "602fb2020680280c",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "alt",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1017.5,
"y": 654,
"wires": []
},
{
"id": "ed424ae3d45dd2ac",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "time",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1018.5,
"y": 688,
"wires": []
},
{
"id": "6f592d47b6a2eac4",
"type": "function",
"z": "23dbb1ee.bc2e8e",
"name": "Split Decoded 1",
"func": "var channelId = { payload: msg.payload.channelId};\nvar gatewayId = { payload: msg.payload.gatewayId};\nvar portnum = { payload: msg.payload.packet.decoded.portnum};\nvar fr= {payload: \"!\" + msg.payload.packet.from.toString(16)};\nvar to = {payload:\"!\"+ msg.payload.packet.to.toString(16)};\n\nreturn [channelId, gatewayId, portnum, fr, to ];",
"outputs": 5,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 577.5,
"y": 294,
"wires": [
["c6afbb9f1665b162"],
["607ef387d5701985", "8abb1bb458af2c4f"],
["61995c9f8e8266b3"],
["fd881fac22422773", "a389f9875da672ec"],
["cf066ad415df30ae"]
]
},
{
"id": "fd881fac22422773",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "from",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 772.5,
"y": 365,
"wires": []
},
{
"id": "cf066ad415df30ae",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "to",
"active": false,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 771.5,
"y": 399,
"wires": []
},
{
"id": "a389f9875da672ec",
"type": "change",
"z": "23dbb1ee.bc2e8e",
"name": "set flow.from",
"rules": [
{ "t": "set", "p": "from", "pt": "flow", "to": "payload", "tot": "msg" }
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1012.5,
"y": 364,
"wires": [[]]
},
{
"id": "a0d4288141f6a629",
"type": "protobuf-file",
"protopath": "E:\\Meshtastic-protobufs-master\\mqtt.proto",
"watchFile": true,
"keepCase": false
},
{
"id": "37cadac381653b1e",
"type": "mqtt-broker",
"name": "",
"broker": "192.168.2.45",
"port": "1883",
"clientid": "",
"autoConnect": true,
"usetls": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willPayload": "",
"willMsg": {},
"userProps": "",
"sessionExpiry": ""
},
{
"id": "dbab6472b07929a0",
"type": "protobuf-file",
"protopath": "E:\\Meshtastic-protobufs-master\\mesh.proto",
"watchFile": true,
"keepCase": false
}
]
```
(documents/mqtt/Flow.txt)
Node-red can rapidly (minutes vs days) put together some pretty impressive output when paired with meshtastic. Here is the output of that flow geofencing and mapping via mqtt data.
[<img src="/documents/mqtt/Mapping.webp" style={{zoom:'50%'}} />](/documents/mqtt/Mapping.webp)
Advanced use, such as encoding Position and sending it to a device via MQTT without using JSON can get a little complicated. An example of how it can be done is below.
[<img src="/documents/mqtt/EncodingPosition.webp" style={{zoom:'50%'}} />](/documents/mqtt/EncodingPosition.webp)
The flow is:
```json
[
{
"id": "32ca608d9e7c5236",
"type": "inject",
"z": "23dbb1ee.bc2e8e",
"name": "",
"props": [{ "p": "payload" }, { "p": "topic", "vt": "str" }],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 96.5,
"y": 1952,
"wires": [["2b536512e8c7aef2"]]
},
{
"id": "20bbd2d1408b8dc5",
"type": "change",
"z": "23dbb1ee.bc2e8e",
"name": "",
"rules": [
{
"t": "set",
"p": "channelId_outbound",
"pt": "flow",
"to": "LongFast",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 772,
"y": 2027,
"wires": [[]]
},
{
"id": "c6cb373157be01d6",
"type": "change",
"z": "23dbb1ee.bc2e8e",
"name": "",
"rules": [
{
"t": "set",
"p": "gatewayId_outbound",
"pt": "flow",
"to": "\"!55c7312c\"",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 772,
"y": 2066,
"wires": [[]]
},
{
"id": "24199ec7eaf89c1a",
"type": "change",
"z": "23dbb1ee.bc2e8e",
"name": "",
"rules": [
{
"t": "set",
"p": "portnum_outbound",
"pt": "flow",
"to": "3",
"tot": "num"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 774,
"y": 2106,
"wires": [[]]
},
{
"id": "de38ad5ef343623a",
"type": "change",
"z": "23dbb1ee.bc2e8e",
"name": "",
"rules": [
{
"t": "set",
"p": "from_outbound",
"pt": "flow",
"to": "1439117612",
"tot": "num"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 781,
"y": 2146,
"wires": [[]]
},
{
"id": "d435e8abe0852f93",
"type": "change",
"z": "23dbb1ee.bc2e8e",
"name": "",
"rules": [
{
"t": "set",
"p": "to_outbound",
"pt": "flow",
"to": "4294967295",
"tot": "num"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 790,
"y": 2188,
"wires": [[]]
},
{
"id": "1f8d172708898860",
"type": "function",
"z": "23dbb1ee.bc2e8e",
"name": "Assemble Position protobuf",
"func": "msg.protobufType=null;\nmsg.payload =\n{\n \"packet\": {\n \"from\": flow.get(\"from_outbound\"),\n \"to\": flow.get(\"to_outbound\"), \n \"decoded\":{\n //how ENUMS are handled here\n //portnum is decoded as string but encoded as number\n //in the encode/decode node-red nodes based on protobuf.js\n \"portnum\": flow.get(\"portnum_outbound\"),\n \"payload\": msg.payload \n } \n },\n\n \"channelId\": flow.get(\"channelId_outbound\"),\n \"gatewayId\": flow.get(\"gatewayId_outbound\"),\n};\nreturn msg;\n//info on how to get json data into protobuf \"bytes\" field\n//https://github.com/protobufjs/protobuf.js/wiki/Changes-in-ProtoBuf.js-3.8",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1086,
"y": 2019,
"wires": [["b8ccf1cfe8bf40a3"]]
},
{
"id": "b8ccf1cfe8bf40a3",
"type": "encode",
"z": "23dbb1ee.bc2e8e",
"name": "",
"protofile": "a0d4288141f6a629",
"protoType": "ServiceEnvelope",
"x": 1287,
"y": 2020,
"wires": [["dbc78f035c9c2b56", "a002c148f3a06bac"]]
},
{
"id": "03a7e69ca6d471fe",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "show hex string",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 1319,
"y": 2180,
"wires": []
},
{
"id": "dbc78f035c9c2b56",
"type": "function",
"z": "23dbb1ee.bc2e8e",
"name": "dump payload as hex string",
"func": "var hex=Buffer.from(msg.payload,\"hex\");\nmsg.payload=hex.toString(\"hex\");\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1096,
"y": 2178,
"wires": [["03a7e69ca6d471fe"]]
},
{
"id": "2b536512e8c7aef2",
"type": "function",
"z": "23dbb1ee.bc2e8e",
"name": "Inject lat lon alt",
"func": "msg.payload={\n \"latitudeI\": 399600000,\n \"longitudeI\": -862600000,\n \"altitude\": 100\n}\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 277.5,
"y": 1953,
"wires": [["9443a9a980e54c75"]]
},
{
"id": "9443a9a980e54c75",
"type": "encode",
"z": "23dbb1ee.bc2e8e",
"name": "encode Position as protobuf",
"protofile": "dbab6472b07929a0",
"protoType": "Position",
"x": 506,
"y": 1953,
"wires": [["5c36d3a7f4dca14e"]]
},
{
"id": "5c36d3a7f4dca14e",
"type": "change",
"z": "23dbb1ee.bc2e8e",
"name": "",
"rules": [
{
"t": "set",
"p": "nested_outbound",
"pt": "flow",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 776,
"y": 1952,
"wires": [
[
"20bbd2d1408b8dc5",
"c6cb373157be01d6",
"24199ec7eaf89c1a",
"de38ad5ef343623a",
"d435e8abe0852f93",
"04d0c4a5f3485c6f"
]
]
},
{
"id": "04d0c4a5f3485c6f",
"type": "function",
"z": "23dbb1ee.bc2e8e",
"name": "dump payload as base64 string",
"func": "var hex=Buffer.from(msg.payload,\"base64\");\nmsg.payload=hex.toString(\"base64\");\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 1082,
"y": 1952,
"wires": [["1f8d172708898860"]]
},
{
"id": "a002c148f3a06bac",
"type": "decode",
"z": "23dbb1ee.bc2e8e",
"name": "test decode Protobuf",
"protofile": "a0d4288141f6a629",
"protoType": "ServiceEnvelope",
"x": 1249,
"y": 1860,
"wires": [["4b6fc79398d05782"]]
},
{
"id": "4b6fc79398d05782",
"type": "debug",
"z": "23dbb1ee.bc2e8e",
"name": "test entire payload",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 1458,
"y": 1859,
"wires": []
},
{
"id": "a0d4288141f6a629",
"type": "protobuf-file",
"protopath": "E:\\Meshtastic-protobufs-master\\mqtt.proto",
"watchFile": true,
"keepCase": false
},
{
"id": "dbab6472b07929a0",
"type": "protobuf-file",
"protopath": "E:\\Meshtastic-protobufs-master\\mesh.proto",
"watchFile": true,
"keepCase": false
}
]
```
Sending a position to a device for broadcast to the mesh is much easier with JSON. It requires the message to be published to a channel called "mqtt". You can let the message send out to a different channel by setting the `channel` field to a channel index (0-7). Then use the MQTT Service Envelope type: "sendposition". A valid MQTT envelope and message to broadcast lat, lon, altitude (optional) and time (optional) looks like this:
```json
{
"from": 2130636288,
"type": "sendposition",
"payload": {
"latitude_i": 399600000,
"longitude_i": -862600000,
"altitude": 100,
"time": 1670201543
}
}
```
An example of doing this in node-red:
[<img src="/documents/mqtt/PosJSON.webp" style={{zoom:'50%'}} />](/documents/mqtt/PosJSON.webp)