diff --git a/examples/companion_radio/AbstractUITask.h b/examples/companion_radio/AbstractUITask.h index 0eee45ae..357b80dd 100644 --- a/examples/companion_radio/AbstractUITask.h +++ b/examples/companion_radio/AbstractUITask.h @@ -43,4 +43,6 @@ public: virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) = 0; virtual void notify(UIEventType t = UIEventType::none) = 0; virtual void loop() = 0; -}; + virtual void showAlert(const char* text, int duration_millis) {} + virtual void forceRefresh() {} +}; \ No newline at end of file diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 25b52542..ec3db4ad 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -446,9 +446,37 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe } bool MyMesh::filterRecvFloodPacket(mesh::Packet* packet) { - // REVISIT: try to determine which Region (from transport_codes[1]) that Sender is indicating for replies/responses - // if unknown, fallback to finding Region from transport_codes[0], the 'scope' used by Sender - return false; + // Check if this incoming flood packet is a repeat of a message we recently sent + if (packet->payload_len >= SENT_FINGERPRINT_SIZE) { + unsigned long now = millis(); + for (int i = 0; i < SENT_TRACK_SIZE; i++) { + SentMsgTrack* t = &_sent_track[i]; + if (!t->active) continue; + + // Expire old entries + if ((now - t->sent_millis) > SENT_TRACK_EXPIRY_MS) { + t->active = false; + continue; + } + + // Compare payload fingerprint + if (memcmp(packet->payload, t->fingerprint, SENT_FINGERPRINT_SIZE) == 0) { + t->repeat_count++; + MESH_DEBUG_PRINTLN("SentTrack: heard repeat #%d (SNR=%.1f)", t->repeat_count, packet->getSNR()); + +#ifdef DISPLAY_CLASS + if (_ui) { + char buf[40]; + snprintf(buf, sizeof(buf), "Sent! (%d)", t->repeat_count); + _ui->showAlert(buf, 2000); // show/extend alert with updated count + } +#endif + break; // found match, no need to check other entries + } + } + } + + return false; // never filter — let normal processing continue } void MyMesh::sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, uint32_t delay_millis) { @@ -463,6 +491,17 @@ void MyMesh::sendFloodScoped(const ContactInfo& recipient, mesh::Packet* pkt, ui } } void MyMesh::sendFloodScoped(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t delay_millis) { + // Capture payload fingerprint for repeat tracking before sending + if (pkt->payload_len >= SENT_FINGERPRINT_SIZE) { + SentMsgTrack* t = &_sent_track[_sent_track_idx]; + memcpy(t->fingerprint, pkt->payload, SENT_FINGERPRINT_SIZE); + t->repeat_count = 0; + t->sent_millis = millis(); + t->active = true; + _sent_track_idx = (_sent_track_idx + 1) % SENT_TRACK_SIZE; + MESH_DEBUG_PRINTLN("SentTrack: captured fingerprint for channel msg"); + } + // TODO: have per-channel send_scope if (send_scope.isNull()) { sendFlood(pkt, delay_millis); @@ -826,6 +865,8 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe dirty_contacts_expiry = 0; memset(advert_paths, 0, sizeof(advert_paths)); memset(send_scope.key, 0, sizeof(send_scope.key)); + memset(_sent_track, 0, sizeof(_sent_track)); + _sent_track_idx = 0; // defaults memset(&_prefs, 0, sizeof(_prefs)); diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index efea86ac..800bdd23 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -12,7 +12,7 @@ #endif #ifndef FIRMWARE_VERSION -#define FIRMWARE_VERSION "Meck v0.6.3" +#define FIRMWARE_VERSION "Meck v0.6.4" #endif #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) @@ -231,6 +231,19 @@ private: #define ADVERT_PATH_TABLE_SIZE 16 AdvertPath advert_paths[ADVERT_PATH_TABLE_SIZE]; // circular table + + // Sent message repeat tracking + #define SENT_TRACK_SIZE 4 + #define SENT_FINGERPRINT_SIZE 12 + #define SENT_TRACK_EXPIRY_MS 30000 // stop tracking after 30 seconds + struct SentMsgTrack { + uint8_t fingerprint[SENT_FINGERPRINT_SIZE]; + uint8_t repeat_count; + unsigned long sent_millis; + bool active; + }; + SentMsgTrack _sent_track[SENT_TRACK_SIZE]; + int _sent_track_idx; // next slot in circular buffer }; extern MyMesh the_mesh; \ No newline at end of file diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 8179c70a..c8cc77d9 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -610,6 +610,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no void UITask::showAlert(const char* text, int duration_millis) { strcpy(_alert, text); _alert_expiry = millis() + duration_millis; + _next_refresh = millis() + 100; // trigger re-render to show updated text } void UITask::notify(UIEventType t) { diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index 0c354eb1..4a390213 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -75,7 +75,8 @@ public: void gotoHomeScreen() { setCurrScreen(home); } void gotoChannelScreen(); // Navigate to channel message screen - void showAlert(const char* text, int duration_millis); + void showAlert(const char* text, int duration_millis) override; + void forceRefresh() override { _next_refresh = 100; } int getMsgCount() const { return _msgcount; } bool hasDisplay() const { return _display != NULL; } bool isButtonPressed() const;