Merge pull request #543 from geeksville/udp

big set of changes from my weeks away
This commit is contained in:
Kevin Hester 2020-12-08 22:46:26 -08:00 committed by GitHub
commit b3b4c2c1c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 911 additions and 274 deletions

View File

@ -49,7 +49,8 @@
"string_view": "cpp",
"cassert": "cpp",
"iterator": "cpp",
"shared_mutex": "cpp"
"shared_mutex": "cpp",
"iostream": "cpp"
},
"cSpell.words": [
"Blox",

View File

@ -0,0 +1,6 @@
set -e
source bin/version.sh
esptool.py --baud 921600 write_flash 0x10000 release/latest/bins/universal/firmware-tbeam-$VERSION.bin

View File

@ -1,6 +1,12 @@
#!/bin/bash
set -e
echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.1"
# the nanopb tool seems to require that the .options file be in the current directory!
cd proto
../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto mesh.proto
../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto *.proto
echo "Regenerating protobuf documentation - if you see an error message"
echo "you can ignore it unless doing a new protobuf release to github."
bin/regen-docs.sh

View File

@ -2,27 +2,89 @@
You probably don't care about this section - skip to the next one.
For app cleanup:
* do fixed position bug https://github.com/meshtastic/Meshtastic-device/issues/536
* check build guide
* generate autodocs
* write user guide
* DONE update android code: https://developer.android.com/topic/libraries/view-binding/migration
* make gpio watch work, use thread and setup
* make hello world example service
* make python ping command
* make python gpio read a bit cleaner
* DONE have python tool check max packet size before sending to device
* DONE if request was sent reliably, send reply reliably
* DONE require a recent python api to talk to these new device loads
* DONE require a recent android app to talk to these new device loads
* DONE fix handleIncomingPosition
* DONE move want_replies handling into plugins
* DONE on android for received positions handle either old or new positions / user messages
* on android side send old or new positions as needed / user messages
* test python side handle new position/user messages
* DONE make a gpio example. --gpiowrb 4 1, --gpiord 0x444, --gpiowatch 0x3ff
* DONE fix position sending to use new plugin
* DONE Add SinglePortNumPlugin - as the new most useful baseclass
* DONE move positions into regular data packets (use new app framework)
* DONE move user info into regular data packets (use new app framework)
* test that positions, text messages and user info still work
* test that position, text messages and user info work properly with new android app and old device code
* fix the RTC drift bug
* move ping functionality into device, reply with rxsnr info
* use channels for gpio security https://github.com/meshtastic/Meshtastic-device/issues/104
* implement GPIO watch
For high speed/lots of devices/short range tasks:
- When guessing numhops for sending: if I've heard from many local (0 hop neighbors) decrease hopcount by 2 rather than 1.
This should nicely help 'router' nodes do the right thing when long range, or if there are many local nodes for short range.
- fix timeouts/delays to be based on packet length at current radio settings
Nimble tasks:
- readerror.txt stress test bug
- started RPA long test, jul 22 6pm
- implement nimble software update api
- update to latest bins, test OTA again (measure times) and then checkin bins
- do alpha release
* update protocol description per cyclomies email thread
* update faq with antennas https://meshtastic.discourse.group/t/range-test-ideas-requested/738/2
* update faq on recommended android version and phones
* add help link inside the app, reference a page on the wiki
* turn on amazon reviews support
* add a tablet layout (with map next to messages) in the android app
# Old docs to merge
MESH RADIO PROTOCOL
Old TODO notes on the mesh radio protocol, merge into real docs someday...
for each named group we have a pre-shared key known by all group members and
wrapped around the device. you can only be in one group at a time (FIXME?!) To
join the group we read a qr code with the preshared key and ParamsCodeEnum. that
gets sent via bluetooth to the device. ParamsCodeEnum maps to a set of various
radio params (regulatory region, center freq, SF, bandwidth, bitrate, power
etc...) so all members of the mesh can have their radios set the same way.
once in that group, we can talk between 254 node numbers.
to get our node number (and announce our presence in the channel) we pick a
random node number and broadcast as that node with WANT-NODENUM(my globally
unique name). If anyone on the channel has seen someone _else_ using that name
within the last 24 hrs(?) they reply with DENY-NODENUM. Note: we might receive
multiple denies. Note: this allows others to speak up for some other node that
might be saving battery right now. Any time we hear from another node (for any
message type), we add that node number to the unpickable list. To dramatically
decrease the odds a node number we request is already used by someone. If no one
denies within TBD seconds, we assume that we have that node number. As long as
we keep talking to folks at least once every 24 hrs, others should remember we
have it.
Once we have a node number we can broadcast POSITION-UPDATE(my globally unique
name, lat, lon, alt, amt battery remaining). All receivers will use this to a)
update the mapping of who is at what node nums, b) the time of last rx, c)
position. If we haven't heard from that node in a while we reply to that node
(only) with our current POSITION_UPDATE state - so that node (presumably just
rejoined the network) can build a map of all participants.
We will periodically broadcast POSITION-UPDATE as needed based on distance moved
or a periodic minimum heartbeat.
If user wants to send a text they can SEND_TEXT(dest user, short text message).
Dest user is a node number, or 0xff for broadcast.
# Medium priority
Items to complete before 1.0.

2
proto

@ -1 +1 @@
Subproject commit a0b8d888961720d70ab7467c94d8fa0687e58020
Subproject commit ebd18145cafb0d09528150b7a5eccd52b581902f

View File

@ -1,6 +1,7 @@
#include "SerialConsole.h"
#include "PowerFSM.h"
#include "configuration.h"
#include "NodeDB.h"
#include <Arduino.h>
#define Port Serial
@ -28,7 +29,8 @@ void SerialConsole::init()
void SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
{
// Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets
setDestination(&noopPrint);
if(!radioConfig.preferences.debug_log_enabled)
setDestination(&noopPrint);
canWrite = true;
StreamAPI::handleToRadio(buf, len);

View File

@ -32,6 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "main.h"
#include "mesh-pb-constants.h"
#include "meshwifi/meshwifi.h"
#include "plugins/TextMessagePlugin.h"
#include "target_specific.h"
#include "utils.h"
@ -720,6 +721,7 @@ void Screen::setup()
powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
textMessageObserver.observe(&textMessagePlugin);
}
void Screen::forceDisplay()
@ -1183,14 +1185,24 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
// DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
switch (arg->getStatusType()) {
case STATUS_TYPE_NODE:
if (showingNormalScreen && (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal())) {
if (showingNormalScreen &&
nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
setFrames(); // Regen the list of screens
}
nodeDB.updateGUI = false;
nodeDB.updateTextMessage = false;
break;
}
return 0;
}
int Screen::handleTextMessage(const MeshPacket *arg)
{
if (showingNormalScreen) {
setFrames(); // Regen the list of screens (will show new text message)
}
return 0;
}
} // namespace graphics

View File

@ -77,6 +77,8 @@ class Screen : public concurrency::OSThread
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const meshtastic::Status *> nodeStatusObserver =
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
CallbackObserver<Screen, const MeshPacket *> textMessageObserver =
CallbackObserver<Screen, const MeshPacket *>(this, &Screen::handleTextMessage);
public:
Screen(uint8_t address, int sda = -1, int scl = -1);
@ -192,6 +194,7 @@ class Screen : public concurrency::OSThread
DebugInfo *debug_info() { return &debugInfo; }
int handleStatusUpdate(const meshtastic::Status *arg);
int handleTextMessage(const MeshPacket *arg);
/// Used to force (super slow) eink displays to draw critical frames
void forceDisplay();

69
src/mesh/MeshPlugin.cpp Normal file
View File

@ -0,0 +1,69 @@
#include "MeshPlugin.h"
#include "NodeDB.h"
#include "MeshService.h"
#include <assert.h>
std::vector<MeshPlugin *> *MeshPlugin::plugins;
MeshPlugin::MeshPlugin(const char *_name) : name(_name)
{
// Can't trust static initalizer order, so we check each time
if(!plugins)
plugins = new std::vector<MeshPlugin *>();
plugins->push_back(this);
}
void MeshPlugin::setup() {
}
MeshPlugin::~MeshPlugin()
{
assert(0); // FIXME - remove from list of plugins once someone needs this feature
}
void MeshPlugin::callPlugins(const MeshPacket &mp)
{
// DEBUG_MSG("In call plugins\n");
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
auto &pi = **i;
if (pi.wantPortnum(mp.decoded.data.portnum)) {
bool handled = pi.handleReceived(mp);
// Possibly send replies (unless we are handling a locally generated message)
if (mp.decoded.want_response && mp.from != nodeDB.getNodeNum())
pi.sendResponse(mp);
DEBUG_MSG("Plugin %s handled=%d\n", pi.name, handled);
if (handled)
break;
}
else {
DEBUG_MSG("Plugin %s not interested\n", pi.name);
}
}
}
/** 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. Implementing this method
* is optional
*/
void MeshPlugin::sendResponse(const MeshPacket &req) {
auto r = allocReply();
if(r) {
DEBUG_MSG("Sending response\n");
setReplyTo(r, req);
service.sendToMesh(r);
}
else {
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
* 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) {
p->to = to.from;
p->want_ack = to.want_ack;
}

67
src/mesh/MeshPlugin.h Normal file
View File

@ -0,0 +1,67 @@
#pragma once
#include "mesh/MeshTypes.h"
#include <vector>
/** A baseclass for any mesh "plugin".
*
* A plugin allows you to add new features to meshtastic device code, without needing to know messaging details.
*
* A key concept for this is that your plugin should use a particular "portnum" for each message type you want to receive
* and handle.
*
* Interally we use plugins to implement the core meshtastic text messaging and gps position sharing features. You
* can use these classes as examples for how to write your own custom plugin. See here: (FIXME)
*/
class MeshPlugin
{
static std::vector<MeshPlugin *> *plugins;
public:
/** Constructor
* name is for debugging output
*/
MeshPlugin(const char *_name);
virtual ~MeshPlugin();
/** For use only by MeshService
*/
static void callPlugins(const MeshPacket &mp);
protected:
const char *name;
/**
* Initialize your plugin. This setup function is called once after all hardware and mesh protocol layers have
* been initialized
*/
virtual void setup();
/**
* @return true if you want to receive the specified portnum
*/
virtual bool wantPortnum(PortNum p) = 0;
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceived(const MeshPacket &mp) { return false; }
/** 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. */
virtual MeshPacket *allocReply() { return NULL; }
private:
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
* so that subclasses can (optionally) send a response back to the original sender. This method calls allocReply()
* to generate the reply message, and if !NULL that message will be delivered to whoever sent req
*/
void sendResponse(const MeshPacket &req);
};
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
*/
void setReplyTo(MeshPacket *p, const MeshPacket &to);

View File

@ -13,6 +13,8 @@
#include "RTC.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "plugins/PositionPlugin.h"
#include "plugins/NodeInfoPlugin.h"
#include "power.h"
/*
@ -51,7 +53,7 @@ MeshService service;
static int32_t sendOwnerCb()
{
service.sendOurOwner();
nodeInfoPlugin.sendOurNodeInfo();
return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000;
}
@ -74,113 +76,25 @@ void MeshService::init()
packetReceivedObserver.observe(&router->notifyPacketReceived);
}
void MeshService::sendOurOwner(NodeNum dest, bool wantReplies)
{
MeshPacket *p = router->allocForSending();
p->to = dest;
p->decoded.want_response = wantReplies;
p->decoded.which_payload = SubPacket_user_tag;
User &u = p->decoded.user;
u = owner;
DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name);
sendToMesh(p);
}
/// handle a user packet that just arrived on the radio, return NULL if we should not process this packet at all
const MeshPacket *MeshService::handleFromRadioUser(const MeshPacket *mp)
{
bool wasBroadcast = mp->to == NODENUM_BROADCAST;
// Disable this collision testing if we use 32 bit nodenums
bool isCollision = (sizeof(NodeNum) == 1) && (mp->from == myNodeInfo.my_node_num);
if (isCollision) {
// we win if we have a lower macaddr
bool weWin = memcmp(&owner.macaddr, &mp->decoded.user.macaddr, sizeof(owner.macaddr)) < 0;
if (weWin) {
DEBUG_MSG("NOTE! Received a nodenum collision and we are vetoing\n");
mp = NULL;
sendOurOwner(); // send our owner as a _broadcast_ because that other guy is mistakenly using our nodenum
} else {
// we lost, we need to try for a new nodenum!
DEBUG_MSG("NOTE! Received a nodenum collision we lost, so picking a new nodenum\n");
nodeDB.updateFrom(
*mp); // update the DB early - before trying to repick (so we don't select the same node number again)
nodeDB.pickNewNodeNum();
sendOurOwner(); // broadcast our new attempt at a node number
}
} else if (wasBroadcast) {
// If we haven't yet abandoned the packet and it was a broadcast, reply (just to them) with our User record so they can
// build their DB
// Someone just sent us a User, reply with our Owner
DEBUG_MSG("Received broadcast Owner from 0x%x, replying with our owner\n", mp->from);
sendOurOwner(mp->from);
String lcd = String("Joined: ") + mp->decoded.user.long_name + "\n";
screen->print(lcd.c_str());
}
return mp;
}
void MeshService::handleIncomingPosition(const MeshPacket *mp)
{
if (mp->which_payload == MeshPacket_decoded_tag && mp->decoded.which_payload == SubPacket_position_tag) {
DEBUG_MSG("handled incoming position time=%u\n", mp->decoded.position.time);
if (mp->decoded.position.time) {
struct timeval tv;
uint32_t secs = mp->decoded.position.time;
tv.tv_sec = secs;
tv.tv_usec = 0;
perhapsSetRTC(RTCQualityFromNet, &tv);
}
} else {
DEBUG_MSG("Ignoring incoming packet - not a position\n");
}
}
int MeshService::handleFromRadio(const MeshPacket *mp)
{
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
// If it is a position packet, perhaps set our clock - this must be before nodeDB.updateFrom
handleIncomingPosition(mp);
printPacket("Forwarding to phone", mp);
nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio
if (mp->which_payload == MeshPacket_decoded_tag && mp->decoded.which_payload == SubPacket_user_tag) {
mp = handleFromRadioUser(mp);
fromNum++;
if (toPhoneQueue.numFree() == 0) {
DEBUG_MSG("NOTE: tophone queue is full, discarding oldest\n");
MeshPacket *d = toPhoneQueue.dequeuePtr(0);
if (d)
releaseToPool(d);
}
// If we veto a received User packet, we don't put it into the DB or forward it to the phone (to prevent confusing it)
if (mp) {
printPacket("Forwarding to phone", mp);
nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio
fromNum++;
if (toPhoneQueue.numFree() == 0) {
DEBUG_MSG("NOTE: tophone queue is full, discarding oldest\n");
MeshPacket *d = toPhoneQueue.dequeuePtr(0);
if (d)
releaseToPool(d);
}
MeshPacket *copied = packetPool.allocCopy(*mp);
assert(toPhoneQueue.enqueue(copied, 0)); // FIXME, instead of failing for full queue, delete the oldest mssages
if (mp->decoded.want_response)
sendNetworkPing(mp->from);
} else {
DEBUG_MSG("Not delivering vetoed User message\n");
}
MeshPacket *copied = packetPool.allocCopy(*mp);
assert(toPhoneQueue.enqueue(copied, 0)); // FIXME, instead of failing for full queue, delete the oldest mssages
return 0;
}
@ -201,7 +115,7 @@ bool MeshService::reloadConfig()
// This will also update the region as needed
bool didReset = nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings
configChanged.notifyObservers(NULL);
nodeDB.saveToDisk();
@ -211,7 +125,7 @@ bool MeshService::reloadConfig()
/// The owner User record just got updated, update our node DB and broadcast the info into the mesh
void MeshService::reloadOwner()
{
sendOurOwner();
nodeInfoPlugin.sendOurNodeInfo();
nodeDB.saveToDisk();
}
@ -222,8 +136,6 @@ void MeshService::reloadOwner()
*/
void MeshService::handleToRadio(MeshPacket &p)
{
handleIncomingPosition(&p); // If it is a position packet, perhaps set our clock
if (p.from == 0) // If the phone didn't set a sending node ID, use ours
p.from = nodeDB.getNodeNum();
@ -253,7 +165,8 @@ void MeshService::sendToMesh(MeshPacket *p)
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
// devices can get time.
if (p->which_payload == MeshPacket_decoded_tag && p->decoded.which_payload == SubPacket_position_tag) {
if (p->which_payload == MeshPacket_decoded_tag && p->decoded.which_payload == SubPacket_position_tag &&
p->decoded.position.time) {
if (getRTCQuality() < RTCQualityGPS) {
DEBUG_MSG("Stripping time %u from position send\n", p->decoded.position.time);
p->decoded.position.time = 0;
@ -272,36 +185,16 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
DEBUG_MSG("Sending network ping to 0x%x, with position=%d, wantReplies=%d\n", dest, node->has_position, wantReplies);
if (node->has_position)
sendOurPosition(dest, wantReplies);
positionPlugin.sendOurPosition(dest, wantReplies);
else
sendOurOwner(dest, wantReplies);
}
void MeshService::sendOurPosition(NodeNum dest, bool wantReplies)
{
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
assert(node);
assert(node->has_position);
// Update our local node info with our position (even if we don't decide to update anyone else)
MeshPacket *p = router->allocForSending();
p->to = dest;
p->decoded.which_payload = SubPacket_position_tag;
p->decoded.position = node->position;
p->decoded.want_response = wantReplies;
p->decoded.position.time =
getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid.
sendToMesh(p);
nodeInfoPlugin.sendOurNodeInfo(dest, wantReplies);
}
int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
{
// Update our local node info with our position (even if we don't decide to update anyone else)
MeshPacket *p = router->allocForSending();
p->decoded.which_payload = SubPacket_position_tag;
Position &pos = p->decoded.position;
Position pos = Position_init_default;
if (gps->hasLock()) {
if (gps->altitude != 0)
@ -309,6 +202,17 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
pos.latitude_i = gps->latitude;
pos.longitude_i = gps->longitude;
}
else {
// The GPS has lost lock, if we are fixed position we should just keep using
// the old position
if(radioConfig.preferences.fixed_position) {
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
assert(node);
assert(node->has_position);
pos = node->position;
DEBUG_MSG("WARNING: Using fixed position\n");
}
}
pos.time = getValidTime(RTCQualityGPS);
@ -316,21 +220,18 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
pos.battery_level = powerStatus->getBatteryChargePercent();
updateBatteryLevel(pos.battery_level);
// DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.latitude_i, pos.time, pos.battery_level);
DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.latitude_i, pos.time, pos.battery_level);
// Update our current position in the local DB
nodeDB.updatePosition(nodeDB.getNodeNum(), pos);
// We limit our GPS broadcasts to a max rate
static uint32_t lastGpsSend;
uint32_t now = millis();
if (lastGpsSend == 0 || now - lastGpsSend > getPref_position_broadcast_secs() * 1000) {
lastGpsSend = now;
DEBUG_MSG("Sending position to mesh\n");
sendToMesh(p);
} else {
// We don't need to send this packet to anyone else, but it still serves as a nice uniform way to update our local state
nodeDB.updateFrom(*p);
releaseToPool(p);
DEBUG_MSG("Sending position to mesh (not requesting replies)\n");
positionPlugin.sendOurPosition();
}
return 0;

View File

@ -75,18 +75,13 @@ class MeshService
/// sends our owner
void sendNetworkPing(NodeNum dest, bool wantReplies = false);
/// Send our owner info to a particular node
void sendOurOwner(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
private:
/// Broadcasts our last known position
void sendOurPosition(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
/// Send a packet into the mesh - note p must have been allocated from packetPool. We will return it to that pool after
/// sending. This is the ONLY function you should use for sending messages into the mesh, because it also updates the nodedb
/// cache
void sendToMesh(MeshPacket *p);
private:
/// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh
/// returns 0 to allow futher processing
int onGPSChanged(const meshtastic::GPSStatus *arg);
@ -94,12 +89,6 @@ class MeshService
/// Handle a packet that just arrived from the radio. This method does _not_ free the provided packet. If it needs
/// to keep the packet around it makes a copy
int handleFromRadio(const MeshPacket *p);
/// handle a user packet that just arrived on the radio, return NULL if we should not process this packet at all
const MeshPacket *handleFromRadioUser(const MeshPacket *mp);
/// look at inbound packets and if they contain a position with time, possibly set our clock
void handleIncomingPosition(const MeshPacket *mp);
};
extern MeshService service;

View File

@ -9,7 +9,7 @@
typedef uint32_t NodeNum;
typedef uint32_t PacketId; // A packet sequence number
#define NODENUM_BROADCAST (sizeof(NodeNum) == 4 ? UINT32_MAX : UINT8_MAX)
#define NODENUM_BROADCAST UINT32_MAX
#define ERRNO_OK 0
#define ERRNO_NO_INTERFACES 33
#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER

View File

@ -131,13 +131,13 @@ bool NodeDB::resetRadioConfig()
radioConfig.preferences.region = RegionCode_TW;
// Enter super deep sleep soon and stay there not very long
//radioConfig.preferences.mesh_sds_timeout_secs = 10;
//radioConfig.preferences.sds_secs = 60;
// radioConfig.preferences.mesh_sds_timeout_secs = 10;
// radioConfig.preferences.sds_secs = 60;
}
// Update the global myRegion
initRegion();
return didFactoryReset;
}
@ -165,7 +165,7 @@ void NodeDB::installDefaultDeviceState()
// default to no GPS, until one has been found by probing
myNodeInfo.has_gps = false;
myNodeInfo.message_timeout_msec = FLOOD_EXPIRE_TIME;
myNodeInfo.min_app_version = 172;
myNodeInfo.min_app_version = 20120; // format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
generatePacketId(); // FIXME - ugly way to init current_packet_id;
// Init our blank owner info to reasonable defaults
@ -248,8 +248,7 @@ void NodeDB::pickNewNodeNum()
// If we don't have a nodenum at app - pick an initial nodenum based on the macaddr
if (r == 0)
r = sizeof(NodeNum) == 1 ? ourMacAddr[5]
: ((ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5]);
r = (ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5];
if (r == NODENUM_BROADCAST || r < NUM_RESERVED)
r = NUM_RESERVED; // don't pick a reserved node number
@ -379,6 +378,48 @@ size_t NodeDB::getNumOnlineNodes()
return numseen;
}
#include "MeshPlugin.h"
/** Update position info for this node based on received position data
*/
void NodeDB::updatePosition(uint32_t nodeId, const Position &p)
{
NodeInfo *info = getOrCreateNode(nodeId);
DEBUG_MSG("DB update position node=0x%x time=%u, latI=%d, lonI=%d\n", nodeId, p.time, p.latitude_i, p.longitude_i);
info->position = p;
info->has_position = true;
updateGUIforNode = info;
notifyObservers(true); // Force an update whether or not our node counts have changed
}
/** Update user info for this node based on received user data
*/
void NodeDB::updateUser(uint32_t nodeId, const User &p)
{
NodeInfo *info = getOrCreateNode(nodeId);
DEBUG_MSG("old user %s/%s/%s\n", info->user.id, info->user.long_name, info->user.short_name);
bool changed = memcmp(&info->user, &p,
sizeof(info->user)); // Both of these blocks start as filled with zero so I think this is okay
info->user = p;
DEBUG_MSG("updating changed=%d user %s/%s/%s\n", changed, info->user.id, info->user.long_name, info->user.short_name);
info->has_user = true;
if (changed) {
updateGUIforNode = info;
powerFSM.trigger(EVENT_NODEDB_UPDATED);
notifyObservers(true); // Force an update whether or not our node counts have changed
// Not really needed - we will save anyways when we go to sleep
// We just changed something important about the user, store our DB
// saveToDisk();
}
}
/// given a subpacket sniffed from the network, update our DB state
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
void NodeDB::updateFrom(const MeshPacket &mp)
@ -398,58 +439,21 @@ void NodeDB::updateFrom(const MeshPacket &mp)
switch (p.which_payload) {
case SubPacket_position_tag: {
// we always trust our local timestamps more
info->position = p.position;
if (mp.rx_time)
info->position.time = mp.rx_time;
info->has_position = true;
updateGUIforNode = info;
notifyObservers(true); // Force an update whether or not our node counts have changed
// handle a legacy position packet
DEBUG_MSG("WARNING: Processing a (deprecated) position packet from %d\n", mp.from);
updatePosition(mp.from, p.position);
break;
}
case SubPacket_data_tag: {
// Keep a copy of the most recent text message.
if (p.data.typ == Data_Type_CLEAR_TEXT) {
DEBUG_MSG("Received text msg from=0x%0x, id=%d, msg=%.*s\n", mp.from, mp.id, p.data.payload.size,
p.data.payload.bytes);
if (mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) {
// We only store/display messages destined for us.
devicestate.rx_text_message = mp;
devicestate.has_rx_text_message = true;
updateTextMessage = true;
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
notifyObservers(true); // Force an update whether or not our node counts have changed
// This is going into the wifidev feature branch
// Only update the WebUI if WiFi is enabled
//#if WiFi_MODE != 0
// notifyWebUI();
//#endif
}
}
if (mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum())
MeshPlugin::callPlugins(mp);
break;
}
case SubPacket_user_tag: {
DEBUG_MSG("old user %s/%s/%s\n", info->user.id, info->user.long_name, info->user.short_name);
bool changed = memcmp(&info->user, &p.user,
sizeof(info->user)); // Both of these blocks start as filled with zero so I think this is okay
info->user = p.user;
DEBUG_MSG("updating changed=%d user %s/%s/%s\n", changed, info->user.id, info->user.long_name, info->user.short_name);
info->has_user = true;
if (changed) {
updateGUIforNode = info;
powerFSM.trigger(EVENT_NODEDB_UPDATED);
notifyObservers(true); // Force an update whether or not our node counts have changed
// Not really needed - we will save anyways when we go to sleep
// We just changed something important about the user, store our DB
// saveToDisk();
}
DEBUG_MSG("WARNING: Processing a (deprecated) user packet from %d\n", mp.from);
updateUser(mp.from, p.user);
break;
}

View File

@ -33,7 +33,6 @@ class NodeDB
public:
bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled
NodeInfo *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI
bool updateTextMessage = false; // if true, the GUI should show a new text message
Observable<const meshtastic::NodeStatus *> newStatus;
/// don't do mesh based algoritm for node id assignment (initially)
@ -58,6 +57,14 @@ class NodeDB
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
void updateFrom(const MeshPacket &p);
/** Update position info for this node based on received position data
*/
void updatePosition(uint32_t nodeId, const Position &p);
/** Update user info for this node based on received user data
*/
void updateUser(uint32_t nodeId, const User &p);
/// @return our node number
NodeNum getNodeNum() { return myNodeInfo.my_node_num; }

View File

@ -0,0 +1,3 @@
#include "ProtobufPlugin.h"

66
src/mesh/ProtobufPlugin.h Normal file
View File

@ -0,0 +1,66 @@
#pragma once
#include "SinglePortPlugin.h"
/**
* A base class for mesh plugins that assume that they are sending/receiving one particular protobuf based
* payload. Using one particular app ID.
*
* If you are using protobufs to encode your packets (recommended) you can use this as a baseclass for your plugin
* and avoid a bunch of boilerplate code.
*/
template <class T> class ProtobufPlugin : private SinglePortPlugin
{
const pb_msgdesc_t *fields;
public:
/** Constructor
* name is for debugging output
*/
ProtobufPlugin(const char *_name, PortNum _ourPortNum, const pb_msgdesc_t *_fields)
: SinglePortPlugin(_name, _ourPortNum), fields(_fields)
{
}
protected:
/**
* Handle a received message, the data field in the message is already decoded and is provided
*/
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const T &decoded) = 0;
/**
* Return a mesh packet which has been preinited with a particular protobuf data payload and port number.
* You can then send this packet (after customizing any of the payload fields you might need) with
* service.sendToMesh()
*/
MeshPacket *allocDataProtobuf(const T &payload)
{
// Update our local node info with our position (even if we don't decide to update anyone else)
MeshPacket *p = allocDataPacket();
p->decoded.data.payload.size =
pb_encode_to_bytes(p->decoded.data.payload.bytes, sizeof(p->decoded.data.payload.bytes), fields, &payload);
// DEBUG_MSG("did encode\n");
return p;
}
private:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceived(const MeshPacket &mp)
{
// FIXME - we currently update position data in the DB only if the message was a broadcast or destined to us
// it would be better to update even if the message was destined to others.
auto &p = mp.decoded.data;
DEBUG_MSG("Received %s from=0x%0x, id=%d, payloadlen=%d\n", name, mp.from, mp.id, p.payload.size);
T scratch;
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch))
handleReceivedProtobuf(mp, scratch);
return false; // Let others look at this message also if they want
}
};

View File

@ -58,7 +58,7 @@ class RadioInterface
uint8_t sf = 9;
uint8_t cr = 7;
uint16_t preambleLength = 32; // 8 is default, but FIXME use longer to increase the amount of sleep time when receiving
uint16_t preambleLength = 32; // 8 is default, but we use longer to increase the amount of sleep time when receiving
MeshPacket *sendingPacket = NULL; // The packet we are currently sending
uint32_t lastTxStart = 0L;

View File

@ -0,0 +1,40 @@
#pragma once
#include "MeshPlugin.h"
#include "Router.h"
/**
* Most plugins are only interested in sending/receving one particular portnum. This baseclass simplifies that common
* case.
*/
class SinglePortPlugin : public MeshPlugin
{
protected:
PortNum ourPortNum;
public:
/** Constructor
* name is for debugging output
*/
SinglePortPlugin(const char *_name, PortNum _ourPortNum) : MeshPlugin(_name), ourPortNum(_ourPortNum) {}
protected:
/**
* @return true if you want to receive the specified portnum
*/
virtual bool wantPortnum(PortNum p) { return p == ourPortNum; }
/**
* Return a mesh packet which has been preinited as a data packet with a particular port number.
* You can then send this packet (after customizing any of the payload fields you might need) with
* service.sendToMesh()
*/
MeshPacket *allocDataPacket()
{
// Update our local node info with our position (even if we don't decide to update anyone else)
MeshPacket *p = router->allocForSending();
p->decoded.which_payload = SubPacket_data_tag;
p->decoded.data.portnum = ourPortNum;
return p;
}
};

View File

@ -51,10 +51,6 @@ PB_BIND(FromRadio, FromRadio, 2)
PB_BIND(ToRadio, ToRadio, 2)
PB_BIND(ManufacturingData, ManufacturingData, AUTO)

View File

@ -4,6 +4,7 @@
#ifndef PB_MESH_PB_H_INCLUDED
#define PB_MESH_PB_H_INCLUDED
#include <pb.h>
#include "portnums.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
@ -22,7 +23,8 @@ typedef enum _RouteError {
} RouteError;
typedef enum _Constants {
Constants_Unused = 0
Constants_Unused = 0,
Constants_DATA_PAYLOAD_LEN = 240
} Constants;
typedef enum _RegionCode {
@ -50,12 +52,6 @@ typedef enum _LocationSharing {
LocationSharing_LocDisabled = 2
} LocationSharing;
typedef enum _Data_Type {
Data_Type_OPAQUE = 0,
Data_Type_CLEAR_TEXT = 1,
Data_Type_CLEAR_READACK = 2
} Data_Type;
typedef enum _ChannelSettings_ModemConfig {
ChannelSettings_ModemConfig_Bw125Cr45Sf128 = 0,
ChannelSettings_ModemConfig_Bw500Cr45Sf128 = 1,
@ -78,7 +74,7 @@ typedef struct _ChannelSettings {
typedef PB_BYTES_ARRAY_T(240) Data_payload_t;
typedef struct _Data {
Data_Type typ;
PortNum portnum;
Data_payload_t payload;
} Data;
@ -86,13 +82,6 @@ typedef struct _DebugString {
char message[256];
} DebugString;
typedef struct _ManufacturingData {
uint32_t fradioFreq;
pb_callback_t hw_model;
pb_callback_t hw_version;
int32_t selftest_result;
} ManufacturingData;
typedef struct _MyNodeInfo {
uint32_t my_node_num;
bool has_gps;
@ -140,7 +129,9 @@ typedef struct _RadioConfig_UserPreferences {
uint32_t gps_attempt_time;
bool is_router;
bool is_low_power;
bool fixed_position;
bool factory_reset;
bool debug_log_enabled;
pb_size_t ignore_incoming_count;
uint32_t ignore_incoming[3];
} RadioConfig_UserPreferences;
@ -260,8 +251,8 @@ typedef struct _ToRadio {
#define _RouteError_ARRAYSIZE ((RouteError)(RouteError_TIMEOUT+1))
#define _Constants_MIN Constants_Unused
#define _Constants_MAX Constants_Unused
#define _Constants_ARRAYSIZE ((Constants)(Constants_Unused+1))
#define _Constants_MAX Constants_DATA_PAYLOAD_LEN
#define _Constants_ARRAYSIZE ((Constants)(Constants_DATA_PAYLOAD_LEN+1))
#define _RegionCode_MIN RegionCode_Unset
#define _RegionCode_MAX RegionCode_TW
@ -275,10 +266,6 @@ typedef struct _ToRadio {
#define _LocationSharing_MAX LocationSharing_LocDisabled
#define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1))
#define _Data_Type_MIN Data_Type_OPAQUE
#define _Data_Type_MAX Data_Type_CLEAR_READACK
#define _Data_Type_ARRAYSIZE ((Data_Type)(Data_Type_CLEAR_READACK+1))
#define _ChannelSettings_ModemConfig_MIN ChannelSettings_ModemConfig_Bw125Cr45Sf128
#define _ChannelSettings_ModemConfig_MAX ChannelSettings_ModemConfig_Bw125Cr48Sf4096
#define _ChannelSettings_ModemConfig_ARRAYSIZE ((ChannelSettings_ModemConfig)(ChannelSettings_ModemConfig_Bw125Cr48Sf4096+1))
@ -286,37 +273,35 @@ typedef struct _ToRadio {
/* Initializer values for message structs */
#define Position_init_default {0, 0, 0, 0, 0}
#define Data_init_default {_Data_Type_MIN, {0, {0}}}
#define Data_init_default {_PortNum_MIN, {0, {0}}}
#define User_init_default {"", "", "", {0}}
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, 0, {0}, 0}
#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0}
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0}
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default}
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 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, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}}
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0}
#define DebugString_init_default {""}
#define FromRadio_init_default {0, 0, {MeshPacket_init_default}}
#define ToRadio_init_default {0, {MeshPacket_init_default}}
#define ManufacturingData_init_default {0, {{NULL}, NULL}, {{NULL}, NULL}, 0}
#define Position_init_zero {0, 0, 0, 0, 0}
#define Data_init_zero {_Data_Type_MIN, {0, {0}}}
#define Data_init_zero {_PortNum_MIN, {0, {0}}}
#define User_init_zero {"", "", "", {0}}
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, 0, {0}, 0}
#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0}
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0}
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero}
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 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, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}}
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0}
#define DebugString_init_zero {""}
#define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}}
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
#define ManufacturingData_init_zero {0, {{NULL}, NULL}, {{NULL}, NULL}, 0}
/* Field tags (for use in manual encoding/decoding) */
#define ChannelSettings_tx_power_tag 1
@ -327,13 +312,9 @@ typedef struct _ToRadio {
#define ChannelSettings_channel_num_tag 9
#define ChannelSettings_psk_tag 4
#define ChannelSettings_name_tag 5
#define Data_typ_tag 1
#define Data_portnum_tag 1
#define Data_payload_tag 2
#define DebugString_message_tag 1
#define ManufacturingData_fradioFreq_tag 1
#define ManufacturingData_hw_model_tag 2
#define ManufacturingData_hw_version_tag 3
#define ManufacturingData_selftest_result_tag 4
#define MyNodeInfo_my_node_num_tag 1
#define MyNodeInfo_has_gps_tag 2
#define MyNodeInfo_num_channels_tag 3
@ -370,7 +351,9 @@ typedef struct _ToRadio {
#define RadioConfig_UserPreferences_region_tag 15
#define RadioConfig_UserPreferences_is_router_tag 37
#define RadioConfig_UserPreferences_is_low_power_tag 38
#define RadioConfig_UserPreferences_fixed_position_tag 39
#define RadioConfig_UserPreferences_factory_reset_tag 100
#define RadioConfig_UserPreferences_debug_log_enabled_tag 101
#define RadioConfig_UserPreferences_location_share_tag 32
#define RadioConfig_UserPreferences_gps_operation_tag 33
#define RadioConfig_UserPreferences_gps_update_interval_tag 34
@ -442,7 +425,7 @@ X(a, STATIC, SINGULAR, FIXED32, time, 9)
#define Position_DEFAULT NULL
#define Data_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UENUM, typ, 1) \
X(a, STATIC, SINGULAR, UENUM, portnum, 1) \
X(a, STATIC, SINGULAR, BYTES, payload, 2)
#define Data_CALLBACK NULL
#define Data_DEFAULT NULL
@ -537,7 +520,9 @@ X(a, STATIC, SINGULAR, UINT32, gps_update_interval, 34) \
X(a, STATIC, SINGULAR, UINT32, gps_attempt_time, 36) \
X(a, STATIC, SINGULAR, BOOL, is_router, 37) \
X(a, STATIC, SINGULAR, BOOL, is_low_power, 38) \
X(a, STATIC, SINGULAR, BOOL, fixed_position, 39) \
X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \
X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 101) \
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103)
#define RadioConfig_UserPreferences_CALLBACK NULL
#define RadioConfig_UserPreferences_DEFAULT NULL
@ -623,14 +608,6 @@ X(a, STATIC, ONEOF, MESSAGE, (variant,set_owner,variant.set_owner), 102)
#define ToRadio_variant_set_radio_MSGTYPE RadioConfig
#define ToRadio_variant_set_owner_MSGTYPE User
#define ManufacturingData_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, fradioFreq, 1) \
X(a, CALLBACK, SINGULAR, STRING, hw_model, 2) \
X(a, CALLBACK, SINGULAR, STRING, hw_version, 3) \
X(a, STATIC, SINGULAR, SINT32, selftest_result, 4)
#define ManufacturingData_CALLBACK pb_default_field_callback
#define ManufacturingData_DEFAULT NULL
extern const pb_msgdesc_t Position_msg;
extern const pb_msgdesc_t Data_msg;
extern const pb_msgdesc_t User_msg;
@ -646,7 +623,6 @@ extern const pb_msgdesc_t DeviceState_msg;
extern const pb_msgdesc_t DebugString_msg;
extern const pb_msgdesc_t FromRadio_msg;
extern const pb_msgdesc_t ToRadio_msg;
extern const pb_msgdesc_t ManufacturingData_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define Position_fields &Position_msg
@ -664,25 +640,23 @@ extern const pb_msgdesc_t ManufacturingData_msg;
#define DebugString_fields &DebugString_msg
#define FromRadio_fields &FromRadio_msg
#define ToRadio_fields &ToRadio_msg
#define ManufacturingData_fields &ManufacturingData_msg
/* Maximum encoded size of messages (where known) */
#define Position_size 39
#define Data_size 245
#define Data_size 246
#define User_size 72
#define RouteDiscovery_size 88
#define SubPacket_size 274
#define MeshPacket_size 313
#define SubPacket_size 275
#define MeshPacket_size 314
#define ChannelSettings_size 84
#define RadioConfig_size 308
#define RadioConfig_UserPreferences_size 219
#define RadioConfig_size 314
#define RadioConfig_UserPreferences_size 225
#define NodeInfo_size 132
#define MyNodeInfo_size 110
#define DeviceState_size 5460
#define DeviceState_size 5468
#define DebugString_size 258
#define FromRadio_size 322
#define ToRadio_size 316
/* ManufacturingData_size depends on runtime parameters */
#define FromRadio_size 323
#define ToRadio_size 318
#ifdef __cplusplus
} /* extern "C" */

10
src/mesh/portnums.pb.c Normal file
View File

@ -0,0 +1,10 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.1 */
#include "portnums.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif

37
src/mesh/portnums.pb.h Normal file
View File

@ -0,0 +1,37 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.1 */
#ifndef PB_PORTNUMS_PB_H_INCLUDED
#define PB_PORTNUMS_PB_H_INCLUDED
#include <pb.h>
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Enum definitions */
typedef enum _PortNum {
PortNum_UNKNOWN_APP = 0,
PortNum_TEXT_MESSAGE_APP = 1,
PortNum_REMOTE_HARDWARE_APP = 2,
PortNum_POSITION_APP = 3,
PortNum_NODEINFO_APP = 4,
PortNum_PRIVATE_APP = 256,
PortNum_IP_TUNNEL_APP = 1024
} PortNum;
/* Helper constants for enums */
#define _PortNum_MIN PortNum_UNKNOWN_APP
#define _PortNum_MAX PortNum_IP_TUNNEL_APP
#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_IP_TUNNEL_APP+1))
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@ -0,0 +1,13 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.1 */
#include "remote_hardware.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(HardwareMessage, HardwareMessage, AUTO)

View File

@ -0,0 +1,69 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.1 */
#ifndef PB_REMOTE_HARDWARE_PB_H_INCLUDED
#define PB_REMOTE_HARDWARE_PB_H_INCLUDED
#include <pb.h>
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Enum definitions */
typedef enum _HardwareMessage_Type {
HardwareMessage_Type_UNSET = 0,
HardwareMessage_Type_WRITE_GPIOS = 1,
HardwareMessage_Type_WATCH_GPIOS = 2,
HardwareMessage_Type_GPIOS_CHANGED = 3,
HardwareMessage_Type_READ_GPIOS = 4,
HardwareMessage_Type_READ_GPIOS_REPLY = 5
} HardwareMessage_Type;
/* Struct definitions */
typedef struct _HardwareMessage {
HardwareMessage_Type typ;
uint64_t gpio_mask;
uint64_t gpio_value;
} HardwareMessage;
/* Helper constants for enums */
#define _HardwareMessage_Type_MIN HardwareMessage_Type_UNSET
#define _HardwareMessage_Type_MAX HardwareMessage_Type_READ_GPIOS_REPLY
#define _HardwareMessage_Type_ARRAYSIZE ((HardwareMessage_Type)(HardwareMessage_Type_READ_GPIOS_REPLY+1))
/* Initializer values for message structs */
#define HardwareMessage_init_default {_HardwareMessage_Type_MIN, 0, 0}
#define HardwareMessage_init_zero {_HardwareMessage_Type_MIN, 0, 0}
/* Field tags (for use in manual encoding/decoding) */
#define HardwareMessage_typ_tag 1
#define HardwareMessage_gpio_mask_tag 2
#define HardwareMessage_gpio_value_tag 3
/* Struct field encoding specification for nanopb */
#define HardwareMessage_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UENUM, typ, 1) \
X(a, STATIC, SINGULAR, UINT64, gpio_mask, 2) \
X(a, STATIC, SINGULAR, UINT64, gpio_value, 3)
#define HardwareMessage_CALLBACK NULL
#define HardwareMessage_DEFAULT NULL
extern const pb_msgdesc_t HardwareMessage_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define HardwareMessage_fields &HardwareMessage_msg
/* Maximum encoded size of messages (where known) */
#define HardwareMessage_size 24
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@ -0,0 +1,44 @@
#include "NodeInfoPlugin.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
#include "configuration.h"
#include "main.h"
NodeInfoPlugin nodeInfoPlugin;
bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User &p)
{
// FIXME - we currently update NodeInfo data in the DB only if the message was a broadcast or destined to us
// it would be better to update even if the message was destined to others.
nodeDB.updateUser(mp.from, p);
bool wasBroadcast = mp.to == NODENUM_BROADCAST;
// Show new nodes on LCD screen
if (wasBroadcast) {
String lcd = String("Joined: ") + p.long_name + "\n";
screen->print(lcd.c_str());
}
return false; // Let others look at this message also if they want
}
void NodeInfoPlugin::sendOurNodeInfo(NodeNum dest, bool wantReplies)
{
MeshPacket *p = allocReply();
p->to = dest;
p->decoded.want_response = wantReplies;
service.sendToMesh(p);
}
MeshPacket *NodeInfoPlugin::allocReply()
{
User &u = owner;
DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name);
return allocDataProtobuf(u);
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "ProtobufPlugin.h"
/**
* NodeInfo plugin for sending/receiving NodeInfos into the mesh
*/
class NodeInfoPlugin : public ProtobufPlugin<User>
{
public:
/** Constructor
* name is for debugging output
*/
NodeInfoPlugin() : ProtobufPlugin("nodeinfo", PortNum_NODEINFO_APP, User_fields) {}
/**
* Send our NodeInfo into the mesh
*/
void sendOurNodeInfo(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
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 User &p);
/** 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. */
virtual MeshPacket *allocReply();
};
extern NodeInfoPlugin nodeInfoPlugin;

View File

@ -0,0 +1,51 @@
#include "PositionPlugin.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
#include "configuration.h"
PositionPlugin positionPlugin;
bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position &p)
{
// FIXME - we currently update position data in the DB only if the message was a broadcast or destined to us
// it would be better to update even if the message was destined to others.
if (p.time) {
struct timeval tv;
uint32_t secs = p.time;
tv.tv_sec = secs;
tv.tv_usec = 0;
perhapsSetRTC(RTCQualityFromNet, &tv);
}
nodeDB.updatePosition(mp.from, p);
return false; // Let others look at this message also if they want
}
MeshPacket *PositionPlugin::allocReply()
{
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
assert(node);
assert(node->has_position);
// Update our local node info with our position (even if we don't decide to update anyone else)
auto position = node->position;
position.time = getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid.
return allocDataProtobuf(position);
}
void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)
{
MeshPacket *p = allocReply();
p->to = dest;
p->decoded.want_response = wantReplies;
service.sendToMesh(p);
}

View File

@ -0,0 +1,33 @@
#pragma once
#include "ProtobufPlugin.h"
/**
* Position plugin for sending/receiving positions into the mesh
*/
class PositionPlugin : public ProtobufPlugin<Position>
{
public:
/** Constructor
* name is for debugging output
*/
PositionPlugin() : ProtobufPlugin("position", PortNum_POSITION_APP, Position_fields) {}
/**
* Send our position into the mesh
*/
void sendOurPosition(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
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 Position &p);
/** 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. */
virtual MeshPacket *allocReply();
};
extern PositionPlugin positionPlugin;

View File

@ -0,0 +1,63 @@
#include "RemoteHardwarePlugin.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
#include "configuration.h"
#include "main.h"
RemoteHardwarePlugin remoteHardwarePlugin;
#define NUM_GPIOS 64
bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const HardwareMessage &p)
{
switch (p.typ) {
case HardwareMessage_Type_WRITE_GPIOS:
// Print notification to LCD screen
screen->print("Write GPIOs\n");
for (uint8_t i = 0; i < NUM_GPIOS; i++) {
uint64_t mask = 1 << i;
if (p.gpio_mask & mask) {
digitalWrite(i, (p.gpio_value & mask) ? 1 : 0);
pinMode(i, OUTPUT);
}
}
break;
case HardwareMessage_Type_READ_GPIOS: {
// Print notification to LCD screen
screen->print("Read GPIOs\n");
uint64_t res = 0;
for (uint8_t i = 0; i < NUM_GPIOS; i++) {
uint64_t mask = 1 << i;
if (p.gpio_mask & mask) {
pinMode(i, INPUT_PULLUP);
if (digitalRead(i))
res |= (1 << i);
}
}
// Send the reply
HardwareMessage reply = HardwareMessage_init_default;
reply.typ = HardwareMessage_Type_READ_GPIOS_REPLY;
reply.gpio_value = res;
MeshPacket *p = allocDataProtobuf(reply);
setReplyTo(p, req);
service.sendToMesh(p);
break;
}
case HardwareMessage_Type_READ_GPIOS_REPLY:
case HardwareMessage_Type_GPIOS_CHANGED:
break; // Ignore - we might see our own replies
default:
DEBUG_MSG("Hardware operation %d not yet implemented! FIXME\n", p.typ);
break;
}
return true; // handled
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "ProtobufPlugin.h"
#include "remote_hardware.pb.h"
/**
* A plugin that provides easy low-level remote access to device hardware.
*/
class RemoteHardwarePlugin : public ProtobufPlugin<HardwareMessage>
{
public:
/** Constructor
* name is for debugging output
*/
RemoteHardwarePlugin() : ProtobufPlugin("remotehardware", PortNum_REMOTE_HARDWARE_APP, HardwareMessage_fields) {}
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 HardwareMessage &p);
};
extern RemoteHardwarePlugin remoteHardwarePlugin;

View File

@ -0,0 +1,28 @@
#include "configuration.h"
#include "TextMessagePlugin.h"
#include "NodeDB.h"
#include "PowerFSM.h"
TextMessagePlugin textMessagePlugin;
bool TextMessagePlugin::handleReceived(const MeshPacket &mp)
{
auto &p = mp.decoded.data;
DEBUG_MSG("Received text msg from=0x%0x, id=%d, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes);
// We only store/display messages destined for us.
// Keep a copy of the most recent text message.
devicestate.rx_text_message = mp;
devicestate.has_rx_text_message = true;
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG);
notifyObservers(&mp);
// This is going into the wifidev feature branch
// Only update the WebUI if WiFi is enabled
//#if WiFi_MODE != 0
// notifyWebUI();
//#endif
return false; // Let others look at this message also if they want
}

View File

@ -0,0 +1,25 @@
#pragma once
#include "SinglePortPlugin.h"
#include "Observer.h"
/**
* Text message handling for meshtastic - draws on the OLED display the most recent received message
*/
class TextMessagePlugin : public SinglePortPlugin, public Observable<const MeshPacket *>
{
public:
/** Constructor
* name is for debugging output
*/
TextMessagePlugin() : SinglePortPlugin("text", PortNum_TEXT_MESSAGE_APP) {}
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 handleReceived(const MeshPacket &mp);
};
extern TextMessagePlugin textMessagePlugin;