From 877e312833f058d721f13895782702cc264f7e44 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 26 Feb 2020 09:00:53 -0800 Subject: [PATCH] allow TBEAMs to provide approx GPS time to Heltec devices --- TODO.md | 14 +++++----- src/MeshBluetoothService.cpp | 19 ++++++------- src/MeshService.cpp | 53 ++++++++++++++++++++++++------------ src/MeshService.h | 3 ++ src/mesh.pb.h | 17 +++++------- 5 files changed, 61 insertions(+), 45 deletions(-) diff --git a/TODO.md b/TODO.md index 22cdb3b9..60317cd7 100644 --- a/TODO.md +++ b/TODO.md @@ -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. @@ -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 @@ -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) @@ -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? @@ -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. @@ -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 \ No newline at end of file +* 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. diff --git a/src/MeshBluetoothService.cpp b/src/MeshBluetoothService.cpp index 94c71587..30e60df9 100644 --- a/src/MeshBluetoothService.cpp +++ b/src/MeshBluetoothService.cpp @@ -33,8 +33,8 @@ public: 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); } @@ -51,8 +51,8 @@ protected: 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); } }; @@ -88,7 +88,7 @@ public: void onWrite(BLECharacteristic *c) { BLEKeepAliveCallbacks::onWrite(c); - DEBUG_MSG("Got on nodeinfo write\n"); + DEBUG_MSG("Reset nodeinfo read pointer\n"); nodeDB.resetReadPointer(); } }; @@ -188,18 +188,17 @@ public: } 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); } } diff --git a/src/MeshService.cpp b/src/MeshService.cpp index 63fcbd48..2b3f0bc4 100644 --- a/src/MeshService.cpp +++ b/src/MeshService.cpp @@ -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); @@ -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 @@ -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); @@ -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) @@ -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()) @@ -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); } diff --git a/src/MeshService.h b/src/MeshService.h index 005e1af0..6c35ebbc 100644 --- a/src/MeshService.h +++ b/src/MeshService.h @@ -86,6 +86,9 @@ private: /// 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; diff --git a/src/mesh.pb.h b/src/mesh.pb.h index 7a3a9ed8..84e411e9 100644 --- a/src/mesh.pb.h +++ b/src/mesh.pb.h @@ -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 */ @@ -63,7 +63,6 @@ typedef struct _Position { double longitude; int32_t altitude; int32_t battery_level; - bool from_hardware; uint32_t time; } Position; @@ -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} @@ -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} @@ -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 @@ -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 @@ -420,7 +417,7 @@ 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 @@ -428,9 +425,9 @@ extern const pb_msgdesc_t ToRadio_msg; #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