2020-02-02 20:45:32 +00:00
# include <Arduino.h>
# include <assert.h>
2020-04-15 03:22:27 +00:00
# include <string>
2020-02-02 20:45:32 +00:00
2020-03-19 02:15:51 +00:00
# include "GPS.h"
2020-04-10 19:18:48 +00:00
//#include "MeshBluetoothService.h"
2020-07-10 02:57:55 +00: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-19 02:15:51 +00:00
# include "MeshService.h"
2020-02-03 17:13:19 +00:00
# include "NodeDB.h"
2020-03-03 21:31:44 +00:00
# include "PowerFSM.h"
2020-10-07 23:28:57 +00:00
# include "RTC.h"
2020-03-19 02:15:51 +00:00
# include "main.h"
# include "mesh-pb-constants.h"
2020-12-03 08:48:44 +00:00
# include "plugins/PositionPlugin.h"
2020-12-05 02:00:46 +00:00
# include "plugins/NodeInfoPlugin.h"
# include "power.h"
2020-02-02 20:45:32 +00: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-19 02:15:51 +00: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 20:45:32 +00:00
2020-03-19 02:15:51 +00: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 20:45:32 +00:00
*/
2020-02-08 20:42:54 +00:00
/* Broadcast when a newly powered mesh node wants to find a node num it can use
The algoritm is as follows :
2020-03-19 02:15:51 +00: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 20:42:54 +00:00
2020-03-19 02:15:51 +00: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 20:42:54 +00:00
2020-03-19 02:15:51 +00: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 20:42:54 +00: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 20:45:32 +00:00
2020-02-08 20:42:54 +00:00
MeshService service ;
2020-02-08 18:00:15 +00:00
2020-04-17 16:48:54 +00:00
# include "Router.h"
2020-02-03 17:13:19 +00:00
2020-10-10 01:57:57 +00:00
static int32_t sendOwnerCb ( )
2020-04-25 17:59:40 +00:00
{
2020-12-17 02:53:29 +00: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 ) ;
nodeInfoPlugin . sendOurNodeInfo ( NODENUM_BROADCAST , requestReplies ) ; // Send our info (don't request replies)
2020-04-25 17:59:40 +00:00
2020-10-06 01:43:00 +00:00
return getPref_send_owner_interval ( ) * getPref_position_broadcast_secs ( ) * 1000 ;
2020-04-25 17:59:40 +00:00
}
2020-10-10 01:57:57 +00:00
static concurrency : : Periodic * sendOwnerPeriod ;
2020-04-25 17:59:40 +00:00
2020-04-17 16:48:54 +00:00
MeshService : : MeshService ( ) : toPhoneQueue ( MAX_RX_TOPHONE )
2020-02-02 20:45:32 +00:00
{
2020-02-03 17:13:19 +00:00
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
2020-02-02 20:45:32 +00:00
}
void MeshService : : init ( )
{
2020-10-10 01:57:57 +00:00
sendOwnerPeriod = new concurrency : : Periodic ( " SendOwner " , sendOwnerCb ) ;
2020-12-17 02:53:29 +00:00
sendOwnerPeriod - > setIntervalFromNow ( 30 * 1000 ) ; // Send our initial owner announcement 30 seconds after we start (to give network time to setup)
2020-10-10 01:57:57 +00:00
2020-12-26 05:36:21 +00:00
// moved much earlier in boot (called from setup())
// nodeDB.init();
2020-02-06 16:49:33 +00:00
2020-09-06 21:45:43 +00:00
if ( gps )
gpsObserver . observe ( & gps - > newStatus ) ;
2020-10-10 01:57:57 +00:00
packetReceivedObserver . observe ( & router - > notifyPacketReceived ) ;
2020-02-06 18:58:19 +00:00
}
2020-02-26 17:00:53 +00:00
2020-04-17 16:48:54 +00:00
int MeshService : : handleFromRadio ( const MeshPacket * mp )
2020-02-18 01:47:01 +00:00
{
2020-03-03 21:31:44 +00:00
powerFSM . trigger ( EVENT_RECEIVED_PACKET ) ; // Possibly keep the node from sleeping
2020-12-05 02:00:46 +00:00
printPacket ( " Forwarding to phone " , mp ) ;
nodeDB . updateFrom ( * mp ) ; // update our DB state based off sniffing every RX packet from the radio
2020-02-18 01:47:01 +00:00
2020-12-05 02:00:46 +00:00
fromNum + + ;
2020-02-18 01:47:01 +00:00
2020-12-05 02:00:46 +00: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 16:48:54 +00:00
2020-12-05 02:00:46 +00:00
MeshPacket * copied = packetPool . allocCopy ( * mp ) ;
assert ( toPhoneQueue . enqueue ( copied , 0 ) ) ; // FIXME, instead of failing for full queue, delete the oldest mssages
2020-02-20 02:51:17 +00:00
2020-04-17 16:48:54 +00:00
return 0 ;
2020-02-02 20:45:32 +00:00
}
2020-02-08 17:39:26 +00:00
/// Do idle processing (mostly processing messages which have been queued from the radio)
void MeshService : : loop ( )
{
2020-04-17 16:48:54 +00:00
if ( oldFromNum ! = fromNum ) { // We don't want to generate extra notifies for multiple new packets
fromNumChanged . notifyObservers ( fromNum ) ;
2020-04-17 20:05:16 +00:00
oldFromNum = fromNum ;
2020-04-17 16:48:54 +00:00
}
2020-02-08 17:39:26 +00:00
}
2020-02-11 19:56:48 +00:00
/// The radioConfig object just changed, call this to force the hw to change to the new settings
2020-09-19 18:19:42 +00:00
bool MeshService : : reloadConfig ( )
2020-02-11 19:56:48 +00:00
{
// If we can successfully set this radio to these settings, save them to disk
2020-10-29 05:26:36 +00:00
// This will also update the region as needed
2020-09-19 18:19:42 +00:00
bool didReset = nodeDB . resetRadioConfig ( ) ; // Don't let the phone send us fatally bad settings
2020-12-05 02:00:46 +00:00
2020-04-14 18:40:49 +00:00
configChanged . notifyObservers ( NULL ) ;
2020-02-11 19:56:48 +00:00
nodeDB . saveToDisk ( ) ;
2020-09-19 18:19:42 +00:00
return didReset ;
2020-02-11 19:56:48 +00:00
}
2020-09-16 16:08:35 +00:00
/// The owner User record just got updated, update our node DB and broadcast the info into the mesh
void MeshService : : reloadOwner ( )
{
2020-12-05 02:00:46 +00:00
nodeInfoPlugin . sendOurNodeInfo ( ) ;
2020-09-16 16:08:35 +00:00
nodeDB . saveToDisk ( ) ;
}
2020-04-22 21:55:36 +00:00
/**
* Given a ToRadio buffer parse it and properly handle it ( setup radio , owner or send packet into the mesh )
2020-04-25 17:59:40 +00: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 21:55:36 +00:00
*/
void MeshService : : handleToRadio ( MeshPacket & p )
2020-02-02 20:45:32 +00:00
{
2020-04-22 21:55:36 +00:00
if ( p . from = = 0 ) // If the phone didn't set a sending node ID, use ours
p . from = nodeDB . getNodeNum ( ) ;
2020-02-19 18:53:09 +00:00
2020-04-22 21:55:36 +00:00
if ( p . id = = 0 )
p . id = generatePacketId ( ) ; // If the phone didn't supply one, then pick one
2020-04-17 18:52:20 +00:00
2020-10-09 02:01:13 +00:00
p . rx_time = getValidTime ( RTCQualityFromNet ) ; // Record the time the packet arrived from the phone
2020-10-09 06:16:51 +00:00
// (so we update our nodedb for the local node)
2020-04-17 19:41:01 +00:00
2020-04-22 21:55:36 +00:00
// Send the packet into the mesh
2020-02-19 18:53:09 +00:00
2020-04-22 21:55:36 +00:00
sendToMesh ( packetPool . allocCopy ( p ) ) ;
2020-04-17 18:52:20 +00:00
2020-04-22 21:55:36 +00: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 23:27:15 +00:00
}
2020-02-02 20:45:32 +00:00
}
2020-02-06 16:49:33 +00:00
void MeshService : : sendToMesh ( MeshPacket * p )
2020-02-02 20:45:32 +00:00
{
2020-02-17 00:03:16 +00:00
nodeDB . updateFrom ( * p ) ; // update our local DB for this packet (because phone might have sent position packets etc...)
2020-02-19 04:17:11 +00:00
2020-03-19 02:15:51 +00:00
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
2020-10-10 01:20:38 +00:00
// nodes shouldn't trust it anyways) Note: we allow a device with a local GPS to include the time, so that gpsless
2020-03-19 02:15:51 +00:00
// devices can get time.
2020-12-05 02:00:46 +00:00
if ( p - > which_payload = = MeshPacket_decoded_tag & & p - > decoded . which_payload = = SubPacket_position_tag & &
p - > decoded . position . time ) {
2020-10-10 01:20:38 +00:00
if ( getRTCQuality ( ) < RTCQualityGPS ) {
2020-05-10 00:51:20 +00:00
DEBUG_MSG ( " Stripping time %u from position send \n " , p - > decoded . position . time ) ;
p - > decoded . position . time = 0 ;
2020-03-25 20:09:12 +00:00
} else
2020-05-10 00:51:20 +00:00
DEBUG_MSG ( " Providing time to mesh %u \n " , p - > decoded . position . time ) ;
2020-02-26 17:00:53 +00:00
}
2020-02-19 23:29:18 +00:00
2020-05-19 18:56:17 +00: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 01:57:57 +00:00
router - > sendLocal ( p ) ;
2020-02-06 16:49:33 +00:00
}
2020-02-02 20:45:32 +00:00
2020-03-25 20:09:12 +00:00
void MeshService : : sendNetworkPing ( NodeNum dest , bool wantReplies )
2020-02-12 22:07:06 +00:00
{
NodeInfo * node = nodeDB . getNode ( nodeDB . getNodeNum ( ) ) ;
assert ( node ) ;
2020-03-25 20:09:12 +00:00
DEBUG_MSG ( " Sending network ping to 0x%x, with position=%d, wantReplies=%d \n " , dest , node - > has_position , wantReplies ) ;
2020-02-12 22:07:06 +00:00
if ( node - > has_position )
2020-12-03 08:48:44 +00:00
positionPlugin . sendOurPosition ( dest , wantReplies ) ;
2020-02-12 22:07:06 +00:00
else
2020-12-05 02:00:46 +00:00
nodeInfoPlugin . sendOurNodeInfo ( dest , wantReplies ) ;
2020-02-12 22:07:06 +00:00
}
2020-08-12 22:51:57 +00:00
int MeshService : : onGPSChanged ( const meshtastic : : GPSStatus * unused )
2020-02-06 18:58:19 +00:00
{
2020-02-12 22:07:06 +00:00
// Update our local node info with our position (even if we don't decide to update anyone else)
2020-03-18 21:51:54 +00:00
2020-12-07 02:27:31 +00:00
Position pos = Position_init_default ;
2020-09-16 16:18:44 +00:00
if ( gps - > hasLock ( ) ) {
2020-05-04 18:15:05 +00:00
if ( gps - > altitude ! = 0 )
pos . altitude = gps - > altitude ;
pos . latitude_i = gps - > latitude ;
pos . longitude_i = gps - > longitude ;
2020-03-19 01:34:22 +00:00
}
2020-12-09 04:05:15 +00:00
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 " ) ;
}
}
2020-02-06 16:49:33 +00:00
2020-10-09 02:01:13 +00:00
pos . time = getValidTime ( RTCQualityGPS ) ;
Report battery level to other nodes in mesh (and apps).
Also today I'll turn back on the the standard "has a battery" BLE
device profile, so the locally connected radio should show up in the
phone's bluetooth popup of battery levels (similar to how a BLE
headphone battery level appears)
cc @lgoix - using code from @professr
```
meshtastic --info
Connected to radio...
my_node_num: 2883444536
has_gps: true
num_channels: 13
region: "unset"
hw_model: "tbeam"
firmware_version: "unset"
packet_id_bits: 32
current_packet_id: 91018534
node_num_bits: 32
message_timeout_msec: 300000
min_app_version: 172
preferences {
position_broadcast_secs: 900
send_owner_interval: 4
wait_bluetooth_secs: 120
screen_on_secs: 300
phone_timeout_secs: 900
phone_sds_timeout_sec: 7200
mesh_sds_timeout_secs: 7200
sds_secs: 31536000
ls_secs: 3600
}
channel_settings {
tx_power: 23
modem_config: Bw125Cr48Sf4096
psk: "\324\361\273: )\007Y\360\274\377\253\317Ni\277"
name: "Default"
}
Nodes in mesh:
{'num': 2883444536, 'user': {'id': '!2462abdddf38', 'longName': 'Unknown df38', 'shortName': '?38', 'macaddr': 'JGKr3d84'}, 'position': {'altitude': 96, 'batteryLevel': 100, 'latitudeI': 375210641, 'longitudeI': -1223090398, 'time': 1592843564, 'latitude': 37.5210641, 'longitude': -122.3090398}}
```
2020-06-22 16:36:19 +00:00
// Include our current battery voltage in our position announcement
2020-06-29 01:17:52 +00:00
pos . battery_level = powerStatus - > getBatteryChargePercent ( ) ;
2020-06-22 17:04:26 +00:00
updateBatteryLevel ( pos . battery_level ) ;
Report battery level to other nodes in mesh (and apps).
Also today I'll turn back on the the standard "has a battery" BLE
device profile, so the locally connected radio should show up in the
phone's bluetooth popup of battery levels (similar to how a BLE
headphone battery level appears)
cc @lgoix - using code from @professr
```
meshtastic --info
Connected to radio...
my_node_num: 2883444536
has_gps: true
num_channels: 13
region: "unset"
hw_model: "tbeam"
firmware_version: "unset"
packet_id_bits: 32
current_packet_id: 91018534
node_num_bits: 32
message_timeout_msec: 300000
min_app_version: 172
preferences {
position_broadcast_secs: 900
send_owner_interval: 4
wait_bluetooth_secs: 120
screen_on_secs: 300
phone_timeout_secs: 900
phone_sds_timeout_sec: 7200
mesh_sds_timeout_secs: 7200
sds_secs: 31536000
ls_secs: 3600
}
channel_settings {
tx_power: 23
modem_config: Bw125Cr48Sf4096
psk: "\324\361\273: )\007Y\360\274\377\253\317Ni\277"
name: "Default"
}
Nodes in mesh:
{'num': 2883444536, 'user': {'id': '!2462abdddf38', 'longName': 'Unknown df38', 'shortName': '?38', 'macaddr': 'JGKr3d84'}, 'position': {'altitude': 96, 'batteryLevel': 100, 'latitudeI': 375210641, 'longitudeI': -1223090398, 'time': 1592843564, 'latitude': 37.5210641, 'longitude': -122.3090398}}
```
2020-06-22 16:36:19 +00:00
2020-12-09 04:05:15 +00:00
DEBUG_MSG ( " got gps notify time=%u, lat=%d, bat=%d \n " , pos . latitude_i , pos . time , pos . battery_level ) ;
2020-08-12 22:51:57 +00:00
2020-12-05 00:46:19 +00:00
// Update our current position in the local DB
nodeDB . updatePosition ( nodeDB . getNodeNum ( ) , pos ) ;
2020-02-12 22:07:06 +00:00
// We limit our GPS broadcasts to a max rate
static uint32_t lastGpsSend ;
2020-09-05 19:34:48 +00:00
uint32_t now = millis ( ) ;
2020-10-06 01:43:00 +00:00
if ( lastGpsSend = = 0 | | now - lastGpsSend > getPref_position_broadcast_secs ( ) * 1000 ) {
2020-02-12 22:07:06 +00:00
lastGpsSend = now ;
2020-12-17 02:53:29 +00: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 position to mesh (wantReplies=%d) \n " , requestReplies ) ;
positionPlugin . sendOurPosition ( NODENUM_BROADCAST , requestReplies ) ;
2020-12-05 02:00:46 +00:00
}
2020-04-10 19:40:44 +00:00
return 0 ;
2020-02-02 20:45:32 +00:00
}