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