From 1e5d0b25adcedc11029dfb9de383198a0152017a Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Fri, 11 Dec 2020 18:29:32 +0800 Subject: [PATCH 01/12] Add doc note about threading and use OSThread to make GPIO watching work Thanks to @mc-hamster for the idea --- docs/software/TODO.md | 2 +- docs/software/plugin-api.md | 5 ++ src/plugins/RemoteHardwarePlugin.cpp | 88 ++++++++++++++++++++++++---- src/plugins/RemoteHardwarePlugin.h | 23 +++++++- 4 files changed, 104 insertions(+), 14 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 2940a5fb6..46f943c07 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -10,7 +10,7 @@ For app cleanup: * DONE write devapi user guide * DONE update android code: https://developer.android.com/topic/libraries/view-binding/migration * only do wantReplies once per packet type, if we change network settings force it again -* make gpio watch work, use thread and setup +* test GPIO watch * DONE make hello world example service * make python ping command * DONE have python tool check max packet size before sending to device diff --git a/docs/software/plugin-api.md b/docs/software/plugin-api.md index ba544928d..e0699a6bc 100644 --- a/docs/software/plugin-api.md +++ b/docs/software/plugin-api.md @@ -49,6 +49,11 @@ The easiest way to get started is: * Rebuild with your new messaging goodness and install on the device * Use the [meshtastic commandline tool](https://github.com/meshtastic/Meshtastic-python) to send a packet to your board "meshtastic --dest 1234 --ping" +## Threading + +It is very common that you would like your plugin to be invoked periodically. +We use a crude/basic cooperative threading system to allow this on any of our supported platforms. Simply inherit from OSThread and implement runOnce(). See the OSThread [documentation](/src/concurrency/OSThread.h) for more details. For an example consumer of this API see RemoteHardwarePlugin::runOnce. + ## Picking a port number For any new 'apps' that run on the device or via sister apps on phones/PCs they should pick and use a unique 'portnum' for their application. diff --git a/src/plugins/RemoteHardwarePlugin.cpp b/src/plugins/RemoteHardwarePlugin.cpp index 11aaad234..dee93363f 100644 --- a/src/plugins/RemoteHardwarePlugin.cpp +++ b/src/plugins/RemoteHardwarePlugin.cpp @@ -10,6 +10,44 @@ RemoteHardwarePlugin remoteHardwarePlugin; #define NUM_GPIOS 64 +// Because (FIXME) we currently don't tell API clients status on sent messages +// we need to throttle our sending, so that if a gpio is bouncing up and down we +// don't generate more messages than the net can send. So we limit watch messages to +// a max of one change per 30 seconds +#define WATCH_INTERVAL_MSEC (30 * 1000) + +/// Set pin modes for every set bit in a mask +static void pinModes(uint64_t mask, uint8_t mode) { + for (uint8_t i = 0; i < NUM_GPIOS; i++) { + if (mask & (1 << i)) { + pinMode(i, mode); + } + } +} + +/// Read all the pins mentioned in a mask +static uint64_t digitalReads(uint64_t mask) { + uint64_t res = 0; + + pinModes(mask, INPUT_PULLUP); + + for (uint8_t i = 0; i < NUM_GPIOS; i++) { + uint64_t m = 1 << i; + if (mask & m) { + if (digitalRead(i)) + res |= m; + } + } + + return res; +} + + +RemoteHardwarePlugin::RemoteHardwarePlugin() + : ProtobufPlugin("remotehardware", PortNum_REMOTE_HARDWARE_APP, HardwareMessage_fields), + concurrency::OSThread("remotehardware") +{ +} bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const HardwareMessage &p) { @@ -22,24 +60,17 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H uint64_t mask = 1 << i; if (p.gpio_mask & mask) { digitalWrite(i, (p.gpio_value & mask) ? 1 : 0); - pinMode(i, OUTPUT); } } + pinModes(p.gpio_mask, OUTPUT); + break; - + case HardwareMessage_Type_READ_GPIOS: { // Print notification to LCD screen screen->print("Read GPIOs\n"); - uint64_t res = 0; - for (uint8_t i = 0; i < NUM_GPIOS; i++) { - uint64_t mask = 1 << i; - if (p.gpio_mask & mask) { - pinMode(i, INPUT_PULLUP); - if (digitalRead(i)) - res |= (1 << i); - } - } + uint64_t res = digitalReads(p.gpio_mask); // Send the reply HardwareMessage reply = HardwareMessage_init_default; @@ -51,6 +82,14 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H break; } + case HardwareMessage_Type_WATCH_GPIOS: { + watchGpios = p.gpio_mask; + lastWatchMsec = 0; // Force a new publish soon + previousWatch = ~watchGpios; // generate a 'previous' value which is guaranteed to not match (to force an initial publish) + enabled = true; // Let our thread run at least once + break; + } + case HardwareMessage_Type_READ_GPIOS_REPLY: case HardwareMessage_Type_GPIOS_CHANGED: break; // Ignore - we might see our own replies @@ -61,3 +100,30 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H } return true; // handled } + +int32_t RemoteHardwarePlugin::runOnce() { + if(watchGpios) { + uint32_t now = millis(); + + if(now - lastWatchMsec >= WATCH_INTERVAL_MSEC) { + uint64_t curVal = digitalReads(watchGpios); + + if(curVal != previousWatch) { + previousWatch = curVal; + + // Something changed! Tell the world with a broadcast message + HardwareMessage reply = HardwareMessage_init_default; + reply.typ = HardwareMessage_Type_GPIOS_CHANGED; + reply.gpio_value = curVal; + MeshPacket *p = allocDataProtobuf(reply); + service.sendToMesh(p); + } + } + } + else { + // No longer watching anything - stop using CPU + enabled = false; + } + + return 200; // Poll our GPIOs every 200ms (FIXME, make adjustable via protobuf arg) +} \ No newline at end of file diff --git a/src/plugins/RemoteHardwarePlugin.h b/src/plugins/RemoteHardwarePlugin.h index faae4894d..fe9bf960c 100644 --- a/src/plugins/RemoteHardwarePlugin.h +++ b/src/plugins/RemoteHardwarePlugin.h @@ -1,17 +1,26 @@ #pragma once #include "ProtobufPlugin.h" #include "remote_hardware.pb.h" +#include "concurrency/OSThread.h" /** * A plugin that provides easy low-level remote access to device hardware. */ -class RemoteHardwarePlugin : public ProtobufPlugin +class RemoteHardwarePlugin : public ProtobufPlugin, private concurrency::OSThread { + /// The current set of GPIOs we've been asked to watch for changes + uint64_t watchGpios = 0; + + /// The previously read value of watched pins + uint64_t previousWatch = 0; + + /// The timestamp of our last watch event (we throttle watches to 1 change every 30 seconds) + uint32_t lastWatchMsec = 0; public: /** Constructor * name is for debugging output */ - RemoteHardwarePlugin() : ProtobufPlugin("remotehardware", PortNum_REMOTE_HARDWARE_APP, HardwareMessage_fields) {} + RemoteHardwarePlugin(); protected: /** Called to handle a particular incoming message @@ -19,6 +28,16 @@ class RemoteHardwarePlugin : public ProtobufPlugin @return true if you've guaranteed you've handled this message and no other handlers should be considered for it */ virtual bool handleReceivedProtobuf(const MeshPacket &mp, const HardwareMessage &p); + + /** + * Periodically read the gpios we have been asked to WATCH, if they have changed, + * broadcast a message with the change information. + * + * The method that will be called each time our thread gets a chance to run + * + * Returns desired period for next invocation (or RUN_SAME for no change) + */ + virtual int32_t runOnce(); }; extern RemoteHardwarePlugin remoteHardwarePlugin; \ No newline at end of file From 138cebbf03fed993d0a1c48acf8d70dd0f266e59 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 13 Dec 2020 11:53:32 +0800 Subject: [PATCH 02/12] turn nrf52 ble back on --- src/nrf52/main-nrf52.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nrf52/main-nrf52.cpp b/src/nrf52/main-nrf52.cpp index 8b7bb65b9..a1a96f3b9 100644 --- a/src/nrf52/main-nrf52.cpp +++ b/src/nrf52/main-nrf52.cpp @@ -51,7 +51,7 @@ void getMacAddr(uint8_t *dmac) NRF52Bluetooth *nrf52Bluetooth; static bool bleOn = false; -static const bool enableBle = false; // Set to false for easier debugging +static const bool enableBle = true; // Set to false for easier debugging void setBluetoothEnable(bool on) { From ad8bcba5ef3347141f63d6d1d78eb75e9cdc1e17 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 13 Dec 2020 11:54:00 +0800 Subject: [PATCH 03/12] remove hack for the othernet ppr1 rev 1 board --- variants/ppr1/variant.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/ppr1/variant.h b/variants/ppr1/variant.h index e6bfd8a03..04a6e3c06 100644 --- a/variants/ppr1/variant.h +++ b/variants/ppr1/variant.h @@ -112,7 +112,7 @@ static const uint8_t AREF = PIN_AREF; // #define PIN_GPS_WAKE 20 // CELL_CTRL in schematic? based on their example code #define PIN_GPS_EN 7 // GPS_EN active high -#define PIN_VUSB_EN 21 +// #define PIN_VUSB_EN 21 // LCD From ee8f4de5abf33af34ae05d92c425d791392c4cae Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 13 Dec 2020 12:57:37 +0800 Subject: [PATCH 04/12] make plugin reply handling simpler --- docs/software/TODO.md | 7 ++++--- src/mesh/MeshPlugin.cpp | 9 +++++++-- src/mesh/MeshPlugin.h | 9 +++++++++ src/plugins/ReplyPlugin.cpp | 12 ++++++------ src/plugins/ReplyPlugin.h | 7 +++---- 5 files changed, 29 insertions(+), 15 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 46f943c07..50796ef1b 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -11,8 +11,9 @@ For app cleanup: * DONE update android code: https://developer.android.com/topic/libraries/view-binding/migration * only do wantReplies once per packet type, if we change network settings force it again * test GPIO watch -* DONE make hello world example service +* writeup docs on gpio * make python ping command +* DONE make hello world example service * DONE have python tool check max packet size before sending to device * DONE if request was sent reliably, send reply reliably * DONE require a recent python api to talk to these new device loads @@ -20,8 +21,8 @@ For app cleanup: * DONE fix handleIncomingPosition * DONE move want_replies handling into plugins * DONE on android for received positions handle either old or new positions / user messages -* on android side send old or new positions as needed / user messages -* test python side handle new position/user messages +* DONE on android side send old or new positions as needed / user messages +* DONE test python side handle new position/user messages * DONE make a gpio example. --gpiowrb 4 1, --gpiord 0x444, --gpiowatch 0x3ff * DONE fix position sending to use new plugin * DONE Add SinglePortNumPlugin - as the new most useful baseclass diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp index 5320b7ae1..e2a6bf289 100644 --- a/src/mesh/MeshPlugin.cpp +++ b/src/mesh/MeshPlugin.cpp @@ -5,6 +5,8 @@ std::vector *MeshPlugin::plugins; +const MeshPacket *MeshPlugin::currentRequest; + MeshPlugin::MeshPlugin(const char *_name) : name(_name) { // Can't trust static initalizer order, so we check each time @@ -27,11 +29,13 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) // DEBUG_MSG("In call plugins\n"); for (auto i = plugins->begin(); i != plugins->end(); ++i) { auto &pi = **i; + + pi.currentRequest = ∓ if (pi.wantPortnum(mp.decoded.data.portnum)) { bool handled = pi.handleReceived(mp); - // Possibly send replies (unless we are handling a locally generated message) - if (mp.decoded.want_response && mp.from != nodeDB.getNodeNum()) + // Possibly send replies + if (mp.decoded.want_response) pi.sendResponse(mp); DEBUG_MSG("Plugin %s handled=%d\n", pi.name, handled); @@ -41,6 +45,7 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) else { DEBUG_MSG("Plugin %s not interested\n", pi.name); } + pi.currentRequest = NULL; } } diff --git a/src/mesh/MeshPlugin.h b/src/mesh/MeshPlugin.h index f753651ec..01af58114 100644 --- a/src/mesh/MeshPlugin.h +++ b/src/mesh/MeshPlugin.h @@ -31,6 +31,15 @@ class MeshPlugin protected: const char *name; + /** + * If this plugin is currently handling a request currentRequest will be preset + * to the packet with the request. This is mostly useful for reply handlers. + * + * Note: this can be static because we are guaranteed to be processing only one + * plugin at a time. + */ + static const MeshPacket *currentRequest; + /** * Initialize your plugin. This setup function is called once after all hardware and mesh protocol layers have * been initialized diff --git a/src/plugins/ReplyPlugin.cpp b/src/plugins/ReplyPlugin.cpp index d5c9e727d..788edc55e 100644 --- a/src/plugins/ReplyPlugin.cpp +++ b/src/plugins/ReplyPlugin.cpp @@ -1,6 +1,6 @@ -#include "configuration.h" #include "ReplyPlugin.h" #include "MeshService.h" +#include "configuration.h" #include "main.h" #include @@ -8,8 +8,10 @@ // Create an a static instance of our plugin - this registers with the plugin system ReplyPlugin replyPlugin; -bool ReplyPlugin::handleReceived(const MeshPacket &req) +MeshPacket *ReplyPlugin::allocReply() { + assert(currentRequest); // should always be !NULL + auto req = *currentRequest; auto &p = req.decoded.data; // The incoming message is in p.payload DEBUG_MSG("Received message from=0x%0x, id=%d, msg=%.*s\n", req.from, req.id, p.payload.size, p.payload.bytes); @@ -17,11 +19,9 @@ bool ReplyPlugin::handleReceived(const MeshPacket &req) screen->print("Sending reply\n"); const char *replyStr = "Message Received"; - auto reply = allocDataPacket(); // Allocate a packet for sending + auto reply = allocDataPacket(); // Allocate a packet for sending reply->decoded.data.payload.size = strlen(replyStr); // You must specify how many bytes are in the reply memcpy(reply->decoded.data.payload.bytes, replyStr, reply->decoded.data.payload.size); - setReplyTo(reply, req); // Set packet params so that this packet is marked as a reply to a previous request - service.sendToMesh(reply); // Queue the reply for sending - return true; // We handled it + return reply; } diff --git a/src/plugins/ReplyPlugin.h b/src/plugins/ReplyPlugin.h index 0d3686b57..7d0ff8b71 100644 --- a/src/plugins/ReplyPlugin.h +++ b/src/plugins/ReplyPlugin.h @@ -15,9 +15,8 @@ class ReplyPlugin : public SinglePortPlugin protected: - /** Called to handle a particular incoming message - - @return true if you've guaranteed you've handled this message and no other handlers should be considered for it + /** For reply plugin we do all of our processing in the (normally optional) + * want_replies handling */ - virtual bool handleReceived(const MeshPacket &mp); + virtual MeshPacket *allocReply(); }; From e80c79edbe97222966a14c2c3cb37004f9803777 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 13 Dec 2020 15:59:26 +0800 Subject: [PATCH 05/12] clean up debug msgs --- docs/software/TODO.md | 4 ++-- src/mesh/DSRRouter.cpp | 18 +++++++++--------- src/mesh/FloodingRouter.cpp | 3 +-- src/mesh/MeshPlugin.cpp | 10 +++++++--- src/mesh/PhoneAPI.cpp | 9 ++++++--- src/mesh/ProtobufPlugin.h | 4 ++-- src/mesh/RadioInterface.cpp | 2 +- src/mesh/ReliableRouter.cpp | 2 +- src/plugins/RemoteHardwarePlugin.cpp | 5 +++++ 9 files changed, 34 insertions(+), 23 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 50796ef1b..ac5c2d337 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -9,10 +9,9 @@ For app cleanup: * DONE check build guide * DONE write devapi user guide * DONE update android code: https://developer.android.com/topic/libraries/view-binding/migration -* only do wantReplies once per packet type, if we change network settings force it again * test GPIO watch * writeup docs on gpio -* make python ping command +* DONE make python ping command * DONE make hello world example service * DONE have python tool check max packet size before sending to device * DONE if request was sent reliably, send reply reliably @@ -32,6 +31,7 @@ For app cleanup: * DONE test that position, text messages and user info work properly with new android app and old device code * do UDP tunnel * fix the RTC drift bug +* only do wantReplies once per packet type, if we change network settings force it again * move python ping functionality into device, reply with rxsnr info * use channels for gpio security https://github.com/meshtastic/Meshtastic-device/issues/104 * generate autodocs diff --git a/src/mesh/DSRRouter.cpp b/src/mesh/DSRRouter.cpp index f1ec32e32..d8ca542d7 100644 --- a/src/mesh/DSRRouter.cpp +++ b/src/mesh/DSRRouter.cpp @@ -170,7 +170,7 @@ bool DSRRouter::weAreInRoute(const RouteDiscovery &route) **/ void DSRRouter::updateRoutes(const RouteDiscovery &route, bool isRequest) { - DEBUG_MSG("FIXME not implemented"); + DEBUG_MSG("FIXME not implemented updateRoutes\n"); } /** @@ -178,7 +178,7 @@ void DSRRouter::updateRoutes(const RouteDiscovery &route, bool isRequest) */ void DSRRouter::sendRouteReply(const RouteDiscovery &route, NodeNum toAppend) { - DEBUG_MSG("FIXME not implemented"); + DEBUG_MSG("FIXME not implemented sendRoute\n"); } /** @@ -188,7 +188,7 @@ void DSRRouter::sendRouteReply(const RouteDiscovery &route, NodeNum toAppend) */ NodeNum DSRRouter::getNextHop(NodeNum dest) { - DEBUG_MSG("FIXME not implemented"); + DEBUG_MSG("FIXME not implemented getNextHop\n"); return 0; } @@ -198,7 +198,7 @@ NodeNum DSRRouter::getNextHop(NodeNum dest) */ void DSRRouter::resendRouteRequest(const MeshPacket *p) { - DEBUG_MSG("FIXME not implemented"); + DEBUG_MSG("FIXME not implemented resendRoute\n"); } /** @@ -208,7 +208,7 @@ void DSRRouter::resendRouteRequest(const MeshPacket *p) */ void DSRRouter::addRoute(NodeNum dest, NodeNum forwarder, uint8_t numHops) { - DEBUG_MSG("FIXME not implemented"); + DEBUG_MSG("FIXME not implemented addRoute\n"); } /** @@ -216,7 +216,7 @@ void DSRRouter::addRoute(NodeNum dest, NodeNum forwarder, uint8_t numHops) */ void DSRRouter::removeRoute(NodeNum dest) { - DEBUG_MSG("FIXME not implemented"); + DEBUG_MSG("FIXME not implemented removeRoute\n"); } /** @@ -224,7 +224,7 @@ void DSRRouter::removeRoute(NodeNum dest) */ void DSRRouter::sendNextHop(NodeNum n, const MeshPacket *p) { - DEBUG_MSG("FIXME not implemented"); + DEBUG_MSG("FIXME not implemented sendNextHop\n"); } /** @@ -232,7 +232,7 @@ void DSRRouter::sendNextHop(NodeNum n, const MeshPacket *p) */ void DSRRouter::sendRouteError(const MeshPacket *p, RouteError err) { - DEBUG_MSG("FIXME not implemented"); + DEBUG_MSG("FIXME not implemented sendRouteError\n"); } /** make a copy of p, start discovery, but only if we don't @@ -241,5 +241,5 @@ void DSRRouter::sendRouteError(const MeshPacket *p, RouteError err) */ void DSRRouter::startDiscovery(NodeNum dest) { - DEBUG_MSG("FIXME not implemented"); + DEBUG_MSG("FIXME not implemented startDiscovery\n"); } \ No newline at end of file diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index ffd652bf8..db3355582 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -38,8 +38,7 @@ void FloodingRouter::sniffReceived(const MeshPacket *p) tosend->hop_limit--; // bump down the hop count - DEBUG_MSG("Rebroadcasting received floodmsg to neighbors, fr=0x%x,to=0x%x,id=%d,hop_limit=%d\n", p->from, p->to, - p->id, tosend->hop_limit); + printPacket("Rebroadcasting received floodmsg to neighbors", p); // Note: we are careful to resend using the original senders node id // We are careful not to call our hooked version of send() - because we don't want to check this again Router::send(tosend); diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp index e2a6bf289..ebaee49a6 100644 --- a/src/mesh/MeshPlugin.cpp +++ b/src/mesh/MeshPlugin.cpp @@ -27,11 +27,14 @@ MeshPlugin::~MeshPlugin() void MeshPlugin::callPlugins(const MeshPacket &mp) { // DEBUG_MSG("In call plugins\n"); + bool pluginFound = false; for (auto i = plugins->begin(); i != plugins->end(); ++i) { auto &pi = **i; pi.currentRequest = ∓ if (pi.wantPortnum(mp.decoded.data.portnum)) { + pluginFound = true; + bool handled = pi.handleReceived(mp); // Possibly send replies @@ -42,11 +45,12 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) if (handled) break; } - else { - DEBUG_MSG("Plugin %s not interested\n", pi.name); - } + pi.currentRequest = NULL; } + + if(!pluginFound) + DEBUG_MSG("No plugins interested in portnum=%d\n", mp.decoded.data.portnum); } /** Messages can be received that have the want_response bit set. If set, this callback will be invoked diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index c3ae6930f..999526de2 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -96,9 +96,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) if (!available()) { // DEBUG_MSG("getFromRadio, !available\n"); return false; - } else { - DEBUG_MSG("getFromRadio, state=%d\n", state); - } + } + + DEBUG_MSG("getFromRadio, state=%d\n", state); // In case we send a FromRadio packet memset(&fromRadioScratch, 0, sizeof(fromRadioScratch)); @@ -162,6 +162,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) case STATE_SEND_PACKETS: // Do we have a message from the mesh? if (packetForPhone) { + + printPacket("phone downloaded packet", packetForPhone); + // Encapsulate as a FromRadio packet fromRadioScratch.which_variant = FromRadio_packet_tag; fromRadioScratch.variant.packet = *packetForPhone; diff --git a/src/mesh/ProtobufPlugin.h b/src/mesh/ProtobufPlugin.h index cda4ed8bf..a5c0aea15 100644 --- a/src/mesh/ProtobufPlugin.h +++ b/src/mesh/ProtobufPlugin.h @@ -55,11 +55,11 @@ template class ProtobufPlugin : private SinglePortPlugin // it would be better to update even if the message was destined to others. auto &p = mp.decoded.data; - DEBUG_MSG("Received %s from=0x%0x, id=%d, payloadlen=%d\n", name, mp.from, mp.id, p.payload.size); + DEBUG_MSG("Received %s from=0x%0x, id=0x%x, payloadlen=%d\n", name, mp.from, mp.id, p.payload.size); T scratch; if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch)) - handleReceivedProtobuf(mp, scratch); + return handleReceivedProtobuf(mp, scratch); return false; // Let others look at this message also if they want } diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index a1bbc5c4d..f52fd7c2d 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -123,7 +123,7 @@ void printPacket(const char *prefix, const MeshPacket *p) auto &s = p->decoded; switch (s.which_payload) { case SubPacket_data_tag: - DEBUG_MSG(" Payload:Data"); + DEBUG_MSG(" Portnum=%d", s.data.portnum); break; case SubPacket_position_tag: DEBUG_MSG(" Payload:Position"); diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index bbbb52386..93402c127 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -92,7 +92,7 @@ void ReliableRouter::sendAckNak(bool isAck, NodeNum to, PacketId idFrom) auto p = allocForSending(); p->hop_limit = 0; // Assume just immediate neighbors for now p->to = to; - DEBUG_MSG("Sending an ack=0x%x,to=0x%x,idFrom=%d,id=%d\n", isAck, to, idFrom, p->id); + DEBUG_MSG("Sending an ack=0x%x,to=0x%x,idFrom=0x%x,id=0x%x\n", isAck, to, idFrom, p->id); if (isAck) { p->decoded.ack.success_id = idFrom; diff --git a/src/plugins/RemoteHardwarePlugin.cpp b/src/plugins/RemoteHardwarePlugin.cpp index dee93363f..e9f25e51a 100644 --- a/src/plugins/RemoteHardwarePlugin.cpp +++ b/src/plugins/RemoteHardwarePlugin.cpp @@ -51,6 +51,8 @@ RemoteHardwarePlugin::RemoteHardwarePlugin() bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const HardwareMessage &p) { + DEBUG_MSG("Received RemoteHardware typ=%d\n", p.typ); + switch (p.typ) { case HardwareMessage_Type_WRITE_GPIOS: // Print notification to LCD screen @@ -87,6 +89,7 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H lastWatchMsec = 0; // Force a new publish soon previousWatch = ~watchGpios; // generate a 'previous' value which is guaranteed to not match (to force an initial publish) enabled = true; // Let our thread run at least once + DEBUG_MSG("Now watching GPIOs 0x%x\n", watchGpios); break; } @@ -98,6 +101,7 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H DEBUG_MSG("Hardware operation %d not yet implemented! FIXME\n", p.typ); break; } + return true; // handled } @@ -110,6 +114,7 @@ int32_t RemoteHardwarePlugin::runOnce() { if(curVal != previousWatch) { previousWatch = curVal; + DEBUG_MSG("Broadcasting GPIOS 0x%x changed!\n", curVal); // Something changed! Tell the world with a broadcast message HardwareMessage reply = HardwareMessage_init_default; From 0cdc1fc959ebcbda8b08b0534dc118e34e284329 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 13 Dec 2020 16:11:38 +0800 Subject: [PATCH 06/12] make gpiowatch work correctly --- src/mesh/Router.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 9ea3ccebc..458a338fe 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -93,19 +93,28 @@ MeshPacket *Router::allocForSending() p->to = NODENUM_BROADCAST; p->hop_limit = HOP_RELIABLE; p->id = generatePacketId(); - p->rx_time = getValidTime(RTCQualityFromNet); // Just in case we process the packet locally - make sure it has a valid timestamp + p->rx_time = + getValidTime(RTCQualityFromNet); // Just in case we process the packet locally - make sure it has a valid timestamp return p; } ErrorCode Router::sendLocal(MeshPacket *p) { + // No need to deliver externally if the destination is the local node if (p->to == nodeDB.getNodeNum()) { - DEBUG_MSG("Enqueuing internal message for the receive queue\n"); + printPacket("Enqueuing local", p); fromRadioQueue.enqueue(p); return ERRNO_OK; - } else - return send(p); + } + + // If we are sending a broadcast, we also treat it as if we just received it ourself + // this allows local apps (and PCs) to see broadcasts sourced locally + if (p->to == NODENUM_BROADCAST) { + handleReceived(p); + } + + return send(p); } /** From 5cdc2f5142ba4c9cc87f07dc2fb5eb231931e6c8 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 14 Dec 2020 20:50:01 +0800 Subject: [PATCH 07/12] Make ChannelSettings SUPER short for common channels --- proto | 2 +- src/mesh/NodeDB.cpp | 55 ++++++++++++++++++++++++++++++++----- src/mesh/NodeDB.h | 1 + src/mesh/RadioInterface.cpp | 4 +-- 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/proto b/proto index d0868e366..b1e1a5433 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit d0868e366bc8f8d9b7ed1d1c5a80cac0de9dc956 +Subproject commit b1e1a54330d1a7f74f097dbf27cdba9cfb739473 diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index f6ef03f5d..bc38ad563 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -57,6 +57,13 @@ static uint8_t ourMacAddr[6]; */ NodeNum displayedNodeNum; +/// A usable (but bigger) version of the channel name in the channelSettings object +const char *channelName; + +/// A usable psk - which has been constructed based on the (possibly short psk) in channelSettings +static uint8_t activePSK[32]; +static uint8_t activePSKSize; + /** * Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different PSKs. * The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why they @@ -77,10 +84,10 @@ const char *getChannelName() static char buf[32]; uint8_t code = 0; - for (int i = 0; i < channelSettings.psk.size; i++) - code ^= channelSettings.psk.bytes[i]; + for (int i = 0; i < activePSKSize; i++) + code ^= activePSK[i]; - snprintf(buf, sizeof(buf), "#%s-%c", channelSettings.name, 'A' + (code % 26)); + snprintf(buf, sizeof(buf), "#%s-%c", channelName, 'A' + (code % 26)); return buf; } @@ -110,13 +117,47 @@ bool NodeDB::resetRadioConfig() channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range channelSettings.tx_power = 0; // default - memcpy(&channelSettings.psk.bytes, defaultpsk, sizeof(channelSettings.psk)); - channelSettings.psk.size = sizeof(defaultpsk); - strcpy(channelSettings.name, "Default"); + uint8_t defaultpskIndex = 1; + channelSettings.psk.bytes[0] = defaultpskIndex; + channelSettings.psk.size = 1; + strcpy(channelSettings.name, ""); + } + + // Convert "Default" to our new short representation + if(strcmp(channelSettings.name, "Default") == 0) + *channelSettings.name = '\0'; + + // Convert the short "" representation for Default into a usable string + channelName = channelSettings.name; + if(!*channelName) // emptystring + channelName = "Default"; + + // Convert any old usage of the defaultpsk into our new short representation. + if(channelSettings.psk.size == sizeof(defaultpsk) && + memcmp(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk)) == 0) { + *channelSettings.psk.bytes = 1; + channelSettings.psk.size = 1; + } + + // Convert the short single byte variants of psk into variant that can be used more generally + memcpy(activePSK, channelSettings.psk.bytes, channelSettings.psk.size); + activePSKSize = channelSettings.psk.size; + if(activePSKSize == 1) { + uint8_t pskIndex = activePSK[0]; + DEBUG_MSG("Expanding short PSK #%d\n", pskIndex); + if(pskIndex == 0) + activePSKSize = 0; // Turn off encryption + else { + memcpy(activePSK, defaultpsk, sizeof(defaultpsk)); + activePSKSize = sizeof(defaultpsk); + // Bump up the last byte of PSK as needed + uint8_t *last = activePSK + sizeof(defaultpsk) - 1; + *last = *last + pskIndex - 1; // index of 1 means no change vs defaultPSK + } } // Tell our crypto engine about the psk - crypto->setKey(channelSettings.psk.size, channelSettings.psk.bytes); + crypto->setKey(activePSKSize, activePSK); // temp hack for quicker testing // devicestate.no_save = true; diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index d595662dd..65a276ae1 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -13,6 +13,7 @@ extern MyNodeInfo &myNodeInfo; extern RadioConfig &radioConfig; extern ChannelSettings &channelSettings; extern User &owner; +extern const char *channelName; /// Given a node, return how many seconds in the past (vs now) that we last heard from it uint32_t sinceLastSeen(const NodeInfo *n); diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index f52fd7c2d..b1aa807bc 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -262,10 +262,10 @@ void RadioInterface::applyModemConfig() // If user has manually specified a channel num, then use that, otherwise generate one by hashing the name int channel_num = - (channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelSettings.name)) % myRegion->numChannels; + (channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelName)) % myRegion->numChannels; freq = myRegion->freq + myRegion->spacing * channel_num; - DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelSettings.name, channelSettings.modem_config, channel_num, + DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelName, channelSettings.modem_config, channel_num, power); DEBUG_MSG("Radio myRegion->freq: %f\n", myRegion->freq); DEBUG_MSG("Radio myRegion->spacing: %f\n", myRegion->spacing); From c9f2318e78602cd4a8887af343201bc2725f08b6 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Tue, 15 Dec 2020 13:14:36 +0800 Subject: [PATCH 08/12] Use simpler names for standard channels --- docs/software/TODO.md | 3 ++- proto | 2 +- src/mesh/NodeDB.cpp | 44 +++++++++++++++++++++++++++++++++---------- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index ac5c2d337..e1133ee7c 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -9,7 +9,8 @@ For app cleanup: * DONE check build guide * DONE write devapi user guide * DONE update android code: https://developer.android.com/topic/libraries/view-binding/migration -* test GPIO watch +* DONE test GPIO watch +* set --set-chan-fast, --set-chan-default * writeup docs on gpio * DONE make python ping command * DONE make hello world example service diff --git a/proto b/proto index b1e1a5433..5f580041b 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit b1e1a54330d1a7f74f097dbf27cdba9cfb739473 +Subproject commit 5f580041beeb593d48eabacd2b959df04558c383 diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index bc38ad563..eb8c8cc3d 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -70,10 +70,11 @@ static uint8_t activePSKSize; their nodes * aren't talking to each other. * - * This string is of the form "#name-XY". + * This string is of the form "#name-X". * - * Where X is a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together. - * Y is not yet used but should eventually indicate 'speed/range' of the link + * Where X is either: + * (for custom PSKS) a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together, + * OR (for the standard minimially secure PSKs) a number from 0 to 9. * * This function will also need to be implemented in GUI apps that talk to the radio. * @@ -83,11 +84,19 @@ const char *getChannelName() { static char buf[32]; - uint8_t code = 0; - for (int i = 0; i < activePSKSize; i++) - code ^= activePSK[i]; + char suffix; + if(channelSettings.psk.size != 1) { + // We have a standard PSK, so generate a letter based hash. + uint8_t code = 0; + for (int i = 0; i < activePSKSize; i++) + code ^= activePSK[i]; - snprintf(buf, sizeof(buf), "#%s-%c", channelName, 'A' + (code % 26)); + suffix = 'A' + (code % 26); + } else { + suffix = '0' + channelSettings.psk.bytes[0]; + } + + snprintf(buf, sizeof(buf), "#%s-%c", channelName, suffix); return buf; } @@ -123,14 +132,29 @@ bool NodeDB::resetRadioConfig() strcpy(channelSettings.name, ""); } - // Convert "Default" to our new short representation + // Convert the old string "Default" to our new short representation if(strcmp(channelSettings.name, "Default") == 0) *channelSettings.name = '\0'; // Convert the short "" representation for Default into a usable string channelName = channelSettings.name; - if(!*channelName) // emptystring - channelName = "Default"; + if(!*channelName) { // emptystring + // Per mesh.proto spec, if bandwidth is specified we must ignore modemConfig enum, we assume that in that case + // the app fucked up and forgot to set channelSettings.name + channelName = "Unset"; + if(channelSettings.bandwidth == 0) switch(channelSettings.modem_config) { + case ChannelSettings_ModemConfig_Bw125Cr45Sf128: + channelName = "MediumRange"; break; + case ChannelSettings_ModemConfig_Bw500Cr45Sf128: + channelName = "Fast"; break; + case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512: + channelName = "LongAlt"; break; + case ChannelSettings_ModemConfig_Bw125Cr48Sf4096: + channelName = "LongSlow"; break; + default: + channelName = "Invalid"; break; + } + } // Convert any old usage of the defaultpsk into our new short representation. if(channelSettings.psk.size == sizeof(defaultpsk) && From be38a58a62aa883b9a99aa1ed88c992f36b6a524 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Tue, 15 Dec 2020 16:13:16 +0800 Subject: [PATCH 09/12] finish channel name cleanup --- docs/software/TODO.md | 2 +- proto | 2 +- src/mesh/NodeDB.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index e1133ee7c..086afc164 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -10,7 +10,7 @@ For app cleanup: * DONE write devapi user guide * DONE update android code: https://developer.android.com/topic/libraries/view-binding/migration * DONE test GPIO watch -* set --set-chan-fast, --set-chan-default +* DONE set --set-chan-fast, --set-chan-default * writeup docs on gpio * DONE make python ping command * DONE make hello world example service diff --git a/proto b/proto index 5f580041b..e0df97118 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 5f580041beeb593d48eabacd2b959df04558c383 +Subproject commit e0df97118b3dc8105c9c8dbd59e9bb8cd859b1bb diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index eb8c8cc3d..ed6720eaa 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -144,9 +144,9 @@ bool NodeDB::resetRadioConfig() channelName = "Unset"; if(channelSettings.bandwidth == 0) switch(channelSettings.modem_config) { case ChannelSettings_ModemConfig_Bw125Cr45Sf128: - channelName = "MediumRange"; break; + channelName = "Medium"; break; case ChannelSettings_ModemConfig_Bw500Cr45Sf128: - channelName = "Fast"; break; + channelName = "ShortFast"; break; case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512: channelName = "LongAlt"; break; case ChannelSettings_ModemConfig_Bw125Cr48Sf4096: From 5bdc7216b33fdea18da188f669b6d61012b3a249 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 17 Dec 2020 10:32:19 +0800 Subject: [PATCH 10/12] begin support for multiple simultanous channels --- proto | 2 +- src/mesh/mesh.pb.h | 28 ++++++++++++++++++---------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/proto b/proto index e0df97118..3d0bcc2af 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit e0df97118b3dc8105c9c8dbd59e9bb8cd859b1bb +Subproject commit 3d0bcc2afa6fb161bd2d0c7c2e76083c3946ed65 diff --git a/src/mesh/mesh.pb.h b/src/mesh/mesh.pb.h index 0a1c5eb64..163190c40 100644 --- a/src/mesh/mesh.pb.h +++ b/src/mesh/mesh.pb.h @@ -159,6 +159,8 @@ typedef struct _RadioConfig { RadioConfig_UserPreferences preferences; bool has_channel_settings; ChannelSettings channel_settings; + pb_size_t secondary_channels_count; + ChannelSettings secondary_channels[4]; } RadioConfig; typedef struct _SubPacket { @@ -191,6 +193,7 @@ typedef struct _MeshPacket { SubPacket decoded; MeshPacket_encrypted_t encrypted; }; + uint32_t channel_index; uint32_t id; float rx_snr; uint32_t rx_time; @@ -277,9 +280,9 @@ extern "C" { #define User_init_default {"", "", "", {0}} #define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, 0, {0}, 0} -#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0} +#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0, 0} #define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0} -#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default} +#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default, 0, {ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default}} #define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}} #define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0} #define MyNodeInfo_init_default {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0} @@ -292,9 +295,9 @@ extern "C" { #define User_init_zero {"", "", "", {0}} #define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}} #define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, 0, {0}, 0} -#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0} +#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0, 0} #define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0} -#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero} +#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero, 0, {ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero}} #define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}} #define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0} #define MyNodeInfo_init_zero {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0} @@ -371,6 +374,7 @@ extern "C" { #define NodeInfo_snr_tag 7 #define RadioConfig_preferences_tag 1 #define RadioConfig_channel_settings_tag 2 +#define RadioConfig_secondary_channels_tag 3 #define SubPacket_position_tag 1 #define SubPacket_data_tag 3 #define SubPacket_user_tag 4 @@ -387,6 +391,7 @@ extern "C" { #define MeshPacket_to_tag 2 #define MeshPacket_decoded_tag 3 #define MeshPacket_encrypted_tag 8 +#define MeshPacket_channel_index_tag 4 #define MeshPacket_id_tag 6 #define MeshPacket_rx_snr_tag 7 #define MeshPacket_rx_time_tag 9 @@ -469,6 +474,7 @@ X(a, STATIC, SINGULAR, UINT32, from, 1) \ X(a, STATIC, SINGULAR, UINT32, to, 2) \ X(a, STATIC, ONEOF, MESSAGE, (payload,decoded,decoded), 3) \ X(a, STATIC, ONEOF, BYTES, (payload,encrypted,encrypted), 8) \ +X(a, STATIC, SINGULAR, UINT32, channel_index, 4) \ X(a, STATIC, SINGULAR, UINT32, id, 6) \ X(a, STATIC, SINGULAR, FLOAT, rx_snr, 7) \ X(a, STATIC, SINGULAR, FIXED32, rx_time, 9) \ @@ -492,11 +498,13 @@ X(a, STATIC, SINGULAR, UINT32, channel_num, 9) #define RadioConfig_FIELDLIST(X, a) \ X(a, STATIC, OPTIONAL, MESSAGE, preferences, 1) \ -X(a, STATIC, OPTIONAL, MESSAGE, channel_settings, 2) +X(a, STATIC, OPTIONAL, MESSAGE, channel_settings, 2) \ +X(a, STATIC, REPEATED, MESSAGE, secondary_channels, 3) #define RadioConfig_CALLBACK NULL #define RadioConfig_DEFAULT NULL #define RadioConfig_preferences_MSGTYPE RadioConfig_UserPreferences #define RadioConfig_channel_settings_MSGTYPE ChannelSettings +#define RadioConfig_secondary_channels_MSGTYPE ChannelSettings #define RadioConfig_UserPreferences_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, position_broadcast_secs, 1) \ @@ -647,16 +655,16 @@ extern const pb_msgdesc_t ToRadio_msg; #define User_size 72 #define RouteDiscovery_size 88 #define SubPacket_size 275 -#define MeshPacket_size 314 +#define MeshPacket_size 320 #define ChannelSettings_size 84 -#define RadioConfig_size 314 +#define RadioConfig_size 658 #define RadioConfig_UserPreferences_size 225 #define NodeInfo_size 132 #define MyNodeInfo_size 110 -#define DeviceState_size 5468 +#define DeviceState_size 5824 #define DebugString_size 258 -#define FromRadio_size 323 -#define ToRadio_size 318 +#define FromRadio_size 667 +#define ToRadio_size 662 #ifdef __cplusplus } /* extern "C" */ From 15e1a3870c2cd0446a94d379b8a55e77fa223cfc Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 17 Dec 2020 10:53:29 +0800 Subject: [PATCH 11/12] When new node joins mesh, all other nodes reply with their current state --- docs/software/TODO.md | 4 ++-- proto | 2 +- src/PowerFSM.cpp | 2 +- src/mesh/FloodingRouter.cpp | 3 +-- src/mesh/MeshService.cpp | 21 ++++++++++++++++++--- src/mesh/NodeDB.cpp | 7 +++++++ src/mesh/NodeDB.h | 5 +++++ src/mesh/PhoneAPI.cpp | 21 +++++++++++++++------ src/mesh/mesh.pb.h | 34 +++++++++++++++++++--------------- 9 files changed, 69 insertions(+), 30 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 086afc164..c08ddabe0 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,6 +4,8 @@ You probably don't care about this section - skip to the next one. For app cleanup: +* DONE only do wantReplies once per packet type, if we change network settings force it again +* update positions and nodeinfos based on packets we just merely witness on the mesh. via isPromsciousPort bool, remove sniffing * DONE make device build always have a valid version * DONE do fixed position bug https://github.com/meshtastic/Meshtastic-device/issues/536 * DONE check build guide @@ -32,11 +34,9 @@ For app cleanup: * DONE test that position, text messages and user info work properly with new android app and old device code * do UDP tunnel * fix the RTC drift bug -* only do wantReplies once per packet type, if we change network settings force it again * move python ping functionality into device, reply with rxsnr info * use channels for gpio security https://github.com/meshtastic/Meshtastic-device/issues/104 * generate autodocs -* update positions and nodeinfos based on packets we just merely witness on the mesh. via isPromsciousPort bool. * MeshPackets for sending should be reference counted so that API clients would have the option of checking sent status (would allow removing the nasty 30 sec timer in gpio watch sending) For high speed/lots of devices/short range tasks: diff --git a/proto b/proto index 3d0bcc2af..75078afe4 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 3d0bcc2afa6fb161bd2d0c7c2e76083c3946ed65 +Subproject commit 75078afe43934f4ce15ef86ebc6950658a170145 diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 4b8b96368..7894361d4 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -144,7 +144,7 @@ static void onEnter() uint32_t now = millis(); - if (now - lastPingMs > 30 * 1000) { // if more than a minute since our last press, ask other nodes to update their state + if (now - lastPingMs > 30 * 1000) { // if more than a minute since our last press, ask node we are looking at to update their state if (displayedNodeNum) service.sendNetworkPing(displayedNodeNum, true); // Refresh the currently displayed node lastPingMs = now; diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index db3355582..f2b718835 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -20,8 +20,7 @@ ErrorCode FloodingRouter::send(MeshPacket *p) bool FloodingRouter::shouldFilterReceived(const MeshPacket *p) { if (wasSeenRecently(p)) { // Note: this will also add a recent packet record - DEBUG_MSG("Ignoring incoming msg, because we've already seen it: fr=0x%x,to=0x%x,id=%d,hop_limit=%d\n", p->from, p->to, - p->id, p->hop_limit); + printPacket("Ignoring incoming msg, because we've already seen it", p); return true; } diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 1c06406f6..5ca841ef5 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -53,7 +53,14 @@ MeshService service; static int32_t sendOwnerCb() { - nodeInfoPlugin.sendOurNodeInfo(); + static uint32_t currentGeneration; + + // If we changed channels, ask everyone else for their latest info + bool requestReplies = currentGeneration != radioGeneration; + currentGeneration = radioGeneration; + + DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies); + nodeInfoPlugin.sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies) return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000; } @@ -68,6 +75,7 @@ MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE) void MeshService::init() { sendOwnerPeriod = new concurrency::Periodic("SendOwner", sendOwnerCb); + sendOwnerPeriod->setIntervalFromNow(30 * 1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup) nodeDB.init(); @@ -230,8 +238,15 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) uint32_t now = millis(); if (lastGpsSend == 0 || now - lastGpsSend > getPref_position_broadcast_secs() * 1000) { lastGpsSend = now; - DEBUG_MSG("Sending position to mesh (not requesting replies)\n"); - positionPlugin.sendOurPosition(); + + static uint32_t currentGeneration; + + // If we changed channels, ask everyone else for their latest info + bool requestReplies = currentGeneration != radioGeneration; + currentGeneration = radioGeneration; + + DEBUG_MSG("Sending position to mesh (wantReplies=%d)\n", requestReplies); + positionPlugin.sendOurPosition(NODENUM_BROADCAST, requestReplies); } return 0; diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index ed6720eaa..a003f20a8 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -28,6 +28,11 @@ MyNodeInfo &myNodeInfo = devicestate.my_node; RadioConfig &radioConfig = devicestate.radio; ChannelSettings &channelSettings = radioConfig.channel_settings; +/** The current change # for radio settings. Starts at 0 on boot and any time the radio settings + * might have changed is incremented. Allows others to detect they might now be on a new channel. + */ +uint32_t radioGeneration; + /* DeviceState versions used to be defined in the .proto file but really only this function cares. So changed to a #define here. @@ -106,6 +111,8 @@ bool NodeDB::resetRadioConfig() { bool didFactoryReset = false; + radioGeneration++; + /// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128) static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59, 0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf}; diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 65a276ae1..e8c230ced 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -167,3 +167,8 @@ PREF_GET(ls_secs, 5 * 60) PREF_GET(phone_timeout_secs, 15 * 60) PREF_GET(min_wake_secs, 10) + +/** The current change # for radio settings. Starts at 0 on boot and any time the radio settings + * might have changed is incremented. Allows others to detect they might now be on a new channel. + */ +extern uint32_t radioGeneration; diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index 999526de2..e122c45ce 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -6,10 +6,16 @@ #include "RadioInterface.h" #include +#if FromRadio_size > MAX_TO_FROM_RADIO_SIZE + #error FromRadio is too big +#endif + +#if ToRadio_size > MAX_TO_FROM_RADIO_SIZE + #error ToRadio is too big +#endif + PhoneAPI::PhoneAPI() { - assert(FromRadio_size <= MAX_TO_FROM_RADIO_SIZE); - assert(ToRadio_size <= MAX_TO_FROM_RADIO_SIZE); } void PhoneAPI::init() @@ -94,8 +100,8 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) size_t PhoneAPI::getFromRadio(uint8_t *buf) { if (!available()) { - // DEBUG_MSG("getFromRadio, !available\n"); - return false; + DEBUG_MSG("getFromRadio, !available\n"); + return 0; } DEBUG_MSG("getFromRadio, state=%d\n", state); @@ -215,11 +221,14 @@ bool PhoneAPI::available() return true; case STATE_LEGACY: // Treat as the same as send packets - case STATE_SEND_PACKETS: + case STATE_SEND_PACKETS: { // Try to pull a new packet from the service (if we haven't already) if (!packetForPhone) packetForPhone = service.getForPhone(); - return !!packetForPhone; + bool hasPacket = !!packetForPhone; + DEBUG_MSG("available hasPacket=%d\n", hasPacket); + return hasPacket; + } default: assert(0); // unexpected state - FIXME, make an error code and reboot diff --git a/src/mesh/mesh.pb.h b/src/mesh/mesh.pb.h index 163190c40..bda4873b5 100644 --- a/src/mesh/mesh.pb.h +++ b/src/mesh/mesh.pb.h @@ -159,8 +159,6 @@ typedef struct _RadioConfig { RadioConfig_UserPreferences preferences; bool has_channel_settings; ChannelSettings channel_settings; - pb_size_t secondary_channels_count; - ChannelSettings secondary_channels[4]; } RadioConfig; typedef struct _SubPacket { @@ -217,6 +215,8 @@ typedef struct _DeviceState { uint32_t version; bool no_save; bool did_gps_reset; + pb_size_t secondary_channels_count; + ChannelSettings secondary_channels[4]; } DeviceState; typedef struct _FromRadio { @@ -230,6 +230,7 @@ typedef struct _FromRadio { DebugString debug_string; uint32_t config_complete_id; bool rebooted; + ChannelSettings secondary_channel; } variant; } FromRadio; @@ -282,11 +283,11 @@ extern "C" { #define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, 0, {0}, 0} #define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0, 0} #define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0} -#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default, 0, {ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default}} +#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default} #define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}} #define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0} #define MyNodeInfo_init_default {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0} -#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0} +#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0, 0, {ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default}} #define DebugString_init_default {""} #define FromRadio_init_default {0, 0, {MeshPacket_init_default}} #define ToRadio_init_default {0, {MeshPacket_init_default}} @@ -297,11 +298,11 @@ extern "C" { #define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, 0, {0}, 0} #define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0, 0} #define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0} -#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero, 0, {ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero}} +#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero} #define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}} #define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0} #define MyNodeInfo_init_zero {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0} -#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0} +#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0, 0, {ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero}} #define DebugString_init_zero {""} #define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}} #define ToRadio_init_zero {0, {MeshPacket_init_zero}} @@ -374,7 +375,6 @@ extern "C" { #define NodeInfo_snr_tag 7 #define RadioConfig_preferences_tag 1 #define RadioConfig_channel_settings_tag 2 -#define RadioConfig_secondary_channels_tag 3 #define SubPacket_position_tag 1 #define SubPacket_data_tag 3 #define SubPacket_user_tag 4 @@ -406,6 +406,7 @@ extern "C" { #define DeviceState_version_tag 8 #define DeviceState_no_save_tag 9 #define DeviceState_did_gps_reset_tag 11 +#define DeviceState_secondary_channels_tag 12 #define FromRadio_num_tag 1 #define FromRadio_packet_tag 2 #define FromRadio_my_info_tag 3 @@ -414,6 +415,7 @@ extern "C" { #define FromRadio_debug_string_tag 7 #define FromRadio_config_complete_id_tag 8 #define FromRadio_rebooted_tag 9 +#define FromRadio_secondary_channel_tag 10 #define ToRadio_packet_tag 1 #define ToRadio_want_config_id_tag 100 #define ToRadio_set_radio_tag 101 @@ -498,13 +500,11 @@ X(a, STATIC, SINGULAR, UINT32, channel_num, 9) #define RadioConfig_FIELDLIST(X, a) \ X(a, STATIC, OPTIONAL, MESSAGE, preferences, 1) \ -X(a, STATIC, OPTIONAL, MESSAGE, channel_settings, 2) \ -X(a, STATIC, REPEATED, MESSAGE, secondary_channels, 3) +X(a, STATIC, OPTIONAL, MESSAGE, channel_settings, 2) #define RadioConfig_CALLBACK NULL #define RadioConfig_DEFAULT NULL #define RadioConfig_preferences_MSGTYPE RadioConfig_UserPreferences #define RadioConfig_channel_settings_MSGTYPE ChannelSettings -#define RadioConfig_secondary_channels_MSGTYPE ChannelSettings #define RadioConfig_UserPreferences_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, position_broadcast_secs, 1) \ @@ -573,7 +573,8 @@ X(a, STATIC, REPEATED, MESSAGE, receive_queue, 5) \ X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) \ X(a, STATIC, SINGULAR, UINT32, version, 8) \ X(a, STATIC, SINGULAR, BOOL, no_save, 9) \ -X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11) +X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11) \ +X(a, STATIC, REPEATED, MESSAGE, secondary_channels, 12) #define DeviceState_CALLBACK NULL #define DeviceState_DEFAULT NULL #define DeviceState_radio_MSGTYPE RadioConfig @@ -582,6 +583,7 @@ X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11) #define DeviceState_node_db_MSGTYPE NodeInfo #define DeviceState_receive_queue_MSGTYPE MeshPacket #define DeviceState_rx_text_message_MSGTYPE MeshPacket +#define DeviceState_secondary_channels_MSGTYPE ChannelSettings #define DebugString_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, STRING, message, 1) @@ -596,7 +598,8 @@ X(a, STATIC, ONEOF, MESSAGE, (variant,node_info,variant.node_info), 4) \ X(a, STATIC, ONEOF, MESSAGE, (variant,radio,variant.radio), 6) \ X(a, STATIC, ONEOF, MESSAGE, (variant,debug_string,variant.debug_string), 7) \ X(a, STATIC, ONEOF, UINT32, (variant,config_complete_id,variant.config_complete_id), 8) \ -X(a, STATIC, ONEOF, BOOL, (variant,rebooted,variant.rebooted), 9) +X(a, STATIC, ONEOF, BOOL, (variant,rebooted,variant.rebooted), 9) \ +X(a, STATIC, ONEOF, MESSAGE, (variant,secondary_channel,variant.secondary_channel), 10) #define FromRadio_CALLBACK NULL #define FromRadio_DEFAULT NULL #define FromRadio_variant_packet_MSGTYPE MeshPacket @@ -604,6 +607,7 @@ X(a, STATIC, ONEOF, BOOL, (variant,rebooted,variant.rebooted), 9) #define FromRadio_variant_node_info_MSGTYPE NodeInfo #define FromRadio_variant_radio_MSGTYPE RadioConfig #define FromRadio_variant_debug_string_MSGTYPE DebugString +#define FromRadio_variant_secondary_channel_MSGTYPE ChannelSettings #define ToRadio_FIELDLIST(X, a) \ X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 1) \ @@ -657,14 +661,14 @@ extern const pb_msgdesc_t ToRadio_msg; #define SubPacket_size 275 #define MeshPacket_size 320 #define ChannelSettings_size 84 -#define RadioConfig_size 658 +#define RadioConfig_size 314 #define RadioConfig_UserPreferences_size 225 #define NodeInfo_size 132 #define MyNodeInfo_size 110 #define DeviceState_size 5824 #define DebugString_size 258 -#define FromRadio_size 667 -#define ToRadio_size 662 +#define FromRadio_size 329 +#define ToRadio_size 323 #ifdef __cplusplus } /* extern "C" */ From ca99b6b3b7bbadf0756e33805d85b9092616fe7e Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 17 Dec 2020 10:59:05 +0800 Subject: [PATCH 12/12] 1.1.23 --- version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.properties b/version.properties index f903d2c5e..2fe4405ea 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 1 minor = 1 -build = 20 \ No newline at end of file +build = 23