From f3f09f0dcfb8ee8add8032e9d3c78b9f7cc32935 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 3 Apr 2021 16:06:40 +0800 Subject: [PATCH] MQTT WIP --- bin/mqtt-listen.sh | 2 +- bin/mqtt-send-status.sh | 2 +- docs/software/TODO.md | 12 ++++++++---- docs/software/mqtt.md | 6 +++--- src/mqtt/MQTT.cpp | 36 ++++++++++++++++++++++++++++-------- src/mqtt/MQTT.h | 4 ++-- src/mqtt/MQTTPlugin.cpp | 18 ++++++++++++++++++ src/mqtt/MQTTPlugin.h | 17 +++++++++++++++++ 8 files changed, 78 insertions(+), 19 deletions(-) create mode 100644 src/mqtt/MQTTPlugin.cpp create mode 100644 src/mqtt/MQTTPlugin.h diff --git a/bin/mqtt-listen.sh b/bin/mqtt-listen.sh index ed2d22722..f7d8458c0 100755 --- a/bin/mqtt-listen.sh +++ b/bin/mqtt-listen.sh @@ -1 +1 @@ -mosquitto_sub -h test.mosquitto.org -v -t mstat/\# -t mesh/\# +mosquitto_sub -h test.mosquitto.org -v -t mesh/\# diff --git a/bin/mqtt-send-status.sh b/bin/mqtt-send-status.sh index 107c3b10a..d14560233 100755 --- a/bin/mqtt-send-status.sh +++ b/bin/mqtt-send-status.sh @@ -1 +1 @@ -mosquitto_pub -h test.mosquitto.org -t mstat/FakeNode -m online -d +mosquitto_pub -h test.mosquitto.org -t mesh/stat/FakeNode -m online -d diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 015f4760d..2ae568069 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -55,18 +55,22 @@ You probably don't care about this section - skip to the next one. * DONE have sim provide a fake wifi connection status saying connected * DONE don't start MQTT if we don't have wifi connected +* have plugin send uplinks from mesh +* have plugin send downlinks to mesh +* don't decrypt messages before uplinking them to MQTT (move out of plugin) * mqtt.meshtastic.org should have VERY basic auth at launch (to prevent abuse) * make a GlobalChat channel as an initial test (with a well known AES128 key), figure out how globally unique IDs work * Give place in android app for users to select which channel they are sending on (and which channels they are watching) * attempt reconnect to server if internet connectivity changes * don't bother contacting server if we don't have any uplink/downlink channels -* test on ESP32 +* DONE test on ESP32 * no need for python gateway to web initially: because both the simulator and ESP32 can talk wifi directly * if simmesh_name is set in preferences, create the MQTTSimInterface using that as the global channel_id * figure out how to use MQTT for simulator mesh network, use a special simmesh_name global channel_id? (because this would allow all nodes in simnet_xxx to subscribe only to those packets) -* do initial development inside of portduino -* do as much possible on the device side (so we can eventually just have ESP32 talk directly to server) -* add mqtt_server to radio prefs +* figure out legality of hosting public mqtt servers with chat msgs +* DONE do initial development inside of portduino +* DONE do as much possible on the device side (so we can eventually just have ESP32 talk directly to server) +* DONE add mqtt_server to radio prefs * eventually add a MQTTPacket on the ToRadio & FromRadio links * LATER: an android gateway would be useful diff --git a/docs/software/mqtt.md b/docs/software/mqtt.md index df6a21348..2c00e9722 100644 --- a/docs/software/mqtt.md +++ b/docs/software/mqtt.md @@ -70,19 +70,19 @@ Any gateway-device will contact the MQTT broker. ### Topics -* The "/mesh/crypt/CHANNELID/NODEID/PORTID" [topic](https://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices/) will be used for messages sent from/to a mesh. +* The "/mesh/crypt/CHANNELID/NODEID" [topic](https://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices/) will be used for (encrypted) messages sent from/to a mesh. Gateway nodes will foward any MeshPacket from a local mesh channel with uplink_enabled. The packet (encapsulated in a ServiceEnvelope) will remain encrypted with the key for the specified channel. For any channels in the gateway node with downlink_enabled, the gateway node will forward packets from MQTT to the local mesh. It will do this by subscribing to mesh/crypt/CHANNELID/# and forwarding relevant packets. -If the channelid 'well known'/public it could be decrypted by a web service (if the web service was provided with the associated channel key), in which case it will be decrypted by a web service and appear at "mesh/clear/CHANNELID/NODEID/PORTID". Note: This is not in the initial deliverable. +* If the channelid 'well known'/public it could be decrypted by a web service (if the web service was provided with the associated channel key), in which case it will be decrypted by a web service and appear at "mesh/clear/CHANNELID/NODEID/PORTID". Note: This is not in the initial deliverable. FIXME, consider how text message global mirroring could scale (or not) FIXME, possibly don't global mirror text messages - instead rely on matrix/riot? FIXME, consider possible attacks by griefers and how they can be prvented -* The "/mstat/NODEID" topic contains a simple string showing connection status of nodes. We rely on the MQTT feature for automatically publishing special failrue messages to this topic when the device disconnects. +* The "/mesh/stat/NODEID" topic contains a simple string showing connection status of nodes. We rely on the MQTT feature for automatically publishing special failrue messages to this topic when the device disconnects. #### Service Envelope diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp index bb7fcc54e..6a731f7ea 100644 --- a/src/mqtt/MQTT.cpp +++ b/src/mqtt/MQTT.cpp @@ -1,12 +1,13 @@ #include "MQTT.h" +#include "MQTTPlugin.h" #include "NodeDB.h" +#include "mesh/generated/mqtt.pb.h" #include #include MQTT *mqtt; -String statusTopic = "mstat/"; -String packetTopic = "mesh/"; +String statusTopic = "mesh/stat/"; void mqttCallback(char *topic, byte *payload, unsigned int length) { @@ -23,8 +24,10 @@ void mqttInit() DEBUG_MSG("MQTT disabled...\n"); else if (!WiFi.isConnected()) DEBUG_MSG("WiFi is not connected, can not start MQTT\n"); - else + else { new MQTT(); + new MQTTPlugin(); + } } MQTT::MQTT() : pubSub(mqttClient) @@ -59,18 +62,35 @@ MQTT::MQTT() : pubSub(mqttClient) } } -void MQTT::publish(const MeshPacket *mp) +void MQTT::publish(const MeshPacket &mp) { - // DEBUG_MSG("publish %s = %s\n", suffix.c_str(), payload.c_str()); + // don't bother sending if not connected... + if (pubSub.connected()) { + // FIXME - check uplink enabled - // pubSub.publish(getTopic(suffix), payload.c_str(), retained); + const char *channelId = "fixmechan"; + + ServiceEnvelope env = ServiceEnvelope_init_default; + env.channel_id = (char *)channelId; + env.gateway_id = owner.id; + env.packet = (MeshPacket *)∓ + + // FIXME - this size calculation is super sloppy, but it will go away once we dynamically alloc meshpackets + static uint8_t bytes[MeshPacket_size + 64]; + size_t numBytes = pb_encode_to_bytes(bytes, sizeof(bytes), ServiceEnvelope_fields, &env); + + const char *topic = getCryptTopic(channelId); + DEBUG_MSG("publish %s, %u bytes\n", topic, numBytes); + + pubSub.publish(topic, bytes, numBytes, false); + } } -const char *MQTT::getTopic(String suffix, const char *direction) +const char *MQTT::getCryptTopic(const char *channelId) { static char buf[128]; // "mesh/crypt/CHANNELID/NODEID/PORTID" - snprintf(buf, sizeof(buf), "mesh/%s/%s/%s", direction, owner.id, suffix.c_str()); + snprintf(buf, sizeof(buf), "mesh/crypt/%s/%s", channelId, owner.id); return buf; } \ No newline at end of file diff --git a/src/mqtt/MQTT.h b/src/mqtt/MQTT.h index c4d9a8b04..39652a11d 100644 --- a/src/mqtt/MQTT.h +++ b/src/mqtt/MQTT.h @@ -23,10 +23,10 @@ class MQTT /** * Publish a packet on the glboal MQTT server. */ - void publish(const MeshPacket *mp); + void publish(const MeshPacket &mp); private: - const char *getTopic(String suffix, const char *direction = "dev"); + const char *getCryptTopic(const char *channelId); }; void mqttInit(); diff --git a/src/mqtt/MQTTPlugin.cpp b/src/mqtt/MQTTPlugin.cpp new file mode 100644 index 000000000..6cd4928f1 --- /dev/null +++ b/src/mqtt/MQTTPlugin.cpp @@ -0,0 +1,18 @@ +#include "MQTTPlugin.h" +#include "MQTT.h" +#include "MeshService.h" +#include "NodeDB.h" +#include "Router.h" +#include "configuration.h" +#include "main.h" + +MQTTPlugin::MQTTPlugin() : MeshPlugin("mqtt") +{ + isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others +} + +bool MQTTPlugin::handleReceived(const MeshPacket &mp) +{ + mqtt->publish(mp); + return false; // never claim handled +} \ No newline at end of file diff --git a/src/mqtt/MQTTPlugin.h b/src/mqtt/MQTTPlugin.h new file mode 100644 index 000000000..786a68c0e --- /dev/null +++ b/src/mqtt/MQTTPlugin.h @@ -0,0 +1,17 @@ +#pragma once +#include "MeshPlugin.h" + +/** + * NodeInfo plugin for sending/receiving NodeInfos into the mesh + */ +class MQTTPlugin : public MeshPlugin +{ + public: + MQTTPlugin(); + + protected: + /** We sniff all packets */ + virtual bool handleReceived(const MeshPacket &mp); + + virtual bool wantPacket(const MeshPacket *p) { return true; } +};