diff --git a/TODO.md b/TODO.md index 48c3a2241..7e947ea4a 100644 --- a/TODO.md +++ b/TODO.md @@ -2,10 +2,10 @@ * have node info screen show real info (including distance and heading) * make debug info screen show real data (including battery level & charging) -* show real text info on the text screen * update build to generate both board types * retest BLE software update for both board types * turn on screen when a new update arrives +* don't forward redundent pings or ping responses to the phone, it just wastes phone battery # Medium priority @@ -129,3 +129,4 @@ until the phone pulls those packets. Ever so often power on bluetooth just so w * send location (or if not available user) when the user wakes the device from display sleep (both for testing and to improve user experience) * make real implementation of getNumOnlineNodes * very occasionally send our position and user packet based on the schedule in the radio info (if for nothing else so that other nodes update last_seen) +* show real text info on the text screen \ No newline at end of file diff --git a/src/MeshService.cpp b/src/MeshService.cpp index c7355a395..68fb8c6a0 100644 --- a/src/MeshService.cpp +++ b/src/MeshService.cpp @@ -75,6 +75,52 @@ void MeshService::sendOurOwner(NodeNum dest) sendToMesh(p); } +/// handle a user packet that just arrived on the radio, return NULL if we should not process this packet at all +MeshPacket *MeshService::handleFromRadioUser(MeshPacket *mp) +{ + bool wasBroadcast = mp->to == NODENUM_BROADCAST; + bool isCollision = mp->from == myNodeInfo.my_node_num; + + // we win if we have a lower macaddr + bool weWin = memcmp(&owner.macaddr, &mp->payload.variant.user.macaddr, sizeof(owner.macaddr)) < 0; + + if (isCollision) + { + if (weWin) + { + DEBUG_MSG("NOTE! Received a nodenum collision and we are vetoing\n"); + + packetPool.release(mp); // discard it + mp = NULL; + + sendOurOwner(); // send our owner as a _broadcast_ because that other guy is mistakenly using our nodenum + } + else + { + // we lost, we need to try for a new nodenum! + DEBUG_MSG("NOTE! Received a nodenum collision we lost, so picking a new nodenum\n"); + nodeDB.updateFrom(*mp); // update the DB early - before trying to repick (so we don't select the same node number again) + nodeDB.pickNewNodeNum(); + sendOurOwner(); // broadcast our new attempt at a node number + } + } + else if (wasBroadcast) + { + // If we haven't yet abandoned the packet and it was a broadcast, reply (just to them) with our User record so they can build their DB + + // Someone just sent us a User, reply with our Owner + DEBUG_MSG("Received broadcast Owner from 0x%x, replying with our owner\n", mp->from); + + sendOurOwner(mp->from); + + String lcd = String("Joined: ") + mp->payload.variant.user.long_name + "\n"; + screen_print(lcd.c_str()); + } + + return mp; +} + + void MeshService::handleFromRadio() { MeshPacket *mp; @@ -82,47 +128,10 @@ void MeshService::handleFromRadio() while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) { mp->rx_time = gps.getTime() / 1000; // store the arrival timestamp for the phone - + if (mp->has_payload && mp->payload.which_variant == SubPacket_user_tag) { - bool wasBroadcast = mp->to == NODENUM_BROADCAST; - bool isCollision = mp->from == myNodeInfo.my_node_num; - - // we win if we have a lower macaddr - bool weWin = memcmp(&owner.macaddr, &mp->payload.variant.user.macaddr, sizeof(owner.macaddr)) < 0; - - if (isCollision) - { - if (weWin) - { - DEBUG_MSG("NOTE! Received a nodenum collision and we are vetoing\n"); - - packetPool.release(mp); // discard it - mp = NULL; - - sendOurOwner(); // send our owner as a _broadcast_ because that other guy is mistakenly using our nodenum - } - else - { - // we lost, we need to try for a new nodenum! - DEBUG_MSG("NOTE! Received a nodenum collision we lost, so picking a new nodenum\n"); - nodeDB.updateFrom(*mp); // update the DB early - before trying to repick (so we don't select the same node number again) - nodeDB.pickNewNodeNum(); - sendOurOwner(); // broadcast our new attempt at a node number - } - } - else if (wasBroadcast) - { - // If we haven't yet abandoned the packet and it was a broadcast, reply (just to them) with our User record so they can build their DB - - // Someone just sent us a User, reply with our Owner - DEBUG_MSG("Received broadcast Owner from 0x%x, replying with our owner\n", mp->from); - - sendOurOwner(mp->from); - - String lcd = String("Joined: ") + mp->payload.variant.user.long_name + "\n"; - screen_print(lcd.c_str()); - } + mp = handleFromRadioUser(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) @@ -206,6 +215,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 return p; } diff --git a/src/MeshService.h b/src/MeshService.h index 818fa81f6..eb3be7edd 100644 --- a/src/MeshService.h +++ b/src/MeshService.h @@ -80,6 +80,9 @@ private: /// handle packets that just arrived from the mesh radio void handleFromRadio(); + + /// 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); }; extern MeshService service; diff --git a/src/NodeDB.cpp b/src/NodeDB.cpp index 2dcab8738..91f359f04 100644 --- a/src/NodeDB.cpp +++ b/src/NodeDB.cpp @@ -236,7 +236,8 @@ void NodeDB::updateFrom(const MeshPacket &mp) if (oldNumNodes != *numNodes) updateGUI = true; // we just created a nodeinfo - info->last_seen = gps.getTime() / 1000; // we keep time in seconds, not msecs + if(mp.rx_time) // if the packet has a valid timestamp use it + info->last_seen = mp.rx_time; switch (p.which_variant) { @@ -246,6 +247,16 @@ void NodeDB::updateFrom(const MeshPacket &mp) updateGUIforNode = info; break; + case SubPacket_data_tag: { + // Keep a copy of the most recent text message. + if(p.variant.data.typ == Data_Type_CLEAR_TEXT) { + devicestate.rx_text_message = mp; + devicestate.has_rx_text_message = true; + updateTextMessage = true; + } + break; + } + case SubPacket_user_tag: { DEBUG_MSG("old user %s/%s/%s\n", info->user.id, info->user.long_name, info->user.short_name); @@ -255,11 +266,12 @@ void NodeDB::updateFrom(const MeshPacket &mp) info->user = p.variant.user; DEBUG_MSG("updating changed=%d user %s/%s/%s\n", changed, info->user.id, info->user.long_name, info->user.short_name); info->has_user = true; - updateGUIforNode = info; if (changed) { - // We just created a user for the first time, store our DB + updateGUIforNode = info; + + // We just changed something important about the user, store our DB saveToDisk(); } break; diff --git a/src/NodeDB.h b/src/NodeDB.h index c8eba85d9..b3cec771c 100644 --- a/src/NodeDB.h +++ b/src/NodeDB.h @@ -6,6 +6,7 @@ #include "mesh-pb-constants.h" #include "MeshTypes.h" +extern DeviceState devicestate; extern MyNodeInfo &myNodeInfo; extern RadioConfig &radioConfig; extern ChannelSettings &channelSettings; @@ -25,12 +26,13 @@ class NodeDB NodeInfo *nodes; pb_size_t *numNodes; - bool updateGUI = false; // we think the gui should definitely be redrawn - NodeInfo *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI - int readPointer = 0; public: + bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled + NodeInfo *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI + bool updateTextMessage = false; // if true, the GUI should show a new text message + /// don't do mesh based algoritm for node id assignment (initially) /// instead just store in flash - possibly even in the initial alpha release do this hack NodeDB(); diff --git a/src/mesh.pb.h b/src/mesh.pb.h index 6531ea4eb..5526a5b33 100644 --- a/src/mesh.pb.h +++ b/src/mesh.pb.h @@ -127,6 +127,8 @@ typedef struct _DeviceState { pb_size_t receive_queue_count; MeshPacket receive_queue[32]; DeviceState_Version version; + bool has_rx_text_message; + MeshPacket rx_text_message; } DeviceState; typedef struct _FromRadio { @@ -174,7 +176,7 @@ typedef struct _ToRadio { #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 MyNodeInfo_init_default {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} +#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} @@ -187,7 +189,7 @@ typedef struct _ToRadio { #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 MyNodeInfo_init_zero {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} +#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}} #define ToRadio_init_zero {0, {MeshPacket_init_zero}} @@ -235,6 +237,7 @@ typedef struct _ToRadio { #define DeviceState_node_db_tag 4 #define DeviceState_receive_queue_tag 5 #define DeviceState_version_tag 6 +#define DeviceState_rx_text_message_tag 7 #define FromRadio_packet_tag 2 #define FromRadio_num_tag 1 #define ToRadio_packet_tag 1 @@ -331,7 +334,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, my_node, 2) \ X(a, STATIC, OPTIONAL, MESSAGE, owner, 3) \ X(a, STATIC, REPEATED, MESSAGE, node_db, 4) \ X(a, STATIC, REPEATED, MESSAGE, receive_queue, 5) \ -X(a, STATIC, SINGULAR, UENUM, version, 6) +X(a, STATIC, SINGULAR, UENUM, version, 6) \ +X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) #define DeviceState_CALLBACK NULL #define DeviceState_DEFAULT NULL #define DeviceState_radio_MSGTYPE RadioConfig @@ -339,6 +343,7 @@ X(a, STATIC, SINGULAR, UENUM, version, 6) #define DeviceState_owner_MSGTYPE User #define DeviceState_node_db_MSGTYPE NodeInfo #define DeviceState_receive_queue_MSGTYPE MeshPacket +#define DeviceState_rx_text_message_MSGTYPE MeshPacket #define FromRadio_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UINT32, num, 1) \ @@ -393,7 +398,7 @@ extern const pb_msgdesc_t ToRadio_msg; #define RadioConfig_UserPreferences_size 18 #define NodeInfo_size 157 #define MyNodeInfo_size 13 -#define DeviceState_size 13029 +#define DeviceState_size 13271 #define FromRadio_size 248 #define ToRadio_size 242 diff --git a/src/screen.cpp b/src/screen.cpp index edd015f35..706aa39b3 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -129,15 +129,22 @@ void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int1 /// Draw the last text message we received void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { + MeshPacket &mp = devicestate.rx_text_message; + NodeInfo *node = nodeDB.getNode(mp.from); + // Demo for drawStringMaxWidth: // with the third parameter you can define the width after which words will be wrapped. // Currently only spaces and "-" are allowed for wrapping display->setTextAlignment(TEXT_ALIGN_LEFT); display->setFont(ArialMT_Plain_16); - String sender = "KH:"; + String sender = (node && node->has_user) ? node->user.short_name : "???"; display->drawString(0 + x, 0 + y, sender); display->setFont(ArialMT_Plain_10); - display->drawStringMaxWidth(4 + x, 10 + y, 128, " Hey - I found the trail, but I've fallen and I can't get up. ;-)"); + + static char tempBuf[96]; + snprintf(tempBuf, sizeof(tempBuf), " %s", mp.payload.variant.data.payload.bytes); // the max length of this buffer is much longer than we can possibly print + + display->drawStringMaxWidth(4 + x, 10 + y, 128, tempBuf); // ui.disableIndicator(); } @@ -495,11 +502,9 @@ uint32_t screen_loop() else // standard screen loop handling ehre { // If the # nodes changes, we need to regen our list of screens - static size_t oldnumnodes = 0; - size_t numnodes = nodeDB.getNumNodes(); - if (numnodes != oldnumnodes) + if (nodeDB.updateGUI) { - oldnumnodes = numnodes; + nodeDB.updateGUI = false; screen_set_frames(); } @@ -536,18 +541,25 @@ void screen_set_frames() { DEBUG_MSG("showing standard frames\n"); - nonBootFrames[0] = drawTextMessageFrame; - nonBootFrames[1] = drawDebugInfo; - size_t numnodes = nodeDB.getNumNodes(); // We don't show the node info our our node (if we have it yet - we should) if (numnodes > 0) numnodes--; - for (size_t i = 0; i < numnodes; i++) - nonBootFrames[NUM_EXTRA_FRAMES + i] = drawNodeInfo; + size_t numframes = 0; - ui.setFrames(nonBootFrames, NUM_EXTRA_FRAMES + numnodes); + // If we have a text message - show it first + if(devicestate.has_rx_text_message) + nonBootFrames[numframes++] = drawTextMessageFrame; + + // then all the nodes + for (size_t i = 0; i < numnodes; i++) + nonBootFrames[numframes++] = drawNodeInfo; + + // then the debug info + nonBootFrames[numframes++] = drawDebugInfo; + + ui.setFrames(nonBootFrames, numframes); showingBluetooth = false; }