---
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)