From d11b023c85d0e93c83a76da8c5ef0f3dbb3ef4b7 Mon Sep 17 00:00:00 2001 From: geeksville Date: Mon, 3 Feb 2020 09:13:19 -0800 Subject: [PATCH] add beginnings of nodedb --- TODO.md | 7 ++- src/MeshRadio.cpp | 16 +------ src/MeshRadio.h | 6 +-- src/MeshService.cpp | 35 ++------------ src/MeshTypes.h | 13 +++++ src/NodeDB.cpp | 102 ++++++++++++++++++++++++++++++++++++++++ src/NodeDB.h | 52 ++++++++++++++++++++ src/mesh-pb-constants.h | 14 ++++++ 8 files changed, 194 insertions(+), 51 deletions(-) create mode 100644 src/MeshTypes.h create mode 100644 src/NodeDB.cpp create mode 100644 src/NodeDB.h create mode 100644 src/mesh-pb-constants.h diff --git a/TODO.md b/TODO.md index 5671b554..c62d8749 100644 --- a/TODO.md +++ b/TODO.md @@ -2,6 +2,9 @@ * have MeshService keep a node DB by sniffing user messages * have meshservice send location data on mesh (if device has a GPS) +* implement getCurrentTime() - set based off gps but then updated locally +* confirm second device receives that gps message and updates device db +* switch to my gui layout manager * have a state machine return the correct FromRadio packet to the phone, it isn't always going to be a MeshPacket. Do a notify on fromnum to force the radio to read our state machine generated packets * send my_node_num when phone sends WantsNodes * make jtag work on second board @@ -10,8 +13,8 @@ # Medium priority -* save our node db (and any rx packets waiting for phone) to flash -* send correct hw vendor in the bluetooth info +* save our node db (and any rx packets waiting for phone) to flash - see DeviceState protobuf +* send correct hw vendor in the bluetooth info - needed so the android app can update different radio models * use https://lastminuteengineers.com/esp32-sleep-modes-power-consumption/ association sleep pattern to save power - but see https://github.com/espressif/esp-idf/issues/2070 * correctly map nodeids to nodenums, currently we just do a proof of concept by always doing a broadcast * add interrupt detach/sleep mode config to lora radio so we can enable deepsleep without panicing diff --git a/src/MeshRadio.cpp b/src/MeshRadio.cpp index 68fff283..1188c2d3 100644 --- a/src/MeshRadio.cpp +++ b/src/MeshRadio.cpp @@ -7,27 +7,15 @@ #include #include "MeshRadio.h" #include "configuration.h" +#include "NodeDB.h" // Change to 434.0 or other frequency, must match RX's freq! #define RF95_FREQ 915.0 -/** - * get our starting (provisional) nodenum from flash. But check first if anyone else is using it, by trying to send a message to it (arping) - */ -NodeNum getDesiredNodeNum() -{ - uint8_t dmac[6]; - esp_efuse_mac_get_default(dmac); - - // FIXME not the right way to guess node numes - uint8_t r = dmac[5]; - assert(r != 0xff); // It better not be the broadcast address - return r; -} MeshRadio::MeshRadio(MemoryPool &_pool, PointerQueue &_rxDest) : rf95(NSS_GPIO, DIO0_GPIO), - manager(rf95, getDesiredNodeNum()), + manager(rf95, nodeDB.getNodeNum()), pool(_pool), rxDest(_rxDest), txQueue(MAX_TX_QUEUE) diff --git a/src/MeshRadio.h b/src/MeshRadio.h index a4829422..03103f7c 100644 --- a/src/MeshRadio.h +++ b/src/MeshRadio.h @@ -5,13 +5,9 @@ #include "MemoryPool.h" #include "mesh.pb.h" #include "PointerQueue.h" +#include "MeshTypes.h" -#define NODENUM_BROADCAST 255 -#define ERRNO_OK 0 -#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER -typedef int ErrorCode; -typedef uint8_t NodeNum; #define MAX_TX_QUEUE 8 // max number of packets which can be waiting for transmission diff --git a/src/MeshService.cpp b/src/MeshService.cpp index 02f74157..44655301 100644 --- a/src/MeshService.cpp +++ b/src/MeshService.cpp @@ -4,9 +4,10 @@ #include #include -#include "mesh.pb.h" +#include "mesh-pb-constants.h" #include "MeshService.h" #include "MeshBluetoothService.h" +#include "NodeDB.h" /* receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone. @@ -17,40 +18,13 @@ of packets we can delete just as soon as we are sure the phone has acked those p mesh - an instance of Mesh class. Which manages the interface to the mesh radio library, reception of packets from other nodes, arbitrating to select a node number and keeping the current nodedb. - -typedef in32_t NodeNum; - -class NodeInfo { - position; - last_seen - user -}; - -class NodeDB { - NodeNum provisionalNodeNum; // if we are trying to find a node num this is our current attempt - - NodeNum ourNodeNum; // -1 if not yet found - - HashMap nodes; -public: - /// don't do mesh based algoritm for node id assignment (initially) - instead just store in flash - possibly even in the initial alpha release do this hack - - /// if returns false, that means our node should send a DenyNodeNum response. If true, we think the number is okay for use - // bool handleWantNodeNum(NodeNum n); - - void handleDenyNodeNum(NodeNum FIXME read mesh proto docs, perhaps picking a random node num is not a great idea - and instead we should use a special 'im unconfigured node number' and include our desired node number in the wantnum message. the - unconfigured node num would only be used while initially joining the mesh so low odds of conflicting (especially if we randomly select - from a small number of nodenums which can be used temporarily for this operation). figure out what the lower level - mesh sw does if it does conflict? would it be better for people who are replying with denynode num to just broadcast their denial?) -}; - */ + MeshService service; #define MAX_PACKETS 32 // max number of packets which can be in flight (either queued from reception or queued for sending) -#define MAX_RX_TOPHONE 16 // max number of packets which can be waiting for delivery to android + #define MAX_RX_FROMRADIO 4 // max number of packets destined to our queue, we dispatch packets quickly so it doesn't need to be big MeshService::MeshService() @@ -60,6 +34,7 @@ MeshService::MeshService() fromNum(0), radio(packetPool, fromRadioQueue) { + // assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro } void MeshService::init() diff --git a/src/MeshTypes.h b/src/MeshTypes.h new file mode 100644 index 00000000..5a45f771 --- /dev/null +++ b/src/MeshTypes.h @@ -0,0 +1,13 @@ +#pragma once + +// low level types + +#include + +typedef uint8_t NodeNum; + +#define NODENUM_BROADCAST 255 +#define ERRNO_OK 0 +#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER + +typedef int ErrorCode; \ No newline at end of file diff --git a/src/NodeDB.cpp b/src/NodeDB.cpp new file mode 100644 index 00000000..23219bb0 --- /dev/null +++ b/src/NodeDB.cpp @@ -0,0 +1,102 @@ + +#include +#include + +#include +#include +#include "mesh-pb-constants.h" +#include "NodeDB.h" + +NodeDB nodeDB; + +/** + * get our starting (provisional) nodenum from flash. But check first if anyone else is using it, by trying to send a message to it (arping) + */ +static NodeNum getDesiredNodeNum() +{ + uint8_t dmac[6]; + esp_efuse_mac_get_default(dmac); + + // FIXME not the right way to guess node numes + uint8_t r = dmac[5]; + assert(r != 0xff); // It better not be the broadcast address + return r; +} + +NodeDB::NodeDB() : ourNodeNum(getDesiredNodeNum()), numNodes(0) +{ +} + +/// return number msecs since 1970 +uint64_t getCurrentTime() { + return 4403; // FIXME +} + +/// 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) +{ + if (mp.has_payload) + { + const SubPacket &p = mp.payload; + Serial.printf("Update DB node %x for %d\n", mp.from, p.which_variant); + if (p.which_variant != SubPacket_want_node_tag) // we don't create nodeinfo records for someone that is just trying to claim a nodenum + { + int oldNumNodes = numNodes; + NodeInfo *info = getOrCreateNode(mp.from); + + if (oldNumNodes != numNodes) + updateGUI = true; // we just created a nodeinfo + + info->last_seen.msecs = getCurrentTime(); + info->has_last_seen = true; + + switch (p.which_variant) + { + case SubPacket_position_tag: + info->position = p.variant.position; + info->has_position = true; + updateGUIforNode = info; + break; + + case SubPacket_user_tag: + info->user = p.variant.user; + info->has_user = true; + updateGUIforNode = info; + break; + + default: + break; // Ignore other packet types + } + } + } +} + +/// Find a node in our DB, return null for missing +NodeInfo *NodeDB::getNode(NodeNum n) +{ + for (int i = 0; i < numNodes; i++) + if (nodes[i].num == n) + return &nodes[i]; + + return NULL; +} + +/// Find a node in our DB, create an empty NodeInfo if missing +NodeInfo *NodeDB::getOrCreateNode(NodeNum n) +{ + NodeInfo *info = getNode(n); + + if (!n) + { + // add the node + assert(numNodes < MAX_NUM_NODES); + info = &nodes[numNodes++]; + + // everything is missing except the nodenum + memset(info, 0, sizeof(*info)); + info->num = n; + } + + return info; +} \ No newline at end of file diff --git a/src/NodeDB.h b/src/NodeDB.h new file mode 100644 index 00000000..84671119 --- /dev/null +++ b/src/NodeDB.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +#include "mesh-pb-constants.h" +#include "MeshTypes.h" + +class NodeDB { + // NodeNum provisionalNodeNum; // if we are trying to find a node num this is our current attempt + + NodeNum ourNodeNum; // -1 if not yet found + + // A NodeInfo for every node we've seen + // Eventually use a smarter datastructure + // HashMap nodes; + NodeInfo nodes[MAX_NUM_NODES]; + int numNodes; + + bool updateGUI; // we think the gui should definitely be redrawn + NodeInfo *updateGUIforNode; // if currently showing this node, we think you should update the GUI + +public: + /// don't do mesh based algoritm for node id assignment (initially) + /// instead just store in flash - possibly even in the initial alpha release do this hack + NodeDB(); + + /// 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 updateFrom(const MeshPacket &p); + + NodeNum getNodeNum() { return ourNodeNum; } + + /// if returns false, that means our node should send a DenyNodeNum response. If true, we think the number is okay for use + // bool handleWantNodeNum(NodeNum n); + + /* void handleDenyNodeNum(NodeNum FIXME read mesh proto docs, perhaps picking a random node num is not a great idea + and instead we should use a special 'im unconfigured node number' and include our desired node number in the wantnum message. the + unconfigured node num would only be used while initially joining the mesh so low odds of conflicting (especially if we randomly select + from a small number of nodenums which can be used temporarily for this operation). figure out what the lower level + mesh sw does if it does conflict? would it be better for people who are replying with denynode num to just broadcast their denial?) + */ + +private: + /// Find a node in our DB, return null for missing + NodeInfo *getNode(NodeNum n); + + /// Find a node in our DB, create an empty NodeInfo if missing + NodeInfo *getOrCreateNode(NodeNum n); +}; + +extern NodeDB nodeDB; diff --git a/src/mesh-pb-constants.h b/src/mesh-pb-constants.h new file mode 100644 index 00000000..6dd47222 --- /dev/null +++ b/src/mesh-pb-constants.h @@ -0,0 +1,14 @@ +#pragma once + +#include "mesh.pb.h" + +// this file defines constants which come from mesh.options + +// Tricky macro to let you find the sizeof a type member +#define member_size(type, member) sizeof(((type *)0)->member) + +/// max number of packets which can be waiting for delivery to android - note, this value comes from mesh.options protobuf +#define MAX_RX_TOPHONE (member_size(DeviceState, receive_queue) / member_size(DeviceState, receive_queue[0])) + +/// max number of nodes allowed in the mesh +#define MAX_NUM_NODES (member_size(DeviceState, node_db) / member_size(DeviceState, node_db[0]))