mirror of
https://github.com/meshtastic/firmware.git
synced 2025-05-02 03:56:47 +00:00
commit
77bac11d82
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
You probably don't care about this section - skip to the next one.
|
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 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)
|
* 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 implement 'get channels' Admin plugin operation
|
||||||
* DONE use get-channels from python
|
* DONE use get-channels from python
|
||||||
* DONE use get channels & get settings from android
|
* 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 make settings changes from python work
|
||||||
* DONE pthon should stop fetching channels once we've reached our first empty channel definition (hasSettings == true)
|
* 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 add check for old devices with new API library
|
||||||
* DONE release python api
|
* DONE release python api
|
||||||
* DONE release protobufs
|
* DONE release protobufs
|
||||||
* DONE release to developers
|
* 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
|
* fix 1.1.50 android debug panel display
|
||||||
* add gui in android app for setting region
|
* DONE test android channel setting
|
||||||
* stress test channel download from python, sometimes it seems like we don't get all replies
|
* DONE release to users
|
||||||
* investigate @mc-hamster report of heap corruption
|
* DONE warn in android app about unset regions
|
||||||
* use set-channel from android
|
* DONE use set-channel from android
|
||||||
* DONE use set-user from android
|
* DONE add gui in android app for setting region
|
||||||
* 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
|
|
||||||
* make python tests more exhaustive
|
* 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
|
* pick default random admin key
|
||||||
* DONE android should stop fetching channels once we've reached our first empty channel definition (hasSettings == true)
|
* exclude admin channels from URL?
|
||||||
* add channel restrictions for plugins (and restrict routing plugin to the "control" channel)
|
* 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)
|
* 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
|
* add channel restrictions for plugins (and restrict routing plugin to the "control" channel)
|
||||||
* 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.
|
* 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 "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 scrub protobufs to make sure they are absoloute minimum wiresize (in particular Data, ChannelSets and positions)
|
||||||
* DONE change syncword (now ox2b)
|
* 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 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 multi hop routing flood broadcasts
|
||||||
* confirm we are still doing resends on unicast reliable packets
|
* 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
|
* add support for full DSR unicast delivery
|
||||||
* DONE move acks into routing
|
* DONE move acks into routing
|
||||||
* DONE make all subpackets different versions of data
|
* DONE make all subpackets different versions of data
|
||||||
|
2
proto
2
proto
@ -1 +1 @@
|
|||||||
Subproject commit 94bd0aae44e2c16c7776289225c804100c856cd4
|
Subproject commit 7c025b9a4d54bb410ec17ee653122861b413f177
|
@ -178,10 +178,11 @@ void PowerFSM_setup()
|
|||||||
bool isLowPower = radioConfig.preferences.is_low_power || isRouter;
|
bool isLowPower = radioConfig.preferences.is_low_power || isRouter;
|
||||||
|
|
||||||
/* To determine if we're externally powered, assumptions
|
/* 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.
|
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.
|
|
||||||
|
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);
|
DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower);
|
||||||
powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout");
|
powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout");
|
||||||
|
@ -54,7 +54,7 @@ class ESP32CryptoEngine : public CryptoEngine
|
|||||||
static uint8_t scratch[MAX_BLOCKSIZE];
|
static uint8_t scratch[MAX_BLOCKSIZE];
|
||||||
size_t nc_off = 0;
|
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);
|
initNonce(fromNode, packetNum);
|
||||||
assert(numBytes <= MAX_BLOCKSIZE);
|
assert(numBytes <= MAX_BLOCKSIZE);
|
||||||
memcpy(scratch, bytes, numBytes);
|
memcpy(scratch, bytes, numBytes);
|
||||||
|
@ -229,7 +229,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
|||||||
displayedNodeNum = 0; // Not currently showing a node pane
|
displayedNodeNum = 0; // Not currently showing a node pane
|
||||||
|
|
||||||
MeshPacket &mp = devicestate.rx_text_message;
|
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,
|
// DEBUG_MSG("drawing text message from 0x%x: %s\n", mp.from,
|
||||||
// mp.decoded.variant.data.decoded.bytes);
|
// mp.decoded.variant.data.decoded.bytes);
|
||||||
|
|
||||||
|
@ -275,10 +275,11 @@ const char *Channels::getPrimaryName()
|
|||||||
bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash)
|
bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash)
|
||||||
{
|
{
|
||||||
if(chIndex > getNumChannels() || getHash(chIndex) != 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;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
DEBUG_MSG("Using channel %d (hash 0x%x)\n", chIndex, channelHash);
|
||||||
setCrypto(chIndex);
|
setCrypto(chIndex);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,10 @@
|
|||||||
void CryptoEngine::setKey(const CryptoKey &k)
|
void CryptoEngine::setKey(const CryptoKey &k)
|
||||||
{
|
{
|
||||||
DEBUG_MSG("Installing AES%d key!\n", k.length * 8);
|
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;
|
key = k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ void DSRRouter::sniffReceived(const MeshPacket *p, const Routing *c)
|
|||||||
// ignore rebroadcasts.
|
// ignore rebroadcasts.
|
||||||
// this will also add records for any ACKs we receive for our messages
|
// this will also add records for any ACKs we receive for our messages
|
||||||
if (p->to != NODENUM_BROADCAST || p->hop_limit != HOP_RELIABLE) {
|
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)
|
if (c)
|
||||||
|
@ -31,7 +31,7 @@ void FloodingRouter::sniffReceived(const MeshPacket *p, const Routing *c)
|
|||||||
{
|
{
|
||||||
// If a broadcast, possibly _also_ send copies out into the mesh.
|
// If a broadcast, possibly _also_ send copies out into the mesh.
|
||||||
// (FIXME, do something smarter than naive flooding here)
|
// (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) {
|
if (p->id != 0) {
|
||||||
MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it
|
MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ static PacketId findId;
|
|||||||
|
|
||||||
static bool isMyPacket(MeshPacket *p)
|
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 */
|
/** Attempt to find and remove a packet from this queue. Returns true the packet which was removed from the queue */
|
||||||
|
@ -1,23 +1,28 @@
|
|||||||
#include "MeshPlugin.h"
|
#include "MeshPlugin.h"
|
||||||
#include "NodeDB.h"
|
|
||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
|
#include "NodeDB.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
std::vector<MeshPlugin *> *MeshPlugin::plugins;
|
std::vector<MeshPlugin *> *MeshPlugin::plugins;
|
||||||
|
|
||||||
const MeshPacket *MeshPlugin::currentRequest;
|
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)
|
MeshPlugin::MeshPlugin(const char *_name) : name(_name)
|
||||||
{
|
{
|
||||||
// Can't trust static initalizer order, so we check each time
|
// Can't trust static initalizer order, so we check each time
|
||||||
if(!plugins)
|
if (!plugins)
|
||||||
plugins = new std::vector<MeshPlugin *>();
|
plugins = new std::vector<MeshPlugin *>();
|
||||||
|
|
||||||
plugins->push_back(this);
|
plugins->push_back(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshPlugin::setup() {
|
void MeshPlugin::setup() {}
|
||||||
}
|
|
||||||
|
|
||||||
MeshPlugin::~MeshPlugin()
|
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?
|
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
|
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
|
||||||
auto ourNodeNum = nodeDB.getNodeNum();
|
auto ourNodeNum = nodeDB.getNodeNum();
|
||||||
bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum;
|
bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum;
|
||||||
@ -48,15 +55,16 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
|
|||||||
bool handled = pi.handleReceived(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)
|
// 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
|
// NOTE: we send a reply *even if the (non broadcast) request was from us* which is unfortunate but necessary because
|
||||||
// sends things, it sends things using the local node ID as the from address. A better solution (FIXME) would be to let phones
|
// currently when the phone sends things, it sends things using the local node ID as the from address. A better
|
||||||
// have their own distinct addresses and we 'route' to them like any other node.
|
// solution (FIXME) would be to let phones have their own distinct addresses and we 'route' to them like any other
|
||||||
if (mp.decoded.want_response && toUs && (mp.from != ourNodeNum || mp.to == ourNodeNum)) {
|
// node.
|
||||||
|
if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) {
|
||||||
pi.sendResponse(mp);
|
pi.sendResponse(mp);
|
||||||
DEBUG_MSG("Plugin %s sent a response\n", pi.name);
|
DEBUG_MSG("Plugin %s sent a response\n", pi.name);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
DEBUG_MSG("Plugin %s considered\n", pi.name);
|
DEBUG_MSG("Plugin %s considered\n", pi.name);
|
||||||
}
|
}
|
||||||
if (handled) {
|
if (handled) {
|
||||||
@ -64,11 +72,17 @@ void MeshPlugin::callPlugins(const MeshPacket &mp)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pi.currentRequest = NULL;
|
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);
|
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
|
* so that subclasses can (optionally) send a response back to the original sender. Implementing this method
|
||||||
* is optional
|
* is optional
|
||||||
*/
|
*/
|
||||||
void MeshPlugin::sendResponse(const MeshPacket &req) {
|
void MeshPlugin::sendResponse(const MeshPacket &req)
|
||||||
|
{
|
||||||
auto r = allocReply();
|
auto r = allocReply();
|
||||||
if(r) {
|
if (r) {
|
||||||
DEBUG_MSG("Sending response\n");
|
|
||||||
setReplyTo(r, req);
|
setReplyTo(r, req);
|
||||||
service.sendToMesh(r);
|
currentReply = r;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// Ignore - this is now expected behavior for routing plugin (because it ignores some replies)
|
// 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");
|
// 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.
|
* 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
|
assert(p->which_payloadVariant == MeshPacket_decoded_tag); // Should already be set by now
|
||||||
p->to = to.from;
|
p->to = getFrom(&to);
|
||||||
p->want_ack = to.want_ack;
|
|
||||||
|
// 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;
|
p->decoded.request_id = to.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<MeshPlugin *> MeshPlugin::GetMeshPluginsWithUIFrames() {
|
std::vector<MeshPlugin *> MeshPlugin::GetMeshPluginsWithUIFrames()
|
||||||
|
{
|
||||||
|
|
||||||
std::vector<MeshPlugin *> pluginsWithUIFrames;
|
std::vector<MeshPlugin *> pluginsWithUIFrames;
|
||||||
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
|
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
|
||||||
auto &pi = **i;
|
auto &pi = **i;
|
||||||
if ( pi.wantUIFrame()) {
|
if (pi.wantUIFrame()) {
|
||||||
DEBUG_MSG("Plugin wants a UI Frame\n");
|
DEBUG_MSG("Plugin wants a UI Frame\n");
|
||||||
pluginsWithUIFrames.push_back(&pi);
|
pluginsWithUIFrames.push_back(&pi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pluginsWithUIFrames;
|
return pluginsWithUIFrames;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,13 @@ class MeshPlugin
|
|||||||
|
|
||||||
private:
|
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
|
/** 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()
|
* 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
|
* to generate the reply message, and if !NULL that message will be delivered to whoever sent req
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
#include "RTC.h"
|
#include "RTC.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "plugins/PositionPlugin.h"
|
|
||||||
#include "plugins/NodeInfoPlugin.h"
|
#include "plugins/NodeInfoPlugin.h"
|
||||||
|
#include "plugins/PositionPlugin.h"
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -51,8 +51,6 @@ MeshService service;
|
|||||||
|
|
||||||
#include "Router.h"
|
#include "Router.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
||||||
{
|
{
|
||||||
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
|
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
|
||||||
@ -67,7 +65,6 @@ void MeshService::init()
|
|||||||
gpsObserver.observe(&gps->newStatus);
|
gpsObserver.observe(&gps->newStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int MeshService::handleFromRadio(const MeshPacket *mp)
|
int MeshService::handleFromRadio(const MeshPacket *mp)
|
||||||
{
|
{
|
||||||
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
|
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
|
||||||
@ -117,7 +114,8 @@ bool MeshService::reloadConfig()
|
|||||||
void MeshService::reloadOwner()
|
void MeshService::reloadOwner()
|
||||||
{
|
{
|
||||||
assert(nodeInfoPlugin);
|
assert(nodeInfoPlugin);
|
||||||
nodeInfoPlugin->sendOurNodeInfo();
|
if(nodeInfoPlugin)
|
||||||
|
nodeInfoPlugin->sendOurNodeInfo();
|
||||||
nodeDB.saveToDisk();
|
nodeDB.saveToDisk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,8 +126,12 @@ void MeshService::reloadOwner()
|
|||||||
*/
|
*/
|
||||||
void MeshService::handleToRadio(MeshPacket &p)
|
void MeshService::handleToRadio(MeshPacket &p)
|
||||||
{
|
{
|
||||||
if (p.from == 0) // If the phone didn't set a sending node ID, use ours
|
if (p.from != 0) { // We don't let phones assign nodenums to their sent messages
|
||||||
p.from = nodeDB.getNodeNum();
|
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)
|
if (p.id == 0)
|
||||||
p.id = generatePacketId(); // If the phone didn't supply one, then pick one
|
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 */
|
/** 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);
|
return router->cancelSending(nodeDB.getNodeNum(), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,29 +171,36 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
|
|||||||
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||||
assert(node);
|
assert(node);
|
||||||
|
|
||||||
DEBUG_MSG("Sending network ping to 0x%x, with position=%d, wantReplies=%d\n", dest, node->has_position, wantReplies);
|
if (node->has_position) {
|
||||||
assert(positionPlugin && nodeInfoPlugin);
|
if(positionPlugin) {
|
||||||
if (node->has_position)
|
DEBUG_MSG("Sending position ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
|
||||||
positionPlugin->sendOurPosition(dest, wantReplies);
|
positionPlugin->sendOurPosition(dest, wantReplies);
|
||||||
else
|
}
|
||||||
nodeInfoPlugin->sendOurNodeInfo(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());
|
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
||||||
assert(node);
|
assert(node);
|
||||||
|
|
||||||
// We might not have a position yet for our local node, in that case, at least try to send the time
|
// 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));
|
memset(&node->position, 0, sizeof(node->position));
|
||||||
node->has_position = true;
|
node->has_position = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Position &position = node->position;
|
Position &position = node->position;
|
||||||
|
|
||||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
// 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();
|
position.battery_level = powerStatus->getBatteryChargePercent();
|
||||||
updateBatteryLevel(position.battery_level);
|
updateBatteryLevel(position.battery_level);
|
||||||
@ -209,11 +219,10 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
|
|||||||
pos.altitude = gps->altitude;
|
pos.altitude = gps->altitude;
|
||||||
pos.latitude_i = gps->latitude;
|
pos.latitude_i = gps->latitude;
|
||||||
pos.longitude_i = gps->longitude;
|
pos.longitude_i = gps->longitude;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// The GPS has lost lock, if we are fixed position we should just keep using
|
// The GPS has lost lock, if we are fixed position we should just keep using
|
||||||
// the old position
|
// the old position
|
||||||
if(radioConfig.preferences.fixed_position) {
|
if (radioConfig.preferences.fixed_position) {
|
||||||
DEBUG_MSG("WARNING: Using fixed position\n");
|
DEBUG_MSG("WARNING: Using fixed position\n");
|
||||||
} else {
|
} else {
|
||||||
// throw away old position
|
// throw away old position
|
||||||
|
@ -32,4 +32,10 @@ typedef uint32_t PacketId; // A packet sequence number
|
|||||||
typedef int ErrorCode;
|
typedef int ErrorCode;
|
||||||
|
|
||||||
/// Alloc and free packets to our global, ISR safe pool
|
/// Alloc and free packets to our global, ISR safe pool
|
||||||
extern Allocator<MeshPacket> &packetPool;
|
extern Allocator<MeshPacket> &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);
|
@ -66,6 +66,14 @@ NodeNum displayedNodeNum;
|
|||||||
|
|
||||||
NodeDB::NodeDB() : nodes(devicestate.node_db), numNodes(&devicestate.node_db_count) {}
|
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 NodeDB::resetRadioConfig()
|
||||||
{
|
{
|
||||||
bool didFactoryReset = false;
|
bool didFactoryReset = false;
|
||||||
@ -406,7 +414,7 @@ void NodeDB::updateFrom(const MeshPacket &mp)
|
|||||||
if (mp.which_payloadVariant == MeshPacket_decoded_tag) {
|
if (mp.which_payloadVariant == MeshPacket_decoded_tag) {
|
||||||
DEBUG_MSG("Update DB node 0x%x, rx_time=%u\n", mp.from, mp.rx_time);
|
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
|
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
|
info->has_position = true; // at least the time is valid
|
||||||
|
@ -154,7 +154,7 @@ extern NodeDB nodeDB;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Our delay functions check for this for times that should never expire
|
// 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))
|
#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(wait_bluetooth_secs, IF_ROUTER(1, 60))
|
||||||
|
|
||||||
PREF_GET(screen_on_secs, 60)
|
PREF_GET(screen_on_secs, 60)
|
||||||
PREF_GET(mesh_sds_timeout_secs, 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(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)
|
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
|
// 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.
|
* might have changed is incremented. Allows others to detect they might now be on a new channel.
|
||||||
*/
|
*/
|
||||||
extern uint32_t radioGeneration;
|
extern uint32_t radioGeneration;
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
|
|||||||
// DEBUG_MSG("Deleting old broadcast record %d\n", i);
|
// DEBUG_MSG("Deleting old broadcast record %d\n", i);
|
||||||
recentPackets.erase(recentPackets.begin() + i); // delete old record
|
recentPackets.erase(recentPackets.begin() + i); // delete old record
|
||||||
} else {
|
} 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);
|
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
|
// Update the time on this record to now
|
||||||
@ -43,7 +43,7 @@ bool PacketHistory::wasSeenRecently(const MeshPacket *p, bool withUpdate)
|
|||||||
if (withUpdate) {
|
if (withUpdate) {
|
||||||
PacketRecord r;
|
PacketRecord r;
|
||||||
r.id = p->id;
|
r.id = p->id;
|
||||||
r.sender = p->from;
|
r.sender = getFrom(p);
|
||||||
r.rxTimeMsec = now;
|
r.rxTimeMsec = now;
|
||||||
recentPackets.push_back(r);
|
recentPackets.push_back(r);
|
||||||
printPacket("Adding packet record", p);
|
printPacket("Adding packet record", p);
|
||||||
|
@ -59,6 +59,7 @@ void PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
|||||||
}
|
}
|
||||||
// return (lastContactMsec != 0) &&
|
// return (lastContactMsec != 0) &&
|
||||||
|
|
||||||
|
memset(&toRadioScratch, 0, sizeof(toRadioScratch));
|
||||||
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) {
|
if (pb_decode_from_bytes(buf, bufLength, ToRadio_fields, &toRadioScratch)) {
|
||||||
switch (toRadioScratch.which_payloadVariant) {
|
switch (toRadioScratch.which_payloadVariant) {
|
||||||
case ToRadio_packet_tag: {
|
case ToRadio_packet_tag: {
|
||||||
@ -178,9 +179,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
// Do we have a message from the mesh?
|
// Do we have a message from the mesh?
|
||||||
if (fromRadioScratch.which_payloadVariant != 0) {
|
if (fromRadioScratch.which_payloadVariant != 0) {
|
||||||
// Encapsulate as a FromRadio packet
|
// 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);
|
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;
|
return numbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,11 +58,12 @@ template <class T> class ProtobufPlugin : protected SinglePortPlugin
|
|||||||
// it would be better to update even if the message was destined to others.
|
// it would be better to update even if the message was destined to others.
|
||||||
|
|
||||||
auto &p = mp.decoded;
|
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 scratch;
|
||||||
T *decoded = NULL;
|
T *decoded = NULL;
|
||||||
if(mp.decoded.portnum == ourPortNum) {
|
if(mp.decoded.portnum == ourPortNum) {
|
||||||
|
memset(&scratch, 0, sizeof(scratch));
|
||||||
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch))
|
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch))
|
||||||
decoded = &scratch;
|
decoded = &scratch;
|
||||||
else
|
else
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
|
|
||||||
|
#include "configuration.h"
|
||||||
#include "RadioInterface.h"
|
#include "RadioInterface.h"
|
||||||
#include "Channels.h"
|
#include "Channels.h"
|
||||||
#include "MeshRadio.h"
|
#include "MeshRadio.h"
|
||||||
#include "MeshService.h"
|
#include "MeshService.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
#include "configuration.h"
|
#include "Router.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <pb_decode.h>
|
#include <pb_decode.h>
|
||||||
#include <pb_encode.h>
|
#include <pb_encode.h>
|
||||||
#include "Channels.h"
|
|
||||||
|
|
||||||
#define RDEF(name, freq, spacing, num_ch, power_limit) \
|
#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
|
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)
|
// 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(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
|
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;
|
const RegionInfo *myRegion;
|
||||||
|
|
||||||
void initRegion()
|
void initRegion()
|
||||||
@ -119,11 +137,11 @@ uint32_t RadioInterface::getTxDelayMsec()
|
|||||||
|
|
||||||
void printPacket(const char *prefix, const MeshPacket *p)
|
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,
|
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->hop_limit, p->channel);
|
p->want_ack, p->hop_limit, p->channel);
|
||||||
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
|
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
|
||||||
auto &s = p->decoded;
|
auto &s = p->decoded;
|
||||||
|
|
||||||
DEBUG_MSG(" Portnum=%d", s.portnum);
|
DEBUG_MSG(" Portnum=%d", s.portnum);
|
||||||
|
|
||||||
if (s.want_response)
|
if (s.want_response)
|
||||||
@ -332,6 +350,10 @@ void RadioInterface::deliverToReceiver(MeshPacket *p)
|
|||||||
{
|
{
|
||||||
assert(rxDest);
|
assert(rxDest);
|
||||||
assert(rxDest->enqueue(p, 0)); // NOWAIT - fixme, if queue is full, delete older messages
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "ReliableRouter.h"
|
#include "ReliableRouter.h"
|
||||||
|
#include "MeshPlugin.h"
|
||||||
#include "MeshTypes.h"
|
#include "MeshTypes.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
@ -26,15 +27,18 @@ ErrorCode ReliableRouter::send(MeshPacket *p)
|
|||||||
|
|
||||||
bool ReliableRouter::shouldFilterReceived(const 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()) {
|
if (p->to == NODENUM_BROADCAST && p->from == getNodeNum()) {
|
||||||
printPacket("Rx someone rebroadcasting for us", p);
|
printPacket("Rx someone rebroadcasting for us", p);
|
||||||
|
|
||||||
// We are seeing someone rebroadcast one of our broadcast attempts.
|
// 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
|
// 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.
|
// the original sending process.
|
||||||
if (stopRetransmission(p->from, p->id)) {
|
if (stopRetransmission(getFrom(p), p->id)) {
|
||||||
DEBUG_MSG("Someone is retransmitting for us, generate implicit ack\n");
|
DEBUG_MSG("generating implicit ack\n");
|
||||||
sendAckNak(Routing_Error_NONE, p->from, p->id);
|
// 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
|
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)
|
// - not DSR routing)
|
||||||
if (p->want_ack) {
|
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
|
// We consider an ack to be either a !routing packet with a request ID or a routing packet with !error
|
||||||
if (c) {
|
PacketId ackId = ((c && c->error_reason == Routing_Error_NONE) || !c) ? p->decoded.request_id : 0;
|
||||||
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 intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
|
// A nak is a routing packt that has an error code
|
||||||
if (ackId || nakId) {
|
PacketId nakId = (c && c->error_reason != Routing_Error_NONE) ? p->decoded.request_id : 0;
|
||||||
if (ackId) {
|
|
||||||
DEBUG_MSG("Received a ack=%d, stopping retransmissions\n", ackId);
|
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
|
||||||
stopRetransmission(p->to, ackId);
|
if (ackId || nakId) {
|
||||||
} else {
|
if (ackId) {
|
||||||
DEBUG_MSG("Received a nak=%d, stopping retransmissions\n", nakId);
|
DEBUG_MSG("Received a ack for 0x%x, stopping retransmissions\n", ackId);
|
||||||
stopRetransmission(p->to, nakId);
|
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 id = GlobalPacketId(p);
|
||||||
auto rec = PendingPacket(p);
|
auto rec = PendingPacket(p);
|
||||||
|
|
||||||
|
stopRetransmission(getFrom(p), p->id);
|
||||||
|
|
||||||
setNextTx(&rec);
|
setNextTx(&rec);
|
||||||
stopRetransmission(p->from, p->id);
|
|
||||||
pending[id] = rec;
|
pending[id] = rec;
|
||||||
|
|
||||||
return &pending[id];
|
return &pending[id];
|
||||||
@ -151,18 +159,21 @@ int32_t ReliableRouter::doRetransmissions()
|
|||||||
++nextIt; // we use this odd pattern because we might be deleting it...
|
++nextIt; // we use this odd pattern because we might be deleting it...
|
||||||
auto &p = it->second;
|
auto &p = it->second;
|
||||||
|
|
||||||
|
bool stillValid = true; // assume we'll keep this record around
|
||||||
|
|
||||||
// FIXME, handle 51 day rolloever here!!!
|
// FIXME, handle 51 day rolloever here!!!
|
||||||
if (p.nextTxMsec <= now) {
|
if (p.nextTxMsec <= now) {
|
||||||
if (p.numRetransmissions == 0) {
|
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);
|
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
|
// 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
|
// allows the DSR version to still be able to look at the PendingPacket
|
||||||
stopRetransmission(it->first);
|
stopRetransmission(it->first);
|
||||||
|
stillValid = false; // just deleted it
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=%d, tries left=%d\n", p.packet->from, p.packet->to,
|
DEBUG_MSG("Sending reliable retransmission fr=0x%x,to=0x%x,id=0x%x, tries left=%d\n", p.packet->from,
|
||||||
p.packet->id, p.numRetransmissions);
|
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
|
// Note: we call the superclass version because we don't want to have our version of send() add a new
|
||||||
// retransmission record
|
// retransmission record
|
||||||
@ -172,8 +183,10 @@ int32_t ReliableRouter::doRetransmissions()
|
|||||||
--p.numRetransmissions;
|
--p.numRetransmissions;
|
||||||
setNextTx(&p);
|
setNextTx(&p);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// Not yet time
|
|
||||||
|
if (stillValid) {
|
||||||
|
// Update our desired sleep delay
|
||||||
int32_t t = p.nextTxMsec - now;
|
int32_t t = p.nextTxMsec - now;
|
||||||
|
|
||||||
d = min(t, d);
|
d = min(t, d);
|
||||||
@ -181,4 +194,14 @@ int32_t ReliableRouter::doRetransmissions()
|
|||||||
}
|
}
|
||||||
|
|
||||||
return d;
|
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
|
||||||
}
|
}
|
@ -15,7 +15,7 @@ struct GlobalPacketId {
|
|||||||
|
|
||||||
GlobalPacketId(const MeshPacket *p)
|
GlobalPacketId(const MeshPacket *p)
|
||||||
{
|
{
|
||||||
node = p->from;
|
node = getFrom(p);
|
||||||
id = p->id;
|
id = p->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +125,5 @@ class ReliableRouter : public FloodingRouter
|
|||||||
*/
|
*/
|
||||||
int32_t doRetransmissions();
|
int32_t doRetransmissions();
|
||||||
|
|
||||||
void setNextTx(PendingPacket *pending) {
|
void setNextTx(PendingPacket *pending);
|
||||||
assert(iface);
|
|
||||||
pending->nextTxMsec = millis() + iface->getRetransmissionMsec(pending->packet); }
|
|
||||||
};
|
};
|
||||||
|
@ -111,16 +111,21 @@ void Router::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom)
|
|||||||
void Router::abortSendAndNak(Routing_Error err, MeshPacket *p)
|
void Router::abortSendAndNak(Routing_Error err, MeshPacket *p)
|
||||||
{
|
{
|
||||||
DEBUG_MSG("Error=%d, returning NAK and dropping packet.\n", err);
|
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);
|
packetPool.release(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Router::setReceivedMessage() {
|
||||||
|
setInterval(0); // Run ASAP, so we can figure out our correct sleep time
|
||||||
|
}
|
||||||
|
|
||||||
ErrorCode Router::sendLocal(MeshPacket *p)
|
ErrorCode Router::sendLocal(MeshPacket *p)
|
||||||
{
|
{
|
||||||
// No need to deliver externally if the destination is the local node
|
// No need to deliver externally if the destination is the local node
|
||||||
if (p->to == nodeDB.getNodeNum()) {
|
if (p->to == nodeDB.getNodeNum()) {
|
||||||
printPacket("Enqueuing local", p);
|
printPacket("Enqueuing local", p);
|
||||||
fromRadioQueue.enqueue(p);
|
fromRadioQueue.enqueue(p);
|
||||||
|
setReceivedMessage();
|
||||||
return ERRNO_OK;
|
return ERRNO_OK;
|
||||||
} else if (!iface) {
|
} else if (!iface) {
|
||||||
// We must be sending to remote nodes also, fail if no interface found
|
// 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
|
* Send a packet on a suitable interface. This routine will
|
||||||
* later free() the packet to pool. This routine is not allowed to stall.
|
* 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)
|
if (p->to == NODENUM_BROADCAST)
|
||||||
p->want_ack = false;
|
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)
|
// 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 ||
|
assert(p->which_payloadVariant == MeshPacket_encrypted_tag ||
|
||||||
@ -164,6 +180,8 @@ ErrorCode Router::send(MeshPacket *p)
|
|||||||
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
|
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
|
||||||
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union
|
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);
|
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded);
|
||||||
|
|
||||||
if (numbytes > MAX_RHPACKETLEN) {
|
if (numbytes > MAX_RHPACKETLEN) {
|
||||||
@ -171,6 +189,8 @@ ErrorCode Router::send(MeshPacket *p)
|
|||||||
return ERRNO_TOO_LARGE;
|
return ERRNO_TOO_LARGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//printBytes("plaintext", bytes, numbytes);
|
||||||
|
|
||||||
auto hash = channels.setActiveByIndex(p->channel);
|
auto hash = channels.setActiveByIndex(p->channel);
|
||||||
if (hash < 0) {
|
if (hash < 0) {
|
||||||
// No suitable channel could be found for sending
|
// 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)
|
// Now that we are encrypting the packet channel should be the hash (no longer the index)
|
||||||
p->channel = hash;
|
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
|
// Copy back into the packet and set the variant type
|
||||||
memcpy(p->encrypted.bytes, bytes, numbytes);
|
memcpy(p->encrypted.bytes, bytes, numbytes);
|
||||||
@ -221,19 +241,26 @@ bool Router::perhapsDecode(MeshPacket *p)
|
|||||||
if (channels.decryptForHash(chIndex, p->channel)) {
|
if (channels.decryptForHash(chIndex, p->channel)) {
|
||||||
// Try to decrypt the packet if we can
|
// Try to decrypt the packet if we can
|
||||||
static uint8_t bytes[MAX_RHPACKETLEN];
|
static uint8_t bytes[MAX_RHPACKETLEN];
|
||||||
|
size_t rawSize = p->encrypted.size;
|
||||||
|
assert(rawSize <= sizeof(bytes));
|
||||||
memcpy(bytes, p->encrypted.bytes,
|
memcpy(bytes, p->encrypted.bytes,
|
||||||
p->encrypted
|
rawSize); // we have to copy into a scratch buffer, because these bytes are a union with the decoded protobuf
|
||||||
.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, rawSize, bytes);
|
||||||
crypto->decrypt(p->from, p->id, p->encrypted.size, bytes);
|
|
||||||
|
//printBytes("plaintext", bytes, p->encrypted.size);
|
||||||
|
|
||||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
// 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)) {
|
memset(&p->decoded, 0, sizeof(p->decoded));
|
||||||
DEBUG_MSG("Invalid protobufs in received mesh packet (bad psk?!\n");
|
if (!pb_decode_from_bytes(bytes, rawSize, Data_fields, &p->decoded)) {
|
||||||
} else {
|
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
|
// 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
|
p->channel = chIndex; // change to store the index instead of the hash
|
||||||
// printPacket("decoded message", p);
|
printPacket("decoded message", p);
|
||||||
p->which_payloadVariant = MeshPacket_decoded_tag;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,11 +285,10 @@ void Router::handleReceived(MeshPacket *p)
|
|||||||
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
|
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
|
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||||
bool decoded = perhapsDecode(p);
|
bool decoded = perhapsDecode(p);
|
||||||
printPacket("handleReceived", p);
|
|
||||||
DEBUG_MSG("decoded=%d\n", decoded);
|
|
||||||
if (decoded) {
|
if (decoded) {
|
||||||
// parsing was successful, queue for our recipient
|
// 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
|
// call any promiscious plugins here, make a (non promisiocous) plugin for forwarding messages to phone api
|
||||||
// sniffReceived(p);
|
// sniffReceived(p);
|
||||||
@ -273,7 +299,7 @@ void Router::handleReceived(MeshPacket *p)
|
|||||||
void Router::perhapsHandleReceived(MeshPacket *p)
|
void Router::perhapsHandleReceived(MeshPacket *p)
|
||||||
{
|
{
|
||||||
assert(radioConfig.has_preferences);
|
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)
|
if (ignore)
|
||||||
DEBUG_MSG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from);
|
DEBUG_MSG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from);
|
||||||
|
@ -63,6 +63,11 @@ class Router : protected concurrency::OSThread
|
|||||||
* @return our local nodenum */
|
* @return our local nodenum */
|
||||||
NodeNum getNodeNum();
|
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:
|
protected:
|
||||||
friend class RoutingPlugin;
|
friend class RoutingPlugin;
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ extern const pb_msgdesc_t AdminMessage_msg;
|
|||||||
#define AdminMessage_fields &AdminMessage_msg
|
#define AdminMessage_fields &AdminMessage_msg
|
||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define AdminMessage_size 338
|
#define AdminMessage_size 351
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
@ -82,7 +82,7 @@ extern const pb_msgdesc_t DeviceState_msg;
|
|||||||
#define DeviceState_fields &DeviceState_msg
|
#define DeviceState_fields &DeviceState_msg
|
||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define DeviceState_size 6156
|
#define DeviceState_size 6169
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
@ -20,10 +20,10 @@ typedef enum _PortNum {
|
|||||||
PortNum_ADMIN_APP = 6,
|
PortNum_ADMIN_APP = 6,
|
||||||
PortNum_REPLY_APP = 32,
|
PortNum_REPLY_APP = 32,
|
||||||
PortNum_IP_TUNNEL_APP = 33,
|
PortNum_IP_TUNNEL_APP = 33,
|
||||||
PortNum_ENVIRONMENTAL_MEASUREMENT_APP = 34,
|
|
||||||
PortNum_SERIAL_APP = 64,
|
PortNum_SERIAL_APP = 64,
|
||||||
PortNum_STORE_FORWARD_APP = 65,
|
PortNum_STORE_FORWARD_APP = 65,
|
||||||
PortNum_RANGE_TEST_APP = 66,
|
PortNum_RANGE_TEST_APP = 66,
|
||||||
|
PortNum_ENVIRONMENTAL_MEASUREMENT_APP = 67,
|
||||||
PortNum_PRIVATE_APP = 256,
|
PortNum_PRIVATE_APP = 256,
|
||||||
PortNum_ATAK_FORWARDER = 257,
|
PortNum_ATAK_FORWARDER = 257,
|
||||||
PortNum_MAX = 511
|
PortNum_MAX = 511
|
||||||
|
@ -17,3 +17,4 @@ PB_BIND(RadioConfig_UserPreferences, RadioConfig_UserPreferences, 2)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,7 +19,8 @@ typedef enum _RegionCode {
|
|||||||
RegionCode_JP = 5,
|
RegionCode_JP = 5,
|
||||||
RegionCode_ANZ = 6,
|
RegionCode_ANZ = 6,
|
||||||
RegionCode_KR = 7,
|
RegionCode_KR = 7,
|
||||||
RegionCode_TW = 8
|
RegionCode_TW = 8,
|
||||||
|
RegionCode_RU = 9
|
||||||
} RegionCode;
|
} RegionCode;
|
||||||
|
|
||||||
typedef enum _ChargeCurrent {
|
typedef enum _ChargeCurrent {
|
||||||
@ -56,6 +57,10 @@ typedef enum _LocationSharing {
|
|||||||
LocationSharing_LocDisabled = 2
|
LocationSharing_LocDisabled = 2
|
||||||
} LocationSharing;
|
} LocationSharing;
|
||||||
|
|
||||||
|
typedef enum _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType {
|
||||||
|
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0
|
||||||
|
} RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType;
|
||||||
|
|
||||||
/* Struct definitions */
|
/* Struct definitions */
|
||||||
typedef struct _RadioConfig_UserPreferences {
|
typedef struct _RadioConfig_UserPreferences {
|
||||||
uint32_t position_broadcast_secs;
|
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_read_error_count_threshold;
|
||||||
uint32_t environmental_measurement_plugin_update_interval;
|
uint32_t environmental_measurement_plugin_update_interval;
|
||||||
uint32_t environmental_measurement_plugin_recovery_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;
|
} RadioConfig_UserPreferences;
|
||||||
|
|
||||||
typedef struct _RadioConfig {
|
typedef struct _RadioConfig {
|
||||||
@ -116,8 +124,8 @@ typedef struct _RadioConfig {
|
|||||||
|
|
||||||
/* Helper constants for enums */
|
/* Helper constants for enums */
|
||||||
#define _RegionCode_MIN RegionCode_Unset
|
#define _RegionCode_MIN RegionCode_Unset
|
||||||
#define _RegionCode_MAX RegionCode_TW
|
#define _RegionCode_MAX RegionCode_RU
|
||||||
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TW+1))
|
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_RU+1))
|
||||||
|
|
||||||
#define _ChargeCurrent_MIN ChargeCurrent_MAUnset
|
#define _ChargeCurrent_MIN ChargeCurrent_MAUnset
|
||||||
#define _ChargeCurrent_MAX ChargeCurrent_MA1320
|
#define _ChargeCurrent_MAX ChargeCurrent_MA1320
|
||||||
@ -131,6 +139,10 @@ typedef struct _RadioConfig {
|
|||||||
#define _LocationSharing_MAX LocationSharing_LocDisabled
|
#define _LocationSharing_MAX LocationSharing_LocDisabled
|
||||||
#define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1))
|
#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
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -138,9 +150,9 @@ extern "C" {
|
|||||||
|
|
||||||
/* Initializer values for message structs */
|
/* Initializer values for message structs */
|
||||||
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default}
|
#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_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) */
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
|
#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_read_error_count_threshold_tag 142
|
||||||
#define RadioConfig_UserPreferences_environmental_measurement_plugin_update_interval_tag 143
|
#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_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
|
#define RadioConfig_preferences_tag 1
|
||||||
|
|
||||||
/* Struct field encoding specification for nanopb */
|
/* 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, 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_read_error_count_threshold, 142) \
|
||||||
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_update_interval, 143) \
|
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_CALLBACK NULL
|
||||||
#define RadioConfig_UserPreferences_DEFAULT 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
|
#define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg
|
||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define RadioConfig_size 335
|
#define RadioConfig_size 348
|
||||||
#define RadioConfig_UserPreferences_size 332
|
#define RadioConfig_UserPreferences_size 345
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
@ -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);
|
pb_ostream_t stream = pb_ostream_from_buffer(destbuf, destbufsize);
|
||||||
if (!pb_encode(&stream, fields, src_struct)) {
|
if (!pb_encode(&stream, fields, src_struct)) {
|
||||||
DEBUG_MSG("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream));
|
DEBUG_MSG("Panic: can't encode protobuf %s, did you make a field too large?\n", PB_GET_ERROR(&stream));
|
||||||
assert(0); // FIXME - panic
|
assert(0); // If this asser fails it probably means you made a field too large for the max limits specified in mesh.options
|
||||||
} else {
|
} else {
|
||||||
return stream.bytes_written;
|
return stream.bytes_written;
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,12 @@ void AdminPlugin::handleGetRadio(const MeshPacket &req)
|
|||||||
// We create the reply here
|
// We create the reply here
|
||||||
AdminMessage r = AdminMessage_init_default;
|
AdminMessage r = AdminMessage_init_default;
|
||||||
r.get_radio_response = devicestate.radio;
|
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;
|
r.which_variant = AdminMessage_get_radio_response_tag;
|
||||||
reply = allocDataProtobuf(r);
|
reply = allocDataProtobuf(r);
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ bool ExternalNotificationPluginRadio::handleReceived(const MeshPacket &mp)
|
|||||||
|
|
||||||
auto &p = mp.decoded;
|
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.
|
// 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.
|
// Need to know if and how this could be a problem.
|
||||||
|
@ -12,7 +12,7 @@ bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User *pp
|
|||||||
{
|
{
|
||||||
auto p = *pptr;
|
auto p = *pptr;
|
||||||
|
|
||||||
nodeDB.updateUser(mp.from, p);
|
nodeDB.updateUser(getFrom(&mp), p);
|
||||||
|
|
||||||
bool wasBroadcast = mp.to == NODENUM_BROADCAST;
|
bool wasBroadcast = mp.to == NODENUM_BROADCAST;
|
||||||
|
|
||||||
@ -65,8 +65,7 @@ int32_t NodeInfoPlugin::runOnce()
|
|||||||
currentGeneration = radioGeneration;
|
currentGeneration = radioGeneration;
|
||||||
|
|
||||||
DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
|
DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
|
||||||
assert(nodeInfoPlugin);
|
sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
|
||||||
nodeInfoPlugin->sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
|
|
||||||
|
|
||||||
return getPref_position_broadcast_secs() * 1000;
|
return getPref_position_broadcast_secs() * 1000;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
void setupPlugins()
|
void setupPlugins()
|
||||||
{
|
{
|
||||||
routingPlugin = new RoutingPlugin();
|
|
||||||
adminPlugin = new AdminPlugin();
|
adminPlugin = new AdminPlugin();
|
||||||
nodeInfoPlugin = new NodeInfoPlugin();
|
nodeInfoPlugin = new NodeInfoPlugin();
|
||||||
positionPlugin = new PositionPlugin();
|
positionPlugin = new PositionPlugin();
|
||||||
@ -48,4 +47,7 @@ void setupPlugins()
|
|||||||
// new StoreForwardPlugin();
|
// new StoreForwardPlugin();
|
||||||
new EnvironmentalMeasurementPlugin();
|
new EnvironmentalMeasurementPlugin();
|
||||||
#endif
|
#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();
|
||||||
}
|
}
|
@ -30,7 +30,7 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position
|
|||||||
perhapsSetRTC(RTCQualityFromNet, &tv);
|
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
|
return false; // Let others look at this message also if they want
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,12 @@ RoutingPlugin *routingPlugin;
|
|||||||
|
|
||||||
bool RoutingPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Routing *r)
|
bool RoutingPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Routing *r)
|
||||||
{
|
{
|
||||||
DEBUG_MSG("Routing sniffing", &mp);
|
printPacket("Routing sniffing", &mp);
|
||||||
router->sniffReceived(&mp, r);
|
router->sniffReceived(&mp, r);
|
||||||
|
|
||||||
// FIXME - move this to a non promsicious PhoneAPI plugin?
|
// 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);
|
printPacket("Delivering rx packet", &mp);
|
||||||
service.handleFromRadio(&mp);
|
service.handleFromRadio(&mp);
|
||||||
}
|
}
|
||||||
|
@ -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",
|
// 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);
|
// 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
|
* If radioConfig.preferences.serialplugin_echo is true, then echo the packets that are sent out back to the TX
|
||||||
|
@ -8,7 +8,7 @@ TextMessagePlugin *textMessagePlugin;
|
|||||||
bool TextMessagePlugin::handleReceived(const MeshPacket &mp)
|
bool TextMessagePlugin::handleReceived(const MeshPacket &mp)
|
||||||
{
|
{
|
||||||
auto &p = mp.decoded;
|
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.
|
// We only store/display messages destined for us.
|
||||||
// Keep a copy of the most recent text message.
|
// Keep a copy of the most recent text message.
|
||||||
|
@ -10,20 +10,10 @@
|
|||||||
#include <OLEDDisplay.h>
|
#include <OLEDDisplay.h>
|
||||||
#include <OLEDDisplayUi.h>
|
#include <OLEDDisplayUi.h>
|
||||||
|
|
||||||
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 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 FAILED_STATE_SENSOR_READ_MULTIPLIER 10
|
||||||
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
|
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
|
||||||
|
|
||||||
DHT dht(DHT_11_GPIO_PIN,DHT11);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAS_EINK
|
#ifdef HAS_EINK
|
||||||
// The screen is bigger so use bigger fonts
|
// 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
|
Uncomment the preferences below if you want to use the plugin
|
||||||
without having to configure it from the PythonAPI or WebUI.
|
without having to configure it from the PythonAPI or WebUI.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*radioConfig.preferences.environmental_measurement_plugin_measurement_enabled = 1;
|
/*radioConfig.preferences.environmental_measurement_plugin_measurement_enabled = 1;
|
||||||
radioConfig.preferences.environmental_measurement_plugin_screen_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_read_error_count_threshold = 5;
|
||||||
radioConfig.preferences.environmental_measurement_plugin_update_interval = 30;
|
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 (! (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
|
// 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) {
|
if (firstTime) {
|
||||||
// This is the first time the OSThread library has called this function, so do some setup
|
// 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;
|
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)
|
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.
|
// 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
|
// 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(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
|
||||||
}
|
}
|
||||||
return (INT32_MAX);
|
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",
|
"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_read_error_count_threshold,
|
||||||
radioConfig.preferences.environmental_measurement_plugin_recovery_interval);
|
radioConfig.preferences.environmental_measurement_plugin_recovery_interval);
|
||||||
|
sensor_read_error_count = 0;
|
||||||
return(radioConfig.preferences.environmental_measurement_plugin_recovery_interval*1000);
|
return(radioConfig.preferences.environmental_measurement_plugin_recovery_interval*1000);
|
||||||
}
|
}
|
||||||
DEBUG_MSG(
|
DEBUG_MSG(
|
||||||
@ -110,7 +117,7 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() {
|
|||||||
sensor_read_error_count,
|
sensor_read_error_count,
|
||||||
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold-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
|
// if we failed to read the sensor, then try again
|
||||||
// as soon as we can according to the maximum polling frequency
|
// as soon as we can according to the maximum polling frequency
|
||||||
return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
|
return(DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
|
||||||
@ -123,25 +130,16 @@ int32_t EnvironmentalMeasurementPlugin::runOnce() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EnvironmentalMeasurementPluginRadio::wantUIFrame() {
|
bool EnvironmentalMeasurementPlugin::wantUIFrame() {
|
||||||
return radioConfig.preferences.environmental_measurement_plugin_screen_enabled;
|
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 GetSenderName(const MeshPacket &mp) {
|
||||||
String sender;
|
String sender;
|
||||||
|
|
||||||
if (nodeDB.getNode(mp.from)){
|
auto node = nodeDB.getNode(getFrom(&mp));
|
||||||
sender = nodeDB.getNode(mp.from)->user.short_name;
|
if (node){
|
||||||
|
sender = node->user.short_name;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sender = "UNK";
|
sender = "UNK";
|
||||||
@ -149,55 +147,99 @@ String GetSenderName(const MeshPacket &mp) {
|
|||||||
return sender;
|
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 (!(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
|
// If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool wasBroadcast = mp.to == NODENUM_BROADCAST;
|
|
||||||
|
|
||||||
String sender = GetSenderName(mp);
|
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: Received data from %s\n", sender);
|
||||||
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", p.relative_humidity);
|
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", p->relative_humidity);
|
||||||
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", p.temperature);
|
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
|
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;
|
EnvironmentalMeasurement m;
|
||||||
|
|
||||||
m.barometric_pressure = 0; // TODO: Add support for barometric sensors
|
m.barometric_pressure = 0; // TODO: Add support for barometric sensors
|
||||||
m.relative_humidity = dht.readHumidity();
|
|
||||||
m.temperature = dht.readTemperature();;
|
|
||||||
|
|
||||||
DEBUG_MSG("-----------------------------------------\n");
|
DEBUG_MSG("-----------------------------------------\n");
|
||||||
|
|
||||||
DEBUG_MSG("EnvironmentalMeasurement: Read data\n");
|
DEBUG_MSG("EnvironmentalMeasurement: Read data\n");
|
||||||
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity);
|
if (!this->dht->read(true)){
|
||||||
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature);
|
|
||||||
|
|
||||||
if (isnan(m.relative_humidity) || isnan(m.temperature) ){
|
|
||||||
sensor_read_error_count++;
|
sensor_read_error_count++;
|
||||||
DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n");
|
DEBUG_MSG("EnvironmentalMeasurement: FAILED TO READ DATA\n");
|
||||||
return false;
|
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;
|
sensor_read_error_count = 0;
|
||||||
|
|
||||||
|
@ -3,61 +3,32 @@
|
|||||||
#include "../mesh/generated/environmental_measurement.pb.h"
|
#include "../mesh/generated/environmental_measurement.pb.h"
|
||||||
#include <OLEDDisplay.h>
|
#include <OLEDDisplay.h>
|
||||||
#include <OLEDDisplayUi.h>
|
#include <OLEDDisplayUi.h>
|
||||||
|
#include <DHT.h>
|
||||||
|
|
||||||
|
class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public ProtobufPlugin<EnvironmentalMeasurement>
|
||||||
class EnvironmentalMeasurementPlugin : private concurrency::OSThread
|
|
||||||
{
|
{
|
||||||
bool firstTime = 1;
|
|
||||||
|
|
||||||
public:
|
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:
|
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();
|
virtual int32_t runOnce();
|
||||||
};
|
|
||||||
|
|
||||||
extern EnvironmentalMeasurementPlugin *environmentalMeasurementPlugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* EnvironmentalMeasurementPluginRadio plugin for sending/receiving environmental measurements to/from the mesh
|
|
||||||
*/
|
|
||||||
class EnvironmentalMeasurementPluginRadio : public ProtobufPlugin<EnvironmentalMeasurement>
|
|
||||||
{
|
|
||||||
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
|
* Send our EnvironmentalMeasurement into the mesh
|
||||||
*/
|
*/
|
||||||
bool sendOurEnvironmentalMeasurement(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
|
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:
|
private:
|
||||||
|
float CelsiusToFarenheit(float c);
|
||||||
EnvironmentalMeasurement lastMeasurement;
|
bool firstTime = 1;
|
||||||
|
DHT* dht;
|
||||||
String lastSender;
|
const MeshPacket *lastMeasurementPacket;
|
||||||
|
uint32_t sensor_read_error_count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern EnvironmentalMeasurementPluginRadio *environmentalMeasurementPluginRadio;
|
|
@ -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",
|
// 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);
|
// 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");
|
// DEBUG_MSG("* * Message came from the mesh\n");
|
||||||
// Serial2.println("* * Message came from the mesh");
|
// 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) {
|
if (radioConfig.preferences.range_test_plugin_save) {
|
||||||
appendFile(mp);
|
appendFile(mp);
|
||||||
@ -209,7 +209,7 @@ bool RangeTestPluginRadio::appendFile(const MeshPacket &mp)
|
|||||||
{
|
{
|
||||||
auto &p = mp.decoded;
|
auto &p = mp.decoded;
|
||||||
|
|
||||||
NodeInfo *n = nodeDB.getNode(mp.from);
|
NodeInfo *n = nodeDB.getNode(getFrom(&mp));
|
||||||
/*
|
/*
|
||||||
DEBUG_MSG("-----------------------------------------\n");
|
DEBUG_MSG("-----------------------------------------\n");
|
||||||
DEBUG_MSG("p.payload.bytes \"%s\"\n", p.payload.bytes);
|
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("??:??:??,"); // Time
|
||||||
}
|
}
|
||||||
|
|
||||||
fileToAppend.printf("%d,", mp.from); // From
|
fileToAppend.printf("%d,", getFrom(&mp)); // From
|
||||||
fileToAppend.printf("%s,", n->user.long_name); // Long Name
|
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.latitude_i * 1e-7); // Sender Lat
|
||||||
fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long
|
fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long
|
||||||
|
@ -263,12 +263,12 @@ bool StoreForwardPluginRadio::handleReceived(const MeshPacket &mp)
|
|||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
if (radioConfig.preferences.store_forward_plugin_enabled) {
|
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("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,
|
// 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);
|
// p->want_ack, p->hop_limit);
|
||||||
printPacket("----- PACKET FROM RADIO -----", &mp);
|
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);
|
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) {
|
if (mp.decoded.portnum == PortNum_UNKNOWN_APP) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
[VERSION]
|
[VERSION]
|
||||||
major = 1
|
major = 1
|
||||||
minor = 2
|
minor = 2
|
||||||
build = 0
|
build = 6
|
||||||
|
Loading…
Reference in New Issue
Block a user