Skip to content

Commit

Permalink
allow TBEAMs to provide approx GPS time to Heltec devices
Browse files Browse the repository at this point in the history
  • Loading branch information
geeksville committed Feb 26, 2020
1 parent cace2f4 commit 877e312
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 45 deletions.
14 changes: 7 additions & 7 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
Items to complete soon (next couple of alpha releases).

* text messages are not showing on local screen if screen was on
* protobufs are sometimes corrupted after sleep!
* stay awake while charging
* check gps battery voltage

* The following three items are all the same:
Have state machine properly enter deep sleep based on loss of mesh and phone comms.
Expand All @@ -17,7 +14,6 @@ for it (because it will redownload the nodedb when it comes back)
being I have it set at 2 minutes to ensure enough time for a GPS lock from scratch.

* retest BLE software update for both board types
* send note about Adafruit Clue
* report on wikifactory
* send note to the guy who designed the cases
* remeasure wake time power draws now that we run CPU down at 80MHz
Expand All @@ -26,6 +22,7 @@ being I have it set at 2 minutes to ensure enough time for a GPS lock from scrat

Items to complete before the first beta release.

* check fcc rules on duty cycle. we might not need to freq hop. https://www.sunfiretesting.com/LoRa-FCC-Certification-Guide/
* use fuse bits to store the board type and region. So one load can be used on all boards
* "AXP192 interrupt is not firing, remove this temporary polling of battery state"
* make mesh aware network timing state machine (sync wake windows to gps time)
Expand All @@ -47,7 +44,7 @@ Items to complete before the first beta release.
* make an about to sleep screen
* don't send location packets if we haven't moved
* scrub default radio config settings for bandwidth/range/speed
* add basic crypto - http://rweather.github.io/arduinolibs/crypto.html with speck https://www.airspayce.com/mikem/arduino/RadioHead/rf95_encrypted_client_8pde-example.html
* add basic crypto - https://github.com/chegewara/esp32-mbedtls-aes-test/blob/master/main/main.c https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation - use ECB at first (though it is shit) because it doesn't require us to send 16 bytes of IV with each packet. Then OFB per example
* override peekAtMessage so we can see any messages that pass through our node (even if not broadcast)? would that be useful?
* sendToMesh can currently block for a long time, instead have it just queue a packet for a radio freertos thread
* How do avalanche beacons work? Could this do that as well? possibly by using beacon mode feature of the RF95?
Expand All @@ -65,7 +62,6 @@ During the beta timeframe the following improvements 'would be nice' (and yeah -
* make an install script to let novices install software on their boards
* fix the frequency error reading in the RF95 RX code (can't do floating point math in an ISR ;-)
* See CustomRF95::send and fix the problem of dropping partially received packets if we want to start sending
* swap out speck for hw-accelerated full AES https://github.com/espressif/arduino-esp32/blob/master/tools/sdk/include/esp32/hwcrypto/aes.h
* use variable length arduino Strings in protobufs (instead of current fixed buffers)
* don't even power on bluetooth until we have some data to send to the android phone. Most of the time we should be sleeping in a lowpower "listening for lora" only mode. Once we have some packets for the phone, then power on bluetooth
until the phone pulls those packets. Ever so often power on bluetooth just so we can see if the phone wants to send some packets. Possibly might need ULP processor to help with this wake process.
Expand Down Expand Up @@ -182,4 +178,8 @@ Items after the first final candidate release.
* don't enter NB state if we've recently talked to the phone (to prevent breaking syncing or bluetooth sw update)
* have sw update prevent BLE sleep
* manually delete characteristics/descs
* leave lora receiver always on
* leave lora receiver always on
* protobufs are sometimes corrupted after sleep!
* stay awake while charging
* check gps battery voltage
* if a position report includes ground truth time and we don't have time yet, set our clock from that. It is better than nothing.
19 changes: 9 additions & 10 deletions src/MeshBluetoothService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ class ProtobufCharacteristic : public CallbackCharacteristic
void onRead(BLECharacteristic *c)
{
BLEKeepAliveCallbacks::onRead(c);
DEBUG_MSG("Got proto read\n");
size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), fields, my_struct);
DEBUG_MSG("pbread from %s returns %d bytes\n", c->getUUID().toString().c_str(), numbytes);
c->setValue(trBytes, numbytes);
}

Expand All @@ -51,8 +51,8 @@ class ProtobufCharacteristic : public CallbackCharacteristic
bool writeToDest(BLECharacteristic *c, void *dest)
{
// dumpCharacteristic(pCharacteristic);
DEBUG_MSG("Got on proto write\n");
std::string src = c->getValue();
DEBUG_MSG("pbwrite to %s of %d bytes\n", c->getUUID().toString().c_str(), src.length());
return pb_decode_from_bytes((const uint8_t *)src.c_str(), src.length(), fields, dest);
}
};
Expand Down Expand Up @@ -88,7 +88,7 @@ class NodeInfoCharacteristic : public BLECharacteristic, public BLEKeepAliveCall
void onWrite(BLECharacteristic *c)
{
BLEKeepAliveCallbacks::onWrite(c);
DEBUG_MSG("Got on nodeinfo write\n");
DEBUG_MSG("Reset nodeinfo read pointer\n");
nodeDB.resetReadPointer();
}
};
Expand Down Expand Up @@ -188,18 +188,17 @@ class FromRadioCharacteristic : public CallbackCharacteristic
}
else
{
DEBUG_MSG("delivering toPhone packet to phone\n");

static FromRadio fradio;
static FromRadio fRadio;

// Encapsulate as a ToRadio packet
memset(&fradio, 0, sizeof(fradio));
fradio.which_variant = FromRadio_packet_tag;
fradio.variant.packet = *mp;
memset(&fRadio, 0, sizeof(fRadio));
fRadio.which_variant = FromRadio_packet_tag;
fRadio.variant.packet = *mp;

service.releaseToPool(mp); // we just copied the bytes, so don't need this buffer anymore

size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), FromRadio_fields, &fradio);
size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), FromRadio_fields, &fRadio);
DEBUG_MSG("delivering toPhone packet to phone %d bytes\n", numbytes);
c->setValue(trBytes, numbytes);
}
}
Expand Down
53 changes: 35 additions & 18 deletions src/MeshService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,33 @@ MeshPacket *MeshService::handleFromRadioUser(MeshPacket *mp)
return mp;
}

void MeshService::handleIncomingPosition(MeshPacket *mp)
{
if (mp->has_payload && mp->payload.which_variant == SubPacket_position_tag)
{
DEBUG_MSG("handled incoming position time=%u\n", mp->payload.variant.position.time);

if (mp->payload.variant.position.time)
{
struct timeval tv;
uint32_t secs = mp->payload.variant.position.time;

tv.tv_sec = secs;
tv.tv_usec = 0;

gps.perhapsSetRTC(&tv);
}
}
}

void MeshService::handleFromRadio(MeshPacket *mp)
{
mp->rx_time = gps.getValidTime(); // store the arrival timestamp for the phone

// If it is a position packet, perhaps set our clock (if we don't have a GPS of our own, otherwise wait for that to work)
if(!myNodeInfo.has_gps)
handleIncomingPosition(mp);

if (mp->has_payload && mp->payload.which_variant == SubPacket_user_tag)
{
mp = handleFromRadioUser(mp);
Expand All @@ -149,7 +172,7 @@ void MeshService::handleFromRadio(MeshPacket *mp)
}
assert(toPhoneQueue.enqueue(mp, 0) == pdTRUE); // FIXME, instead of failing for full queue, delete the oldest mssages

if(mp->payload.want_response)
if (mp->payload.want_response)
sendNetworkPing(mp->from);
}
else
Expand All @@ -168,12 +191,11 @@ void MeshService::handleFromRadio()
bluetoothNotifyFromNum(fromNum);
}


uint32_t sendOwnerCb()
{
service.sendOurOwner();
service.sendOurOwner();

return radioConfig.preferences.send_owner_interval * radioConfig.preferences.position_broadcast_secs * 1000;
return radioConfig.preferences.send_owner_interval * radioConfig.preferences.position_broadcast_secs * 1000;
}

Periodic sendOwnerPeriod(sendOwnerCb);
Expand Down Expand Up @@ -209,17 +231,7 @@ void MeshService::handleToRadio(std::string s)
case ToRadio_packet_tag:
{
// If our phone is sending a position, see if we can use it to set our RTC
if (r.variant.packet.has_payload && r.variant.packet.payload.which_variant == SubPacket_position_tag && r.variant.packet.payload.variant.position.time)
{
struct timeval tv;
uint32_t secs = r.variant.packet.payload.variant.position.time;

// FIXME, this is a shit not right version of the standard def of unix time!!!
tv.tv_sec = secs;
tv.tv_usec = 0;

gps.perhapsSetRTC(&tv);
}
handleIncomingPosition(&r.variant.packet); // If it is a position packet, perhaps set our clock

r.variant.packet.rx_time = gps.getValidTime(); // Record the time the packet arrived from the phone (so we update our nodedb for the local node)

Expand Down Expand Up @@ -247,8 +259,14 @@ void MeshService::sendToMesh(MeshPacket *p)
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)

// 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: for now, we allow a device with a local GPS to include the time, so that gpsless devices can get time.
if (p->has_payload && p->payload.which_variant == SubPacket_position_tag)
p->payload.variant.position.time = 0;
{
if (!myNodeInfo.has_gps)
p->payload.variant.position.time = 0;
else
DEBUG_MSG("Providing time to mesh %u\n", p->payload.variant.position.time);
}

// If the phone sent a packet just to us, don't send it out into the network
if (p->to == nodeDB.getNodeNum())
Expand Down Expand Up @@ -295,8 +313,7 @@ void MeshService::sendOurPosition(NodeNum dest)
p->to = dest;
p->payload.which_variant = SubPacket_position_tag;
p->payload.variant.position = node->position;
// FIXME - for now we are leaving this in the sent packets (for debugging)
//p->payload.variant.position.time = 0; // No need to send time, other node won't trust it anyways
p->payload.variant.position.time = gps.getValidTime(); // This nodedb timestamp might be stale, so update it if our clock is valid.
sendToMesh(p);
}

Expand Down
3 changes: 3 additions & 0 deletions src/MeshService.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ class MeshService: private Observer

/// handle a user packet that just arrived on the radio, return NULL if we should not process this packet at all
MeshPacket *handleFromRadioUser(MeshPacket *mp);

/// look at inbound packets and if they contain a position with time, possibly set our clock
void handleIncomingPosition(MeshPacket *mp);
};

extern MeshService service;
Expand Down
17 changes: 7 additions & 10 deletions src/mesh.pb.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ typedef enum _ChannelSettings_ModemConfig {

typedef enum _DeviceState_Version {
DeviceState_Version_Unset = 0,
DeviceState_Version_Minimum = 16,
DeviceState_Version_Current = 16
DeviceState_Version_Minimum = 17,
DeviceState_Version_Current = 17
} DeviceState_Version;

/* Struct definitions */
Expand Down Expand Up @@ -63,7 +63,6 @@ typedef struct _Position {
double longitude;
int32_t altitude;
int32_t battery_level;
bool from_hardware;
uint32_t time;
} Position;

Expand Down Expand Up @@ -176,7 +175,7 @@ typedef struct _ToRadio {


/* Initializer values for message structs */
#define Position_init_default {0, 0, 0, 0, 0, 0}
#define Position_init_default {0, 0, 0, 0, 0}
#define Data_init_default {_Data_Type_MIN, {0, {0}}}
#define User_init_default {"", "", "", {0}}
#define SubPacket_init_default {0, {Position_init_default}, 0}
Expand All @@ -189,7 +188,7 @@ typedef struct _ToRadio {
#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, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default, MeshPacket_init_default}, _DeviceState_Version_MIN, false, MeshPacket_init_default}
#define FromRadio_init_default {0, 0, {MeshPacket_init_default}}
#define ToRadio_init_default {0, {MeshPacket_init_default}}
#define Position_init_zero {0, 0, 0, 0, 0, 0}
#define Position_init_zero {0, 0, 0, 0, 0}
#define Data_init_zero {_Data_Type_MIN, {0, {0}}}
#define User_init_zero {"", "", "", {0}}
#define SubPacket_init_zero {0, {Position_init_zero}, 0}
Expand Down Expand Up @@ -218,7 +217,6 @@ typedef struct _ToRadio {
#define Position_longitude_tag 2
#define Position_altitude_tag 3
#define Position_battery_level_tag 4
#define Position_from_hardware_tag 5
#define Position_time_tag 6
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
#define RadioConfig_UserPreferences_send_owner_interval_tag 2
Expand Down Expand Up @@ -269,7 +267,6 @@ X(a, STATIC, SINGULAR, DOUBLE, latitude, 1) \
X(a, STATIC, SINGULAR, DOUBLE, longitude, 2) \
X(a, STATIC, SINGULAR, INT32, altitude, 3) \
X(a, STATIC, SINGULAR, INT32, battery_level, 4) \
X(a, STATIC, SINGULAR, BOOL, from_hardware, 5) \
X(a, STATIC, SINGULAR, UINT32, time, 6)
#define Position_CALLBACK NULL
#define Position_DEFAULT NULL
Expand Down Expand Up @@ -420,17 +417,17 @@ extern const pb_msgdesc_t ToRadio_msg;
#define ToRadio_fields &ToRadio_msg

/* Maximum encoded size of messages (where known) */
#define Position_size 48
#define Position_size 46
#define Data_size 256
#define User_size 72
#define SubPacket_size 261
#define MeshPacket_size 292
#define ChannelSettings_size 50
#define RadioConfig_size 126
#define RadioConfig_UserPreferences_size 72
#define NodeInfo_size 157
#define NodeInfo_size 155
#define MyNodeInfo_size 24
#define DeviceState_size 15085
#define DeviceState_size 15021
#define FromRadio_size 301
#define ToRadio_size 295

Expand Down

0 comments on commit 877e312

Please sign in to comment.