diff --git a/docs/software/TODO.md b/docs/software/TODO.md index b47fdb95b..26fb08851 100644 --- a/docs/software/TODO.md +++ b/docs/software/TODO.md @@ -2,7 +2,7 @@ You probably don't care about this section - skip to the next one. -1.2 cleanup & multichannel support: +## 1.2 cleanup & multichannel support: * DONE call RouterPlugin for *all* packets - not just Router packets * DONE generate channel hash from the name of the channel+the psk (not just one or the other) @@ -21,31 +21,40 @@ You probably don't care about this section - skip to the next one. * DONE implement 'get channels' Admin plugin operation * DONE use get-channels from python * DONE use get channels & get settings from android -* use set-channel from python +* DONE use set-channel from python * DONE make settings changes from python work * DONE pthon should stop fetching channels once we've reached our first empty channel definition (hasSettings == true) * DONE add check for old devices with new API library * DONE release python api * DONE release protobufs * DONE release to developers +* DONE fix setch-fast in python tool +* age out pendingrequests in the python API +* DONE stress test channel download from python, sometimes it seems like we don't get all replies, bug was due to simultaneous android connection +* DONE combine acks and responses in a single message if possible (do routing plugin LAST and drop ACK if someone else has already replied) +* DONE don't send packets we received from the phone BACK TOWARDS THE PHONE (possibly use fromnode 0 for packets the phone sends?) * fix 1.1.50 android debug panel display -* add gui in android app for setting region -* stress test channel download from python, sometimes it seems like we don't get all replies -* investigate @mc-hamster report of heap corruption -* use set-channel from android -* DONE use set-user from android -* combine acks and responses in a single message if possible (do routing plugin LAST and drop ACK if someone else has already replied) -* don't send packets we received from the phone BACK TOWARDS THE PHONE (possibly use fromnode 0 for packets the phone sends?) -* use portuino TCP connection to debug with python API +* DONE test android channel setting +* DONE release to users +* DONE warn in android app about unset regions +* DONE use set-channel from android +* DONE add gui in android app for setting region * make python tests more exhaustive -* document the relationship between want_response (indicating remote node received it) and want_ack (indicating that this message should be sent reliably - and also get acks from the first rx node and naks if it is never delivered) -* stress test multi channel * pick default random admin key -* DONE android should stop fetching channels once we've reached our first empty channel definition (hasSettings == true) -* add channel restrictions for plugins (and restrict routing plugin to the "control" channel) +* exclude admin channels from URL? +* make a way to share just secondary channels via URL +* use single byte 'well known' channel names for the four default channel names (longslow etc), and for admin, gpio, etc... +* use presence of gpio channel to enable gpio ops, same for serial etc... * restrict gpio & serial & settings operations to the admin channel (unless local to the current node) -* warn in python api if we are too new to talk to the device code -* make a post warning about 1.2, telling how to stay on old android & python clients. link to this from the android dialog message and python version warning. +* add channel restrictions for plugins (and restrict routing plugin to the "control" channel) +* stress test multi channel +* investigate @mc-hamster report of heap corruption +* DONE use set-user from android +* use portuino TCP connection to debug with python API +* document the relationship between want_response (indicating remote node received it) and want_ack (indicating that this message should be sent reliably - and also get acks from the first rx node and naks if it is never delivered) +* DONE android should stop fetching channels once we've reached our first empty channel definition (hasSettings == true) +* DONE warn in python api if we are too new to talk to the device code +* DONE make a post warning about 1.2, telling how to stay on old android & python clients. link to this from the android dialog message and python version warning. * DONE "FIXME - move the radioconfig/user/channel READ operations into SettingsMessage as well" * DONE scrub protobufs to make sure they are absoloute minimum wiresize (in particular Data, ChannelSets and positions) * DONE change syncword (now ox2b) @@ -55,6 +64,7 @@ You probably don't care about this section - skip to the next one. * confirm we are still calling the plugins for messages inbound from the phone (or generated locally) * confirm we are still multi hop routing flood broadcasts * confirm we are still doing resends on unicast reliable packets +* add history to routed packets: https://meshtastic.discourse.group/t/packet-source-tracking/2764/2 * add support for full DSR unicast delivery * DONE move acks into routing * DONE make all subpackets different versions of data diff --git a/proto b/proto index 94bd0aae4..7c025b9a4 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 94bd0aae44e2c16c7776289225c804100c856cd4 +Subproject commit 7c025b9a4d54bb410ec17ee653122861b413f177 diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp index 85e08f536..09fc35e6c 100644 --- a/src/PowerFSM.cpp +++ b/src/PowerFSM.cpp @@ -178,10 +178,11 @@ void PowerFSM_setup() bool isLowPower = radioConfig.preferences.is_low_power || isRouter; /* To determine if we're externally powered, assumptions - 1) If we're powered up and there's no battery, we must be getting power externally. - 2) If we detect USB power from the power management chip, we must be getting power externally. + 1) If we're powered up and there's no battery, we must be getting power externally. (because we'd be dead otherwise) + + 2) If we detect USB power from the power management chip, we must be getting power externally. */ - bool hasPower = (powerStatus && !powerStatus->getHasBattery()) || (!isLowPower && powerStatus && powerStatus->getHasUSB()); + bool hasPower = !isLowPower && powerStatus && (!powerStatus->getHasBattery() || powerStatus->getHasUSB()); DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower); powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout"); diff --git a/src/esp32/ESP32CryptoEngine.cpp b/src/esp32/ESP32CryptoEngine.cpp index 9d86ffeb0..f04614c72 100644 --- a/src/esp32/ESP32CryptoEngine.cpp +++ b/src/esp32/ESP32CryptoEngine.cpp @@ -54,7 +54,7 @@ class ESP32CryptoEngine : public CryptoEngine static uint8_t scratch[MAX_BLOCKSIZE]; size_t nc_off = 0; - // DEBUG_MSG("ESP32 encrypt!\n"); + // DEBUG_MSG("ESP32 crypt fr=%x, num=%x, numBytes=%d!\n", fromNode, (uint32_t) packetNum, numBytes); initNonce(fromNode, packetNum); assert(numBytes <= MAX_BLOCKSIZE); memcpy(scratch, bytes, numBytes); diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index d04b9960b..6e52abdde 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -229,7 +229,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state displayedNodeNum = 0; // Not currently showing a node pane MeshPacket &mp = devicestate.rx_text_message; - NodeInfo *node = nodeDB.getNode(mp.from); + NodeInfo *node = nodeDB.getNode(getFrom(&mp)); // DEBUG_MSG("drawing text message from 0x%x: %s\n", mp.from, // mp.decoded.variant.data.decoded.bytes); diff --git a/src/mesh/Channels.cpp b/src/mesh/Channels.cpp index 76ea3fb92..414930182 100644 --- a/src/mesh/Channels.cpp +++ b/src/mesh/Channels.cpp @@ -275,10 +275,11 @@ const char *Channels::getPrimaryName() bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash) { if(chIndex > getNumChannels() || getHash(chIndex) != channelHash) { - DEBUG_MSG("Skipping channel %d due to invalid hash/index\n", chIndex); + // DEBUG_MSG("Skipping channel %d (hash %x) due to invalid hash/index, want=%x\n", chIndex, getHash(chIndex), channelHash); return false; } else { + DEBUG_MSG("Using channel %d (hash 0x%x)\n", chIndex, channelHash); setCrypto(chIndex); return true; } diff --git a/src/mesh/CryptoEngine.cpp b/src/mesh/CryptoEngine.cpp index 74f4b7836..59cb7ad8f 100644 --- a/src/mesh/CryptoEngine.cpp +++ b/src/mesh/CryptoEngine.cpp @@ -4,6 +4,10 @@ void CryptoEngine::setKey(const CryptoKey &k) { DEBUG_MSG("Installing AES%d key!\n", k.length * 8); + /* for(uint8_t i = 0; i < k.length; i++) + DEBUG_MSG("%02x ", k.bytes[i]); + DEBUG_MSG("\n"); */ + key = k; } diff --git a/src/mesh/DSRRouter.cpp b/src/mesh/DSRRouter.cpp index 5fa6b1609..f7d8588e4 100644 --- a/src/mesh/DSRRouter.cpp +++ b/src/mesh/DSRRouter.cpp @@ -69,7 +69,7 @@ void DSRRouter::sniffReceived(const MeshPacket *p, const Routing *c) // ignore rebroadcasts. // this will also add records for any ACKs we receive for our messages if (p->to != NODENUM_BROADCAST || p->hop_limit != HOP_RELIABLE) { - addRoute(p->from, p->from, 0); // We are adjacent with zero hops + addRoute(getFrom(p), getFrom(p), 0); // We are adjacent with zero hops } if (c) diff --git a/src/mesh/FloodingRouter.cpp b/src/mesh/FloodingRouter.cpp index 049d038c7..491501e91 100644 --- a/src/mesh/FloodingRouter.cpp +++ b/src/mesh/FloodingRouter.cpp @@ -31,7 +31,7 @@ void FloodingRouter::sniffReceived(const MeshPacket *p, const Routing *c) { // If a broadcast, possibly _also_ send copies out into the mesh. // (FIXME, do something smarter than naive flooding here) - if (p->to == NODENUM_BROADCAST && p->hop_limit > 0 && p->from != getNodeNum()) { + if (p->to == NODENUM_BROADCAST && p->hop_limit > 0 && getFrom(p) != getNodeNum()) { if (p->id != 0) { MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it diff --git a/src/mesh/MeshPacketQueue.cpp b/src/mesh/MeshPacketQueue.cpp index 56db3feb3..6c964e509 100644 --- a/src/mesh/MeshPacketQueue.cpp +++ b/src/mesh/MeshPacketQueue.cpp @@ -71,7 +71,7 @@ static PacketId findId; static bool isMyPacket(MeshPacket *p) { - return p->id == findId && p->from == findFrom; + return p->id == findId && getFrom(p) == findFrom; } /** Attempt to find and remove a packet from this queue. Returns true the packet which was removed from the queue */ diff --git a/src/mesh/MeshPlugin.cpp b/src/mesh/MeshPlugin.cpp index 6fd3c72b8..e17cc5475 100644 --- a/src/mesh/MeshPlugin.cpp +++ b/src/mesh/MeshPlugin.cpp @@ -1,23 +1,28 @@ #include "MeshPlugin.h" -#include "NodeDB.h" #include "MeshService.h" +#include "NodeDB.h" #include std::vector *MeshPlugin::plugins; const MeshPacket *MeshPlugin::currentRequest; +/** + * If any of the current chain of plugins has already sent a reply, it will be here. This is useful to allow + * the RoutingPlugin to avoid sending redundant acks + */ + MeshPacket *MeshPlugin::currentReply; + MeshPlugin::MeshPlugin(const char *_name) : name(_name) { // Can't trust static initalizer order, so we check each time - if(!plugins) + if (!plugins) plugins = new std::vector(); plugins->push_back(this); } -void MeshPlugin::setup() { -} +void MeshPlugin::setup() {} MeshPlugin::~MeshPlugin() { @@ -31,6 +36,8 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) assert(mp.which_payloadVariant == MeshPacket_decoded_tag); // I think we are guarnteed the packet is decoded by this point? + currentReply = NULL; // No reply yet + // Was this message directed to us specifically? Will be false if we are sniffing someone elses packets auto ourNodeNum = nodeDB.getNodeNum(); bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum; @@ -48,15 +55,16 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) bool handled = pi.handleReceived(mp); // Possibly send replies (but only if the message was directed to us specifically, i.e. not for promiscious sniffing) + // also: we only let the one plugin send a reply, once that happens, remaining plugins are not considered - // NOTE: we send a reply *even if the (non broadcast) request was from us* which is unfortunate but necessary because currently when the phone - // sends things, it sends things using the local node ID as the from address. A better solution (FIXME) would be to let phones - // have their own distinct addresses and we 'route' to them like any other node. - if (mp.decoded.want_response && toUs && (mp.from != ourNodeNum || mp.to == ourNodeNum)) { + // NOTE: we send a reply *even if the (non broadcast) request was from us* which is unfortunate but necessary because + // currently when the phone sends things, it sends things using the local node ID as the from address. A better + // solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like any other + // node. + if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) { pi.sendResponse(mp); DEBUG_MSG("Plugin %s sent a response\n", pi.name); - } - else { + } else { DEBUG_MSG("Plugin %s considered\n", pi.name); } if (handled) { @@ -64,11 +72,17 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) break; } } - + pi.currentRequest = NULL; } - if(!pluginFound) + if(currentReply) { + DEBUG_MSG("Sending response\n"); + service.sendToMesh(currentReply); + currentReply = NULL; + } + + if (!pluginFound) DEBUG_MSG("No plugins interested in portnum=%d\n", mp.decoded.portnum); } @@ -76,39 +90,43 @@ void MeshPlugin::callPlugins(const MeshPacket &mp) * so that subclasses can (optionally) send a response back to the original sender. Implementing this method * is optional */ -void MeshPlugin::sendResponse(const MeshPacket &req) { +void MeshPlugin::sendResponse(const MeshPacket &req) +{ auto r = allocReply(); - if(r) { - DEBUG_MSG("Sending response\n"); + if (r) { setReplyTo(r, req); - service.sendToMesh(r); - } - else { + currentReply = r; + } else { // Ignore - this is now expected behavior for routing plugin (because it ignores some replies) // 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 +/** 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) { + */ +void setReplyTo(MeshPacket *p, const MeshPacket &to) +{ assert(p->which_payloadVariant == MeshPacket_decoded_tag); // Should already be set by now - p->to = to.from; - p->want_ack = to.want_ack; + p->to = getFrom(&to); + + // No need for an ack if we are just delivering locally (it just generates an ignored ack) + p->want_ack = (to.from != 0) ? to.want_ack : false; + if(p->priority == MeshPacket_Priority_UNSET) + p->priority = MeshPacket_Priority_RELIABLE; p->decoded.request_id = to.id; } -std::vector MeshPlugin::GetMeshPluginsWithUIFrames() { +std::vector MeshPlugin::GetMeshPluginsWithUIFrames() +{ - std::vector pluginsWithUIFrames; + std::vector pluginsWithUIFrames; for (auto i = plugins->begin(); i != plugins->end(); ++i) { auto &pi = **i; - if ( pi.wantUIFrame()) { + if (pi.wantUIFrame()) { DEBUG_MSG("Plugin wants a UI Frame\n"); pluginsWithUIFrames.push_back(&pi); } } return pluginsWithUIFrames; - } diff --git a/src/mesh/MeshPlugin.h b/src/mesh/MeshPlugin.h index 7220a198b..7234b6b0a 100644 --- a/src/mesh/MeshPlugin.h +++ b/src/mesh/MeshPlugin.h @@ -82,6 +82,13 @@ class MeshPlugin private: + /** + * If any of the current chain of plugins has already sent a reply, it will be here. This is useful to allow + * the RoutingPlugin to avoid sending redundant acks + */ + static MeshPacket *currentReply; + friend class ReliableRouter; + /** 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 diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp index 753d942df..5590fa9cf 100644 --- a/src/mesh/MeshService.cpp +++ b/src/mesh/MeshService.cpp @@ -13,8 +13,8 @@ #include "RTC.h" #include "main.h" #include "mesh-pb-constants.h" -#include "plugins/PositionPlugin.h" #include "plugins/NodeInfoPlugin.h" +#include "plugins/PositionPlugin.h" #include "power.h" /* @@ -51,8 +51,6 @@ MeshService service; #include "Router.h" - - MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE) { // assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro @@ -67,7 +65,6 @@ void MeshService::init() gpsObserver.observe(&gps->newStatus); } - int MeshService::handleFromRadio(const MeshPacket *mp) { powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping @@ -117,7 +114,8 @@ bool MeshService::reloadConfig() void MeshService::reloadOwner() { assert(nodeInfoPlugin); - nodeInfoPlugin->sendOurNodeInfo(); + if(nodeInfoPlugin) + nodeInfoPlugin->sendOurNodeInfo(); nodeDB.saveToDisk(); } @@ -128,8 +126,12 @@ void MeshService::reloadOwner() */ void MeshService::handleToRadio(MeshPacket &p) { - if (p.from == 0) // If the phone didn't set a sending node ID, use ours - p.from = nodeDB.getNodeNum(); + if (p.from != 0) { // We don't let phones assign nodenums to their sent messages + DEBUG_MSG("Warning: phone tried to pick a nodenum, we don't allow that.\n"); + p.from = 0; + } else { + // p.from = nodeDB.getNodeNum(); + } if (p.id == 0) p.id = generatePacketId(); // If the phone didn't supply one, then pick one @@ -151,7 +153,8 @@ void MeshService::handleToRadio(MeshPacket &p) } /** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */ -bool MeshService::cancelSending(PacketId id) { +bool MeshService::cancelSending(PacketId id) +{ return router->cancelSending(nodeDB.getNodeNum(), id); } @@ -168,29 +171,36 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies) NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); assert(node); - DEBUG_MSG("Sending network ping to 0x%x, with position=%d, wantReplies=%d\n", dest, node->has_position, wantReplies); - assert(positionPlugin && nodeInfoPlugin); - if (node->has_position) - positionPlugin->sendOurPosition(dest, wantReplies); - else - nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies); + if (node->has_position) { + if(positionPlugin) { + DEBUG_MSG("Sending position ping to 0x%x, wantReplies=%d\n", dest, wantReplies); + positionPlugin->sendOurPosition(dest, wantReplies); + } + } + else { + if(nodeInfoPlugin) { + DEBUG_MSG("Sending nodeinfo ping to 0x%x, wantReplies=%d\n", dest, wantReplies); + nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies); + } + } } - -NodeInfo *MeshService::refreshMyNodeInfo() { +NodeInfo *MeshService::refreshMyNodeInfo() +{ NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum()); assert(node); // We might not have a position yet for our local node, in that case, at least try to send the time - if(!node->has_position) { + if (!node->has_position) { memset(&node->position, 0, sizeof(node->position)); node->has_position = true; } - + Position &position = node->position; // Update our local node info with our position (even if we don't decide to update anyone else) - position.time = getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid + position.time = + getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid position.battery_level = powerStatus->getBatteryChargePercent(); updateBatteryLevel(position.battery_level); @@ -209,11 +219,10 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused) pos.altitude = gps->altitude; pos.latitude_i = gps->latitude; pos.longitude_i = gps->longitude; - } - else { + } else { // The GPS has lost lock, if we are fixed position we should just keep using // the old position - if(radioConfig.preferences.fixed_position) { + if (radioConfig.preferences.fixed_position) { DEBUG_MSG("WARNING: Using fixed position\n"); } else { // throw away old position diff --git a/src/mesh/MeshTypes.h b/src/mesh/MeshTypes.h index 06f2bf480..e7e6265a4 100644 --- a/src/mesh/MeshTypes.h +++ b/src/mesh/MeshTypes.h @@ -32,4 +32,10 @@ typedef uint32_t PacketId; // A packet sequence number typedef int ErrorCode; /// Alloc and free packets to our global, ISR safe pool -extern Allocator &packetPool; \ No newline at end of file +extern Allocator &packetPool; + +/** + * Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on the local node. + * If from is zero this function returns our node number instead + */ +NodeNum getFrom(const MeshPacket *p); \ No newline at end of file diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index 22c1ccb6f..166e159f2 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -66,6 +66,14 @@ NodeNum displayedNodeNum; NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {} +/** + * Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on the local node. + * If from is zero this function returns our node number instead + */ +NodeNum getFrom(const MeshPacket *p) { + return (p->from == 0) ? nodeDB.getNodeNum() : p->from; +} + bool NodeDB::resetRadioConfig() { bool didFactoryReset = false; @@ -406,7 +414,7 @@ void NodeDB::updateFrom(const MeshPacket &mp) if (mp.which_payloadVariant == MeshPacket_decoded_tag) { DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time); - NodeInfo *info = getOrCreateNode(mp.from); + NodeInfo *info = getOrCreateNode(getFrom(&mp)); if (mp.rx_time) { // if the packet has a valid timestamp use it to update our last_seen info->has_position = true; // at least the time is valid diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index 29c35369f..e5f1b6564 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -154,7 +154,7 @@ extern NodeDB nodeDB; */ // Our delay functions check for this for times that should never expire -#define DELAY_FOREVER 0xffffffff +#define NODE_DELAY_FOREVER 0xffffffff #define IF_ROUTER(routerVal, normalVal) (radioConfig.preferences.is_router ? (routerVal) : (normalVal)) @@ -168,8 +168,8 @@ PREF_GET(position_broadcast_secs, IF_ROUTER(12 * 60 * 60, 15 * 60)) PREF_GET(wait_bluetooth_secs, IF_ROUTER(1, 60)) PREF_GET(screen_on_secs, 60) -PREF_GET(mesh_sds_timeout_secs, IF_ROUTER(DELAY_FOREVER, 2 * 60 * 60)) -PREF_GET(phone_sds_timeout_sec, IF_ROUTER(DELAY_FOREVER, 2 * 60 * 60)) +PREF_GET(mesh_sds_timeout_secs, IF_ROUTER(NODE_DELAY_FOREVER, 2 * 60 * 60)) +PREF_GET(phone_sds_timeout_sec, IF_ROUTER(NODE_DELAY_FOREVER, 2 * 60 * 60)) PREF_GET(sds_secs, 365 * 24 * 60 * 60) // We default to sleeping (with bluetooth off for 5 minutes at a time). This seems to be a good tradeoff between @@ -183,3 +183,4 @@ PREF_GET(min_wake_secs, 10) * might have changed is incremented. Allows others to detect they might now be on a new channel. */ extern uint32_t radioGeneration; + diff --git a/src/mesh/PacketHistory.cpp b/src/mesh/PacketHistory.cpp index 3d1884ace..19ce41bc8 100644 --- a/src/mesh/PacketHistory.cpp +++ b/src/mesh/PacketHistory.cpp @@ -26,7 +26,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate) // DEBUG_MSG("Deleting old broadcast record %d\n", i); recentPackets.erase(recentPackets.begin() + i); // delete old record } else { - if (r.id == p->id && r.sender == p->from) { + if (r.id == p->id && r.sender == getFrom(p)) { DEBUG_MSG("Found existing packet record for fr=0x%x,to=0x%x,id=%d\n", p->from, p->to, p->id); // Update the time on this record to now @@ -43,7 +43,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate) if (withUpdate) { PacketRecord r; r.id = p->id; - r.sender = p->from; + r.sender = getFrom(p); r.rxTimeMsec = now; recentPackets.push_back(r); printPacket("Adding packet record", p); diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp index a7797c255..6e2ac479b 100644 --- a/src/mesh/PhoneAPI.cpp +++ b/src/mesh/PhoneAPI.cpp @@ -59,6 +59,7 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength) } // return (lastContactMsec != 0) && + memset(&toRadioScratch, 0, sizeof(toRadioScratch)); if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) { switch (toRadioScratch.which_payloadVariant) { case ToRadio_packet_tag: { @@ -178,9 +179,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf) // Do we have a message from the mesh? if (fromRadioScratch.which_payloadVariant != 0) { // Encapsulate as a FromRadio packet - DEBUG_MSG("encoding toPhone packet to phone variant=%d", fromRadioScratch.which_payloadVariant); size_t numbytes = pb_encode_to_bytes(buf, FromRadio_size, FromRadio_fields, &fromRadioScratch); - DEBUG_MSG(", %d bytes\n", numbytes); + // DEBUG_MSG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payloadVariant, numbytes); return numbytes; } diff --git a/src/mesh/ProtobufPlugin.h b/src/mesh/ProtobufPlugin.h index c68885df2..30874b24b 100644 --- a/src/mesh/ProtobufPlugin.h +++ b/src/mesh/ProtobufPlugin.h @@ -58,11 +58,12 @@ template class ProtobufPlugin : protected SinglePortPlugin // it would be better to update even if the message was destined to others. auto &p = mp.decoded; - DEBUG_MSG("Received %s from=0x%0x, id=0x%x, payloadlen=%d\n", name, mp.from, mp.id, p.payload.size); + DEBUG_MSG("Received %s from=0x%0x, id=0x%x, portnum=%d, payloadlen=%d\n", name, mp.from, mp.id, p.portnum, p.payload.size); T scratch; T *decoded = NULL; if(mp.decoded.portnum == ourPortNum) { + memset(&scratch, 0, sizeof(scratch)); if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch)) decoded = &scratch; else diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp index 7b8aec605..49dc69927 100644 --- a/src/mesh/RadioInterface.cpp +++ b/src/mesh/RadioInterface.cpp @@ -1,16 +1,16 @@ +#include "configuration.h" #include "RadioInterface.h" #include "Channels.h" #include "MeshRadio.h" #include "MeshService.h" #include "NodeDB.h" #include "assert.h" -#include "configuration.h" +#include "Router.h" #include "sleep.h" #include #include #include -#include "Channels.h" #define RDEF(name, freq, spacing, num_ch, power_limit) \ { \ @@ -25,9 +25,27 @@ const RegionInfo regions[] = { RDEF(KR, 921.9f, 0.2f, 8, 0), // KR channel settings (KR920-923) Start from TTN download channel // freq. (921.9f is for download, others are for uplink) RDEF(TW, 923.0f, 0.2f, 10, 0), // TW channel settings (AS2 bandplan 923-925MHz) + RDEF(RU, 868.9f, 0.2f, 2, 20), // See notes below RDEF(Unset, 903.08f, 2.16f, 13, 0) // Assume US freqs if unset, Must be last }; +/* Notes about the RU bandplan (from @denis-d in https://meshtastic.discourse.group/t/russian-band-plan-proposal/2786/2): + +According to Annex 12 to GKRCh (National Radio Frequency Commission) decision № 18-46-03-1 (September 11th 2018) https://digital.gov.ru/uploaded/files/prilozhenie-12-k-reshenyu-gkrch-18-46-03-1.pdf 1 +We have 3 options for 868 MHz: + +864,0 - 865,0 MHz ERP 25mW, Duty Cycle 0.1% (3.6 sec in hour) or LBT (Listen Before Talk), prohibited in airports. +866,0 - 868,0 MHz ERP 25mW, Duty Cycle 1% or LBT, PSD (Power Spectrum Density) 1000mW/MHz, prohibited in airports +868,7 - 869,2 MHz ERP 100mW, Duty Cycle 10% or LBT, no resctrictions +and according to RP2-1.0.2 LoRaWAN® Regional Parameters RP2-1.0.2 LoRaWAN® Regional Parameters - LoRa Alliance® +I propose to use following meshtastic bandplan: +1 channel - central frequency 868.9MHz 125kHz band +Protective band - 75kHz +2 channel - central frequency 869.1MHz 125kHz band + +RDEF(RU, 868.9f, 0.2f, 2, 20) +*/ + const RegionInfo *myRegion; void initRegion() @@ -119,11 +137,11 @@ uint32_t RadioInterface::getTxDelayMsec() void printPacket(const char *prefix, const MeshPacket *p) { - DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d Ch0x%x", prefix, p->id, p->from & 0xff, p->to & 0xff, p->want_ack, - p->hop_limit, p->channel); + DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d Ch0x%x", prefix, p->id, p->from & 0xff, p->to & 0xff, + p->want_ack, p->hop_limit, p->channel); if (p->which_payloadVariant == MeshPacket_decoded_tag) { auto &s = p->decoded; - + DEBUG_MSG(" Portnum=%d", s.portnum); if (s.want_response) @@ -332,6 +350,10 @@ void RadioInterface::deliverToReceiver(MeshPacket *p) { assert(rxDest); assert(rxDest->enqueue(p, 0)); // NOWAIT - fixme, if queue is full, delete older messages + + // Nasty hack because our threading is primitive. interfaces shouldn't need to know about routers FIXME + if (router) + router->setReceivedMessage(); } /*** diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index da743c389..962aabeb7 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -1,4 +1,5 @@ #include "ReliableRouter.h" +#include "MeshPlugin.h" #include "MeshTypes.h" #include "configuration.h" #include "mesh-pb-constants.h" @@ -26,15 +27,18 @@ ErrorCode ReliableRouter::send(MeshPacket *p) bool ReliableRouter::shouldFilterReceived(const MeshPacket *p) { + // Note: do not use getFrom() here, because we want to ignore messages sent from phone if (p->to == NODENUM_BROADCAST && p->from == getNodeNum()) { printPacket("Rx someone rebroadcasting for us", p); // We are seeing someone rebroadcast one of our broadcast attempts. // If this is the first time we saw this, cancel any retransmissions we have queued up and generate an internal ack for // the original sending process. - if (stopRetransmission(p->from, p->id)) { - DEBUG_MSG("Someone is retransmitting for us, generate implicit ack\n"); - sendAckNak(Routing_Error_NONE, p->from, p->id); + if (stopRetransmission(getFrom(p), p->id)) { + DEBUG_MSG("generating implicit ack\n"); + // NOTE: we do NOT check p->wantAck here because p is the INCOMING rebroadcast and that packet is not expected to be + // marked as wantAck + sendAckNak(Routing_Error_NONE, getFrom(p), p->id); } } @@ -60,23 +64,26 @@ void ReliableRouter::sniffReceived(const MeshPacket *p, const Routing *c) if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability // - not DSR routing) if (p->want_ack) { - sendAckNak(Routing_Error_NONE, p->from, p->id); + if (MeshPlugin::currentReply) + DEBUG_MSG("Someone else has replied to this message, no need for a 2nd ack"); + else + sendAckNak(Routing_Error_NONE, getFrom(p), p->id); } - // If the payload is valid, look for ack/nak - if (c) { - PacketId ackId = c->error_reason == Routing_Error_NONE ? p->decoded.request_id : 0; - PacketId nakId = c->error_reason != Routing_Error_NONE ? p->decoded.request_id : 0; + // We consider an ack to be either a !routing packet with a request ID or a routing packet with !error + PacketId ackId = ((c && c->error_reason == Routing_Error_NONE) || !c) ? p->decoded.request_id : 0; - // We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records - if (ackId || nakId) { - if (ackId) { - DEBUG_MSG("Received a ack=%d, stopping retransmissions\n", ackId); - stopRetransmission(p->to, ackId); - } else { - DEBUG_MSG("Received a nak=%d, stopping retransmissions\n", nakId); - stopRetransmission(p->to, nakId); - } + // A nak is a routing packt that has an error code + PacketId nakId = (c && c->error_reason != Routing_Error_NONE) ? p->decoded.request_id : 0; + + // We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records + if (ackId || nakId) { + if (ackId) { + DEBUG_MSG("Received a ack for 0x%x, stopping retransmissions\n", ackId); + stopRetransmission(p->to, ackId); + } else { + DEBUG_MSG("Received a nak for 0x%x, stopping retransmissions\n", nakId); + stopRetransmission(p->to, nakId); } } } @@ -130,8 +137,9 @@ PendingPacket *ReliableRouter::startRetransmission(MeshPacket *p) auto id = GlobalPacketId(p); auto rec = PendingPacket(p); + stopRetransmission(getFrom(p), p->id); + setNextTx(&rec); - stopRetransmission(p->from, p->id); pending[id] = rec; return &pending[id]; @@ -151,18 +159,21 @@ int32_t ReliableRouter::doRetransmissions() ++nextIt; // we use this odd pattern because we might be deleting it... auto &p = it->second; + bool stillValid = true; // assume we'll keep this record around + // FIXME, handle 51 day rolloever here!!! if (p.nextTxMsec <= now) { if (p.numRetransmissions == 0) { - DEBUG_MSG("Reliable send failed, returning a nak fr=0x%x,to=0x%x,id=%d\n", p.packet->from, p.packet->to, + DEBUG_MSG("Reliable send failed, returning a nak for fr=0x%x,to=0x%x,id=0x%x\n", p.packet->from, p.packet->to, p.packet->id); - sendAckNak(Routing_Error_MAX_RETRANSMIT, p.packet->from, p.packet->id); + sendAckNak(Routing_Error_MAX_RETRANSMIT, getFrom(p.packet), p.packet->id); // Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived - which // allows the DSR version to still be able to look at the PendingPacket stopRetransmission(it->first); + stillValid = false; // just deleted it } else { - DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=%d, tries left=%d\n", p.packet->from, p.packet->to, - p.packet->id, p.numRetransmissions); + DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=0x%x, tries left=%d\n", p.packet->from, + p.packet->to, p.packet->id, p.numRetransmissions); // Note: we call the superclass version because we don't want to have our version of send() add a new // retransmission record @@ -172,8 +183,10 @@ int32_t ReliableRouter::doRetransmissions() --p.numRetransmissions; setNextTx(&p); } - } else { - // Not yet time + } + + if (stillValid) { + // Update our desired sleep delay int32_t t = p.nextTxMsec - now; d = min(t, d); @@ -181,4 +194,14 @@ int32_t ReliableRouter::doRetransmissions() } return d; +} + +void ReliableRouter::setNextTx(PendingPacket *pending) +{ + assert(iface); + auto d = iface->getRetransmissionMsec(pending->packet); + pending->nextTxMsec = millis() + d; + DEBUG_MSG("Setting next retransmission in %u msecs: ", d); + printPacket("", pending->packet); + setReceivedMessage(); // Run ASAP, so we can figure out our correct sleep time } \ No newline at end of file diff --git a/src/mesh/ReliableRouter.h b/src/mesh/ReliableRouter.h index e6a71f423..db161b984 100644 --- a/src/mesh/ReliableRouter.h +++ b/src/mesh/ReliableRouter.h @@ -15,7 +15,7 @@ struct GlobalPacketId { GlobalPacketId(const MeshPacket *p) { - node = p->from; + node = getFrom(p); id = p->id; } @@ -125,7 +125,5 @@ class ReliableRouter : public FloodingRouter */ int32_t doRetransmissions(); - void setNextTx(PendingPacket *pending) { - assert(iface); - pending->nextTxMsec = millis() + iface->getRetransmissionMsec(pending->packet); } + void setNextTx(PendingPacket *pending); }; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index a6afe952e..8f566dffe 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -111,16 +111,21 @@ void Router::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom) void Router::abortSendAndNak(Routing_Error err, MeshPacket *p) { DEBUG_MSG("Error=%d, returning NAK and dropping packet.\n", err); - sendAckNak(Routing_Error_NO_INTERFACE, p->from, p->id); + sendAckNak(Routing_Error_NO_INTERFACE, getFrom(p), p->id); packetPool.release(p); } +void Router::setReceivedMessage() { + setInterval(0); // Run ASAP, so we can figure out our correct sleep time +} + ErrorCode Router::sendLocal(MeshPacket *p) { // No need to deliver externally if the destination is the local node if (p->to == nodeDB.getNodeNum()) { printPacket("Enqueuing local", p); fromRadioQueue.enqueue(p); + setReceivedMessage(); return ERRNO_OK; } else if (!iface) { // We must be sending to remote nodes also, fail if no interface found @@ -138,6 +143,13 @@ ErrorCode Router::sendLocal(MeshPacket *p) } } +void printBytes(const char *label, const uint8_t *p, size_t numbytes) { + DEBUG_MSG("%s: ", label); + for(size_t i = 0; i < numbytes; i++) + DEBUG_MSG("%02x ", p[i]); + DEBUG_MSG("\n"); +} + /** * Send a packet on a suitable interface. This routine will * later free() the packet to pool. This routine is not allowed to stall. @@ -155,6 +167,10 @@ ErrorCode Router::send(MeshPacket *p) if (p->to == NODENUM_BROADCAST) p->want_ack = false; + // Up until this point we might have been using 0 for the from address (if it started with the phone), but when we send over + // the lora we need to make sure we have replaced it with our local address + p->from = getFrom(p); + // If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it) assert(p->which_payloadVariant == MeshPacket_encrypted_tag || @@ -164,6 +180,8 @@ ErrorCode Router::send(MeshPacket *p) if (p->which_payloadVariant == MeshPacket_decoded_tag) { static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union + // printPacket("pre encrypt", p); // portnum valid here + size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded); if (numbytes > MAX_RHPACKETLEN) { @@ -171,6 +189,8 @@ ErrorCode Router::send(MeshPacket *p) return ERRNO_TOO_LARGE; } + //printBytes("plaintext", bytes, numbytes); + auto hash = channels.setActiveByIndex(p->channel); if (hash < 0) { // No suitable channel could be found for sending @@ -180,7 +200,7 @@ ErrorCode Router::send(MeshPacket *p) // Now that we are encrypting the packet channel should be the hash (no longer the index) p->channel = hash; - crypto->encrypt(p->from, p->id, numbytes, bytes); + crypto->encrypt(getFrom(p), p->id, numbytes, bytes); // Copy back into the packet and set the variant type memcpy(p->encrypted.bytes, bytes, numbytes); @@ -221,19 +241,26 @@ bool Router::perhapsDecode(MeshPacket *p) if (channels.decryptForHash(chIndex, p->channel)) { // Try to decrypt the packet if we can static uint8_t bytes[MAX_RHPACKETLEN]; + size_t rawSize = p->encrypted.size; + assert(rawSize <= sizeof(bytes)); memcpy(bytes, p->encrypted.bytes, - p->encrypted - .size); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf - crypto->decrypt(p->from, p->id, p->encrypted.size, bytes); + rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf + crypto->decrypt(p->from, p->id, rawSize, bytes); + + //printBytes("plaintext", bytes, p->encrypted.size); // Take those raw bytes and convert them back into a well structured protobuf we can understand - if (!pb_decode_from_bytes(bytes, p->encrypted.size, Data_fields, &p->decoded)) { - DEBUG_MSG("Invalid protobufs in received mesh packet (bad psk?!\n"); - } else { + memset(&p->decoded, 0, sizeof(p->decoded)); + if (!pb_decode_from_bytes(bytes, rawSize, Data_fields, &p->decoded)) { + DEBUG_MSG("Invalid protobufs in received mesh packet (bad psk?)!\n"); + } else if(p->decoded.portnum == PortNum_UNKNOWN_APP) { + DEBUG_MSG("Invalid portnum (bad psk?)!\n"); + } + else { // parsing was successful + p->which_payloadVariant = MeshPacket_decoded_tag; // change type to decoded p->channel = chIndex; // change to store the index instead of the hash - // printPacket("decoded message", p); - p->which_payloadVariant = MeshPacket_decoded_tag; + printPacket("decoded message", p); return true; } } @@ -258,11 +285,10 @@ void Router::handleReceived(MeshPacket *p) p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone // Take those raw bytes and convert them back into a well structured protobuf we can understand - bool decoded = perhapsDecode(p); - printPacket("handleReceived", p); - DEBUG_MSG("decoded=%d\n", decoded); + bool decoded = perhapsDecode(p); if (decoded) { // parsing was successful, queue for our recipient + printPacket("handleReceived", p); // call any promiscious plugins here, make a (non promisiocous) plugin for forwarding messages to phone api // sniffReceived(p); @@ -273,7 +299,7 @@ void Router::handleReceived(MeshPacket *p) void Router::perhapsHandleReceived(MeshPacket *p) { assert(radioConfig.has_preferences); - bool ignore = is_in_repeated(radioConfig.preferences.ignore_incoming, p->from); + bool ignore = is_in_repeated(radioConfig.preferences.ignore_incoming, getFrom(p)); if (ignore) DEBUG_MSG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from); diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 6e6bf2c2a..6e217017b 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -63,6 +63,11 @@ class Router : protected concurrency::OSThread * @return our local nodenum */ NodeNum getNodeNum(); + /** Wake up the router thread ASAP, because we just queued a message for it. + * FIXME, this is kinda a hack because we don't have a nice way yet to say 'wake us because we are 'blocked on this queue' + */ + void setReceivedMessage(); + protected: friend class RoutingPlugin; diff --git a/src/mesh/generated/admin.pb.h b/src/mesh/generated/admin.pb.h index f9be1cada..7c0ac9145 100644 --- a/src/mesh/generated/admin.pb.h +++ b/src/mesh/generated/admin.pb.h @@ -67,7 +67,7 @@ extern const pb_msgdesc_t AdminMessage_msg; #define AdminMessage_fields &AdminMessage_msg /* Maximum encoded size of messages (where known) */ -#define AdminMessage_size 338 +#define AdminMessage_size 351 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/deviceonly.pb.h b/src/mesh/generated/deviceonly.pb.h index a1e6de3af..dd3451849 100644 --- a/src/mesh/generated/deviceonly.pb.h +++ b/src/mesh/generated/deviceonly.pb.h @@ -82,7 +82,7 @@ extern const pb_msgdesc_t DeviceState_msg; #define DeviceState_fields &DeviceState_msg /* Maximum encoded size of messages (where known) */ -#define DeviceState_size 6156 +#define DeviceState_size 6169 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/generated/portnums.pb.h b/src/mesh/generated/portnums.pb.h index 758db47a6..5904a548c 100644 --- a/src/mesh/generated/portnums.pb.h +++ b/src/mesh/generated/portnums.pb.h @@ -20,10 +20,10 @@ typedef enum _PortNum { PortNum_ADMIN_APP = 6, PortNum_REPLY_APP = 32, PortNum_IP_TUNNEL_APP = 33, - PortNum_ENVIRONMENTAL_MEASUREMENT_APP = 34, PortNum_SERIAL_APP = 64, PortNum_STORE_FORWARD_APP = 65, PortNum_RANGE_TEST_APP = 66, + PortNum_ENVIRONMENTAL_MEASUREMENT_APP = 67, PortNum_PRIVATE_APP = 256, PortNum_ATAK_FORWARDER = 257, PortNum_MAX = 511 diff --git a/src/mesh/generated/radioconfig.pb.c b/src/mesh/generated/radioconfig.pb.c index 85aa7aa18..716b804e8 100644 --- a/src/mesh/generated/radioconfig.pb.c +++ b/src/mesh/generated/radioconfig.pb.c @@ -17,3 +17,4 @@ PB_BIND(RadioConfig_UserPreferences, RadioConfig_UserPreferences, 2) + diff --git a/src/mesh/generated/radioconfig.pb.h b/src/mesh/generated/radioconfig.pb.h index b8a6bfd7a..9ea021f13 100644 --- a/src/mesh/generated/radioconfig.pb.h +++ b/src/mesh/generated/radioconfig.pb.h @@ -19,7 +19,8 @@ typedef enum _RegionCode { RegionCode_JP = 5, RegionCode_ANZ = 6, RegionCode_KR = 7, - RegionCode_TW = 8 + RegionCode_TW = 8, + RegionCode_RU = 9 } RegionCode; typedef enum _ChargeCurrent { @@ -56,6 +57,10 @@ typedef enum _LocationSharing { LocationSharing_LocDisabled = 2 } LocationSharing; +typedef enum _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType { + RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0 +} RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType; + /* Struct definitions */ typedef struct _RadioConfig_UserPreferences { uint32_t position_broadcast_secs; @@ -106,6 +111,9 @@ typedef struct _RadioConfig_UserPreferences { uint32_t environmental_measurement_plugin_read_error_count_threshold; uint32_t environmental_measurement_plugin_update_interval; uint32_t environmental_measurement_plugin_recovery_interval; + bool environmental_measurement_plugin_display_farenheit; + RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType environmental_measurement_plugin_sensor_type; + uint32_t environmental_measurement_plugin_sensor_pin; } RadioConfig_UserPreferences; typedef struct _RadioConfig { @@ -116,8 +124,8 @@ typedef struct _RadioConfig { /* Helper constants for enums */ #define _RegionCode_MIN RegionCode_Unset -#define _RegionCode_MAX RegionCode_TW -#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TW+1)) +#define _RegionCode_MAX RegionCode_RU +#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_RU+1)) #define _ChargeCurrent_MIN ChargeCurrent_MAUnset #define _ChargeCurrent_MAX ChargeCurrent_MA1320 @@ -131,6 +139,10 @@ typedef struct _RadioConfig { #define _LocationSharing_MAX LocationSharing_LocDisabled #define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1)) +#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 +#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MAX RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 +#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_ARRAYSIZE ((RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType)(RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11+1)) + #ifdef __cplusplus extern "C" { @@ -138,9 +150,9 @@ extern "C" { /* Initializer values for message structs */ #define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default} -#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0} #define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero} -#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0} /* Field tags (for use in manual encoding/decoding) */ #define RadioConfig_UserPreferences_position_broadcast_secs_tag 1 @@ -190,6 +202,9 @@ extern "C" { #define RadioConfig_UserPreferences_environmental_measurement_plugin_read_error_count_threshold_tag 142 #define RadioConfig_UserPreferences_environmental_measurement_plugin_update_interval_tag 143 #define RadioConfig_UserPreferences_environmental_measurement_plugin_recovery_interval_tag 144 +#define RadioConfig_UserPreferences_environmental_measurement_plugin_display_farenheit_tag 145 +#define RadioConfig_UserPreferences_environmental_measurement_plugin_sensor_type_tag 146 +#define RadioConfig_UserPreferences_environmental_measurement_plugin_sensor_pin_tag 147 #define RadioConfig_preferences_tag 1 /* Struct field encoding specification for nanopb */ @@ -246,7 +261,10 @@ X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_measurement_ X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_screen_enabled, 141) \ X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_read_error_count_threshold, 142) \ X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_update_interval, 143) \ -X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_recovery_interval, 144) +X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_recovery_interval, 144) \ +X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_display_farenheit, 145) \ +X(a, STATIC, SINGULAR, UENUM, environmental_measurement_plugin_sensor_type, 146) \ +X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_sensor_pin, 147) #define RadioConfig_UserPreferences_CALLBACK NULL #define RadioConfig_UserPreferences_DEFAULT NULL @@ -258,8 +276,8 @@ extern const pb_msgdesc_t RadioConfig_UserPreferences_msg; #define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg /* Maximum encoded size of messages (where known) */ -#define RadioConfig_size 335 -#define RadioConfig_UserPreferences_size 332 +#define RadioConfig_size 348 +#define RadioConfig_UserPreferences_size 345 #ifdef __cplusplus } /* extern "C" */ diff --git a/src/mesh/mesh-pb-constants.cpp b/src/mesh/mesh-pb-constants.cpp index 337e585de..fac31872c 100644 --- a/src/mesh/mesh-pb-constants.cpp +++ b/src/mesh/mesh-pb-constants.cpp @@ -18,8 +18,8 @@ size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc pb_ostream_t stream = pb_ostream_from_buffer(destbuf, destbufsize); if (!pb_encode(&stream, fields, src_struct)) { - DEBUG_MSG("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream)); - assert(0); // FIXME - panic + DEBUG_MSG("Panic: can't encode protobuf %s, did you make a field too large?\n", PB_GET_ERROR(&stream)); + assert(0); // If this asser fails it probably means you made a field too large for the max limits specified in mesh.options } else { return stream.bytes_written; } diff --git a/src/plugins/AdminPlugin.cpp b/src/plugins/AdminPlugin.cpp index bba1b39a9..7e0a67074 100644 --- a/src/plugins/AdminPlugin.cpp +++ b/src/plugins/AdminPlugin.cpp @@ -24,6 +24,12 @@ void AdminPlugin::handleGetRadio(const MeshPacket &req) // We create the reply here AdminMessage r = AdminMessage_init_default; r.get_radio_response = devicestate.radio; + + // NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior. + // So even if we internally use 0 to represent 'use default' we still need to send the value we are + // using to the app (so that even old phone apps work with new device loads). + r.get_radio_response.preferences.ls_secs = getPref_ls_secs(); + r.which_variant = AdminMessage_get_radio_response_tag; reply = allocDataProtobuf(r); } diff --git a/src/plugins/ExternalNotificationPlugin.cpp b/src/plugins/ExternalNotificationPlugin.cpp index 754259828..42865f3cc 100644 --- a/src/plugins/ExternalNotificationPlugin.cpp +++ b/src/plugins/ExternalNotificationPlugin.cpp @@ -147,7 +147,7 @@ bool ExternalNotificationPluginRadio::handleReceived(const MeshPacket &mp) auto &p = mp.decoded; - if (mp.from != nodeDB.getNodeNum()) { + if (getFrom(&mp) != nodeDB.getNodeNum()) { // TODO: This may be a problem if messages are sent in unicide, but I'm not sure if it will. // Need to know if and how this could be a problem. diff --git a/src/plugins/NodeInfoPlugin.cpp b/src/plugins/NodeInfoPlugin.cpp index 2524ffa58..f5f3ae194 100644 --- a/src/plugins/NodeInfoPlugin.cpp +++ b/src/plugins/NodeInfoPlugin.cpp @@ -12,7 +12,7 @@ bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User *pp { auto p = *pptr; - nodeDB.updateUser(mp.from, p); + nodeDB.updateUser(getFrom(&mp), p); bool wasBroadcast = mp.to == NODENUM_BROADCAST; @@ -65,8 +65,7 @@ int32_t NodeInfoPlugin::runOnce() currentGeneration = radioGeneration; DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies); - assert(nodeInfoPlugin); - nodeInfoPlugin->sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies) + sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies) return getPref_position_broadcast_secs() * 1000; } diff --git a/src/plugins/Plugins.cpp b/src/plugins/Plugins.cpp index 65f1f5f03..ca4cb52f4 100644 --- a/src/plugins/Plugins.cpp +++ b/src/plugins/Plugins.cpp @@ -20,7 +20,6 @@ */ void setupPlugins() { - routingPlugin = new RoutingPlugin(); adminPlugin = new AdminPlugin(); nodeInfoPlugin = new NodeInfoPlugin(); positionPlugin = new PositionPlugin(); @@ -48,4 +47,7 @@ void setupPlugins() // new StoreForwardPlugin(); new EnvironmentalMeasurementPlugin(); #endif + + // NOTE! This plugin must be added LAST because it likes to check for replies from other plugins and avoid sending extra acks + routingPlugin = new RoutingPlugin(); } \ No newline at end of file diff --git a/src/plugins/PositionPlugin.cpp b/src/plugins/PositionPlugin.cpp index 251605865..b6f76ad72 100644 --- a/src/plugins/PositionPlugin.cpp +++ b/src/plugins/PositionPlugin.cpp @@ -30,7 +30,7 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position perhapsSetRTC(RTCQualityFromNet, &tv); } - nodeDB.updatePosition(mp.from, p); + nodeDB.updatePosition(getFrom(&mp), p); return false; // Let others look at this message also if they want } diff --git a/src/plugins/RoutingPlugin.cpp b/src/plugins/RoutingPlugin.cpp index 18427bdca..f6c2ea03a 100644 --- a/src/plugins/RoutingPlugin.cpp +++ b/src/plugins/RoutingPlugin.cpp @@ -9,11 +9,12 @@ RoutingPlugin *routingPlugin; bool RoutingPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Routing *r) { - DEBUG_MSG("Routing sniffing", &mp); + printPacket("Routing sniffing", &mp); router->sniffReceived(&mp, r); // FIXME - move this to a non promsicious PhoneAPI plugin? - if (mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) { + // Note: we are careful not to send back packets that started with the phone back to the phone + if ((mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) && (mp.from != 0)) { printPacket("Delivering rx packet", &mp); service.handleFromRadio(&mp); } diff --git a/src/plugins/SerialPlugin.cpp b/src/plugins/SerialPlugin.cpp index 060f05ba1..c93bf8521 100644 --- a/src/plugins/SerialPlugin.cpp +++ b/src/plugins/SerialPlugin.cpp @@ -160,7 +160,7 @@ bool SerialPluginRadio::handleReceived(const MeshPacket &mp) // DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n", // nodeDB.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes); - if (mp.from == nodeDB.getNodeNum()) { + if (getFrom(&mp) == nodeDB.getNodeNum()) { /* * If radioConfig.preferences.serialplugin_echo is true, then echo the packets that are sent out back to the TX diff --git a/src/plugins/TextMessagePlugin.cpp b/src/plugins/TextMessagePlugin.cpp index 8da2a10f9..b285363aa 100644 --- a/src/plugins/TextMessagePlugin.cpp +++ b/src/plugins/TextMessagePlugin.cpp @@ -8,7 +8,7 @@ TextMessagePlugin *textMessagePlugin; bool TextMessagePlugin::handleReceived(const MeshPacket &mp) { auto &p = mp.decoded; - DEBUG_MSG("Received text msg from=0x%0x, id=%d, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes); + DEBUG_MSG("Received text msg from=0x%0x, id=0x%x, 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. diff --git a/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp b/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp index b332efd6e..69482dbc7 100644 --- a/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp +++ b/src/plugins/esp32/EnvironmentalMeasurementPlugin.cpp @@ -10,20 +10,10 @@ #include #include -EnvironmentalMeasurementPlugin *environmentalMeasurementPlugin; -EnvironmentalMeasurementPluginRadio *environmentalMeasurementPluginRadio; - -EnvironmentalMeasurementPlugin::EnvironmentalMeasurementPlugin() : concurrency::OSThread("EnvironmentalMeasurementPlugin") {} - -uint32_t sensor_read_error_count = 0; - -#define DHT_11_GPIO_PIN 13 #define DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000 // Some sensors (the DHT11) have a minimum required duration between read attempts #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true -DHT dht(DHT_11_GPIO_PIN,DHT11); - #ifdef HAS_EINK // The screen is bigger so use bigger fonts @@ -49,11 +39,15 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() { Uncomment the preferences below if you want to use the plugin without having to configure it from the PythonAPI or WebUI. */ + /*radioConfig.preferences.environmental_measurement_plugin_measurement_enabled = 1; radioConfig.preferences.environmental_measurement_plugin_screen_enabled = 1; radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold = 5; radioConfig.preferences.environmental_measurement_plugin_update_interval = 30; - radioConfig.preferences.environmental_measurement_plugin_recovery_interval = 600;*/ + radioConfig.preferences.environmental_measurement_plugin_recovery_interval = 60; + radioConfig.preferences.environmental_measurement_plugin_display_farenheit = true; + radioConfig.preferences.environmental_measurement_plugin_sensor_pin = 13; + radioConfig.preferences.environmental_measurement_plugin_sensor_type = RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType::RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11;*/ if (! (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){ // If this plugin is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it @@ -62,20 +56,32 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() { if (firstTime) { // This is the first time the OSThread library has called this function, so do some setup - DEBUG_MSG("EnvironmentalMeasurement: Initializing\n"); - environmentalMeasurementPluginRadio = new EnvironmentalMeasurementPluginRadio(); + firstTime = 0; - // begin reading measurements from the sensor - // DHT have a max read-rate of 1HZ, so we should wait at least 1 second - // after initializing the sensor before we try to read from it. - // returning the interval here means that the next time OSThread - // calls our plugin, we'll run the other branch of this if statement - // and actually do a "sendOurEnvironmentalMeasurement()" + if (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled) { + DEBUG_MSG("EnvironmentalMeasurement: Initializing\n"); // it's possible to have this plugin enabled, only for displaying values on the screen. // therefore, we should only enable the sensor loop if measurement is also enabled - dht.begin(); + switch(radioConfig.preferences.environmental_measurement_plugin_sensor_type) { + case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11: + dht = new DHT(radioConfig.preferences.environmental_measurement_plugin_sensor_pin,DHT11); + this->dht->begin(); + this->dht->read(); + DEBUG_MSG("EnvironmentalMeasurement: Opened DHT11 on pin: %d\n",radioConfig.preferences.environmental_measurement_plugin_sensor_pin); + break; + default: + DEBUG_MSG("EnvironmentalMeasurement: Invalid sensor type selected; Disabling plugin"); + return (INT32_MAX); + break; + } + // begin reading measurements from the sensor + // DHT have a max read-rate of 1HZ, so we should wait at least 1 second + // after initializing the sensor before we try to read from it. + // returning the interval here means that the next time OSThread + // calls our plugin, we'll run the other branch of this if statement + // and actually do a "sendOurEnvironmentalMeasurement()" return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); } return (INT32_MAX); @@ -96,6 +102,7 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() { "EnvironmentalMeasurement: TEMPORARILY DISABLED; The environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Will retry reads in %d seconds\n", radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold, radioConfig.preferences.environmental_measurement_plugin_recovery_interval); + sensor_read_error_count = 0; return(radioConfig.preferences.environmental_measurement_plugin_recovery_interval*1000); } DEBUG_MSG( @@ -110,7 +117,7 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() { sensor_read_error_count, radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold-sensor_read_error_count); } - if (! environmentalMeasurementPluginRadio->sendOurEnvironmentalMeasurement() ){ + if (!sendOurEnvironmentalMeasurement() ){ // if we failed to read the sensor, then try again // as soon as we can according to the maximum polling frequency return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS); @@ -123,25 +130,16 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() { #endif } -bool EnvironmentalMeasurementPluginRadio::wantUIFrame() { +bool EnvironmentalMeasurementPlugin::wantUIFrame() { return radioConfig.preferences.environmental_measurement_plugin_screen_enabled; } -void EnvironmentalMeasurementPluginRadio::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -{ - display->setTextAlignment(TEXT_ALIGN_LEFT); - display->setFont(FONT_MEDIUM); - display->drawString(x, y, "Environment"); - display->setFont(FONT_SMALL); - display->drawString(x, y += fontHeight(FONT_MEDIUM), lastSender+": T:"+ String(lastMeasurement.temperature,2) + " H:" + String(lastMeasurement.relative_humidity,2)); - -} - String GetSenderName(const MeshPacket &mp) { String sender; - if (nodeDB.getNode(mp.from)){ - sender = nodeDB.getNode(mp.from)->user.short_name; + auto node = nodeDB.getNode(getFrom(&mp)); + if (node){ + sender = node->user.short_name; } else { sender = "UNK"; @@ -149,55 +147,99 @@ String GetSenderName(const MeshPacket &mp) { return sender; } -bool EnvironmentalMeasurementPluginRadio::handleReceivedProtobuf(const MeshPacket &mp, const EnvironmentalMeasurement *pptr) +uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp) { + uint32_t now = getTime(); + + uint32_t last_seen = mp->rx_time; + int delta = (int)(now - last_seen); + if (delta < 0) // our clock must be slightly off still - not set from GPS yet + delta = 0; + + return delta; + +} + + +float EnvironmentalMeasurementPlugin::CelsiusToFarenheit(float c) { + return (c*9)/5 + 32; +} + + +void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { - const EnvironmentalMeasurement &p = *pptr; + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(FONT_MEDIUM); + display->drawString(x, y, "Environment"); + if (lastMeasurementPacket == nullptr) { + display->setFont(FONT_SMALL); + display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement"); + //DEBUG_MSG("EnvironmentalMeasurement: No previous measurement; not drawing frame\n"); + return; + } + + EnvironmentalMeasurement lastMeasurement; + + uint32_t agoSecs = GetTimeSinceMeshPacket(lastMeasurementPacket); + String lastSender = GetSenderName(*lastMeasurementPacket); + + auto &p = lastMeasurementPacket->decoded; + if (!pb_decode_from_bytes(p.payload.bytes, + p.payload.size, + EnvironmentalMeasurement_fields, + &lastMeasurement)) { + display->setFont(FONT_SMALL); + display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error"); + DEBUG_MSG("EnvironmentalMeasurement: unable to decode last packet"); + return; + } + + display->setFont(FONT_SMALL); + String last_temp = String(lastMeasurement.temperature,0) +"°C"; + if (radioConfig.preferences.environmental_measurement_plugin_display_farenheit){ + last_temp = String(CelsiusToFarenheit(lastMeasurement.temperature),0) +"°F";; + } + + display->drawString(x, y += fontHeight(FONT_MEDIUM), lastSender+": "+last_temp +"/"+ String(lastMeasurement.relative_humidity,0) + "%("+String(agoSecs)+"s)"); + +} + +bool EnvironmentalMeasurementPlugin::handleReceivedProtobuf(const MeshPacket &mp, const EnvironmentalMeasurement *p) +{ if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled || radioConfig.preferences.environmental_measurement_plugin_screen_enabled)){ // If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume return false; } - bool wasBroadcast = mp.to == NODENUM_BROADCAST; String sender = GetSenderName(mp); - - // Show new nodes on LCD screen - if (DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN && wasBroadcast) { - String lcd = String("Env Measured: ") +sender + "\n" + - "T: " + p.temperature + "\n" + - "H: " + p.relative_humidity + "\n"; - screen->print(lcd.c_str()); - } - DEBUG_MSG("-----------------------------------------\n"); DEBUG_MSG("EnvironmentalMeasurement: Received data from %s\n", sender); - DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", p.relative_humidity); - DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", p.temperature); + DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", p->relative_humidity); + DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", p->temperature); + + lastMeasurementPacket = packetPool.allocCopy(mp); - lastMeasurement = p; - lastSender = sender; return false; // Let others look at this message also if they want } -bool EnvironmentalMeasurementPluginRadio::sendOurEnvironmentalMeasurement(NodeNum dest, bool wantReplies) +bool EnvironmentalMeasurementPlugin::sendOurEnvironmentalMeasurement(NodeNum dest, bool wantReplies) { EnvironmentalMeasurement m; m.barometric_pressure = 0; // TODO: Add support for barometric sensors - m.relative_humidity = dht.readHumidity(); - m.temperature = dht.readTemperature();; - DEBUG_MSG("-----------------------------------------\n"); DEBUG_MSG("EnvironmentalMeasurement: Read data\n"); - DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity); - DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature); - - if (isnan(m.relative_humidity) || isnan(m.temperature) ){ + if (!this->dht->read(true)){ sensor_read_error_count++; DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n"); return false; } + m.relative_humidity = this->dht->readHumidity(); + m.temperature = this->dht->readTemperature(); + + DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity); + DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature); sensor_read_error_count = 0; diff --git a/src/plugins/esp32/EnvironmentalMeasurementPlugin.h b/src/plugins/esp32/EnvironmentalMeasurementPlugin.h index e29ea983e..3123cc0f0 100644 --- a/src/plugins/esp32/EnvironmentalMeasurementPlugin.h +++ b/src/plugins/esp32/EnvironmentalMeasurementPlugin.h @@ -3,61 +3,32 @@ #include "../mesh/generated/environmental_measurement.pb.h" #include #include +#include - -class EnvironmentalMeasurementPlugin : private concurrency::OSThread +class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public ProtobufPlugin { - bool firstTime = 1; - public: - EnvironmentalMeasurementPlugin(); + EnvironmentalMeasurementPlugin(): concurrency::OSThread("EnvironmentalMeasurementPlugin"), ProtobufPlugin("EnvironmentalMeasurement", PortNum_ENVIRONMENTAL_MEASUREMENT_APP, &EnvironmentalMeasurement_msg) { + lastMeasurementPacket = nullptr; + } + virtual bool wantUIFrame(); + virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); 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 EnvironmentalMeasurement *p); virtual int32_t runOnce(); -}; - -extern EnvironmentalMeasurementPlugin *environmentalMeasurementPlugin; - -/** - * EnvironmentalMeasurementPluginRadio plugin for sending/receiving environmental measurements to/from the mesh - */ -class EnvironmentalMeasurementPluginRadio : public ProtobufPlugin -{ - public: - /** Constructor - * name is for debugging output - */ - EnvironmentalMeasurementPluginRadio() : ProtobufPlugin("EnvironmentalMeasurement", PortNum_ENVIRONMENTAL_MEASUREMENT_APP, &EnvironmentalMeasurement_msg) { - lastMeasurement.barometric_pressure = nanf(""); - lastMeasurement.relative_humidity = nanf(""); - lastMeasurement.temperature = nanf(""); - lastSender = "N/A"; - } - /** * Send our EnvironmentalMeasurement into the mesh */ bool sendOurEnvironmentalMeasurement(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false); - virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); - - 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 EnvironmentalMeasurement *p); - - virtual bool wantUIFrame(); - - private: - - EnvironmentalMeasurement lastMeasurement; - - String lastSender; - -}; - -extern EnvironmentalMeasurementPluginRadio *environmentalMeasurementPluginRadio; \ No newline at end of file + float CelsiusToFarenheit(float c); + bool firstTime = 1; + DHT* dht; + const MeshPacket *lastMeasurementPacket; + uint32_t sensor_read_error_count = 0; +}; \ No newline at end of file diff --git a/src/plugins/esp32/RangeTestPlugin.cpp b/src/plugins/esp32/RangeTestPlugin.cpp index 227deba4d..2332b687a 100644 --- a/src/plugins/esp32/RangeTestPlugin.cpp +++ b/src/plugins/esp32/RangeTestPlugin.cpp @@ -133,7 +133,7 @@ bool RangeTestPluginRadio::handleReceived(const MeshPacket &mp) // DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n", // nodeDB.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes); - if (mp.from != nodeDB.getNodeNum()) { + if (getFrom(&mp) != nodeDB.getNodeNum()) { // DEBUG_MSG("* * Message came from the mesh\n"); // Serial2.println("* * Message came from the mesh"); @@ -144,7 +144,7 @@ bool RangeTestPluginRadio::handleReceived(const MeshPacket &mp) */ - NodeInfo *n = nodeDB.getNode(mp.from); + NodeInfo *n = nodeDB.getNode(getFrom(&mp)); if (radioConfig.preferences.range_test_plugin_save) { appendFile(mp); @@ -209,7 +209,7 @@ bool RangeTestPluginRadio::appendFile(const MeshPacket &mp) { auto &p = mp.decoded; - NodeInfo *n = nodeDB.getNode(mp.from); + NodeInfo *n = nodeDB.getNode(getFrom(&mp)); /* DEBUG_MSG("-----------------------------------------\n"); DEBUG_MSG("p.payload.bytes \"%s\"\n", p.payload.bytes); @@ -290,7 +290,7 @@ bool RangeTestPluginRadio::appendFile(const MeshPacket &mp) fileToAppend.printf("??:??:??,"); // Time } - fileToAppend.printf("%d,", mp.from); // From + fileToAppend.printf("%d,", getFrom(&mp)); // From fileToAppend.printf("%s,", n->user.long_name); // Long Name fileToAppend.printf("%f,", n->position.latitude_i * 1e-7); // Sender Lat fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long diff --git a/src/plugins/esp32/StoreForwardPlugin.cpp b/src/plugins/esp32/StoreForwardPlugin.cpp index 43af6dbb3..5fec31ad9 100644 --- a/src/plugins/esp32/StoreForwardPlugin.cpp +++ b/src/plugins/esp32/StoreForwardPlugin.cpp @@ -263,12 +263,12 @@ bool StoreForwardPluginRadio::handleReceived(const MeshPacket &mp) #ifndef NO_ESP32 if (radioConfig.preferences.store_forward_plugin_enabled) { - if (mp.from != nodeDB.getNodeNum()) { + if (getFrom(&mp) != nodeDB.getNodeNum()) { // DEBUG_MSG("Store & Forward Plugin -- Print Start ---------- ---------- ---------- ---------- ----------\n\n\n"); // DEBUG_MSG("%s (id=0x%08x Fr0x%02x To0x%02x, WantAck%d, HopLim%d", prefix, p->id, p->from & 0xff, p->to & 0xff, // p->want_ack, p->hop_limit); printPacket("----- PACKET FROM RADIO -----", &mp); - uint32_t sawTime = storeForwardPlugin->sawNode(mp.from & 0xffffffff); + uint32_t sawTime = storeForwardPlugin->sawNode(getFrom(&mp) & 0xffffffff); DEBUG_MSG("We last saw this node (%u), %u sec ago\n", mp.from & 0xffffffff, (millis() - sawTime) / 1000); if (mp.decoded.portnum == PortNum_UNKNOWN_APP) { diff --git a/version.properties b/version.properties index c77424e6a..d0634458c 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 1 minor = 2 -build = 0 +build = 6