2020-02-02 12:45:32 -08:00
# include <Arduino.h>
# include <assert.h>
2020-04-14 20:22:27 -07:00
# include <string>
2020-02-02 12:45:32 -08:00
2020-03-18 19:15:51 -07:00
# include "GPS.h"
2020-04-10 12:18:48 -07:00
//#include "MeshBluetoothService.h"
2020-07-09 19:57:55 -07:00
# include "../concurrency/Periodic.h"
# include "BluetoothCommon.h" // needed for updateBatteryLevel, FIXME, eventually when we pull mesh out into a lib we shouldn't be whacking bluetooth from here
2020-03-18 19:15:51 -07:00
# include "MeshService.h"
2020-02-03 09:13:19 -08:00
# include "NodeDB.h"
2020-03-03 13:31:44 -08:00
# include "PowerFSM.h"
2020-10-08 07:28:57 +08:00
# include "RTC.h"
2020-03-18 19:15:51 -07:00
# include "main.h"
# include "mesh-pb-constants.h"
2020-12-03 16:48:44 +08:00
# include "plugins/PositionPlugin.h"
2020-12-05 10:00:46 +08:00
# include "plugins/NodeInfoPlugin.h"
# include "power.h"
2020-02-02 12:45:32 -08:00
/*
receivedPacketQueue - this is a queue of messages we ' ve received from the mesh , which we are keeping to deliver to the phone .
2020-03-18 19:15:51 -07:00
It is implemented with a FreeRTos queue ( wrapped with a little RTQueue class ) of pointers to MeshPacket protobufs ( which were
alloced with new ) . After a packet ptr is removed from the queue and processed it should be deleted . ( eventually we should move
sent packets into a ' sentToPhone ' queue of packets we can delete just as soon as we are sure the phone has acked those packets -
when the phone writes to FromNum )
2020-02-02 12:45:32 -08:00
2020-03-18 19:15:51 -07:00
mesh - an instance of Mesh class . Which manages the interface to the mesh radio library , reception of packets from other nodes ,
arbitrating to select a node number and keeping the current nodedb .
2020-02-02 12:45:32 -08:00
*/
2020-02-08 12:42:54 -08:00
/* Broadcast when a newly powered mesh node wants to find a node num it can use
The algoritm is as follows :
2020-03-18 19:15:51 -07:00
* when a node starts up , it broadcasts their user and the normal flow is for all other nodes to reply with their User as well ( so
the new node can build its node db )
* If a node ever receives a User ( not just the first broadcast ) message where the sender node number equals our node number , that
indicates a collision has occurred and the following steps should happen :
2020-02-08 12:42:54 -08:00
2020-03-18 19:15:51 -07:00
If the receiving node ( that was already in the mesh ) ' s macaddr is LOWER than the new User who just tried to sign in : it gets to
keep its nodenum . We send a broadcast message of OUR User ( we use a broadcast so that the other node can receive our message ,
considering we have the same id - it also serves to let observers correct their nodedb ) - this case is rare so it should be okay .
2020-02-08 12:42:54 -08:00
2020-03-18 19:15:51 -07:00
If any node receives a User where the macaddr is GTE than their local macaddr , they have been vetoed and should pick a new random
nodenum ( filtering against whatever it knows about the nodedb ) and rebroadcast their User .
2020-02-08 12:42:54 -08:00
FIXME in the initial proof of concept we just skip the entire want / deny flow and just hand pick node numbers at first .
*/
2020-02-02 12:45:32 -08:00
2020-02-08 12:42:54 -08:00
MeshService service ;
2020-02-08 10:00:15 -08:00
2020-04-17 09:48:54 -07:00
# include "Router.h"
2020-02-03 09:13:19 -08:00
2020-10-10 09:57:57 +08:00
static int32_t sendOwnerCb ( )
2020-04-25 10:59:40 -07:00
{
2020-12-17 10:53:29 +08:00
static uint32_t currentGeneration ;
// If we changed channels, ask everyone else for their latest info
bool requestReplies = currentGeneration ! = radioGeneration ;
currentGeneration = radioGeneration ;
DEBUG_MSG ( " Sending our nodeinfo to mesh (wantReplies=%d) \n " , requestReplies ) ;
2021-01-08 13:15:49 +08:00
assert ( nodeInfoPlugin ) ;
nodeInfoPlugin - > sendOurNodeInfo ( NODENUM_BROADCAST , requestReplies ) ; // Send our info (don't request replies)
2020-04-25 10:59:40 -07:00
2020-10-06 09:43:00 +08:00
return getPref_send_owner_interval ( ) * getPref_position_broadcast_secs ( ) * 1000 ;
2020-04-25 10:59:40 -07:00
}
2020-10-10 09:57:57 +08:00
static concurrency : : Periodic * sendOwnerPeriod ;
2020-04-25 10:59:40 -07:00
2020-04-17 09:48:54 -07:00
MeshService : : MeshService ( ) : toPhoneQueue ( MAX_RX_TOPHONE )
2020-02-02 12:45:32 -08:00
{
2020-02-03 09:13:19 -08:00
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
2020-02-02 12:45:32 -08:00
}
void MeshService : : init ( )
{
2020-10-10 09:57:57 +08:00
sendOwnerPeriod = new concurrency : : Periodic ( " SendOwner " , sendOwnerCb ) ;
2020-12-17 10:53:29 +08:00
sendOwnerPeriod - > setIntervalFromNow ( 30 * 1000 ) ; // Send our initial owner announcement 30 seconds after we start (to give network time to setup)
2020-10-10 09:57:57 +08:00
2020-12-26 13:36:21 +08:00
// moved much earlier in boot (called from setup())
// nodeDB.init();
2020-02-06 08:49:33 -08:00
2020-09-06 14:45:43 -07:00
if ( gps )
gpsObserver . observe ( & gps - > newStatus ) ;
2020-10-10 09:57:57 +08:00
packetReceivedObserver . observe ( & router - > notifyPacketReceived ) ;
2020-02-06 10:58:19 -08:00
}
2020-02-26 09:00:53 -08:00
2020-04-17 09:48:54 -07:00
int MeshService : : handleFromRadio ( const MeshPacket * mp )
2020-02-17 17:47:01 -08:00
{
2020-03-03 13:31:44 -08:00
powerFSM . trigger ( EVENT_RECEIVED_PACKET ) ; // Possibly keep the node from sleeping
2020-12-05 10:00:46 +08:00
printPacket ( " Forwarding to phone " , mp ) ;
nodeDB . updateFrom ( * mp ) ; // update our DB state based off sniffing every RX packet from the radio
2020-02-17 17:47:01 -08:00
2020-12-05 10:00:46 +08:00
fromNum + + ;
2020-02-17 17:47:01 -08:00
2020-12-05 10:00:46 +08:00
if ( toPhoneQueue . numFree ( ) = = 0 ) {
DEBUG_MSG ( " NOTE: tophone queue is full, discarding oldest \n " ) ;
MeshPacket * d = toPhoneQueue . dequeuePtr ( 0 ) ;
if ( d )
releaseToPool ( d ) ;
}
2020-04-17 09:48:54 -07:00
2020-12-05 10:00:46 +08:00
MeshPacket * copied = packetPool . allocCopy ( * mp ) ;
assert ( toPhoneQueue . enqueue ( copied , 0 ) ) ; // FIXME, instead of failing for full queue, delete the oldest mssages
2020-02-19 18:51:17 -08:00
2020-04-17 09:48:54 -07:00
return 0 ;
2020-02-02 12:45:32 -08:00
}
2020-02-08 09:39:26 -08:00
/// Do idle processing (mostly processing messages which have been queued from the radio)
void MeshService : : loop ( )
{
2020-04-17 09:48:54 -07:00
if ( oldFromNum ! = fromNum ) { // We don't want to generate extra notifies for multiple new packets
fromNumChanged . notifyObservers ( fromNum ) ;
2020-04-17 13:05:16 -07:00
oldFromNum = fromNum ;
2020-04-17 09:48:54 -07:00
}
2020-02-08 09:39:26 -08:00
}
2020-02-11 11:56:48 -08:00
/// The radioConfig object just changed, call this to force the hw to change to the new settings
2020-09-19 11:19:42 -07:00
bool MeshService : : reloadConfig ( )
2020-02-11 11:56:48 -08:00
{
// If we can successfully set this radio to these settings, save them to disk
2020-10-29 13:26:36 +08:00
// This will also update the region as needed
2020-09-19 11:19:42 -07:00
bool didReset = nodeDB . resetRadioConfig ( ) ; // Don't let the phone send us fatally bad settings
2020-12-05 10:00:46 +08:00
2020-04-14 11:40:49 -07:00
configChanged . notifyObservers ( NULL ) ;
2020-02-11 11:56:48 -08:00
nodeDB . saveToDisk ( ) ;
2020-09-19 11:19:42 -07:00
return didReset ;
2020-02-11 11:56:48 -08:00
}
2020-09-16 09:08:35 -07:00
/// The owner User record just got updated, update our node DB and broadcast the info into the mesh
void MeshService : : reloadOwner ( )
{
2021-01-08 13:15:49 +08:00
assert ( nodeInfoPlugin ) ;
nodeInfoPlugin - > sendOurNodeInfo ( ) ;
2020-09-16 09:08:35 -07:00
nodeDB . saveToDisk ( ) ;
}
2020-04-22 14:55:36 -07:00
/**
* Given a ToRadio buffer parse it and properly handle it ( setup radio , owner or send packet into the mesh )
2020-04-25 10:59:40 -07:00
* Called by PhoneAPI . handleToRadio . Note : p is a scratch buffer , this function is allowed to write to it but it can not keep a
* reference
2020-04-22 14:55:36 -07:00
*/
void MeshService : : handleToRadio ( MeshPacket & p )
2020-02-02 12:45:32 -08:00
{
2020-04-22 14:55:36 -07:00
if ( p . from = = 0 ) // If the phone didn't set a sending node ID, use ours
p . from = nodeDB . getNodeNum ( ) ;
2020-02-19 10:53:09 -08:00
2020-04-22 14:55:36 -07:00
if ( p . id = = 0 )
p . id = generatePacketId ( ) ; // If the phone didn't supply one, then pick one
2020-04-17 11:52:20 -07:00
2020-10-09 10:01:13 +08:00
p . rx_time = getValidTime ( RTCQualityFromNet ) ; // Record the time the packet arrived from the phone
2020-10-09 14:16:51 +08:00
// (so we update our nodedb for the local node)
2020-04-17 12:41:01 -07:00
2020-04-22 14:55:36 -07:00
// Send the packet into the mesh
2020-02-19 10:53:09 -08:00
2020-04-22 14:55:36 -07:00
sendToMesh ( packetPool . allocCopy ( p ) ) ;
2020-04-17 11:52:20 -07:00
2020-04-22 14:55:36 -07:00
bool loopback = false ; // if true send any packet the phone sends back itself (for testing)
if ( loopback ) {
// no need to copy anymore because handle from radio assumes it should _not_ delete
// packetPool.allocCopy(r.variant.packet);
handleFromRadio ( & p ) ;
// handleFromRadio will tell the phone a new packet arrived
2020-03-15 16:27:15 -07:00
}
2020-02-02 12:45:32 -08:00
}
2021-02-11 17:39:53 +08:00
/** 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 ) {
return router - > cancelSending ( nodeDB . getNodeNum ( ) , id ) ;
}
2020-02-06 08:49:33 -08:00
void MeshService : : sendToMesh ( MeshPacket * p )
2020-02-02 12:45:32 -08:00
{
2020-02-16 16:03:16 -08:00
nodeDB . updateFrom ( * p ) ; // update our local DB for this packet (because phone might have sent position packets etc...)
2020-02-18 20:17:11 -08:00
2020-05-19 11:56:17 -07:00
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
2020-10-10 09:57:57 +08:00
router - > sendLocal ( p ) ;
2020-02-06 08:49:33 -08:00
}
2020-02-02 12:45:32 -08:00
2020-03-25 13:09:12 -07:00
void MeshService : : sendNetworkPing ( NodeNum dest , bool wantReplies )
2020-02-12 14:07:06 -08:00
{
NodeInfo * node = nodeDB . getNode ( nodeDB . getNodeNum ( ) ) ;
assert ( node ) ;
2020-03-25 13:09:12 -07:00
DEBUG_MSG ( " Sending network ping to 0x%x, with position=%d, wantReplies=%d \n " , dest , node - > has_position , wantReplies ) ;
2021-01-08 13:15:49 +08:00
assert ( positionPlugin & & nodeInfoPlugin ) ;
2020-02-12 14:07:06 -08:00
if ( node - > has_position )
2021-01-08 13:15:49 +08:00
positionPlugin - > sendOurPosition ( dest , wantReplies ) ;
2020-02-12 14:07:06 -08:00
else
2021-01-08 13:15:49 +08:00
nodeInfoPlugin - > sendOurNodeInfo ( dest , wantReplies ) ;
2020-02-12 14:07:06 -08:00
}
2021-01-04 09:59:53 +08:00
NodeInfo * MeshService : : refreshMyNodeInfo ( ) {
NodeInfo * node = nodeDB . getNode ( nodeDB . getNodeNum ( ) ) ;
assert ( node ) ;
// We might not have a position yet for our local node, in that case, at least try to send the time
if ( ! node - > has_position ) {
memset ( & node - > position , 0 , sizeof ( node - > position ) ) ;
node - > has_position = true ;
}
Position & position = node - > position ;
// Update our local node info with our position (even if we don't decide to update anyone else)
2021-02-14 11:37:32 +08:00
position . time = getValidTime ( RTCQualityFromNet ) ; // This nodedb timestamp might be stale, so update it if our clock is kinda valid
2021-01-04 09:59:53 +08:00
position . battery_level = powerStatus - > getBatteryChargePercent ( ) ;
updateBatteryLevel ( position . battery_level ) ;
return node ;
}
2020-08-12 15:51:57 -07:00
int MeshService : : onGPSChanged ( const meshtastic : : GPSStatus * unused )
2020-02-06 10:58:19 -08:00
{
2020-02-12 14:07:06 -08:00
// Update our local node info with our position (even if we don't decide to update anyone else)
2021-01-04 09:59:53 +08:00
NodeInfo * node = refreshMyNodeInfo ( ) ;
Position pos = node - > position ;
2020-09-16 09:18:44 -07:00
if ( gps - > hasLock ( ) ) {
2020-05-04 11:15:05 -07:00
if ( gps - > altitude ! = 0 )
pos . altitude = gps - > altitude ;
pos . latitude_i = gps - > latitude ;
pos . longitude_i = gps - > longitude ;
2020-03-18 18:34:22 -07:00
}
2020-12-09 12:05:15 +08:00
else {
// The GPS has lost lock, if we are fixed position we should just keep using
// the old position
2021-01-04 09:59:53 +08:00
if ( ! radioConfig . preferences . fixed_position ) {
2020-12-09 12:05:15 +08:00
DEBUG_MSG ( " WARNING: Using fixed position \n " ) ;
2021-01-04 09:59:53 +08:00
} else {
// throw away old position
pos . latitude_i = 0 ;
pos . longitude_i = 0 ;
pos . altitude = 0 ;
2020-12-09 12:05:15 +08:00
}
}
2020-02-06 08:49:33 -08:00
2020-12-09 12:05:15 +08:00
DEBUG_MSG ( " got gps notify time=%u, lat=%d, bat=%d \n " , pos . latitude_i , pos . time , pos . battery_level ) ;
2020-08-12 15:51:57 -07:00
2020-12-05 08:46:19 +08:00
// Update our current position in the local DB
nodeDB . updatePosition ( nodeDB . getNodeNum ( ) , pos ) ;
2020-04-10 12:40:44 -07:00
return 0 ;
2020-02-02 12:45:32 -08:00
}