From ccc1600bc92f35a7de7131aa4f7a238c46f0179c Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 14 Nov 2020 10:19:55 +0800 Subject: [PATCH 01/21] remove stale fixme --- src/mesh/RadioInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/RadioInterface.h b/src/mesh/RadioInterface.h index 62774a37c..e5253d7a3 100644 --- a/src/mesh/RadioInterface.h +++ b/src/mesh/RadioInterface.h @@ -58,7 +58,7 @@ class RadioInterface uint8_t sf = 9; uint8_t cr = 7; - uint16_t preambleLength = 32; // 8 is default, but FIXME use longer to increase the amount of sleep time when receiving + uint16_t preambleLength = 32; // 8 is default, but we use longer to increase the amount of sleep time when receiving MeshPacket *sendingPacket = NULL; // The packet we are currently sending uint32_t lastTxStart = 0L; From 2c9c5991a08933ea09dc50a8464aeef66953a6dc Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 15 Nov 2020 08:24:34 +0800 Subject: [PATCH 02/21] add a script for testing release builds --- bin/program-release-universal.sh | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100755 bin/program-release-universal.sh diff --git a/bin/program-release-universal.sh b/bin/program-release-universal.sh new file mode 100755 index 000000000..554f55256 --- /dev/null +++ b/bin/program-release-universal.sh @@ -0,0 +1,6 @@ + +set -e + +source bin/version.sh + +esptool.py --baud 921600 write_flash 0x10000 release/latest/bins/universal/firmware-tbeam-$VERSION.bin From f5e42b2533a21027d60b863be0f6eb2f706f1c40 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 28 Nov 2020 09:17:20 +0800 Subject: [PATCH 03/21] update protos --- proto | 2 +- src/mesh/mesh.pb.h | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/proto b/proto index a0b8d8889..34cc5cca1 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit a0b8d888961720d70ab7467c94d8fa0687e58020 +Subproject commit 34cc5cca1d2f48ffa8f7cb8f1fbf7dfa08da8cb5 diff --git a/src/mesh/mesh.pb.h b/src/mesh/mesh.pb.h index 01ba5b5dd..851bf88fb 100644 --- a/src/mesh/mesh.pb.h +++ b/src/mesh/mesh.pb.h @@ -22,7 +22,8 @@ typedef enum _RouteError { } RouteError; typedef enum _Constants { - Constants_Unused = 0 + Constants_Unused = 0, + Constants_DATA_PAYLOAD_LEN = 240 } Constants; typedef enum _RegionCode { @@ -260,8 +261,8 @@ typedef struct _ToRadio { #define _RouteError_ARRAYSIZE ((RouteError)(RouteError_TIMEOUT+1)) #define _Constants_MIN Constants_Unused -#define _Constants_MAX Constants_Unused -#define _Constants_ARRAYSIZE ((Constants)(Constants_Unused+1)) +#define _Constants_MAX Constants_DATA_PAYLOAD_LEN +#define _Constants_ARRAYSIZE ((Constants)(Constants_DATA_PAYLOAD_LEN+1)) #define _RegionCode_MIN RegionCode_Unset #define _RegionCode_MAX RegionCode_TW From ddab4a023596a00cd80801ffdd03238c39e855ce Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 28 Nov 2020 09:56:21 +0800 Subject: [PATCH 04/21] remove support for 8bit nodenums --- src/mesh/MeshService.cpp | 3 ++- src/mesh/MeshTypes.h | 2 +- src/mesh/NodeDB.cpp | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 77c0f2c92..2c339f72e 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -93,7 +93,8 @@ const MeshPacket *MeshService::handleFromRadioUser(const MeshPacket *mp) bool wasBroadcast = mp->to == NODENUM_BROADCAST; // Disable this collision testing if we use 32 bit nodenums - bool isCollision = (sizeof(NodeNum) == 1) && (mp->from == myNodeInfo.my_node_num); + // (We do this always now, because we don't use 8 bit nodenums since 0.6 ish) + bool isCollision = false; // (sizeof(NodeNum) == 1) && (mp->from == myNodeInfo.my_node_num); if (isCollision) { // we win if we have a lower macaddr diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index 7c58b2e3e..38f18c3a4 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -9,7 +9,7 @@ typedef uint32_t NodeNum; typedef uint32_t PacketId; // A packet sequence number -#define NODENUM_BROADCAST (sizeof(NodeNum) == 4 ? UINT32_MAX : UINT8_MAX) +#define NODENUM_BROADCAST UINT32_MAX #define ERRNO_OK 0 #define ERRNO_NO_INTERFACES 33 #define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 61be815ec..2e245d214 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -273,8 +273,7 @@ void NodeDB::pickNewNodeNum() // If we don't have a nodenum at app - pick an initial nodenum based on the macaddr if (r == 0) - r = sizeof(NodeNum) == 1 ? ourMacAddr[5] - : ((ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5]); + r = (ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5]; if (r == NODENUM_BROADCAST || r < NUM_RESERVED) r = NUM_RESERVED; // don't pick a reserved node number From 0b0d293a66c47841268bcd0e5a4e7cd8cd191834 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 28 Nov 2020 12:10:19 +0800 Subject: [PATCH 05/21] Move text message handling into the new plugin system --- .vscode/settings.json | 3 +- bin/regen-protos.sh | 2 +- docs/software/TODO.md | 18 ++++++------ proto | 2 +- src/graphics/Screen.cpp | 16 ++++++++-- src/graphics/Screen.h | 3 ++ src/mesh/MeshPlugin.cpp | 22 ++++++++++++++ src/mesh/MeshPlugin.h | 49 +++++++++++++++++++++++++++++++ src/mesh/NodeDB.cpp | 29 +++++------------- src/mesh/NodeDB.h | 1 - src/mesh/mesh.pb.c | 1 - src/mesh/mesh.pb.h | 33 ++++++++------------- src/mesh/portnums.pb.c | 10 +++++++ src/mesh/portnums.pb.h | 37 +++++++++++++++++++++++ src/plugins/TextMessagePlugin.cpp | 28 ++++++++++++++++++ src/plugins/TextMessagePlugin.h | 28 ++++++++++++++++++ 16 files changed, 223 insertions(+), 59 deletions(-) create mode 100644 src/mesh/MeshPlugin.cpp create mode 100644 src/mesh/MeshPlugin.h create mode 100644 src/mesh/portnums.pb.c create mode 100644 src/mesh/portnums.pb.h create mode 100644 src/plugins/TextMessagePlugin.cpp create mode 100644 src/plugins/TextMessagePlugin.h diff --git a/.vscode/settings.json b/.vscode/settings.json index 45052fa77..f78fe51b8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -49,7 +49,8 @@ "string_view": "cpp", "cassert": "cpp", "iterator": "cpp", - "shared_mutex": "cpp" + "shared_mutex": "cpp", + "iostream": "cpp" }, "cSpell.words": [ "Blox", diff --git a/bin/regen-protos.sh b/bin/regen-protos.sh index 0f1ae43c6..dd266c54b 100755 --- a/bin/regen-protos.sh +++ b/bin/regen-protos.sh @@ -3,4 +3,4 @@ echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.1" # the nanopb tool seems to require that the .options file be in the current directory! cd proto -../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto mesh.proto +../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto portnums.proto mesh.proto diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 4173296e1..178583af0 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -2,21 +2,21 @@ You probably don't care about this section - skip to the next one. +For app cleanup: + +* add app handlers in device code (make new app framework) +* move positions into regular data packets (use new app framework) +* move user info into regular data packets (use new app framework) +* test that positions, text messages and user info still work +* test that position, text messages and user info work properly with new android app and old device code +* call the plugin setup functions + For high speed/lots of devices/short range tasks: - When guessing numhops for sending: if I've heard from many local (0 hop neighbors) decrease hopcount by 2 rather than 1. This should nicely help 'router' nodes do the right thing when long range, or if there are many local nodes for short range. - fix timeouts/delays to be based on packet length at current radio settings -Nimble tasks: - -- readerror.txt stress test bug -- started RPA long test, jul 22 6pm -- implement nimble software update api -- update to latest bins, test OTA again (measure times) and then checkin bins -- do alpha release - -* update protocol description per cyclomies email thread * update faq with antennas https://meshtastic.discourse.group/t/range-test-ideas-requested/738/2 * update faq on recommended android version and phones * add help link inside the app, reference a page on the wiki diff --git a/proto b/proto index 34cc5cca1..95ef92160 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 34cc5cca1d2f48ffa8f7cb8f1fbf7dfa08da8cb5 +Subproject commit 95ef921604cdab46e32adc245a8d72c7bdbbe319 diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 75aa40ae6..a0b6d35aa 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -32,6 +32,7 @@ along with this program. If not, see . #include "main.h" #include "mesh-pb-constants.h" #include "meshwifi/meshwifi.h" +#include "plugins/TextMessagePlugin.h" #include "target_specific.h" #include "utils.h" @@ -720,6 +721,7 @@ void Screen::setup() powerStatusObserver.observe(&powerStatus->onNewStatus); gpsStatusObserver.observe(&gpsStatus->onNewStatus); nodeStatusObserver.observe(&nodeStatus->onNewStatus); + textMessageObserver.observe(&textMessagePlugin); } void Screen::forceDisplay() @@ -1183,14 +1185,24 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg) // DEBUG_MSG("Screen got status update %d\n", arg->getStatusType()); switch (arg->getStatusType()) { case STATUS_TYPE_NODE: - if (showingNormalScreen && (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal())) { + if (showingNormalScreen && + nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) { setFrames(); // Regen the list of screens } nodeDB.updateGUI = false; - nodeDB.updateTextMessage = false; break; } return 0; } + +int Screen::handleTextMessage(const MeshPacket *arg) +{ + if (showingNormalScreen) { + setFrames(); // Regen the list of screens (will show new text message) + } + + return 0; +} + } // namespace graphics diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index d0bf62286..90e8ad089 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -77,6 +77,8 @@ class Screen : public concurrency::OSThread CallbackObserver(this, &Screen::handleStatusUpdate); CallbackObserver nodeStatusObserver = CallbackObserver(this, &Screen::handleStatusUpdate); + CallbackObserver textMessageObserver = + CallbackObserver(this, &Screen::handleTextMessage); public: Screen(uint8_t address, int sda = -1, int scl = -1); @@ -192,6 +194,7 @@ class Screen : public concurrency::OSThread DebugInfo *debug_info() { return &debugInfo; } int handleStatusUpdate(const meshtastic::Status *arg); + int handleTextMessage(const MeshPacket *arg); /// Used to force (super slow) eink displays to draw critical frames void forceDisplay(); diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp new file mode 100644 index 000000000..8e99f8d79 --- /dev/null +++ b/src/mesh/MeshPlugin.cpp @@ -0,0 +1,22 @@ +#include "MeshPlugin.h" +#include + +std::vector MeshPlugin::plugins; + +MeshPlugin::MeshPlugin(const char *_name) : name(_name) { + plugins.push_back(this); +} + +MeshPlugin::~MeshPlugin() +{ + assert(0); // FIXME - remove from list of plugins once someone needs this feature +} + +void MeshPlugin::callPlugins(const MeshPacket &mp) +{ + for (auto i = plugins.begin(); i != plugins.end(); ++i) { + if ((*i)->wantPortnum(mp.decoded.data.portnum)) + if ((*i)->handleReceived(mp)) + break; + } +} \ No newline at end of file diff --git a/src/mesh/MeshPlugin.h b/src/mesh/MeshPlugin.h new file mode 100644 index 000000000..812e80c17 --- /dev/null +++ b/src/mesh/MeshPlugin.h @@ -0,0 +1,49 @@ +#pragma once + +#include "mesh/MeshTypes.h" +#include +/** A baseclass for any mesh "plugin". + * + * A plugin allows you to add new features to meshtastic device code, without needing to know messaging details. + * + * A key concept for this is that your plugin should use a particular "portnum" for each message type you want to receive + * and handle. + * + * Interally we use plugins to implement the core meshtastic text messaging and gps position sharing features. You + * can use these classes as examples for how to write your own custom plugin. See here: (FIXME) + */ +class MeshPlugin +{ + const char *name; + + static std::vector plugins; + + public: + /** Constructor + * name is for debugging output + */ + MeshPlugin(const char *_name); + + virtual ~MeshPlugin(); + + /** + * Initialize your plugin. This setup function is called once after all hardware and mesh protocol layers have + * been initialized + */ + virtual void setup() {} + + /** + * @return true if you want to receive the specified portnum + */ + virtual bool wantPortnum(PortNum p) = 0; + + /** 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 + */ + virtual bool handleReceived(const MeshPacket &mp) { return false; } + + /** For use only by MeshService + */ + static void callPlugins(const MeshPacket &mp); +}; \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 2e245d214..aed5a2859 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -150,13 +150,13 @@ bool NodeDB::resetRadioConfig() radioConfig.preferences.region = RegionCode_TW; // Enter super deep sleep soon and stay there not very long - //radioConfig.preferences.mesh_sds_timeout_secs = 10; - //radioConfig.preferences.sds_secs = 60; + // radioConfig.preferences.mesh_sds_timeout_secs = 10; + // radioConfig.preferences.sds_secs = 60; } // Update the global myRegion initRegion(); - + return didFactoryReset; } @@ -403,6 +403,8 @@ size_t NodeDB::getNumOnlineNodes() return numseen; } +#include "MeshPlugin.h" + /// given a subpacket sniffed from the network, update our DB state /// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw void NodeDB::updateFrom(const MeshPacket &mp) @@ -433,25 +435,8 @@ void NodeDB::updateFrom(const MeshPacket &mp) } case SubPacket_data_tag: { - // Keep a copy of the most recent text message. - if (p.data.typ == Data_Type_CLEAR_TEXT) { - DEBUG_MSG("Received text msg from=0x%0x, id=%d, msg=%.*s\n", mp.from, mp.id, p.data.payload.size, - p.data.payload.bytes); - if (mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) { - // We only store/display messages destined for us. - devicestate.rx_text_message = mp; - devicestate.has_rx_text_message = true; - updateTextMessage = true; - powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); - notifyObservers(true); // Force an update whether or not our node counts have changed - - // This is going into the wifidev feature branch - // Only update the WebUI if WiFi is enabled - //#if WiFi_MODE != 0 - // notifyWebUI(); - //#endif - } - } + if(mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) + MeshPlugin::callPlugins(mp); break; } diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index c98e050d8..72dca69fb 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -33,7 +33,6 @@ class NodeDB public: bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled NodeInfo *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI - bool updateTextMessage = false; // if true, the GUI should show a new text message Observable newStatus; /// don't do mesh based algoritm for node id assignment (initially) diff --git a/src/mesh/mesh.pb.c b/src/mesh/mesh.pb.c index d799c33d2..032d4ed7f 100644 --- a/src/mesh/mesh.pb.c +++ b/src/mesh/mesh.pb.c @@ -61,4 +61,3 @@ PB_BIND(ManufacturingData, ManufacturingData, AUTO) - diff --git a/src/mesh/mesh.pb.h b/src/mesh/mesh.pb.h index 851bf88fb..58323a98a 100644 --- a/src/mesh/mesh.pb.h +++ b/src/mesh/mesh.pb.h @@ -4,6 +4,7 @@ #ifndef PB_MESH_PB_H_INCLUDED #define PB_MESH_PB_H_INCLUDED #include +#include "portnums.pb.h" #if PB_PROTO_HEADER_VERSION != 40 #error Regenerate this file with the current version of nanopb generator. @@ -51,12 +52,6 @@ typedef enum _LocationSharing { LocationSharing_LocDisabled = 2 } LocationSharing; -typedef enum _Data_Type { - Data_Type_OPAQUE = 0, - Data_Type_CLEAR_TEXT = 1, - Data_Type_CLEAR_READACK = 2 -} Data_Type; - typedef enum _ChannelSettings_ModemConfig { ChannelSettings_ModemConfig_Bw125Cr45Sf128 = 0, ChannelSettings_ModemConfig_Bw500Cr45Sf128 = 1, @@ -79,7 +74,7 @@ typedef struct _ChannelSettings { typedef PB_BYTES_ARRAY_T(240) Data_payload_t; typedef struct _Data { - Data_Type typ; + PortNum portnum; Data_payload_t payload; } Data; @@ -276,10 +271,6 @@ typedef struct _ToRadio { #define _LocationSharing_MAX LocationSharing_LocDisabled #define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1)) -#define _Data_Type_MIN Data_Type_OPAQUE -#define _Data_Type_MAX Data_Type_CLEAR_READACK -#define _Data_Type_ARRAYSIZE ((Data_Type)(Data_Type_CLEAR_READACK+1)) - #define _ChannelSettings_ModemConfig_MIN ChannelSettings_ModemConfig_Bw125Cr45Sf128 #define _ChannelSettings_ModemConfig_MAX ChannelSettings_ModemConfig_Bw125Cr48Sf4096 #define _ChannelSettings_ModemConfig_ARRAYSIZE ((ChannelSettings_ModemConfig)(ChannelSettings_ModemConfig_Bw125Cr48Sf4096+1)) @@ -287,7 +278,7 @@ typedef struct _ToRadio { /* Initializer values for message structs */ #define Position_init_default {0, 0, 0, 0, 0} -#define Data_init_default {_Data_Type_MIN, {0, {0}}} +#define Data_init_default {_PortNum_MIN, {0, {0}}} #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} @@ -303,7 +294,7 @@ typedef struct _ToRadio { #define ToRadio_init_default {0, {MeshPacket_init_default}} #define ManufacturingData_init_default {0, {{NULL}, NULL}, {{NULL}, NULL}, 0} #define Position_init_zero {0, 0, 0, 0, 0} -#define Data_init_zero {_Data_Type_MIN, {0, {0}}} +#define Data_init_zero {_PortNum_MIN, {0, {0}}} #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} @@ -328,7 +319,7 @@ typedef struct _ToRadio { #define ChannelSettings_channel_num_tag 9 #define ChannelSettings_psk_tag 4 #define ChannelSettings_name_tag 5 -#define Data_typ_tag 1 +#define Data_portnum_tag 1 #define Data_payload_tag 2 #define DebugString_message_tag 1 #define ManufacturingData_fradioFreq_tag 1 @@ -443,7 +434,7 @@ X(a, STATIC, SINGULAR, FIXED32, time, 9) #define Position_DEFAULT NULL #define Data_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UENUM, typ, 1) \ +X(a, STATIC, SINGULAR, UENUM, portnum, 1) \ X(a, STATIC, SINGULAR, BYTES, payload, 2) #define Data_CALLBACK NULL #define Data_DEFAULT NULL @@ -669,20 +660,20 @@ extern const pb_msgdesc_t ManufacturingData_msg; /* Maximum encoded size of messages (where known) */ #define Position_size 39 -#define Data_size 245 +#define Data_size 246 #define User_size 72 #define RouteDiscovery_size 88 -#define SubPacket_size 274 -#define MeshPacket_size 313 +#define SubPacket_size 275 +#define MeshPacket_size 314 #define ChannelSettings_size 84 #define RadioConfig_size 308 #define RadioConfig_UserPreferences_size 219 #define NodeInfo_size 132 #define MyNodeInfo_size 110 -#define DeviceState_size 5460 +#define DeviceState_size 5462 #define DebugString_size 258 -#define FromRadio_size 322 -#define ToRadio_size 316 +#define FromRadio_size 323 +#define ToRadio_size 317 /* ManufacturingData_size depends on runtime parameters */ #ifdef __cplusplus diff --git a/src/mesh/portnums.pb.c b/src/mesh/portnums.pb.c new file mode 100644 index 000000000..0995ab494 --- /dev/null +++ b/src/mesh/portnums.pb.c @@ -0,0 +1,10 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.1 */ + +#include "portnums.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + diff --git a/src/mesh/portnums.pb.h b/src/mesh/portnums.pb.h new file mode 100644 index 000000000..c9a2a5f23 --- /dev/null +++ b/src/mesh/portnums.pb.h @@ -0,0 +1,37 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.1 */ + +#ifndef PB_PORTNUMS_PB_H_INCLUDED +#define PB_PORTNUMS_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enum definitions */ +typedef enum _PortNum { + PortNum_UNKNOWN_APP = 0, + PortNum_TEXT_MESSAGE_APP = 1, + PortNum_GPIO_APP = 2, + PortNum_GPS_POSITION_APP = 3, + PortNum_MESH_USERINFO_APP = 4, + PortNum_IP_TUNNEL_APP = 5, + PortNum_PRIVATE_APP = 256 +} PortNum; + +/* Helper constants for enums */ +#define _PortNum_MIN PortNum_UNKNOWN_APP +#define _PortNum_MAX PortNum_PRIVATE_APP +#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_PRIVATE_APP+1)) + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/plugins/TextMessagePlugin.cpp b/src/plugins/TextMessagePlugin.cpp new file mode 100644 index 000000000..31d98feee --- /dev/null +++ b/src/plugins/TextMessagePlugin.cpp @@ -0,0 +1,28 @@ +#include "configuration.h" +#include "TextMessagePlugin.h" +#include "NodeDB.h" +#include "PowerFSM.h" + +TextMessagePlugin textMessagePlugin; + +bool TextMessagePlugin::handleReceived(const MeshPacket &mp) +{ + auto &p = mp.decoded.data; + DEBUG_MSG("Received text msg from=0x%0x, id=%d, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes); + + // We only store/display messages destined for us. + // Keep a copy of the most recent text message. + devicestate.rx_text_message = mp; + devicestate.has_rx_text_message = true; + + powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); + notifyObservers(&mp); + + // This is going into the wifidev feature branch + // Only update the WebUI if WiFi is enabled + //#if WiFi_MODE != 0 + // notifyWebUI(); + //#endif + + return false; // Let others look at this message also if they want +} diff --git a/src/plugins/TextMessagePlugin.h b/src/plugins/TextMessagePlugin.h new file mode 100644 index 000000000..1df13a990 --- /dev/null +++ b/src/plugins/TextMessagePlugin.h @@ -0,0 +1,28 @@ +#include "MeshPlugin.h" +#include "Observer.h" + +/** + * Text message handling for meshtastic - draws on the OLED display the most recent received message + */ +class TextMessagePlugin : public MeshPlugin, public Observable +{ + public: + + /** Constructor + * name is for debugging output + */ + TextMessagePlugin() : MeshPlugin("text") {} + + /** + * @return true if you want to receive the specified portnum + */ + virtual bool wantPortnum(PortNum p) { return p == PortNum_TEXT_MESSAGE_APP; } + + /** 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 + */ + virtual bool handleReceived(const MeshPacket &mp); +}; + +extern TextMessagePlugin textMessagePlugin; \ No newline at end of file From 5138aff4b256f924d0785d42bc4f56acdc6702cf Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 28 Nov 2020 13:25:03 +0800 Subject: [PATCH 06/21] fix static initializer bug with mesh plugins --- proto | 2 +- src/mesh/MeshPlugin.cpp | 28 ++++++++++++++++++++++------ src/mesh/MeshPlugin.h | 4 ++-- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/proto b/proto index 95ef92160..7c1016b8a 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 95ef921604cdab46e32adc245a8d72c7bdbbe319 +Subproject commit 7c1016b8a01d4d019c4239fe46624dee7bde22c7 diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp index 8e99f8d79..853124981 100644 --- a/src/mesh/MeshPlugin.cpp +++ b/src/mesh/MeshPlugin.cpp @@ -1,10 +1,18 @@ #include "MeshPlugin.h" #include -std::vector MeshPlugin::plugins; +std::vector *MeshPlugin::plugins; -MeshPlugin::MeshPlugin(const char *_name) : name(_name) { - plugins.push_back(this); +MeshPlugin::MeshPlugin(const char *_name) : name(_name) +{ + // Can't trust static initalizer order, so we check each time + if(!plugins) + plugins = new std::vector(); + + plugins->push_back(this); +} + +void MeshPlugin::setup() { } MeshPlugin::~MeshPlugin() @@ -14,9 +22,17 @@ MeshPlugin::~MeshPlugin() void MeshPlugin::callPlugins(const MeshPacket &mp) { - for (auto i = plugins.begin(); i != plugins.end(); ++i) { - if ((*i)->wantPortnum(mp.decoded.data.portnum)) - if ((*i)->handleReceived(mp)) + DEBUG_MSG("In call plugins\n"); + for (auto i = plugins->begin(); i != plugins->end(); ++i) { + auto &pi = **i; + if (pi.wantPortnum(mp.decoded.data.portnum)) { + bool handled = pi.handleReceived(mp); + DEBUG_MSG("Plugin %s handled=%d\n", pi.name, handled); + if (handled) break; + } + else { + DEBUG_MSG("Plugin %s not interested\n", pi.name); + } } } \ No newline at end of file diff --git a/src/mesh/MeshPlugin.h b/src/mesh/MeshPlugin.h index 812e80c17..19189fb52 100644 --- a/src/mesh/MeshPlugin.h +++ b/src/mesh/MeshPlugin.h @@ -16,7 +16,7 @@ class MeshPlugin { const char *name; - static std::vector plugins; + static std::vector *plugins; public: /** Constructor @@ -30,7 +30,7 @@ class MeshPlugin * Initialize your plugin. This setup function is called once after all hardware and mesh protocol layers have * been initialized */ - virtual void setup() {} + virtual void setup(); /** * @return true if you want to receive the specified portnum From 7737123d0f9d61e1d93bf62616beb90842164ba6 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 28 Nov 2020 13:51:51 +0800 Subject: [PATCH 07/21] begin moving position stuff into plugin --- docs/software/TODO.md | 4 +++- proto | 2 +- src/mesh/MeshPlugin.cpp | 2 +- src/mesh/MeshPlugin.h | 9 +++++---- src/mesh/MeshService.cpp | 1 - src/mesh/portnums.pb.h | 10 +++++----- src/plugins/PositionPlugin.cpp | 13 +++++++++++++ src/plugins/TextMessagePlugin.h | 2 +- 8 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 src/plugins/PositionPlugin.cpp diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 178583af0..6ba049427 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,7 +4,9 @@ You probably don't care about this section - skip to the next one. For app cleanup: -* add app handlers in device code (make new app framework) +* require a recent python api to talk to these new device loads +* on android for received positions handle either old or new positions +* on android side send old or new positions as needed * move positions into regular data packets (use new app framework) * move user info into regular data packets (use new app framework) * test that positions, text messages and user info still work diff --git a/proto b/proto index 7c1016b8a..9a7ffbecc 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 7c1016b8a01d4d019c4239fe46624dee7bde22c7 +Subproject commit 9a7ffbecc72a11904bd4e85d086956e4e77eed6d diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp index 853124981..938a3bffa 100644 --- a/src/mesh/MeshPlugin.cpp +++ b/src/mesh/MeshPlugin.cpp @@ -22,7 +22,7 @@ MeshPlugin::~MeshPlugin() void MeshPlugin::callPlugins(const MeshPacket &mp) { - DEBUG_MSG("In call plugins\n"); + // DEBUG_MSG("In call plugins\n"); for (auto i = plugins->begin(); i != plugins->end(); ++i) { auto &pi = **i; if (pi.wantPortnum(mp.decoded.data.portnum)) { diff --git a/src/mesh/MeshPlugin.h b/src/mesh/MeshPlugin.h index 19189fb52..e795eb19c 100644 --- a/src/mesh/MeshPlugin.h +++ b/src/mesh/MeshPlugin.h @@ -26,6 +26,11 @@ class MeshPlugin virtual ~MeshPlugin(); + /** For use only by MeshService + */ + static void callPlugins(const MeshPacket &mp); + + protected: /** * Initialize your plugin. This setup function is called once after all hardware and mesh protocol layers have * been initialized @@ -42,8 +47,4 @@ class MeshPlugin @return true if you've guaranteed you've handled this message and no other handlers should be considered for it */ virtual bool handleReceived(const MeshPacket &mp) { return false; } - - /** For use only by MeshService - */ - static void callPlugins(const MeshPacket &mp); }; \ No newline at end of file diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 2c339f72e..d99078f0c 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -297,7 +297,6 @@ void MeshService::sendOurPosition(NodeNum dest, bool wantReplies) int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) { - // Update our local node info with our position (even if we don't decide to update anyone else) MeshPacket *p = router->allocForSending(); p->decoded.which_payload = SubPacket_position_tag; diff --git a/src/mesh/portnums.pb.h b/src/mesh/portnums.pb.h index c9a2a5f23..285910510 100644 --- a/src/mesh/portnums.pb.h +++ b/src/mesh/portnums.pb.h @@ -18,16 +18,16 @@ typedef enum _PortNum { PortNum_UNKNOWN_APP = 0, PortNum_TEXT_MESSAGE_APP = 1, PortNum_GPIO_APP = 2, - PortNum_GPS_POSITION_APP = 3, + PortNum_POSITION_APP = 3, PortNum_MESH_USERINFO_APP = 4, - PortNum_IP_TUNNEL_APP = 5, - PortNum_PRIVATE_APP = 256 + PortNum_PRIVATE_APP = 256, + PortNum_IP_TUNNEL_APP = 1024 } PortNum; /* Helper constants for enums */ #define _PortNum_MIN PortNum_UNKNOWN_APP -#define _PortNum_MAX PortNum_PRIVATE_APP -#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_PRIVATE_APP+1)) +#define _PortNum_MAX PortNum_IP_TUNNEL_APP +#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_IP_TUNNEL_APP+1)) #ifdef __cplusplus diff --git a/src/plugins/PositionPlugin.cpp b/src/plugins/PositionPlugin.cpp new file mode 100644 index 000000000..4b2624baf --- /dev/null +++ b/src/plugins/PositionPlugin.cpp @@ -0,0 +1,13 @@ +#include "configuration.h" +#include "PositionPlugin.h" +#include "NodeDB.h" + +PositionPlugin positionPlugin; + +bool PositionPlugin::handleReceived(const MeshPacket &mp) +{ + auto &p = mp.decoded.data; + DEBUG_MSG("Received position from=0x%0x, id=%d, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes); + + return false; // Let others look at this message also if they want +} diff --git a/src/plugins/TextMessagePlugin.h b/src/plugins/TextMessagePlugin.h index 1df13a990..6dacf9045 100644 --- a/src/plugins/TextMessagePlugin.h +++ b/src/plugins/TextMessagePlugin.h @@ -7,12 +7,12 @@ class TextMessagePlugin : public MeshPlugin, public Observable { public: - /** Constructor * name is for debugging output */ TextMessagePlugin() : MeshPlugin("text") {} + protected: /** * @return true if you want to receive the specified portnum */ From d3cb9bdd4ae3de21cd210d2f4d03f12337aaf33d Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Thu, 3 Dec 2020 16:48:44 +0800 Subject: [PATCH 08/21] WIP moving positions to new system --- docs/software/TODO.md | 2 + src/mesh/MeshPlugin.h | 4 +- src/mesh/MeshService.cpp | 20 ++------ src/mesh/MeshService.h | 6 +-- src/mesh/NodeDB.cpp | 89 +++++++++++++++++++--------------- src/mesh/NodeDB.h | 4 ++ src/mesh/ProtobufPlugin.cpp | 3 ++ src/mesh/ProtobufPlugin.h | 72 +++++++++++++++++++++++++++ src/plugins/PositionPlugin.cpp | 30 ++++++++++-- src/plugins/PositionPlugin.h | 28 +++++++++++ 10 files changed, 191 insertions(+), 67 deletions(-) create mode 100644 src/mesh/ProtobufPlugin.cpp create mode 100644 src/mesh/ProtobufPlugin.h create mode 100644 src/plugins/PositionPlugin.h diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 6ba049427..d6119aebf 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,9 +4,11 @@ You probably don't care about this section - skip to the next one. For app cleanup: +* have python tool check max packet size before sending to device * require a recent python api to talk to these new device loads * on android for received positions handle either old or new positions * on android side send old or new positions as needed +* fix position sending to use new plugin * move positions into regular data packets (use new app framework) * move user info into regular data packets (use new app framework) * test that positions, text messages and user info still work diff --git a/src/mesh/MeshPlugin.h b/src/mesh/MeshPlugin.h index e795eb19c..c2e372cca 100644 --- a/src/mesh/MeshPlugin.h +++ b/src/mesh/MeshPlugin.h @@ -14,8 +14,6 @@ */ class MeshPlugin { - const char *name; - static std::vector *plugins; public: @@ -31,6 +29,8 @@ class MeshPlugin static void callPlugins(const MeshPacket &mp); protected: + const char *name; + /** * Initialize your plugin. This setup function is called once after all hardware and mesh protocol layers have * been initialized diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index d99078f0c..9c45853f7 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -14,6 +14,7 @@ #include "main.h" #include "mesh-pb-constants.h" #include "power.h" +#include "plugins/PositionPlugin.h" /* receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone. @@ -254,7 +255,7 @@ void MeshService::sendToMesh(MeshPacket *p) // Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other // nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless // devices can get time. - if (p->which_payload == MeshPacket_decoded_tag && p->decoded.which_payload == SubPacket_position_tag) { + if (p->which_payload == MeshPacket_decoded_tag && p->decoded.which_payload == SubPacket_position_tag && p->decoded.position.time) { if (getRTCQuality() < RTCQualityGPS) { DEBUG_MSG("Stripping time %u from position send\n", p->decoded.position.time); p->decoded.position.time = 0; @@ -273,27 +274,12 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies) DEBUG_MSG("Sending network ping to 0x%x, with position=%d, wantReplies=%d\n", dest, node->has_position, wantReplies); if (node->has_position) - sendOurPosition(dest, wantReplies); + positionPlugin.sendOurPosition(dest, wantReplies); else sendOurOwner(dest, wantReplies); } -void MeshService::sendOurPosition(NodeNum dest, bool wantReplies) -{ - NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); - assert(node); - assert(node->has_position); - // Update our local node info with our position (even if we don't decide to update anyone else) - MeshPacket *p = router->allocForSending(); - p->to = dest; - p->decoded.which_payload = SubPacket_position_tag; - p->decoded.position = node->position; - p->decoded.want_response = wantReplies; - p->decoded.position.time = - getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid. - sendToMesh(p); -} int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) { diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index a30f3d4ec..66cfdefc2 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -78,15 +78,13 @@ class MeshService /// Send our owner info to a particular node void sendOurOwner(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); - private: - /// Broadcasts our last known position - void sendOurPosition(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); - /// Send a packet into the mesh - note p must have been allocated from packetPool. We will return it to that pool after /// sending. This is the ONLY function you should use for sending messages into the mesh, because it also updates the nodedb /// cache void sendToMesh(MeshPacket *p); + private: + /// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh /// returns 0 to allow futher processing int onGPSChanged(const meshtastic::GPSStatus *arg); diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index aed5a2859..8fe424109 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -405,6 +405,19 @@ size_t NodeDB::getNumOnlineNodes() #include "MeshPlugin.h" +/** Update position info for this node based on received position data + */ +void NodeDB::updatePosition(uint32_t nodeId, const Position &p) { + + NodeInfo *info = getOrCreateNode(nodeId); + + // we always trust our local timestamps more + info->position = p; + info->has_position = true; + updateGUIforNode = info; + notifyObservers(true); // Force an update whether or not our node counts have changed +} + /// given a subpacket sniffed from the network, update our DB state /// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw void NodeDB::updateFrom(const MeshPacket &mp) @@ -423,48 +436,44 @@ void NodeDB::updateFrom(const MeshPacket &mp) info->snr = mp.rx_snr; // keep the most recent SNR we received for this node. switch (p.which_payload) { - case SubPacket_position_tag: { - // we always trust our local timestamps more - info->position = p.position; - if (mp.rx_time) - info->position.time = mp.rx_time; - info->has_position = true; - updateGUIforNode = info; - notifyObservers(true); // Force an update whether or not our node counts have changed - break; - } - - case SubPacket_data_tag: { - if(mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) - MeshPlugin::callPlugins(mp); - break; - } - - case SubPacket_user_tag: { - DEBUG_MSG("old user %s/%s/%s\n", info->user.id, info->user.long_name, info->user.short_name); - - bool changed = memcmp(&info->user, &p.user, - sizeof(info->user)); // Both of these blocks start as filled with zero so I think this is okay - - info->user = p.user; - DEBUG_MSG("updating changed=%d user %s/%s/%s\n", changed, info->user.id, info->user.long_name, info->user.short_name); - info->has_user = true; - - if (changed) { - updateGUIforNode = info; - powerFSM.trigger(EVENT_NODEDB_UPDATED); - notifyObservers(true); // Force an update whether or not our node counts have changed - - // Not really needed - we will save anyways when we go to sleep - // We just changed something important about the user, store our DB - // saveToDisk(); + case SubPacket_position_tag: { + // handle a legacy position packet + DEBUG_MSG("WARNING: Processing a (deprecated) position packet from %d\n", mp.from); + updatePosition(mp.from, p.position); + break; } - break; - } - default: { - notifyObservers(); // If the node counts have changed, notify observers - } + case SubPacket_data_tag: { + if(mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) + MeshPlugin::callPlugins(mp); + break; + } + + case SubPacket_user_tag: { + DEBUG_MSG("old user %s/%s/%s\n", info->user.id, info->user.long_name, info->user.short_name); + + bool changed = memcmp(&info->user, &p.user, + sizeof(info->user)); // Both of these blocks start as filled with zero so I think this is okay + + info->user = p.user; + DEBUG_MSG("updating changed=%d user %s/%s/%s\n", changed, info->user.id, info->user.long_name, info->user.short_name); + info->has_user = true; + + if (changed) { + updateGUIforNode = info; + powerFSM.trigger(EVENT_NODEDB_UPDATED); + notifyObservers(true); // Force an update whether or not our node counts have changed + + // Not really needed - we will save anyways when we go to sleep + // We just changed something important about the user, store our DB + // saveToDisk(); + } + break; + } + + default: { + notifyObservers(); // If the node counts have changed, notify observers + } } } } diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 72dca69fb..01a5c9870 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -57,6 +57,10 @@ class NodeDB /// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw void updateFrom(const MeshPacket &p); + /** Update position info for this node based on received position data + */ + void updatePosition(uint32_t nodeId, const Position &p); + /// @return our node number NodeNum getNodeNum() { return myNodeInfo.my_node_num; } diff --git a/src/mesh/ProtobufPlugin.cpp b/src/mesh/ProtobufPlugin.cpp new file mode 100644 index 000000000..36728c3cd --- /dev/null +++ b/src/mesh/ProtobufPlugin.cpp @@ -0,0 +1,3 @@ +#include "ProtobufPlugin.h" + + diff --git a/src/mesh/ProtobufPlugin.h b/src/mesh/ProtobufPlugin.h new file mode 100644 index 000000000..21392f29d --- /dev/null +++ b/src/mesh/ProtobufPlugin.h @@ -0,0 +1,72 @@ +#include "MeshPlugin.h" +#include "Router.h" + +/** + * A base class for mesh plugins that assume that they are sending/receiving one particular protobuf based + * payload. Using one particular app ID. + * + * If you are using protobufs to encode your packets (recommended) you can use this as a baseclass for your plugin + * and avoid a bunch of boilerplate code. + */ +template class ProtobufPlugin : private MeshPlugin +{ + const pb_msgdesc_t *fields; + PortNum ourPortNum; + + public: + /** Constructor + * name is for debugging output + */ + ProtobufPlugin(const char *_name, PortNum _ourPortNum, const pb_msgdesc_t *_fields) + : MeshPlugin(_name), fields(_fields), ourPortNum(_ourPortNum) + { + } + + protected: + /** + * @return true if you want to receive the specified portnum + */ + virtual bool wantPortnum(PortNum p) { return p == ourPortNum; } + + /** + * Handle a received message, the data field in the message is already decoded and is provided + */ + virtual bool handleReceivedProtobuf(const MeshPacket &mp, const T &decoded) = 0; + + /** + * Return a mesh packet which has been preinited with a particular protobuf data payload and port number. + * You can then send this packet (after customizing any of the payload fields you might need) with + * service.sendToMesh() + */ + MeshPacket *allocForSending(const T &payload) + { + // Update our local node info with our position (even if we don't decide to update anyone else) + MeshPacket *p = router->allocForSending(); + p->decoded.which_payload = SubPacket_data_tag; + p->decoded.data.portnum = ourPortNum; + + p->decoded.data.payload.size = + pb_encode_to_bytes(p->decoded.data.payload.bytes, sizeof(p->decoded.data.payload.bytes), fields, &payload); + return p; + } + + private: + /** 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 + */ + virtual bool handleReceived(const MeshPacket &mp) + { + // FIXME - we currently update position data in the DB only if the message was a broadcast or destined to us + // 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, msg=%.*s\n", name, mp.from, mp.id, p.payload.size, p.payload.bytes); + + T scratch; + if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch)) + handleReceivedProtobuf(mp, scratch); + + return false; // Let others look at this message also if they want + } +}; diff --git a/src/plugins/PositionPlugin.cpp b/src/plugins/PositionPlugin.cpp index 4b2624baf..ef2a9fec7 100644 --- a/src/plugins/PositionPlugin.cpp +++ b/src/plugins/PositionPlugin.cpp @@ -1,13 +1,35 @@ -#include "configuration.h" #include "PositionPlugin.h" +#include "MeshService.h" #include "NodeDB.h" +#include "RTC.h" +#include "Router.h" +#include "configuration.h" PositionPlugin positionPlugin; -bool PositionPlugin::handleReceived(const MeshPacket &mp) +bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position &p) { - auto &p = mp.decoded.data; - DEBUG_MSG("Received position from=0x%0x, id=%d, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes); + // FIXME - we currently update position data in the DB only if the message was a broadcast or destined to us + // it would be better to update even if the message was destined to others. + + nodeDB.updatePosition(mp.from, p); return false; // Let others look at this message also if they want } + +void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies) +{ + NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); + assert(node); + assert(node->has_position); + + // Update our local node info with our position (even if we don't decide to update anyone else) + auto position = node->position; + position.time = getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid. + + MeshPacket *p = allocForSending(position); + p->to = dest; + p->decoded.want_response = wantReplies; + + service.sendToMesh(p); +} diff --git a/src/plugins/PositionPlugin.h b/src/plugins/PositionPlugin.h new file mode 100644 index 000000000..18bf2bcdf --- /dev/null +++ b/src/plugins/PositionPlugin.h @@ -0,0 +1,28 @@ +#include "ProtobufPlugin.h" + +/** + * Position plugin for sending/receiving positions into the mesh + */ +class PositionPlugin : public ProtobufPlugin +{ + public: + /** Constructor + * name is for debugging output + */ + PositionPlugin() : ProtobufPlugin("position", PortNum_POSITION_APP, Position_fields) {} + + /** + * Send our position into the mesh + */ + void sendOurPosition(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); + + 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 + */ + virtual bool handleReceivedProtobuf(const MeshPacket &mp, const Position &p); +}; + +extern PositionPlugin positionPlugin; \ No newline at end of file From 9b24cc6dd62c3e0883cde7d2e77a83cdd599a58a Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Fri, 4 Dec 2020 18:54:00 +0800 Subject: [PATCH 09/21] update protobufs --- bin/regen-protos.sh | 6 ++++++ docs/software/TODO.md | 40 ++++++++++++++++++++++++++++++++++++++++ proto | 2 +- src/mesh/mesh.pb.c | 3 --- src/mesh/mesh.pb.h | 24 ------------------------ src/mesh/portnums.pb.h | 2 +- 6 files changed, 48 insertions(+), 29 deletions(-) diff --git a/bin/regen-protos.sh b/bin/regen-protos.sh index dd266c54b..655db9a7a 100755 --- a/bin/regen-protos.sh +++ b/bin/regen-protos.sh @@ -1,6 +1,12 @@ #!/bin/bash +set -e + echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.1" # the nanopb tool seems to require that the .options file be in the current directory! cd proto ../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto portnums.proto mesh.proto + +echo "Regenerating protobuf documentation - if you see an error message" +echo "you can ignore it unless doing a new protobuf release to github." +bin/regen-docs.sh \ No newline at end of file diff --git a/docs/software/TODO.md b/docs/software/TODO.md index d6119aebf..492d51def 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -9,6 +9,7 @@ For app cleanup: * on android for received positions handle either old or new positions * on android side send old or new positions as needed * fix position sending to use new plugin +* Add SinglePortNumPlugin - as the new most useful baseclass * move positions into regular data packets (use new app framework) * move user info into regular data packets (use new app framework) * test that positions, text messages and user info still work @@ -27,6 +28,45 @@ This should nicely help 'router' nodes do the right thing when long range, or if * turn on amazon reviews support * add a tablet layout (with map next to messages) in the android app +# Old docs to merge + +MESH RADIO PROTOCOL + +Old TODO notes on the mesh radio protocol, merge into real docs someday... + +for each named group we have a pre-shared key known by all group members and +wrapped around the device. you can only be in one group at a time (FIXME?!) To +join the group we read a qr code with the preshared key and ParamsCodeEnum. that +gets sent via bluetooth to the device. ParamsCodeEnum maps to a set of various +radio params (regulatory region, center freq, SF, bandwidth, bitrate, power +etc...) so all members of the mesh can have their radios set the same way. + +once in that group, we can talk between 254 node numbers. +to get our node number (and announce our presence in the channel) we pick a +random node number and broadcast as that node with WANT-NODENUM(my globally +unique name). If anyone on the channel has seen someone _else_ using that name +within the last 24 hrs(?) they reply with DENY-NODENUM. Note: we might receive +multiple denies. Note: this allows others to speak up for some other node that +might be saving battery right now. Any time we hear from another node (for any +message type), we add that node number to the unpickable list. To dramatically +decrease the odds a node number we request is already used by someone. If no one +denies within TBD seconds, we assume that we have that node number. As long as +we keep talking to folks at least once every 24 hrs, others should remember we +have it. + +Once we have a node number we can broadcast POSITION-UPDATE(my globally unique +name, lat, lon, alt, amt battery remaining). All receivers will use this to a) +update the mapping of who is at what node nums, b) the time of last rx, c) +position. If we haven't heard from that node in a while we reply to that node +(only) with our current POSITION_UPDATE state - so that node (presumably just +rejoined the network) can build a map of all participants. + +We will periodically broadcast POSITION-UPDATE as needed based on distance moved +or a periodic minimum heartbeat. + +If user wants to send a text they can SEND_TEXT(dest user, short text message). +Dest user is a node number, or 0xff for broadcast. + # Medium priority Items to complete before 1.0. diff --git a/proto b/proto index 9a7ffbecc..8b24fbab1 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 9a7ffbecc72a11904bd4e85d086956e4e77eed6d +Subproject commit 8b24fbab195ca76932e70456750cd0172d47db79 diff --git a/src/mesh/mesh.pb.c b/src/mesh/mesh.pb.c index 032d4ed7f..42b5eb949 100644 --- a/src/mesh/mesh.pb.c +++ b/src/mesh/mesh.pb.c @@ -51,9 +51,6 @@ PB_BIND(FromRadio, FromRadio, 2) PB_BIND(ToRadio, ToRadio, 2) -PB_BIND(ManufacturingData, ManufacturingData, AUTO) - - diff --git a/src/mesh/mesh.pb.h b/src/mesh/mesh.pb.h index 58323a98a..9fb4900b0 100644 --- a/src/mesh/mesh.pb.h +++ b/src/mesh/mesh.pb.h @@ -82,13 +82,6 @@ typedef struct _DebugString { char message[256]; } DebugString; -typedef struct _ManufacturingData { - uint32_t fradioFreq; - pb_callback_t hw_model; - pb_callback_t hw_version; - int32_t selftest_result; -} ManufacturingData; - typedef struct _MyNodeInfo { uint32_t my_node_num; bool has_gps; @@ -292,7 +285,6 @@ typedef struct _ToRadio { #define DebugString_init_default {""} #define FromRadio_init_default {0, 0, {MeshPacket_init_default}} #define ToRadio_init_default {0, {MeshPacket_init_default}} -#define ManufacturingData_init_default {0, {{NULL}, NULL}, {{NULL}, NULL}, 0} #define Position_init_zero {0, 0, 0, 0, 0} #define Data_init_zero {_PortNum_MIN, {0, {0}}} #define User_init_zero {"", "", "", {0}} @@ -308,7 +300,6 @@ typedef struct _ToRadio { #define DebugString_init_zero {""} #define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}} #define ToRadio_init_zero {0, {MeshPacket_init_zero}} -#define ManufacturingData_init_zero {0, {{NULL}, NULL}, {{NULL}, NULL}, 0} /* Field tags (for use in manual encoding/decoding) */ #define ChannelSettings_tx_power_tag 1 @@ -322,10 +313,6 @@ typedef struct _ToRadio { #define Data_portnum_tag 1 #define Data_payload_tag 2 #define DebugString_message_tag 1 -#define ManufacturingData_fradioFreq_tag 1 -#define ManufacturingData_hw_model_tag 2 -#define ManufacturingData_hw_version_tag 3 -#define ManufacturingData_selftest_result_tag 4 #define MyNodeInfo_my_node_num_tag 1 #define MyNodeInfo_has_gps_tag 2 #define MyNodeInfo_num_channels_tag 3 @@ -615,14 +602,6 @@ X(a, STATIC, ONEOF, MESSAGE, (variant,set_owner,variant.set_owner), 102) #define ToRadio_variant_set_radio_MSGTYPE RadioConfig #define ToRadio_variant_set_owner_MSGTYPE User -#define ManufacturingData_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, fradioFreq, 1) \ -X(a, CALLBACK, SINGULAR, STRING, hw_model, 2) \ -X(a, CALLBACK, SINGULAR, STRING, hw_version, 3) \ -X(a, STATIC, SINGULAR, SINT32, selftest_result, 4) -#define ManufacturingData_CALLBACK pb_default_field_callback -#define ManufacturingData_DEFAULT NULL - extern const pb_msgdesc_t Position_msg; extern const pb_msgdesc_t Data_msg; extern const pb_msgdesc_t User_msg; @@ -638,7 +617,6 @@ extern const pb_msgdesc_t DeviceState_msg; extern const pb_msgdesc_t DebugString_msg; extern const pb_msgdesc_t FromRadio_msg; extern const pb_msgdesc_t ToRadio_msg; -extern const pb_msgdesc_t ManufacturingData_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define Position_fields &Position_msg @@ -656,7 +634,6 @@ extern const pb_msgdesc_t ManufacturingData_msg; #define DebugString_fields &DebugString_msg #define FromRadio_fields &FromRadio_msg #define ToRadio_fields &ToRadio_msg -#define ManufacturingData_fields &ManufacturingData_msg /* Maximum encoded size of messages (where known) */ #define Position_size 39 @@ -674,7 +651,6 @@ extern const pb_msgdesc_t ManufacturingData_msg; #define DebugString_size 258 #define FromRadio_size 323 #define ToRadio_size 317 -/* ManufacturingData_size depends on runtime parameters */ #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/portnums.pb.h b/src/mesh/portnums.pb.h index 285910510..179b9b5d6 100644 --- a/src/mesh/portnums.pb.h +++ b/src/mesh/portnums.pb.h @@ -17,8 +17,8 @@ extern "C" { typedef enum _PortNum { PortNum_UNKNOWN_APP = 0, PortNum_TEXT_MESSAGE_APP = 1, - PortNum_GPIO_APP = 2, PortNum_POSITION_APP = 3, + PortNum_GPIO_APP = 2, PortNum_MESH_USERINFO_APP = 4, PortNum_PRIVATE_APP = 256, PortNum_IP_TUNNEL_APP = 1024 From f1179bd3eaae7f3d7663c9b473847eda0ed92ee7 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 5 Dec 2020 08:46:19 +0800 Subject: [PATCH 10/21] positions now sent using the new API --- docs/software/TODO.md | 5 +++-- proto | 2 +- src/mesh/MeshService.cpp | 21 +++++++++------------ 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 492d51def..1ec5719d1 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -8,13 +8,14 @@ For app cleanup: * require a recent python api to talk to these new device loads * on android for received positions handle either old or new positions * on android side send old or new positions as needed -* fix position sending to use new plugin +* DONE fix position sending to use new plugin * Add SinglePortNumPlugin - as the new most useful baseclass -* move positions into regular data packets (use new app framework) +* DONE move positions into regular data packets (use new app framework) * move user info into regular data packets (use new app framework) * test that positions, text messages and user info still work * test that position, text messages and user info work properly with new android app and old device code * call the plugin setup functions +* fix the RTC drift bug For high speed/lots of devices/short range tasks: diff --git a/proto b/proto index 8b24fbab1..be48f1cbe 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 8b24fbab195ca76932e70456750cd0172d47db79 +Subproject commit be48f1cbef1f00a4dbe67c81780dc53916ba5335 diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 9c45853f7..a02de4184 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -284,10 +284,10 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies) int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) { // Update our local node info with our position (even if we don't decide to update anyone else) - MeshPacket *p = router->allocForSending(); - p->decoded.which_payload = SubPacket_position_tag; - Position &pos = p->decoded.position; + Position pos; + + memset(&pos, 0, sizeof(pos)); if (gps->hasLock()) { if (gps->altitude != 0) @@ -304,20 +304,17 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) // DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.latitude_i, pos.time, pos.battery_level); + // Update our current position in the local DB + nodeDB.updatePosition(nodeDB.getNodeNum(), pos); + // We limit our GPS broadcasts to a max rate static uint32_t lastGpsSend; uint32_t now = millis(); if (lastGpsSend == 0 || now - lastGpsSend > getPref_position_broadcast_secs() * 1000) { lastGpsSend = now; - DEBUG_MSG("Sending position to mesh\n"); - - sendToMesh(p); - } else { - // We don't need to send this packet to anyone else, but it still serves as a nice uniform way to update our local state - nodeDB.updateFrom(*p); - - releaseToPool(p); - } + DEBUG_MSG("Sending position to mesh (not requesting replies)\n"); + positionPlugin.sendOurPosition(); + } return 0; } From ae7d3ee5ed1c39a212bb370137952e58388db4c4 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 5 Dec 2020 10:00:46 +0800 Subject: [PATCH 11/21] move nodeinfo messages into new plugin system --- docs/software/TODO.md | 10 ++- proto | 2 +- src/mesh/MeshService.cpp | 132 ++++++-------------------------- src/mesh/MeshService.h | 9 --- src/mesh/NodeDB.cpp | 94 +++++++++++++---------- src/mesh/NodeDB.h | 4 + src/mesh/ProtobufPlugin.h | 4 +- src/mesh/portnums.pb.h | 4 +- src/plugins/NodeInfoPlugin.cpp | 39 ++++++++++ src/plugins/NodeInfoPlugin.h | 29 +++++++ src/plugins/PositionPlugin.cpp | 11 +++ src/plugins/PositionPlugin.h | 1 + src/plugins/TextMessagePlugin.h | 1 + 13 files changed, 172 insertions(+), 168 deletions(-) create mode 100644 src/plugins/NodeInfoPlugin.cpp create mode 100644 src/plugins/NodeInfoPlugin.h diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 1ec5719d1..80c818bf6 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -6,12 +6,16 @@ For app cleanup: * have python tool check max packet size before sending to device * require a recent python api to talk to these new device loads -* on android for received positions handle either old or new positions -* on android side send old or new positions as needed +* DONE fix handleIncomingPosition +* move want_replies handling into plugins +* 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 +* on python side print error messages if old position/user messages seen +* on python side handle new position/user messages * DONE fix position sending to use new plugin * Add SinglePortNumPlugin - as the new most useful baseclass * DONE move positions into regular data packets (use new app framework) -* move user info into regular data packets (use new app framework) +* DONE move user info into regular data packets (use new app framework) * test that positions, text messages and user info still work * test that position, text messages and user info work properly with new android app and old device code * call the plugin setup functions diff --git a/proto b/proto index be48f1cbe..13b69ad55 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit be48f1cbef1f00a4dbe67c81780dc53916ba5335 +Subproject commit 13b69ad55079e3f35774f63e960064867de20235 diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index a02de4184..5566d2a30 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -13,8 +13,9 @@ #include "RTC.h" #include "main.h" #include "mesh-pb-constants.h" -#include "power.h" #include "plugins/PositionPlugin.h" +#include "plugins/NodeInfoPlugin.h" +#include "power.h" /* receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone. @@ -52,7 +53,7 @@ MeshService service; static int32_t sendOwnerCb() { - service.sendOurOwner(); + nodeInfoPlugin.sendOurNodeInfo(); return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000; } @@ -75,114 +76,28 @@ void MeshService::init() packetReceivedObserver.observe(&router->notifyPacketReceived); } -void MeshService::sendOurOwner(NodeNum dest, bool wantReplies) -{ - MeshPacket *p = router->allocForSending(); - p->to = dest; - p->decoded.want_response = wantReplies; - p->decoded.which_payload = SubPacket_user_tag; - User &u = p->decoded.user; - u = owner; - DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name); - - sendToMesh(p); -} - -/// handle a user packet that just arrived on the radio, return NULL if we should not process this packet at all -const MeshPacket *MeshService::handleFromRadioUser(const MeshPacket *mp) -{ - bool wasBroadcast = mp->to == NODENUM_BROADCAST; - - // Disable this collision testing if we use 32 bit nodenums - // (We do this always now, because we don't use 8 bit nodenums since 0.6 ish) - bool isCollision = false; // (sizeof(NodeNum) == 1) && (mp->from == myNodeInfo.my_node_num); - - if (isCollision) { - // we win if we have a lower macaddr - bool weWin = memcmp(&owner.macaddr, &mp->decoded.user.macaddr, sizeof(owner.macaddr)) < 0; - - if (weWin) { - DEBUG_MSG("NOTE! Received a nodenum collision and we are vetoing\n"); - - mp = NULL; - - sendOurOwner(); // send our owner as a _broadcast_ because that other guy is mistakenly using our nodenum - } else { - // we lost, we need to try for a new nodenum! - DEBUG_MSG("NOTE! Received a nodenum collision we lost, so picking a new nodenum\n"); - nodeDB.updateFrom( - *mp); // update the DB early - before trying to repick (so we don't select the same node number again) - nodeDB.pickNewNodeNum(); - sendOurOwner(); // broadcast our new attempt at a node number - } - } else if (wasBroadcast) { - // If we haven't yet abandoned the packet and it was a broadcast, reply (just to them) with our User record so they can - // build their DB - - // Someone just sent us a User, reply with our Owner - DEBUG_MSG("Received broadcast Owner from 0x%x, replying with our owner\n", mp->from); - - sendOurOwner(mp->from); - - String lcd = String("Joined: ") + mp->decoded.user.long_name + "\n"; - screen->print(lcd.c_str()); - } - - return mp; -} - -void MeshService::handleIncomingPosition(const MeshPacket *mp) -{ - if (mp->which_payload == MeshPacket_decoded_tag && mp->decoded.which_payload == SubPacket_position_tag) { - DEBUG_MSG("handled incoming position time=%u\n", mp->decoded.position.time); - - if (mp->decoded.position.time) { - struct timeval tv; - uint32_t secs = mp->decoded.position.time; - - tv.tv_sec = secs; - tv.tv_usec = 0; - - perhapsSetRTC(RTCQualityFromNet, &tv); - } - } else { - DEBUG_MSG("Ignoring incoming packet - not a position\n"); - } -} int MeshService::handleFromRadio(const MeshPacket *mp) { powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping - // If it is a position packet, perhaps set our clock - this must be before nodeDB.updateFrom - handleIncomingPosition(mp); + printPacket("Forwarding to phone", mp); + nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio - if (mp->which_payload == MeshPacket_decoded_tag && mp->decoded.which_payload == SubPacket_user_tag) { - mp = handleFromRadioUser(mp); + fromNum++; + + if (toPhoneQueue.numFree() == 0) { + DEBUG_MSG("NOTE: tophone queue is full, discarding oldest\n"); + MeshPacket *d = toPhoneQueue.dequeuePtr(0); + if (d) + releaseToPool(d); } - // If we veto a received User packet, we don't put it into the DB or forward it to the phone (to prevent confusing it) - if (mp) { - printPacket("Forwarding to phone", mp); - nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio + MeshPacket *copied = packetPool.allocCopy(*mp); + assert(toPhoneQueue.enqueue(copied, 0)); // FIXME, instead of failing for full queue, delete the oldest mssages - fromNum++; - - if (toPhoneQueue.numFree() == 0) { - DEBUG_MSG("NOTE: tophone queue is full, discarding oldest\n"); - MeshPacket *d = toPhoneQueue.dequeuePtr(0); - if (d) - releaseToPool(d); - } - - MeshPacket *copied = packetPool.allocCopy(*mp); - assert(toPhoneQueue.enqueue(copied, 0)); // FIXME, instead of failing for full queue, delete the oldest mssages - - if (mp->decoded.want_response) - sendNetworkPing(mp->from); - } else { - DEBUG_MSG("Not delivering vetoed User message\n"); - } + if (mp->decoded.want_response) + sendNetworkPing(mp->from); return 0; } @@ -203,7 +118,7 @@ bool MeshService::reloadConfig() // This will also update the region as needed bool didReset = nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings - + configChanged.notifyObservers(NULL); nodeDB.saveToDisk(); @@ -213,7 +128,7 @@ bool MeshService::reloadConfig() /// The owner User record just got updated, update our node DB and broadcast the info into the mesh void MeshService::reloadOwner() { - sendOurOwner(); + nodeInfoPlugin.sendOurNodeInfo(); nodeDB.saveToDisk(); } @@ -224,8 +139,6 @@ void MeshService::reloadOwner() */ void MeshService::handleToRadio(MeshPacket &p) { - handleIncomingPosition(&p); // If it is a position packet, perhaps set our clock - if (p.from == 0) // If the phone didn't set a sending node ID, use ours p.from = nodeDB.getNodeNum(); @@ -255,7 +168,8 @@ void MeshService::sendToMesh(MeshPacket *p) // Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other // nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless // devices can get time. - if (p->which_payload == MeshPacket_decoded_tag && p->decoded.which_payload == SubPacket_position_tag && p->decoded.position.time) { + if (p->which_payload == MeshPacket_decoded_tag && p->decoded.which_payload == SubPacket_position_tag && + p->decoded.position.time) { if (getRTCQuality() < RTCQualityGPS) { DEBUG_MSG("Stripping time %u from position send\n", p->decoded.position.time); p->decoded.position.time = 0; @@ -276,11 +190,9 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies) if (node->has_position) positionPlugin.sendOurPosition(dest, wantReplies); else - sendOurOwner(dest, wantReplies); + nodeInfoPlugin.sendOurNodeInfo(dest, wantReplies); } - - int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) { // Update our local node info with our position (even if we don't decide to update anyone else) @@ -314,7 +226,7 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) lastGpsSend = now; DEBUG_MSG("Sending position to mesh (not requesting replies)\n"); positionPlugin.sendOurPosition(); - } + } return 0; } diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h index 66cfdefc2..b7cfd2432 100644 --- a/src/mesh/MeshService.h +++ b/src/mesh/MeshService.h @@ -75,9 +75,6 @@ class MeshService /// sends our owner void sendNetworkPing(NodeNum dest, bool wantReplies = false); - /// Send our owner info to a particular node - void sendOurOwner(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); - /// Send a packet into the mesh - note p must have been allocated from packetPool. We will return it to that pool after /// sending. This is the ONLY function you should use for sending messages into the mesh, because it also updates the nodedb /// cache @@ -92,12 +89,6 @@ class MeshService /// Handle a packet that just arrived from the radio. This method does _not_ free the provided packet. If it needs /// to keep the packet around it makes a copy int handleFromRadio(const MeshPacket *p); - - /// handle a user packet that just arrived on the radio, return NULL if we should not process this packet at all - const MeshPacket *handleFromRadioUser(const MeshPacket *mp); - - /// look at inbound packets and if they contain a position with time, possibly set our clock - void handleIncomingPosition(const MeshPacket *mp); }; extern MeshService service; diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 8fe424109..102f87793 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -407,15 +407,41 @@ size_t NodeDB::getNumOnlineNodes() /** Update position info for this node based on received position data */ -void NodeDB::updatePosition(uint32_t nodeId, const Position &p) { - +void NodeDB::updatePosition(uint32_t nodeId, const Position &p) +{ NodeInfo *info = getOrCreateNode(nodeId); - // we always trust our local timestamps more - info->position = p; - info->has_position = true; - updateGUIforNode = info; - notifyObservers(true); // Force an update whether or not our node counts have changed + // we always trust our local timestamps more + info->position = p; + info->has_position = true; + updateGUIforNode = info; + notifyObservers(true); // Force an update whether or not our node counts have changed +} + +/** Update user info for this node based on received user data + */ +void NodeDB::updateUser(uint32_t nodeId, const User &p) +{ + NodeInfo *info = getOrCreateNode(nodeId); + + DEBUG_MSG("old user %s/%s/%s\n", info->user.id, info->user.long_name, info->user.short_name); + + bool changed = memcmp(&info->user, &p, + sizeof(info->user)); // Both of these blocks start as filled with zero so I think this is okay + + info->user = p; + DEBUG_MSG("updating changed=%d user %s/%s/%s\n", changed, info->user.id, info->user.long_name, info->user.short_name); + info->has_user = true; + + if (changed) { + updateGUIforNode = info; + powerFSM.trigger(EVENT_NODEDB_UPDATED); + notifyObservers(true); // Force an update whether or not our node counts have changed + + // Not really needed - we will save anyways when we go to sleep + // We just changed something important about the user, store our DB + // saveToDisk(); + } } /// given a subpacket sniffed from the network, update our DB state @@ -436,44 +462,28 @@ void NodeDB::updateFrom(const MeshPacket &mp) info->snr = mp.rx_snr; // keep the most recent SNR we received for this node. switch (p.which_payload) { - case SubPacket_position_tag: { - // handle a legacy position packet - DEBUG_MSG("WARNING: Processing a (deprecated) position packet from %d\n", mp.from); - updatePosition(mp.from, p.position); - break; - } + case SubPacket_position_tag: { + // handle a legacy position packet + DEBUG_MSG("WARNING: Processing a (deprecated) position packet from %d\n", mp.from); + updatePosition(mp.from, p.position); + break; + } - case SubPacket_data_tag: { - if(mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) - MeshPlugin::callPlugins(mp); - break; - } + case SubPacket_data_tag: { + if (mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) + MeshPlugin::callPlugins(mp); + break; + } - case SubPacket_user_tag: { - DEBUG_MSG("old user %s/%s/%s\n", info->user.id, info->user.long_name, info->user.short_name); + case SubPacket_user_tag: { + DEBUG_MSG("WARNING: Processing a (deprecated) user packet from %d\n", mp.from); + updateUser(mp.from, p.user); + break; + } - bool changed = memcmp(&info->user, &p.user, - sizeof(info->user)); // Both of these blocks start as filled with zero so I think this is okay - - info->user = p.user; - DEBUG_MSG("updating changed=%d user %s/%s/%s\n", changed, info->user.id, info->user.long_name, info->user.short_name); - info->has_user = true; - - if (changed) { - updateGUIforNode = info; - powerFSM.trigger(EVENT_NODEDB_UPDATED); - notifyObservers(true); // Force an update whether or not our node counts have changed - - // Not really needed - we will save anyways when we go to sleep - // We just changed something important about the user, store our DB - // saveToDisk(); - } - break; - } - - default: { - notifyObservers(); // If the node counts have changed, notify observers - } + default: { + notifyObservers(); // If the node counts have changed, notify observers + } } } } diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 01a5c9870..d595662dd 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -61,6 +61,10 @@ class NodeDB */ void updatePosition(uint32_t nodeId, const Position &p); + /** Update user info for this node based on received user data + */ + void updateUser(uint32_t nodeId, const User &p); + /// @return our node number NodeNum getNodeNum() { return myNodeInfo.my_node_num; } diff --git a/src/mesh/ProtobufPlugin.h b/src/mesh/ProtobufPlugin.h index 21392f29d..6d385553b 100644 --- a/src/mesh/ProtobufPlugin.h +++ b/src/mesh/ProtobufPlugin.h @@ -1,3 +1,4 @@ +#pragma once #include "MeshPlugin.h" #include "Router.h" @@ -47,6 +48,7 @@ template class ProtobufPlugin : private MeshPlugin p->decoded.data.payload.size = pb_encode_to_bytes(p->decoded.data.payload.bytes, sizeof(p->decoded.data.payload.bytes), fields, &payload); + // DEBUG_MSG("did encode\n"); return p; } @@ -61,7 +63,7 @@ template class ProtobufPlugin : private MeshPlugin // 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, msg=%.*s\n", name, mp.from, mp.id, p.payload.size, p.payload.bytes); + DEBUG_MSG("Received %s from=0x%0x, id=%d, 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)) diff --git a/src/mesh/portnums.pb.h b/src/mesh/portnums.pb.h index 179b9b5d6..595d31146 100644 --- a/src/mesh/portnums.pb.h +++ b/src/mesh/portnums.pb.h @@ -17,9 +17,9 @@ extern "C" { typedef enum _PortNum { PortNum_UNKNOWN_APP = 0, PortNum_TEXT_MESSAGE_APP = 1, - PortNum_POSITION_APP = 3, PortNum_GPIO_APP = 2, - PortNum_MESH_USERINFO_APP = 4, + PortNum_POSITION_APP = 3, + PortNum_NODEINFO_APP = 4, PortNum_PRIVATE_APP = 256, PortNum_IP_TUNNEL_APP = 1024 } PortNum; diff --git a/src/plugins/NodeInfoPlugin.cpp b/src/plugins/NodeInfoPlugin.cpp new file mode 100644 index 000000000..d02766340 --- /dev/null +++ b/src/plugins/NodeInfoPlugin.cpp @@ -0,0 +1,39 @@ +#include "NodeInfoPlugin.h" +#include "MeshService.h" +#include "NodeDB.h" +#include "RTC.h" +#include "Router.h" +#include "configuration.h" +#include "main.h" + +NodeInfoPlugin nodeInfoPlugin; + +bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User &p) +{ + // FIXME - we currently update NodeInfo data in the DB only if the message was a broadcast or destined to us + // it would be better to update even if the message was destined to others. + + nodeDB.updateUser(mp.from, p); + + bool wasBroadcast = mp.to == NODENUM_BROADCAST; + + // Show new nodes on LCD screen + if (wasBroadcast) { + String lcd = String("Joined: ") + p.long_name + "\n"; + screen->print(lcd.c_str()); + } + + return false; // Let others look at this message also if they want +} + +void NodeInfoPlugin::sendOurNodeInfo(NodeNum dest, bool wantReplies) +{ + User &u = owner; + + DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name); + MeshPacket *p = allocForSending(u); + p->to = dest; + p->decoded.want_response = wantReplies; + + service.sendToMesh(p); +} diff --git a/src/plugins/NodeInfoPlugin.h b/src/plugins/NodeInfoPlugin.h new file mode 100644 index 000000000..627ab83e9 --- /dev/null +++ b/src/plugins/NodeInfoPlugin.h @@ -0,0 +1,29 @@ +#pragma once +#include "ProtobufPlugin.h" + +/** + * NodeInfo plugin for sending/receiving NodeInfos into the mesh + */ +class NodeInfoPlugin : public ProtobufPlugin +{ + public: + /** Constructor + * name is for debugging output + */ + NodeInfoPlugin() : ProtobufPlugin("nodeinfo", PortNum_NODEINFO_APP, User_fields) {} + + /** + * Send our NodeInfo into the mesh + */ + void sendOurNodeInfo(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); + + 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 + */ + virtual bool handleReceivedProtobuf(const MeshPacket &mp, const User &p); +}; + +extern NodeInfoPlugin nodeInfoPlugin; \ No newline at end of file diff --git a/src/plugins/PositionPlugin.cpp b/src/plugins/PositionPlugin.cpp index ef2a9fec7..1562c0f62 100644 --- a/src/plugins/PositionPlugin.cpp +++ b/src/plugins/PositionPlugin.cpp @@ -12,6 +12,17 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position // FIXME - we currently update position data in the DB only if the message was a broadcast or destined to us // it would be better to update even if the message was destined to others. + DEBUG_MSG("handled incoming position time=%u\n", p.time); + if (p.time) { + struct timeval tv; + uint32_t secs = p.time; + + tv.tv_sec = secs; + tv.tv_usec = 0; + + perhapsSetRTC(RTCQualityFromNet, &tv); + } + nodeDB.updatePosition(mp.from, p); return false; // Let others look at this message also if they want diff --git a/src/plugins/PositionPlugin.h b/src/plugins/PositionPlugin.h index 18bf2bcdf..6f967c57a 100644 --- a/src/plugins/PositionPlugin.h +++ b/src/plugins/PositionPlugin.h @@ -1,3 +1,4 @@ +#pragma once #include "ProtobufPlugin.h" /** diff --git a/src/plugins/TextMessagePlugin.h b/src/plugins/TextMessagePlugin.h index 6dacf9045..c213570f7 100644 --- a/src/plugins/TextMessagePlugin.h +++ b/src/plugins/TextMessagePlugin.h @@ -1,3 +1,4 @@ +#pragma once #include "MeshPlugin.h" #include "Observer.h" From b6e21bcbcd1ebdd7ce15bb77ac34c5fbdd1efe5c Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 5 Dec 2020 10:14:15 +0800 Subject: [PATCH 12/21] add SinglePortPlugin to simpilify api --- docs/software/TODO.md | 2 +- src/mesh/ProtobufPlugin.h | 11 +++-------- src/mesh/SinglePortPlugin.h | 24 ++++++++++++++++++++++++ src/plugins/TextMessagePlugin.h | 10 +++------- 4 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 src/mesh/SinglePortPlugin.h diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 80c818bf6..6088c535b 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -13,7 +13,7 @@ For app cleanup: * on python side print error messages if old position/user messages seen * on python side handle new position/user messages * DONE fix position sending to use new plugin -* Add SinglePortNumPlugin - as the new most useful baseclass +* DONE Add SinglePortNumPlugin - as the new most useful baseclass * DONE move positions into regular data packets (use new app framework) * DONE move user info into regular data packets (use new app framework) * test that positions, text messages and user info still work diff --git a/src/mesh/ProtobufPlugin.h b/src/mesh/ProtobufPlugin.h index 6d385553b..f6bac2345 100644 --- a/src/mesh/ProtobufPlugin.h +++ b/src/mesh/ProtobufPlugin.h @@ -1,5 +1,5 @@ #pragma once -#include "MeshPlugin.h" +#include "SinglePortPlugin.h" #include "Router.h" /** @@ -9,25 +9,20 @@ * If you are using protobufs to encode your packets (recommended) you can use this as a baseclass for your plugin * and avoid a bunch of boilerplate code. */ -template class ProtobufPlugin : private MeshPlugin +template class ProtobufPlugin : private SinglePortPlugin { const pb_msgdesc_t *fields; - PortNum ourPortNum; public: /** Constructor * name is for debugging output */ ProtobufPlugin(const char *_name, PortNum _ourPortNum, const pb_msgdesc_t *_fields) - : MeshPlugin(_name), fields(_fields), ourPortNum(_ourPortNum) + : SinglePortPlugin(_name, _ourPortNum), fields(_fields) { } protected: - /** - * @return true if you want to receive the specified portnum - */ - virtual bool wantPortnum(PortNum p) { return p == ourPortNum; } /** * Handle a received message, the data field in the message is already decoded and is provided diff --git a/src/mesh/SinglePortPlugin.h b/src/mesh/SinglePortPlugin.h new file mode 100644 index 000000000..9d7e41030 --- /dev/null +++ b/src/mesh/SinglePortPlugin.h @@ -0,0 +1,24 @@ +#pragma once +#include "MeshPlugin.h" + +/** + * Most plugins are only interested in sending/receving one particular portnum. This baseclass simplifies that common + * case. + */ +class SinglePortPlugin : public MeshPlugin +{ + protected: + PortNum ourPortNum; + + public: + /** Constructor + * name is for debugging output + */ + SinglePortPlugin(const char *_name, PortNum _ourPortNum) : MeshPlugin(_name), ourPortNum(_ourPortNum) {} + + protected: + /** + * @return true if you want to receive the specified portnum + */ + virtual bool wantPortnum(PortNum p) { return p == ourPortNum; } +}; diff --git a/src/plugins/TextMessagePlugin.h b/src/plugins/TextMessagePlugin.h index c213570f7..1a6a45076 100644 --- a/src/plugins/TextMessagePlugin.h +++ b/src/plugins/TextMessagePlugin.h @@ -1,23 +1,19 @@ #pragma once -#include "MeshPlugin.h" +#include "SinglePortPlugin.h" #include "Observer.h" /** * Text message handling for meshtastic - draws on the OLED display the most recent received message */ -class TextMessagePlugin : public MeshPlugin, public Observable +class TextMessagePlugin : public SinglePortPlugin, public Observable { public: /** Constructor * name is for debugging output */ - TextMessagePlugin() : MeshPlugin("text") {} + TextMessagePlugin() : SinglePortPlugin("text", PortNum_TEXT_MESSAGE_APP) {} protected: - /** - * @return true if you want to receive the specified portnum - */ - virtual bool wantPortnum(PortNum p) { return p == PortNum_TEXT_MESSAGE_APP; } /** Called to handle a particular incoming message From 91b99bd58465efa3c45e579e4aabeb481def8963 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 5 Dec 2020 10:27:04 +0800 Subject: [PATCH 13/21] require apps to be 1.1.20 or later --- docs/software/TODO.md | 4 ++-- src/mesh/NodeDB.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 6088c535b..1cb6b17e0 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -5,12 +5,12 @@ You probably don't care about this section - skip to the next one. For app cleanup: * have python tool check max packet size before sending to device -* require a recent python api to talk to these new device loads +* DONE require a recent python api to talk to these new device loads +* DONE require a recent android app to talk to these new device loads * DONE fix handleIncomingPosition * move want_replies handling into plugins * 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 -* on python side print error messages if old position/user messages seen * on python side handle new position/user messages * DONE fix position sending to use new plugin * DONE Add SinglePortNumPlugin - as the new most useful baseclass diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 102f87793..05895c932 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -184,7 +184,7 @@ void NodeDB::installDefaultDeviceState() // default to no GPS, until one has been found by probing myNodeInfo.has_gps = false; myNodeInfo.message_timeout_msec = FLOOD_EXPIRE_TIME; - myNodeInfo.min_app_version = 172; + myNodeInfo.min_app_version = 20120; // format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20 generatePacketId(); // FIXME - ugly way to init current_packet_id; // Init our blank owner info to reasonable defaults From 3e0dc44210f7f559c6d8853eda113e8690d70496 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sat, 5 Dec 2020 11:15:06 +0800 Subject: [PATCH 14/21] move want_replies into new plugin system --- docs/software/TODO.md | 2 +- src/mesh/MeshPlugin.cpp | 6 ++++++ src/mesh/MeshPlugin.h | 6 ++++++ src/mesh/MeshService.cpp | 3 --- src/plugins/NodeInfoPlugin.cpp | 10 ++++++++++ src/plugins/NodeInfoPlugin.h | 7 ++++++- src/plugins/PositionPlugin.cpp | 9 +++++++++ src/plugins/PositionPlugin.h | 6 ++++++ 8 files changed, 44 insertions(+), 5 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 1cb6b17e0..fc1c27c83 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -8,7 +8,7 @@ For app cleanup: * DONE require a recent python api to talk to these new device loads * DONE require a recent android app to talk to these new device loads * DONE fix handleIncomingPosition -* move want_replies handling into plugins +* DONE move want_replies handling into plugins * 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 * on python side handle new position/user messages diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp index 938a3bffa..77aa1b0a5 100644 --- a/src/mesh/MeshPlugin.cpp +++ b/src/mesh/MeshPlugin.cpp @@ -1,4 +1,5 @@ #include "MeshPlugin.h" +#include "NodeDB.h" #include std::vector *MeshPlugin::plugins; @@ -27,6 +28,11 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) auto &pi = **i; 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()) + pi.sendResponse(mp.from); + DEBUG_MSG("Plugin %s handled=%d\n", pi.name, handled); if (handled) break; diff --git a/src/mesh/MeshPlugin.h b/src/mesh/MeshPlugin.h index c2e372cca..926157af9 100644 --- a/src/mesh/MeshPlugin.h +++ b/src/mesh/MeshPlugin.h @@ -47,4 +47,10 @@ class MeshPlugin @return true if you've guaranteed you've handled this message and no other handlers should be considered for it */ virtual bool handleReceived(const MeshPacket &mp) { return false; } + + /** Messages can be received that have the want_response bit set. If set, this callback will be invoked + * so that subclasses can (optionally) send a response back to the original sender. Implementing this method + * is optional + */ + virtual void sendResponse(NodeNum to) {} }; \ No newline at end of file diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 5566d2a30..059cd70d2 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -96,9 +96,6 @@ int MeshService::handleFromRadio(const MeshPacket *mp) MeshPacket *copied = packetPool.allocCopy(*mp); assert(toPhoneQueue.enqueue(copied, 0)); // FIXME, instead of failing for full queue, delete the oldest mssages - if (mp->decoded.want_response) - sendNetworkPing(mp->from); - return 0; } diff --git a/src/plugins/NodeInfoPlugin.cpp b/src/plugins/NodeInfoPlugin.cpp index d02766340..c6d8e6a70 100644 --- a/src/plugins/NodeInfoPlugin.cpp +++ b/src/plugins/NodeInfoPlugin.cpp @@ -37,3 +37,13 @@ void NodeInfoPlugin::sendOurNodeInfo(NodeNum dest, bool wantReplies) service.sendToMesh(p); } + + +/** Messages can be received that have the want_response bit set. If set, this callback will be invoked + * so that subclasses can (optionally) send a response back to the original sender. Implementing this method + * is optional + */ +void NodeInfoPlugin::sendResponse(NodeNum to) { + DEBUG_MSG("Sending user reply\n"); + sendOurNodeInfo(to, false); +} \ No newline at end of file diff --git a/src/plugins/NodeInfoPlugin.h b/src/plugins/NodeInfoPlugin.h index 627ab83e9..98a620e25 100644 --- a/src/plugins/NodeInfoPlugin.h +++ b/src/plugins/NodeInfoPlugin.h @@ -18,12 +18,17 @@ class NodeInfoPlugin : public ProtobufPlugin void sendOurNodeInfo(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); 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 */ virtual bool handleReceivedProtobuf(const MeshPacket &mp, const User &p); + + /** Messages can be received that have the want_response bit set. If set, this callback will be invoked + * so that subclasses can (optionally) send a response back to the original sender. Implementing this method + * is optional + */ + virtual void sendResponse(NodeNum to); }; extern NodeInfoPlugin nodeInfoPlugin; \ No newline at end of file diff --git a/src/plugins/PositionPlugin.cpp b/src/plugins/PositionPlugin.cpp index 1562c0f62..c1f813a4b 100644 --- a/src/plugins/PositionPlugin.cpp +++ b/src/plugins/PositionPlugin.cpp @@ -44,3 +44,12 @@ void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies) service.sendToMesh(p); } + +/** Messages can be received that have the want_response bit set. If set, this callback will be invoked + * so that subclasses can (optionally) send a response back to the original sender. Implementing this method + * is optional + */ +void PositionPlugin::sendResponse(NodeNum to) { + DEBUG_MSG("Sending posistion reply\n"); + sendOurPosition(to, false); +} \ No newline at end of file diff --git a/src/plugins/PositionPlugin.h b/src/plugins/PositionPlugin.h index 6f967c57a..4ca146dd4 100644 --- a/src/plugins/PositionPlugin.h +++ b/src/plugins/PositionPlugin.h @@ -24,6 +24,12 @@ class PositionPlugin : 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 Position &p); + + /** Messages can be received that have the want_response bit set. If set, this callback will be invoked + * so that subclasses can (optionally) send a response back to the original sender. Implementing this method + * is optional + */ + virtual void sendResponse(NodeNum to); }; extern PositionPlugin positionPlugin; \ No newline at end of file From 8f5a1f19d3af4fb1d268b13d94c4c09661e156c1 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Sun, 6 Dec 2020 18:33:42 +0800 Subject: [PATCH 15/21] add remote gpio control as an example plugin https://github.com/meshtastic/Meshtastic-device/issues/182 --- bin/regen-protos.sh | 2 +- docs/software/TODO.md | 7 ++- proto | 2 +- src/mesh/portnums.pb.h | 2 +- src/mesh/remote_hardware.pb.c | 13 ++++++ src/mesh/remote_hardware.pb.h | 69 ++++++++++++++++++++++++++++ src/plugins/RemoteHardwarePlugin.cpp | 17 +++++++ src/plugins/RemoteHardwarePlugin.h | 24 ++++++++++ 8 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 src/mesh/remote_hardware.pb.c create mode 100644 src/mesh/remote_hardware.pb.h create mode 100644 src/plugins/RemoteHardwarePlugin.cpp create mode 100644 src/plugins/RemoteHardwarePlugin.h diff --git a/bin/regen-protos.sh b/bin/regen-protos.sh index 655db9a7a..817dc5d24 100755 --- a/bin/regen-protos.sh +++ b/bin/regen-protos.sh @@ -5,7 +5,7 @@ set -e echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.1" # the nanopb tool seems to require that the .options file be in the current directory! cd proto -../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto portnums.proto mesh.proto +../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto *.proto echo "Regenerating protobuf documentation - if you see an error message" echo "you can ignore it unless doing a new protobuf release to github." diff --git a/docs/software/TODO.md b/docs/software/TODO.md index fc1c27c83..5a26c8e10 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,14 +4,16 @@ You probably don't care about this section - skip to the next one. For app cleanup: -* have python tool check max packet size before sending to device +* DONE have python tool check max packet size before sending to device +* if request was sent reliably, send reply reliably * DONE require a recent python api to talk to these new device loads * DONE require a recent android app to talk to these new device loads * DONE fix handleIncomingPosition * DONE move want_replies handling into plugins * 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 -* on python side handle new position/user messages +* test python side handle new position/user messages +* make a gpio example * DONE fix position sending to use new plugin * DONE Add SinglePortNumPlugin - as the new most useful baseclass * DONE move positions into regular data packets (use new app framework) @@ -20,6 +22,7 @@ For app cleanup: * test that position, text messages and user info work properly with new android app and old device code * call the plugin setup functions * fix the RTC drift bug +* move ping functionality into device, reply with rxsnr info For high speed/lots of devices/short range tasks: diff --git a/proto b/proto index 13b69ad55..6e8d220ad 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 13b69ad55079e3f35774f63e960064867de20235 +Subproject commit 6e8d220ad0d9f7ae6ce37db94c2b3f55a70f4f45 diff --git a/src/mesh/portnums.pb.h b/src/mesh/portnums.pb.h index 595d31146..eab09126b 100644 --- a/src/mesh/portnums.pb.h +++ b/src/mesh/portnums.pb.h @@ -17,7 +17,7 @@ extern "C" { typedef enum _PortNum { PortNum_UNKNOWN_APP = 0, PortNum_TEXT_MESSAGE_APP = 1, - PortNum_GPIO_APP = 2, + PortNum_REMOTE_HARDWARE_APP = 2, PortNum_POSITION_APP = 3, PortNum_NODEINFO_APP = 4, PortNum_PRIVATE_APP = 256, diff --git a/src/mesh/remote_hardware.pb.c b/src/mesh/remote_hardware.pb.c new file mode 100644 index 000000000..a334db461 --- /dev/null +++ b/src/mesh/remote_hardware.pb.c @@ -0,0 +1,13 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.1 */ + +#include "remote_hardware.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(HardwareMessage, HardwareMessage, AUTO) + + + + diff --git a/src/mesh/remote_hardware.pb.h b/src/mesh/remote_hardware.pb.h new file mode 100644 index 000000000..036d82fb8 --- /dev/null +++ b/src/mesh/remote_hardware.pb.h @@ -0,0 +1,69 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.1 */ + +#ifndef PB_REMOTE_HARDWARE_PB_H_INCLUDED +#define PB_REMOTE_HARDWARE_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enum definitions */ +typedef enum _HardwareMessage_MessageType { + HardwareMessage_MessageType_UNSET = 0, + HardwareMessage_MessageType_WRITE_GPIOS = 1, + HardwareMessage_MessageType_WATCH_GPIOS = 2, + HardwareMessage_MessageType_GPIOS_CHANGED = 3, + HardwareMessage_MessageType_READ_GPIOS = 4, + HardwareMessage_MessageType_READ_GPIOS_REPLY = 5 +} HardwareMessage_MessageType; + +/* Struct definitions */ +typedef struct _HardwareMessage { + HardwareMessage_MessageType typ; + uint64_t gpio_mask; + uint64_t gpio_value; +} HardwareMessage; + + +/* Helper constants for enums */ +#define _HardwareMessage_MessageType_MIN HardwareMessage_MessageType_UNSET +#define _HardwareMessage_MessageType_MAX HardwareMessage_MessageType_READ_GPIOS_REPLY +#define _HardwareMessage_MessageType_ARRAYSIZE ((HardwareMessage_MessageType)(HardwareMessage_MessageType_READ_GPIOS_REPLY+1)) + + +/* Initializer values for message structs */ +#define HardwareMessage_init_default {_HardwareMessage_MessageType_MIN, 0, 0} +#define HardwareMessage_init_zero {_HardwareMessage_MessageType_MIN, 0, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define HardwareMessage_typ_tag 1 +#define HardwareMessage_gpio_mask_tag 2 +#define HardwareMessage_gpio_value_tag 3 + +/* Struct field encoding specification for nanopb */ +#define HardwareMessage_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, typ, 1) \ +X(a, STATIC, SINGULAR, UINT64, gpio_mask, 2) \ +X(a, STATIC, SINGULAR, UINT64, gpio_value, 3) +#define HardwareMessage_CALLBACK NULL +#define HardwareMessage_DEFAULT NULL + +extern const pb_msgdesc_t HardwareMessage_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define HardwareMessage_fields &HardwareMessage_msg + +/* Maximum encoded size of messages (where known) */ +#define HardwareMessage_size 24 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/src/plugins/RemoteHardwarePlugin.cpp b/src/plugins/RemoteHardwarePlugin.cpp new file mode 100644 index 000000000..70c7232af --- /dev/null +++ b/src/plugins/RemoteHardwarePlugin.cpp @@ -0,0 +1,17 @@ +#include "RemoteHardwarePlugin.h" +#include "MeshService.h" +#include "NodeDB.h" +#include "RTC.h" +#include "Router.h" +#include "configuration.h" +#include "main.h" + +RemoteHardwarePlugin remoteHardwarePlugin; + +bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &mp, const HardwareMessage &p) +{ + + return false; // Let others look at this message also if they want +} + + diff --git a/src/plugins/RemoteHardwarePlugin.h b/src/plugins/RemoteHardwarePlugin.h new file mode 100644 index 000000000..faae4894d --- /dev/null +++ b/src/plugins/RemoteHardwarePlugin.h @@ -0,0 +1,24 @@ +#pragma once +#include "ProtobufPlugin.h" +#include "remote_hardware.pb.h" + +/** + * A plugin that provides easy low-level remote access to device hardware. + */ +class RemoteHardwarePlugin : public ProtobufPlugin +{ + public: + /** Constructor + * name is for debugging output + */ + RemoteHardwarePlugin() : ProtobufPlugin("remotehardware", PortNum_REMOTE_HARDWARE_APP, HardwareMessage_fields) {} + + 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 + */ + virtual bool handleReceivedProtobuf(const MeshPacket &mp, const HardwareMessage &p); +}; + +extern RemoteHardwarePlugin remoteHardwarePlugin; \ No newline at end of file From 90060e84c004822a439296cac0f987a8a68c2892 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 7 Dec 2020 10:18:11 +0800 Subject: [PATCH 16/21] WIP on GPIO example --- docs/software/TODO.md | 2 +- proto | 2 +- src/mesh/MeshPlugin.cpp | 27 ++++++++++++++- src/mesh/MeshPlugin.h | 19 ++++++++--- src/mesh/ProtobufPlugin.h | 7 ++-- src/mesh/SinglePortPlugin.h | 16 +++++++++ src/mesh/remote_hardware.pb.h | 28 +++++++-------- src/plugins/NodeInfoPlugin.cpp | 19 ++++------- src/plugins/NodeInfoPlugin.h | 6 ++-- src/plugins/PositionPlugin.cpp | 17 ++++------ src/plugins/PositionPlugin.h | 6 ++-- src/plugins/RemoteHardwarePlugin.cpp | 51 +++++++++++++++++++++++++--- 12 files changed, 140 insertions(+), 60 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 5a26c8e10..4901ad40c 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -13,7 +13,7 @@ For app cleanup: * 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 -* make a gpio example +* make a gpio example. --gpiowrb 5, --gpiord 0x444, --gpiowatch 0x3ff * DONE fix position sending to use new plugin * DONE Add SinglePortNumPlugin - as the new most useful baseclass * DONE move positions into regular data packets (use new app framework) diff --git a/proto b/proto index 6e8d220ad..f38b8e304 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 6e8d220ad0d9f7ae6ce37db94c2b3f55a70f4f45 +Subproject commit f38b8e3040528aaf1f1b4b9024ff8df2e14ba961 diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp index 77aa1b0a5..5320b7ae1 100644 --- a/src/mesh/MeshPlugin.cpp +++ b/src/mesh/MeshPlugin.cpp @@ -1,5 +1,6 @@ #include "MeshPlugin.h" #include "NodeDB.h" +#include "MeshService.h" #include std::vector *MeshPlugin::plugins; @@ -31,7 +32,7 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) // Possibly send replies (unless we are handling a locally generated message) if (mp.decoded.want_response && mp.from != nodeDB.getNodeNum()) - pi.sendResponse(mp.from); + pi.sendResponse(mp); DEBUG_MSG("Plugin %s handled=%d\n", pi.name, handled); if (handled) @@ -41,4 +42,28 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) DEBUG_MSG("Plugin %s not interested\n", pi.name); } } +} + +/** Messages can be received that have the want_response bit set. If set, this callback will be invoked + * so that subclasses can (optionally) send a response back to the original sender. Implementing this method + * is optional + */ +void MeshPlugin::sendResponse(const MeshPacket &req) { + auto r = allocReply(); + if(r) { + DEBUG_MSG("Sending response\n"); + setReplyTo(r, req); + service.sendToMesh(r); + } + else { + DEBUG_MSG("WARNING: Client requested response but this plugin did not provide\n"); + } +} + +/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet + * This ensures that if the request packet was sent reliably, the reply is sent that way as well. +*/ +void setReplyTo(MeshPacket *p, const MeshPacket &to) { + p->to = to.from; + p->want_ack = to.want_ack; } \ No newline at end of file diff --git a/src/mesh/MeshPlugin.h b/src/mesh/MeshPlugin.h index 926157af9..f753651ec 100644 --- a/src/mesh/MeshPlugin.h +++ b/src/mesh/MeshPlugin.h @@ -49,8 +49,19 @@ class MeshPlugin virtual bool handleReceived(const MeshPacket &mp) { return false; } /** Messages can be received that have the want_response bit set. If set, this callback will be invoked - * so that subclasses can (optionally) send a response back to the original sender. Implementing this method - * is optional + * so that subclasses can (optionally) send a response back to the original sender. */ + virtual MeshPacket *allocReply() { return NULL; } + + private: + + /** Messages can be received that have the want_response bit set. If set, this callback will be invoked + * so that subclasses can (optionally) send a response back to the original sender. This method calls allocReply() + * to generate the reply message, and if !NULL that message will be delivered to whoever sent req */ - virtual void sendResponse(NodeNum to) {} -}; \ No newline at end of file + void sendResponse(const MeshPacket &req); +}; + +/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet + * This ensures that if the request packet was sent reliably, the reply is sent that way as well. +*/ +void setReplyTo(MeshPacket *p, const MeshPacket &to); \ No newline at end of file diff --git a/src/mesh/ProtobufPlugin.h b/src/mesh/ProtobufPlugin.h index f6bac2345..cda4ed8bf 100644 --- a/src/mesh/ProtobufPlugin.h +++ b/src/mesh/ProtobufPlugin.h @@ -1,6 +1,5 @@ #pragma once #include "SinglePortPlugin.h" -#include "Router.h" /** * A base class for mesh plugins that assume that they are sending/receiving one particular protobuf based @@ -34,12 +33,10 @@ template class ProtobufPlugin : private SinglePortPlugin * You can then send this packet (after customizing any of the payload fields you might need) with * service.sendToMesh() */ - MeshPacket *allocForSending(const T &payload) + MeshPacket *allocDataProtobuf(const T &payload) { // Update our local node info with our position (even if we don't decide to update anyone else) - MeshPacket *p = router->allocForSending(); - p->decoded.which_payload = SubPacket_data_tag; - p->decoded.data.portnum = ourPortNum; + MeshPacket *p = allocDataPacket(); p->decoded.data.payload.size = pb_encode_to_bytes(p->decoded.data.payload.bytes, sizeof(p->decoded.data.payload.bytes), fields, &payload); diff --git a/src/mesh/SinglePortPlugin.h b/src/mesh/SinglePortPlugin.h index 9d7e41030..01ee1963a 100644 --- a/src/mesh/SinglePortPlugin.h +++ b/src/mesh/SinglePortPlugin.h @@ -1,5 +1,6 @@ #pragma once #include "MeshPlugin.h" +#include "Router.h" /** * Most plugins are only interested in sending/receving one particular portnum. This baseclass simplifies that common @@ -21,4 +22,19 @@ class SinglePortPlugin : public MeshPlugin * @return true if you want to receive the specified portnum */ virtual bool wantPortnum(PortNum p) { return p == ourPortNum; } + + /** + * Return a mesh packet which has been preinited as a data packet with a particular port number. + * You can then send this packet (after customizing any of the payload fields you might need) with + * service.sendToMesh() + */ + MeshPacket *allocDataPacket() + { + // Update our local node info with our position (even if we don't decide to update anyone else) + MeshPacket *p = router->allocForSending(); + p->decoded.which_payload = SubPacket_data_tag; + p->decoded.data.portnum = ourPortNum; + + return p; + } }; diff --git a/src/mesh/remote_hardware.pb.h b/src/mesh/remote_hardware.pb.h index 036d82fb8..f85cd8ff2 100644 --- a/src/mesh/remote_hardware.pb.h +++ b/src/mesh/remote_hardware.pb.h @@ -14,32 +14,32 @@ extern "C" { #endif /* Enum definitions */ -typedef enum _HardwareMessage_MessageType { - HardwareMessage_MessageType_UNSET = 0, - HardwareMessage_MessageType_WRITE_GPIOS = 1, - HardwareMessage_MessageType_WATCH_GPIOS = 2, - HardwareMessage_MessageType_GPIOS_CHANGED = 3, - HardwareMessage_MessageType_READ_GPIOS = 4, - HardwareMessage_MessageType_READ_GPIOS_REPLY = 5 -} HardwareMessage_MessageType; +typedef enum _HardwareMessage_Type { + HardwareMessage_Type_UNSET = 0, + HardwareMessage_Type_WRITE_GPIOS = 1, + HardwareMessage_Type_WATCH_GPIOS = 2, + HardwareMessage_Type_GPIOS_CHANGED = 3, + HardwareMessage_Type_READ_GPIOS = 4, + HardwareMessage_Type_READ_GPIOS_REPLY = 5 +} HardwareMessage_Type; /* Struct definitions */ typedef struct _HardwareMessage { - HardwareMessage_MessageType typ; + HardwareMessage_Type typ; uint64_t gpio_mask; uint64_t gpio_value; } HardwareMessage; /* Helper constants for enums */ -#define _HardwareMessage_MessageType_MIN HardwareMessage_MessageType_UNSET -#define _HardwareMessage_MessageType_MAX HardwareMessage_MessageType_READ_GPIOS_REPLY -#define _HardwareMessage_MessageType_ARRAYSIZE ((HardwareMessage_MessageType)(HardwareMessage_MessageType_READ_GPIOS_REPLY+1)) +#define _HardwareMessage_Type_MIN HardwareMessage_Type_UNSET +#define _HardwareMessage_Type_MAX HardwareMessage_Type_READ_GPIOS_REPLY +#define _HardwareMessage_Type_ARRAYSIZE ((HardwareMessage_Type)(HardwareMessage_Type_READ_GPIOS_REPLY+1)) /* Initializer values for message structs */ -#define HardwareMessage_init_default {_HardwareMessage_MessageType_MIN, 0, 0} -#define HardwareMessage_init_zero {_HardwareMessage_MessageType_MIN, 0, 0} +#define HardwareMessage_init_default {_HardwareMessage_Type_MIN, 0, 0} +#define HardwareMessage_init_zero {_HardwareMessage_Type_MIN, 0, 0} /* Field tags (for use in manual encoding/decoding) */ #define HardwareMessage_typ_tag 1 diff --git a/src/plugins/NodeInfoPlugin.cpp b/src/plugins/NodeInfoPlugin.cpp index c6d8e6a70..876f5ea02 100644 --- a/src/plugins/NodeInfoPlugin.cpp +++ b/src/plugins/NodeInfoPlugin.cpp @@ -28,22 +28,17 @@ bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User &p) void NodeInfoPlugin::sendOurNodeInfo(NodeNum dest, bool wantReplies) { - User &u = owner; - - DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name); - MeshPacket *p = allocForSending(u); + MeshPacket *p = allocReply(); p->to = dest; p->decoded.want_response = wantReplies; service.sendToMesh(p); } +MeshPacket *NodeInfoPlugin::allocReply() +{ + User &u = owner; -/** Messages can be received that have the want_response bit set. If set, this callback will be invoked - * so that subclasses can (optionally) send a response back to the original sender. Implementing this method - * is optional - */ -void NodeInfoPlugin::sendResponse(NodeNum to) { - DEBUG_MSG("Sending user reply\n"); - sendOurNodeInfo(to, false); -} \ No newline at end of file + DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name); + return allocDataProtobuf(u); +} diff --git a/src/plugins/NodeInfoPlugin.h b/src/plugins/NodeInfoPlugin.h index 98a620e25..968d5f8b7 100644 --- a/src/plugins/NodeInfoPlugin.h +++ b/src/plugins/NodeInfoPlugin.h @@ -25,10 +25,8 @@ class NodeInfoPlugin : public ProtobufPlugin virtual bool handleReceivedProtobuf(const MeshPacket &mp, const User &p); /** Messages can be received that have the want_response bit set. If set, this callback will be invoked - * so that subclasses can (optionally) send a response back to the original sender. Implementing this method - * is optional - */ - virtual void sendResponse(NodeNum to); + * so that subclasses can (optionally) send a response back to the original sender. */ + virtual MeshPacket *allocReply(); }; extern NodeInfoPlugin nodeInfoPlugin; \ No newline at end of file diff --git a/src/plugins/PositionPlugin.cpp b/src/plugins/PositionPlugin.cpp index c1f813a4b..4b06f0eed 100644 --- a/src/plugins/PositionPlugin.cpp +++ b/src/plugins/PositionPlugin.cpp @@ -28,7 +28,7 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position return false; // Let others look at this message also if they want } -void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies) +MeshPacket *PositionPlugin::allocReply() { NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); assert(node); @@ -38,18 +38,15 @@ void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies) auto position = node->position; position.time = getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid. - MeshPacket *p = allocForSending(position); + return allocDataProtobuf(position); +} + +void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies) +{ + MeshPacket *p = allocReply(); p->to = dest; p->decoded.want_response = wantReplies; service.sendToMesh(p); } -/** Messages can be received that have the want_response bit set. If set, this callback will be invoked - * so that subclasses can (optionally) send a response back to the original sender. Implementing this method - * is optional - */ -void PositionPlugin::sendResponse(NodeNum to) { - DEBUG_MSG("Sending posistion reply\n"); - sendOurPosition(to, false); -} \ No newline at end of file diff --git a/src/plugins/PositionPlugin.h b/src/plugins/PositionPlugin.h index 4ca146dd4..9153a2288 100644 --- a/src/plugins/PositionPlugin.h +++ b/src/plugins/PositionPlugin.h @@ -26,10 +26,8 @@ class PositionPlugin : public ProtobufPlugin virtual bool handleReceivedProtobuf(const MeshPacket &mp, const Position &p); /** Messages can be received that have the want_response bit set. If set, this callback will be invoked - * so that subclasses can (optionally) send a response back to the original sender. Implementing this method - * is optional - */ - virtual void sendResponse(NodeNum to); + * so that subclasses can (optionally) send a response back to the original sender. */ + virtual MeshPacket *allocReply(); }; extern PositionPlugin positionPlugin; \ No newline at end of file diff --git a/src/plugins/RemoteHardwarePlugin.cpp b/src/plugins/RemoteHardwarePlugin.cpp index 70c7232af..085d7d693 100644 --- a/src/plugins/RemoteHardwarePlugin.cpp +++ b/src/plugins/RemoteHardwarePlugin.cpp @@ -8,10 +8,53 @@ RemoteHardwarePlugin remoteHardwarePlugin; -bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &mp, const HardwareMessage &p) +#define NUM_GPIOS 64 + +// A macro for clearing a struct, FIXME, move elsewhere +#define CLEAR_STRUCT(r) memset(&r, 0, sizeof(r)) + +bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const HardwareMessage &p) { + switch (p.typ) { + case HardwareMessage_Type_WRITE_GPIOS: + // Print notification to LCD screen + screen->print("Write GPIOs"); - return false; // Let others look at this message also if they want + for (uint8_t i = 0; i < NUM_GPIOS; i++) { + uint64_t mask = 1 << i; + if (p.gpio_mask & mask) { + digitalWrite(i, (p.gpio_value & mask) ? 1 : 0); + pinMode(i, OUTPUT); + } + } + break; + case HardwareMessage_Type_READ_GPIOS: { + // Print notification to LCD screen + screen->print("Read GPIOs"); + + 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); + } + } + + // Send the reply + HardwareMessage reply; + CLEAR_STRUCT(reply); + reply.typ = HardwareMessage_Type_READ_GPIOS_REPLY; + reply.gpio_value = res; + MeshPacket *p = allocDataProtobuf(reply); + setReplyTo(p, req); + service.sendToMesh(p); + break; + } + default: + DEBUG_MSG("Hardware operation %d not yet implemented! FIXME\n", p.typ); + break; + } + return true; // handled } - - From 79a24c200ef2b2ce0268b80cf5425c02d458ed56 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 7 Dec 2020 10:27:31 +0800 Subject: [PATCH 17/21] use autogened protobuf init code --- src/mesh/MeshService.cpp | 4 +--- src/plugins/RemoteHardwarePlugin.cpp | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 059cd70d2..afd5a6d48 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -194,9 +194,7 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) { // Update our local node info with our position (even if we don't decide to update anyone else) - Position pos; - - memset(&pos, 0, sizeof(pos)); + Position pos = Position_init_default; if (gps->hasLock()) { if (gps->altitude != 0) diff --git a/src/plugins/RemoteHardwarePlugin.cpp b/src/plugins/RemoteHardwarePlugin.cpp index 085d7d693..eab0f1253 100644 --- a/src/plugins/RemoteHardwarePlugin.cpp +++ b/src/plugins/RemoteHardwarePlugin.cpp @@ -13,6 +13,7 @@ RemoteHardwarePlugin remoteHardwarePlugin; // A macro for clearing a struct, FIXME, move elsewhere #define CLEAR_STRUCT(r) memset(&r, 0, sizeof(r)) + bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const HardwareMessage &p) { switch (p.typ) { @@ -43,8 +44,7 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H } // Send the reply - HardwareMessage reply; - CLEAR_STRUCT(reply); + HardwareMessage reply = HardwareMessage_init_default; reply.typ = HardwareMessage_Type_READ_GPIOS_REPLY; reply.gpio_value = res; MeshPacket *p = allocDataProtobuf(reply); From 4bd22dd5db8eceb4f3cb65e1af11840a720c48e2 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Tue, 8 Dec 2020 08:16:58 +0800 Subject: [PATCH 18/21] ignore our own msgs for gpio ctrl --- docs/software/TODO.md | 15 +++++++++++---- src/plugins/RemoteHardwarePlugin.cpp | 13 ++++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 4901ad40c..17bb2069c 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,25 +4,32 @@ You probably don't care about this section - skip to the next one. For app cleanup: +* do fixed position bug +* DONE update android code: https://developer.android.com/topic/libraries/view-binding/migration +* make gpio watch work, use thread and setup +* make hello world example service +* make python ping command +* make python gpio read a bit cleaner * DONE have python tool check max packet size before sending to device -* if request was sent reliably, send reply reliably +* DONE if request was sent reliably, send reply reliably * DONE require a recent python api to talk to these new device loads * DONE require a recent android app to talk to these new device loads * DONE fix handleIncomingPosition * DONE move want_replies handling into plugins -* on android for received positions handle either old or new positions / user messages +* 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 -* make a gpio example. --gpiowrb 5, --gpiord 0x444, --gpiowatch 0x3ff +* 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 * DONE move positions into regular data packets (use new app framework) * DONE move user info into regular data packets (use new app framework) * test that positions, text messages and user info still work * test that position, text messages and user info work properly with new android app and old device code -* call the plugin setup functions * fix the RTC drift bug * move ping functionality into device, reply with rxsnr info +* use channels for gpio security https://github.com/meshtastic/Meshtastic-device/issues/104 +* implement GPIO watch For high speed/lots of devices/short range tasks: diff --git a/src/plugins/RemoteHardwarePlugin.cpp b/src/plugins/RemoteHardwarePlugin.cpp index eab0f1253..11aaad234 100644 --- a/src/plugins/RemoteHardwarePlugin.cpp +++ b/src/plugins/RemoteHardwarePlugin.cpp @@ -10,16 +10,13 @@ RemoteHardwarePlugin remoteHardwarePlugin; #define NUM_GPIOS 64 -// A macro for clearing a struct, FIXME, move elsewhere -#define CLEAR_STRUCT(r) memset(&r, 0, sizeof(r)) - bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const HardwareMessage &p) { switch (p.typ) { case HardwareMessage_Type_WRITE_GPIOS: // Print notification to LCD screen - screen->print("Write GPIOs"); + screen->print("Write GPIOs\n"); for (uint8_t i = 0; i < NUM_GPIOS; i++) { uint64_t mask = 1 << i; @@ -29,9 +26,10 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H } } break; + case HardwareMessage_Type_READ_GPIOS: { // Print notification to LCD screen - screen->print("Read GPIOs"); + screen->print("Read GPIOs\n"); uint64_t res = 0; for (uint8_t i = 0; i < NUM_GPIOS; i++) { @@ -52,6 +50,11 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H service.sendToMesh(p); break; } + + case HardwareMessage_Type_READ_GPIOS_REPLY: + case HardwareMessage_Type_GPIOS_CHANGED: + break; // Ignore - we might see our own replies + default: DEBUG_MSG("Hardware operation %d not yet implemented! FIXME\n", p.typ); break; From 3753fef2986e15466c7ccdf73de417eb70c64d23 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 9 Dec 2020 11:56:41 +0800 Subject: [PATCH 19/21] add debug_log_enabled --- docs/software/TODO.md | 5 ++++- proto | 2 +- src/SerialConsole.cpp | 4 +++- src/mesh/mesh.pb.h | 18 ++++++++++++------ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/docs/software/TODO.md b/docs/software/TODO.md index 17bb2069c..822fbd4a4 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -4,7 +4,10 @@ You probably don't care about this section - skip to the next one. For app cleanup: -* do fixed position bug +* do fixed position bug https://github.com/meshtastic/Meshtastic-device/issues/536 +* check build guide +* generate autodocs +* write user guide * DONE update android code: https://developer.android.com/topic/libraries/view-binding/migration * make gpio watch work, use thread and setup * make hello world example service diff --git a/proto b/proto index f38b8e304..ebd18145c 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit f38b8e3040528aaf1f1b4b9024ff8df2e14ba961 +Subproject commit ebd18145cafb0d09528150b7a5eccd52b581902f diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp index f246b607c..9bb539fa5 100644 --- a/src/SerialConsole.cpp +++ b/src/SerialConsole.cpp @@ -1,6 +1,7 @@ #include "SerialConsole.h" #include "PowerFSM.h" #include "configuration.h" +#include "NodeDB.h" #include #define Port Serial @@ -28,7 +29,8 @@ void SerialConsole::init() void SerialConsole::handleToRadio(const uint8_t *buf, size_t len) { // Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets - setDestination(&noopPrint); + if(!radioConfig.preferences.debug_log_enabled) + setDestination(&noopPrint); canWrite = true; StreamAPI::handleToRadio(buf, len); diff --git a/src/mesh/mesh.pb.h b/src/mesh/mesh.pb.h index 9fb4900b0..06e4aa3b4 100644 --- a/src/mesh/mesh.pb.h +++ b/src/mesh/mesh.pb.h @@ -129,7 +129,9 @@ typedef struct _RadioConfig_UserPreferences { uint32_t gps_attempt_time; bool is_router; bool is_low_power; + bool fixed_position; bool factory_reset; + bool debug_log_enabled; pb_size_t ignore_incoming_count; uint32_t ignore_incoming[3]; } RadioConfig_UserPreferences; @@ -278,7 +280,7 @@ typedef struct _ToRadio { #define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 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_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}} +#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} @@ -293,7 +295,7 @@ typedef struct _ToRadio { #define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 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_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}} +#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} @@ -349,7 +351,9 @@ typedef struct _ToRadio { #define RadioConfig_UserPreferences_region_tag 15 #define RadioConfig_UserPreferences_is_router_tag 37 #define RadioConfig_UserPreferences_is_low_power_tag 38 +#define RadioConfig_UserPreferences_fixed_position_tag 39 #define RadioConfig_UserPreferences_factory_reset_tag 100 +#define RadioConfig_UserPreferences_debug_log_enabled_tag 101 #define RadioConfig_UserPreferences_location_share_tag 32 #define RadioConfig_UserPreferences_gps_operation_tag 33 #define RadioConfig_UserPreferences_gps_update_interval_tag 34 @@ -516,7 +520,9 @@ X(a, STATIC, SINGULAR, UINT32, gps_update_interval, 34) \ X(a, STATIC, SINGULAR, UINT32, gps_attempt_time, 36) \ X(a, STATIC, SINGULAR, BOOL, is_router, 37) \ X(a, STATIC, SINGULAR, BOOL, is_low_power, 38) \ +X(a, STATIC, SINGULAR, BOOL, fixed_position, 39) \ X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \ +X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 101) \ X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) #define RadioConfig_UserPreferences_CALLBACK NULL #define RadioConfig_UserPreferences_DEFAULT NULL @@ -643,14 +649,14 @@ extern const pb_msgdesc_t ToRadio_msg; #define SubPacket_size 275 #define MeshPacket_size 314 #define ChannelSettings_size 84 -#define RadioConfig_size 308 -#define RadioConfig_UserPreferences_size 219 +#define RadioConfig_size 314 +#define RadioConfig_UserPreferences_size 225 #define NodeInfo_size 132 #define MyNodeInfo_size 110 -#define DeviceState_size 5462 +#define DeviceState_size 5468 #define DebugString_size 258 #define FromRadio_size 323 -#define ToRadio_size 317 +#define ToRadio_size 318 #ifdef __cplusplus } /* extern "C" */ From 32b8e4f20aac4c2f0144ddfe1d97c41d28b90a4d Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 9 Dec 2020 12:05:15 +0800 Subject: [PATCH 20/21] fix #536 allow fixed positions meshtastic --setlat 32.7767 --setlon -96.7970 --setalt 1337 --- src/mesh/MeshService.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index afd5a6d48..1c06406f6 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -202,6 +202,17 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) pos.latitude_i = gps->latitude; pos.longitude_i = gps->longitude; } + else { + // The GPS has lost lock, if we are fixed position we should just keep using + // the old position + if(radioConfig.preferences.fixed_position) { + NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); + assert(node); + assert(node->has_position); + pos = node->position; + DEBUG_MSG("WARNING: Using fixed position\n"); + } + } pos.time = getValidTime(RTCQualityGPS); @@ -209,7 +220,7 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) pos.battery_level = powerStatus->getBatteryChargePercent(); updateBatteryLevel(pos.battery_level); - // DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.latitude_i, pos.time, pos.battery_level); + DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.latitude_i, pos.time, pos.battery_level); // Update our current position in the local DB nodeDB.updatePosition(nodeDB.getNodeNum(), pos); From a0076eb394a763d9afcde488a773497abf142c78 Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Wed, 9 Dec 2020 13:42:36 +0800 Subject: [PATCH 21/21] better position debug output --- src/mesh/NodeDB.cpp | 3 ++- src/plugins/PositionPlugin.cpp | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 13c64bd41..de21cb58f 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -386,7 +386,8 @@ void NodeDB::updatePosition(uint32_t nodeId, const Position &p) { NodeInfo *info = getOrCreateNode(nodeId); - // we always trust our local timestamps more + DEBUG_MSG("DB update position node=0x%x time=%u, latI=%d, lonI=%d\n", nodeId, p.time, p.latitude_i, p.longitude_i); + info->position = p; info->has_position = true; updateGUIforNode = info; diff --git a/src/plugins/PositionPlugin.cpp b/src/plugins/PositionPlugin.cpp index 4b06f0eed..b0ce8e217 100644 --- a/src/plugins/PositionPlugin.cpp +++ b/src/plugins/PositionPlugin.cpp @@ -12,7 +12,6 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position // FIXME - we currently update position data in the DB only if the message was a broadcast or destined to us // it would be better to update even if the message was destined to others. - DEBUG_MSG("handled incoming position time=%u\n", p.time); if (p.time) { struct timeval tv; uint32_t secs = p.time;