From cd4cd6fe56d5b4b0319f77972a81177c812d4436 Mon Sep 17 00:00:00 2001 From: geeksville Date: Wed, 19 Feb 2020 10:53:09 -0800 Subject: [PATCH] progress on keeping gps off more --- docs/README.md | 3 ++- src/GPS.cpp | 31 ++++++++++++++++++++++--------- src/GPS.h | 11 +++++++++-- src/MeshBluetoothService.cpp | 3 ++- src/MeshService.cpp | 29 ++++++++++++++++++++++++++--- src/NodeDB.cpp | 19 +++++++++++++------ src/configuration.h | 4 ++-- src/mesh.pb.h | 35 +++++++++++++++++++---------------- 8 files changed, 95 insertions(+), 40 deletions(-) diff --git a/docs/README.md b/docs/README.md index b461a403d..a1ccf21bc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -34,4 +34,5 @@ is optional, but highly recommended. We don't yet distribute prebuilt binaries. But soon (by Feb 22) we will have a file that you can easily install on your radio via USB. Once our software is installed, all future software updates happen over bluetooth from your phone. -For a nice 3D printable case see this [design](https://www.thingiverse.com/thing:3773717) by [bsiege](https://www.thingiverse.com/bsiege). +For a nice TTGO 3D printable case see this [design](https://www.thingiverse.com/thing:3773717) by [bsiege](https://www.thingiverse.com/bsiege). +For a nice Heltec 3D printable case see this [design](https://www.thingiverse.com/thing:3125854) by [ornotermes](https://www.thingiverse.com/ornotermes). diff --git a/src/GPS.cpp b/src/GPS.cpp index e6cc7487c..c05215080 100644 --- a/src/GPS.cpp +++ b/src/GPS.cpp @@ -6,7 +6,7 @@ // stuff that really should be in in the instance instead... HardwareSerial _serial_gps(GPS_SERIAL_NUM); uint32_t timeStartMsec; // Once we have a GPS lock, this is where we hold the initial msec clock that corresponds to that time -uint64_t zeroOffset; // GPS based time in millis since 1970 - only updated once on initial lock +uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only updated once on initial lock bool timeSetFromGPS; // We only reset our time once per wake GPS gps; @@ -34,7 +34,19 @@ void GPS::readFromRTC() DEBUG_MSG("Read RTC time as %ld (cur millis %u)\n", tv.tv_sec, now); timeStartMsec = now; - zeroOffset = tv.tv_sec * 1000LL + tv.tv_usec / 1000LL; + zeroOffsetSecs = tv.tv_sec; + } +} + +/// If we haven't yet set our RTC this boot, set it from a GPS derived time +void GPS::perhapsSetRTC(const struct timeval *tv) +{ + if (!timeSetFromGPS) + { + timeSetFromGPS = true; + DEBUG_MSG("Setting RTC %ld secs\n", tv->tv_sec); + settimeofday(tv, NULL); + readFromRTC(); } } @@ -51,8 +63,6 @@ void GPS::loop() if (!timeSetFromGPS && time.isValid() && date.isValid()) { - timeSetFromGPS = true; - struct timeval tv; // FIXME, this is a shit not right version of the standard def of unix time!!! @@ -61,16 +71,19 @@ void GPS::loop() tv.tv_usec = time.centisecond() * (10 / 1000); - DEBUG_MSG("Setting RTC %ld secs\n", tv.tv_sec); - settimeofday(&tv, NULL); - readFromRTC(); + perhapsSetRTC(&tv); } #endif } -uint64_t GPS::getTime() +uint32_t GPS::getTime() { - return ((uint64_t)millis() - timeStartMsec) + zeroOffset; + return ((millis() - timeStartMsec) / 1000) + zeroOffsetSecs; +} + +uint32_t GPS::getValidTime() +{ + return timeSetFromGPS ? getTime() : 0; } void GPS::doTask() diff --git a/src/GPS.h b/src/GPS.h index e01b18d72..9ff98cca3 100644 --- a/src/GPS.h +++ b/src/GPS.h @@ -3,6 +3,7 @@ #include #include "PeriodicTask.h" #include "Observer.h" +#include "sys/time.h" /** * A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading) @@ -14,8 +15,11 @@ class GPS : public PeriodicTask, public Observable, public TinyGPSPlus public: GPS(); - /// Return time since 1970 in msecs. Until we have a GPS lock we will be returning time based at zero - uint64_t getTime(); + /// Return time since 1970 in secs. Until we have a GPS lock we will be returning time based at zero + uint32_t getTime(); + + /// Return time since 1970 in secs. If we don't have a GPS lock return zero + uint32_t getValidTime(); String getTimeStr(); @@ -25,6 +29,9 @@ public: virtual void doTask(); + /// If we haven't yet set our RTC this boot, set it from a GPS derived time + void perhapsSetRTC(const struct timeval *tv); + private: void readFromRTC(); }; diff --git a/src/MeshBluetoothService.cpp b/src/MeshBluetoothService.cpp index 3978fa8e5..d6722e12f 100644 --- a/src/MeshBluetoothService.cpp +++ b/src/MeshBluetoothService.cpp @@ -64,17 +64,18 @@ public: void onRead(BLECharacteristic *c) { - DEBUG_MSG("Got nodeinfo read\n"); const NodeInfo *info = nodeDB.readNextInfo(); if (info) { + DEBUG_MSG("Sending nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s\n", info->num, info->position.time, info->user.id, info->user.long_name); size_t numbytes = pb_encode_to_bytes(trBytes, sizeof(trBytes), NodeInfo_fields, info); c->setValue(trBytes, numbytes); } else { c->setValue(trBytes, 0); // Send an empty response + DEBUG_MSG("Done sending nodeinfos\n"); } } diff --git a/src/MeshService.cpp b/src/MeshService.cpp index 13cb488fa..adec1496b 100644 --- a/src/MeshService.cpp +++ b/src/MeshService.cpp @@ -122,7 +122,7 @@ MeshPacket *MeshService::handleFromRadioUser(MeshPacket *mp) void MeshService::handleFromRadio(MeshPacket *mp) { - mp->rx_time = gps.getTime() / 1000; // store the arrival timestamp for the phone + mp->rx_time = gps.getValidTime(); // store the arrival timestamp for the phone if (mp->has_payload && mp->payload.which_variant == SubPacket_user_tag) { @@ -132,6 +132,7 @@ void MeshService::handleFromRadio(MeshPacket *mp) // 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) { + DEBUG_MSG("Forwarding to phone, from=0x%x, rx_time=%u\n", mp->from, mp->rx_time); nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio fromNum++; @@ -197,6 +198,25 @@ 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 msecs = 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 = msecs / 1000; + tv.tv_usec = (msecs % 1000) * 1000; // scale only the msecs portion of the timestamp (i.e. remainder after dividing by 1s) + + gps.perhapsSetRTC(&tv); + + // leave in for now FIXME + // r.variant.packet.payload.variant.position.time = 0; // Set it to the default value so that we don't waste bytes broadcasting it (other nodes will use their own time) + } + + 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) + + // Send the packet into the mesh sendToMesh(packetPool.allocCopy(r.variant.packet)); bool loopback = false; // if true send any packet the phone sends back itself (for testing) @@ -220,7 +240,7 @@ void MeshService::sendToMesh(MeshPacket *p) nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...) // Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it - if(radio.send(p) != ERRNO_OK) + if (radio.send(p) != ERRNO_OK) DEBUG_MSG("Dropped packet because send queue was full!\n"); } @@ -231,7 +251,7 @@ MeshPacket *MeshService::allocForSending() p->has_payload = true; p->from = nodeDB.getNodeNum(); p->to = NODENUM_BROADCAST; - p->rx_time = gps.getTime() / 1000; // Just in case we process the packet locally - make sure it has a valid timestamp + p->rx_time = gps.getValidTime(); // Just in case we process the packet locally - make sure it has a valid timestamp return p; } @@ -257,6 +277,8 @@ void MeshService::sendOurPosition() MeshPacket *p = allocForSending(); 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 sendToMesh(p); } @@ -270,6 +292,7 @@ void MeshService::onGPSChanged() pos.altitude = gps.altitude.meters(); pos.latitude = gps.location.lat(); pos.longitude = gps.location.lng(); + pos.time = gps.getValidTime(); // We limit our GPS broadcasts to a max rate static uint32_t lastGpsSend; diff --git a/src/NodeDB.cpp b/src/NodeDB.cpp index 53be0a858..ccb1a8328 100644 --- a/src/NodeDB.cpp +++ b/src/NodeDB.cpp @@ -76,7 +76,6 @@ void NodeDB::init() NodeInfo *info = getOrCreateNode(getNodeNum()); info->user = owner; info->has_user = true; - info->last_seen = 0; // haven't heard a real message yet if (!FS.begin(true)) // FIXME - do this in main? { @@ -198,9 +197,10 @@ const NodeInfo *NodeDB::readNextInfo() /// Given a node, return how many seconds in the past (vs now) that we last heard from it uint32_t sinceLastSeen(const NodeInfo *n) { - uint32_t now = gps.getTime() / 1000; + uint32_t now = gps.getTime(); - int delta = (int)(now - n->last_seen); + uint32_t last_seen = n->position.time; + int delta = (int)(now - last_seen); if (delta < 0) // our clock must be slightly off still - not set from GPS yet delta = 0; @@ -236,16 +236,23 @@ void NodeDB::updateFrom(const MeshPacket &mp) if (oldNumNodes != *numNodes) updateGUI = true; // we just created a nodeinfo - if (mp.rx_time) // if the packet has a valid timestamp use it - info->last_seen = mp.rx_time; + if (mp.rx_time) + { // if the packet has a valid timestamp use it to update our last_seen + info->has_position = true; // at least the time is valid + info->position.time = mp.rx_time; + } switch (p.which_variant) { - case SubPacket_position_tag: + case SubPacket_position_tag: { + // we carefully preserve the old time, because we always trust our local timestamps more + uint32_t oldtime = info->position.time; info->position = p.variant.position; + info->position.time = oldtime; info->has_position = true; updateGUIforNode = info; break; + } case SubPacket_data_tag: { diff --git a/src/configuration.h b/src/configuration.h index 57b98a399..a1602c56c 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -43,8 +43,8 @@ along with this program. If not, see . // Select which board is being used. If the outside build environment has sent a choice, just use that #if !defined(T_BEAM_V10) && !defined(HELTEC_LORA32) -#define T_BEAM_V10 // AKA Rev1 (second board released) -//#define HELTEC_LORA32 +//#define T_BEAM_V10 // AKA Rev1 (second board released) +#define HELTEC_LORA32 #define HW_VERSION_US // We encode the hardware freq range in the hw version string, so sw update can eventually install the correct build #endif diff --git a/src/mesh.pb.h b/src/mesh.pb.h index 2d22519a6..b9322f9cb 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 = 13, - DeviceState_Version_Current = 13 + DeviceState_Version_Minimum = 15, + DeviceState_Version_Current = 15 } DeviceState_Version; /* Struct definitions */ @@ -64,11 +64,13 @@ typedef struct _Position { int32_t altitude; int32_t battery_level; bool from_hardware; + uint32_t time; } Position; typedef struct _RadioConfig_UserPreferences { uint32_t position_broadcast_secs; uint32_t send_owner_secs; + uint32_t num_missed_to_fail; bool keep_all_packets; bool promiscuous_mode; } RadioConfig_UserPreferences; @@ -86,7 +88,6 @@ typedef struct _NodeInfo { User user; bool has_position; Position position; - uint32_t last_seen; int32_t snr; int32_t frequency_error; } NodeInfo; @@ -166,28 +167,28 @@ typedef struct _ToRadio { /* Initializer values for message structs */ -#define Position_init_default {0, 0, 0, 0, 0} +#define Position_init_default {0, 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}} #define MeshPacket_init_default {0, 0, false, SubPacket_init_default, 0} #define ChannelSettings_init_default {0, 0, _ChannelSettings_ModemConfig_MIN, {0}, ""} #define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default} -#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0} -#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0, 0} +#define RadioConfig_UserPreferences_init_default {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} #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} +#define Position_init_zero {0, 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}} #define MeshPacket_init_zero {0, 0, false, SubPacket_init_zero, 0} #define ChannelSettings_init_zero {0, 0, _ChannelSettings_ModemConfig_MIN, {0}, ""} #define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero} -#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0} -#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0, 0} +#define RadioConfig_UserPreferences_init_zero {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} #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, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero, MeshPacket_init_zero}, _DeviceState_Version_MIN, false, MeshPacket_init_zero} #define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}} @@ -209,8 +210,10 @@ typedef struct _ToRadio { #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_secs_tag 2 +#define RadioConfig_UserPreferences_num_missed_to_fail_tag 3 #define RadioConfig_UserPreferences_keep_all_packets_tag 100 #define RadioConfig_UserPreferences_promiscuous_mode_tag 101 #define User_id_tag 1 @@ -220,7 +223,6 @@ typedef struct _ToRadio { #define NodeInfo_num_tag 1 #define NodeInfo_user_tag 2 #define NodeInfo_position_tag 3 -#define NodeInfo_last_seen_tag 4 #define NodeInfo_snr_tag 5 #define NodeInfo_frequency_error_tag 6 #define RadioConfig_preferences_tag 1 @@ -249,7 +251,8 @@ 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, BOOL, from_hardware, 5) \ +X(a, STATIC, SINGULAR, UINT32, time, 6) #define Position_CALLBACK NULL #define Position_DEFAULT NULL @@ -306,6 +309,7 @@ X(a, STATIC, OPTIONAL, MESSAGE, channel_settings, 2) #define RadioConfig_UserPreferences_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, position_broadcast_secs, 1) \ X(a, STATIC, SINGULAR, UINT32, send_owner_secs, 2) \ +X(a, STATIC, SINGULAR, UINT32, num_missed_to_fail, 3) \ X(a, STATIC, SINGULAR, BOOL, keep_all_packets, 100) \ X(a, STATIC, SINGULAR, BOOL, promiscuous_mode, 101) #define RadioConfig_UserPreferences_CALLBACK NULL @@ -315,7 +319,6 @@ X(a, STATIC, SINGULAR, BOOL, promiscuous_mode, 101) X(a, STATIC, SINGULAR, INT32, num, 1) \ X(a, STATIC, OPTIONAL, MESSAGE, user, 2) \ X(a, STATIC, OPTIONAL, MESSAGE, position, 3) \ -X(a, STATIC, SINGULAR, UINT32, last_seen, 4) \ X(a, STATIC, SINGULAR, INT32, snr, 5) \ X(a, STATIC, SINGULAR, INT32, frequency_error, 6) #define NodeInfo_CALLBACK NULL @@ -390,17 +393,17 @@ extern const pb_msgdesc_t ToRadio_msg; #define ToRadio_fields &ToRadio_msg /* Maximum encoded size of messages (where known) */ -#define Position_size 42 +#define Position_size 48 #define Data_size 256 #define User_size 72 #define SubPacket_size 259 #define MeshPacket_size 290 #define ChannelSettings_size 50 -#define RadioConfig_size 72 -#define RadioConfig_UserPreferences_size 18 +#define RadioConfig_size 78 +#define RadioConfig_UserPreferences_size 24 #define NodeInfo_size 157 #define MyNodeInfo_size 24 -#define DeviceState_size 14965 +#define DeviceState_size 14971 #define FromRadio_size 299 #define ToRadio_size 293