From ea24394110759a35f1aa141ea6809e74f4f677ab Mon Sep 17 00:00:00 2001 From: geeksville Date: Fri, 17 Apr 2020 11:52:20 -0700 Subject: [PATCH] add first cut of mesh naive flooding --- proto | 2 +- src/MeshService.cpp | 16 ++++--- src/MeshTypes.h | 1 + src/main.cpp | 4 ++ src/rf95/CustomRF95.cpp | 10 +++-- src/rf95/FloodingRouter.cpp | 90 +++++++++++++++++++++++++++++++++++++ src/rf95/Router.cpp | 4 +- src/rf95/Router.h | 8 ++-- 8 files changed, 117 insertions(+), 18 deletions(-) create mode 100644 src/rf95/FloodingRouter.cpp diff --git a/proto b/proto index 793d3e65..e06645d8 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 793d3e65ca66c3a0914e74a285a729429952a042 +Subproject commit e06645d8db16b9e4f23e74a931b8d5cd07bcbe3c diff --git a/src/MeshService.cpp b/src/MeshService.cpp index ce7fa697..a8a2ff60 100644 --- a/src/MeshService.cpp +++ b/src/MeshService.cpp @@ -214,20 +214,24 @@ void MeshService::handleToRadio(std::string s) switch (r.which_variant) { case ToRadio_packet_tag: { // If our phone is sending a position, see if we can use it to set our RTC - handleIncomingPosition(&r.variant.packet); // If it is a position packet, perhaps set our clock + MeshPacket &p = r.variant.packet; + handleIncomingPosition(&p); // If it is a position packet, perhaps set our clock - r.variant.packet.rx_time = gps.getValidTime(); // Record the time the packet arrived from the phone (so we update our - // nodedb for the local node) + if (p.from == 0) // If the phone didn't set a sending node ID, use ours + p.from = nodeDB.getNodeNum(); + + p.rx_time = gps.getValidTime(); // Record the time the packet arrived from the phone + // (so we update our nodedb for the local node) // Send the packet into the mesh - sendToMesh(packetPool.allocCopy(r.variant.packet)); + + sendToMesh(packetPool.allocCopy(p)); bool loopback = false; // if true send any packet the phone sends back itself (for testing) if (loopback) { - const MeshPacket *mp = &r.variant.packet; // no need to copy anymore because handle from radio assumes it should _not_ delete // packetPool.allocCopy(r.variant.packet); - handleFromRadio(mp); + handleFromRadio(&p); // handleFromRadio will tell the phone a new packet arrived } break; diff --git a/src/MeshTypes.h b/src/MeshTypes.h index 13ead046..ab4203e6 100644 --- a/src/MeshTypes.h +++ b/src/MeshTypes.h @@ -7,6 +7,7 @@ #include typedef uint8_t NodeNum; +typedef uint8_t PacketId; // A packet sequence number #define NODENUM_BROADCAST 255 #define ERRNO_OK 0 diff --git a/src/main.cpp b/src/main.cpp index b98e86c7..1fed2a74 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,6 +31,7 @@ #include "error.h" #include "power.h" // #include "rom/rtc.h" +#include "FloodingRouter.h" #include "screen.h" #include "sleep.h" #include @@ -60,6 +61,9 @@ static meshtastic::PowerStatus powerStatus; bool ssd1306_found; bool axp192_found; +FloodingRouter realRouter; +Router &router = realRouter; // Users of router don't care what sort of subclass implements that API + // ----------------------------------------------------------------------------- // Application // ----------------------------------------------------------------------------- diff --git a/src/rf95/CustomRF95.cpp b/src/rf95/CustomRF95.cpp index a2a7b17a..f1269cff 100644 --- a/src/rf95/CustomRF95.cpp +++ b/src/rf95/CustomRF95.cpp @@ -1,5 +1,5 @@ #include "CustomRF95.h" -#include "NodeDB.h" +#include "NodeDB.h" // FIXME, this class should not need to touch nodedb #include "assert.h" #include "configuration.h" #include @@ -97,6 +97,8 @@ void CustomRF95::handleInterrupt() mp->from = _rxHeaderFrom; mp->to = _rxHeaderTo; + mp->id = _rxHeaderId; + //_rxHeaderId = _buf[2]; //_rxHeaderFlags = _buf[3]; @@ -162,9 +164,11 @@ void CustomRF95::startSend(MeshPacket *txp) sendingPacket = txp; setHeaderTo(txp->to); - setHeaderFrom(nodeDB.getNodeNum()); // We must do this before each send, because we might have just changed our nodenum + setHeaderId(txp->id); - // setHeaderId(0); + // if the sender nodenum is zero, that means uninitialized + assert(txp->from); + setHeaderFrom(txp->from); // We must do this before each send, because we might have just changed our nodenum assert(numbytes <= 251); // Make sure we don't overflow the tiny max packet size diff --git a/src/rf95/FloodingRouter.cpp b/src/rf95/FloodingRouter.cpp new file mode 100644 index 00000000..388fbf3c --- /dev/null +++ b/src/rf95/FloodingRouter.cpp @@ -0,0 +1,90 @@ +#include "FloodingRouter.h" +#include "configuration.h" +#include "mesh-pb-constants.h" + +/// We clear our old flood record five minute after we see the last of it +#define FLOOD_EXPIRE_TIME (5 * 60 * 1000L) + +FloodingRouter::FloodingRouter() +{ + recentBroadcasts.reserve(MAX_NUM_NODES); // Prealloc the worst case # of records - to prevent heap fragmentation +} + +/** + * Send a packet on a suitable interface. This routine will + * later free() the packet to pool. This routine is not allowed to stall. + * If the txmit queue is full it might return an error + */ +ErrorCode FloodingRouter::send(MeshPacket *p) +{ + // We update our table of recent broadcasts, even for messages we send + wasSeenRecently(p); + + return Router::send(p); +} + +/** + * Called from loop() + * Handle any packet that is received by an interface on this node. + * Note: some packets may merely being passed through this node and will be forwarded elsewhere. + * + * Note: this method will free the provided packet + */ +void FloodingRouter::handleReceived(MeshPacket *p) +{ + if (wasSeenRecently(p)) { + DEBUG_MSG("Ignoring incoming floodmsg, because we've already seen it\n"); + packetPool.release(p); + } else { + if (p->to == NODENUM_BROADCAST && p->id != 0) { + DEBUG_MSG("Rebroadcasting received floodmsg to neighbors\n"); + // FIXME, wait a random delay + + MeshPacket *tosend = packetPool.allocCopy(*p); + // Note: we are careful to resend using the original senders node id + Router::send(tosend); // We are careful not to call our hooked version of send() + } + + // handle the packet as normal + Router::handleReceived(p); + } +} + +/** + * Update recentBroadcasts and return true if we have already seen this packet + */ +bool FloodingRouter::wasSeenRecently(const MeshPacket *p) +{ + if (p->to != NODENUM_BROADCAST) + return false; // Not a broadcast, so we don't care + + if (p->id == 0) + return false; // Not a floodable message ID, so we don't care + + uint32_t now = millis(); + for (int i = 0; i < recentBroadcasts.size();) { + BroadcastRecord &r = recentBroadcasts[i]; + + if ((now - r.rxTimeMsec) >= FLOOD_EXPIRE_TIME) { + DEBUG_MSG("Deleting old recentBroadcast %d\n", i); + recentBroadcasts.erase(recentBroadcasts.begin() + i); // delete old record + } else { + if (r.id == p->id && r.sender == p->from) { + // Update the time on this record to now + r.rxTimeMsec = now; + return true; + } + + i++; + } + } + + // Didn't find an existing record, make one + BroadcastRecord r; + r.id = p->id; + r.sender = p->from; + r.rxTimeMsec = now; + recentBroadcasts.push_back(r); + + return false; +} \ No newline at end of file diff --git a/src/rf95/Router.cpp b/src/rf95/Router.cpp index 715ab712..abd0006c 100644 --- a/src/rf95/Router.cpp +++ b/src/rf95/Router.cpp @@ -5,7 +5,7 @@ /** * Router todo * - * Implement basic interface and use it elsewhere in app + * DONE: Implement basic interface and use it elsewhere in app * Add naive flooding mixin (& drop duplicate rx broadcasts), add tools for sending broadcasts with incrementing sequence #s * Add an optional adjacent node only 'send with ack' mixin. If we timeout waiting for the ack, call handleAckTimeout(packet) * Add DSR mixin @@ -22,8 +22,6 @@ MemoryPool packetPool(MAX_PACKETS); -Router router; - /** * Constructor * diff --git a/src/rf95/Router.h b/src/rf95/Router.h index 00f34666..8f2ef6fa 100644 --- a/src/rf95/Router.h +++ b/src/rf95/Router.h @@ -8,8 +8,6 @@ #include "mesh.pb.h" #include - - /** * A mesh aware router that supports multiple interfaces. */ @@ -56,7 +54,7 @@ class Router */ virtual ErrorCode send(MeshPacket *p); - private: + protected: /** * Called from loop() * Handle any packet that is received by an interface on this node. @@ -64,7 +62,7 @@ class Router * * Note: this method will free the provided packet */ - void handleReceived(MeshPacket *p); + virtual void handleReceived(MeshPacket *p); }; -extern Router router; \ No newline at end of file +extern Router &router; \ No newline at end of file