nodered
Использование MQTT с Node-RED
Node-RED — это бесплатный кросс-платформенный инструмент программирования для соединения аппаратного обеспечения, API и онлайн-сервисов, первоначально разработанный IBM для IoT. Он широко используется для домашней автоматизации многими непрофессиональными программистами и хорошо работает на Raspberry Pi. Node-RED имеет множество модулей плагинов, написанных сообществом.
Я буду использовать эту платформу в качестве практического примера того, как взаимодействовать с функциями MQTT Meshtastic. Всё можно сделать из графических интерфейсов без использования командной строки.
Включение MQTT
Используйте http://client.meshtastic.org/ , python CLI или приложение для Apple или Android, чтобы подключиться к вашему устройству и настроить эти параметры.
-
Настройки--> Конфигурация радио--> Сеть
-
На узле, который будет выступать в роли шлюза между сеткой и MQTT, включите сетевое соединение (например, WiFi, Ethernet).
-
Сохранить
-
Настройки--> Конфигурация модуля--> Конфигурация MQTT
- Настройте сетевую конфигурацию шлюза MQTT.
- Убедитесь, что Encryption Enabled выключено.
- (опционально) Включите JSON Output Enabled.
- Сохранить
- Редактор каналов
- Перейдите в Редактор каналов и включите Uplink и Downlink на каналах, которые вы хотите публиковать в MQTT.
- Сохранить
Использование Node-RED с Meshtastic
Существует три распространённых подхода:
- Использование сообщений, закодированных в JSON
- Использование сообщений, закодированных в protobuf, с узлом декодирования Meshtastic
- Использование сообщений, закодированных в protobuf, с узлом декодирования protobuf и определениями protobuf Meshtastic
Вывод в JSON публикует только следующий подмножество сообщений в сети Meshtastic:
- Текстовое сообщение
- Телеметрия
- Метрики устройства
- Метрики окружающей среды
- Метрики питания
- Информация об узле
- Позиция
- Точка маршрута
- Информация о соседях
JSON предназначен для удобства потребления данных в других приложениях, таких как Home Assistant.
Protobuf — это родной формат сетки.
1. Использование сообщений, закодированных в JSON
:::note JSON не поддерживается на платформе nRF52. :::
Убедитесь, что опция JSON Output Enabled установлена в опциях модуля MQTT, и у вас есть канал с названием "mqtt".
Ниже приведена действительная оболочка JSON для информации, отправляемой MQTT на устройство для трансляции в сетку. Поле to является опциональным и может быть опущено для трансляции. Поле channel также опционально и может быть опущено для отправки на основной канал.
{
"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. Использование сообщений, закодированных в protobuf, с узлом декодирования Meshtastic
Установите плагин Node-RED: https://flows.nodered.org/node/@meshtastic/node-red-contrib-meshtastic
Дополнительная информация в репозитории исходного кода плагина.
Доступен пример потока, использующий этот механизм https://github.com/scruplelesswizard/meshtastic-node-red
3. Использование сообщений, закодированных в protobuf, с узлом декодирования protobuf и определениями protobuf Meshtastic
Если вы не хотите зависеть от декодирования JSON на устройстве, вы можете декодировать сообщения protobuf вне устройства. Для этого вам нужно получить файлы .proto из https://github.com/meshtastic/protobufs. Они служат схемой и необходимы для декодирования в Node-RED. Сохраните файлы там, где приложение Node-RED может к ним получить доступ, и запишите путь к файлу "mqtt.proto".
Установите плагины Node-RED в ваше приложение Node-RED для встроенного MQTT-сервера и декодера protobuf. https://flows.nodered.org/node/node-red-contrib-aedes https://flows.nodered.org/node/node-red-contrib-protobuf
Перетащите, сбросьте и соедините узлы следующим образом. В этом примере Node-RED запущен на машине Windows. Обратите внимание, что пути к файлам могут указываться по-разному на разных платформах. Подстановочные знаки MQTT-сервера обычно одинаковы. "+" — это подстановочный знак одного уровня для конкретного уровня темы. "#" — это подстановочный знак нескольких уровней, который можно использовать в конце фильтра темы. Отображаемые отладочные сообщения показывают, что происходит, когда кнопка inject отправляет JSON-сообщение с темой, предназначенной для захвата указанным устройством Meshtastic, а затем устройство повторно транслирует сообщение.
Брокер aedes должен быть настроен в том же потоке, что и другие узлы. Активируя узел Publish debug, вы можете увидеть все опубликованные сообщения.
Получение JSON-сообщения MQTT очень просто.
Внедрение JSON-сообщения для отправки устройством также очень просто. Вам нужна правильная оболочка.
Пересылка текстового сообщения с одного устройства через брокер на другой брокер/устройство/канал будет выглядеть так.
Если вы хотите декодировать текстовые и позиционные сообщения без 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 может быстро (минуты против дней) собрать довольно впечатляющий результат в паре с Meshtastic. Вот результат этого потока: геозаборы и картирование на основе данных MQTT.
[<img src="../../../../../images/ff218bbbe8fb1545faa46e136733b105.webp" style={{zoom:'50%'}} />](../../../../documents/mqtt/Mapping.webp.html)
Расширенное использование, такое как кодирование Position и отправка его на устройство через MQTT без использования JSON, может быть немного сложным. Ниже приведён пример того, как это можно сделать.
[<img src="../../../../../images/1d04c6672d4dd57038ffbf1430f9568a.webp" style={{zoom:'50%'}} />](../../../../documents/mqtt/EncodingPosition.webp.html)
Поток следующий:
```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
}
]
Отправка позиции на устройство для трансляции в mesh-сеть гораздо проще с использованием JSON. Сообщение должно быть опубликовано в канал под названием "mqtt". Чтобы отправить сообщение в другой канал, установите поле channel в индекс канала (0-7). Затем используйте тип MQTT Service Envelope: "sendposition". Действительный MQTT-оковелоп и сообщение для трансляции lat, lon, высоты (необязательно) и времени (необязательно) выглядит следующим образом:
{
"from": 2130636288,
"type": "sendposition",
"payload": {
"latitude_i": 399600000,
"longitude_i": -862600000,
"altitude": 100,
"time": 1670201543
}
}
Пример реализации этого в Node-RED:

```


