diff --git a/exercises/205_sustained_link/README.md b/exercises/205_sustained_link/README.md index 973d6c5..6d25715 100644 --- a/exercises/205_sustained_link/README.md +++ b/exercises/205_sustained_link/README.md @@ -17,6 +17,19 @@ There is no simulated BOB/CY physical block in this exercise. `SIM_PHY_ENVELOPE` Exercise 205 does not intentionally tear down Links after a message count. A Link is reused while it remains active. If a Link becomes stale or closes, the unit clears local state and attempts to recreate the outbound Link. +Each unit sends one Link message per active peer per minute at its allocated second: + +```text +AMY: 04 BOB: 08 CY: 12 DAN: 16 +ED: 20 FLO: 24 GUY: 28 +``` + +The message payload uses board IDs and includes an iteration counter: + +```text +BOB says Hi to CY iter=0 +``` + The outbound Link retry budget is: ```text @@ -41,13 +54,15 @@ RX ANNOUNCE: label=Cy hash= phy=Cy(2) TX LINKREQUEST: opening link to Cy slot=19 attempt=1/3 LINK ACTIVE: initiator link established to Cy hash= RX LINK: inbound link established hash= phy=Bob(1) -TX LINK: Hi from Bob iter=0 to=Cy via=outbound hash= status=2 -RX LINK: Hi from Cy iter=0 to=Bob | phy=Cy(2) RSSI=... SNR=... +TX LINK: BOB says Hi to CY iter=0 via=outbound hash= status=2 +RX LINK: CY says Hi to BOB iter=0 | phy=Cy(2) RSSI=... SNR=... LINK RETRY: no establishment after 60000 ms; retrying Cy attempts=1/3 LINK FAILED: peer=Cy attempts=3 window_ms=... waiting_for_announce=1 LINK RETRY RESET: fresh announce from Cy ``` +Reticulum library logging is set to warning level in this exercise. Heap, path-store, entries, and byte-count diagnostics are intentionally suppressed so serial logs remain focused on field-test results. + # Build, Upload, And Monitor ```bash diff --git a/exercises/205_sustained_link/src/main.cpp b/exercises/205_sustained_link/src/main.cpp index 2a038cf..e5d03a5 100644 --- a/exercises/205_sustained_link/src/main.cpp +++ b/exercises/205_sustained_link/src/main.cpp @@ -43,9 +43,6 @@ #define SIM_PHY_BLOCK_BOB_CY 0 #endif -#ifndef EX205_RNS_TRACE -#define EX205_RNS_TRACE 0 -#endif #ifndef MR_LINKFWD_DELAY_MS #define MR_LINKFWD_DELAY_MS 0 #endif @@ -127,7 +124,6 @@ struct PeerState { uint32_t last_tx_ms = 0; uint32_t tx_iter = 0; uint8_t last_tx_second = 255; - uint8_t last_skip_second = 255; }; static PeerState peers[MAX_PEERS]; @@ -142,12 +138,66 @@ static const char* node_label_for_slot(uint8_t slot) { case 2: return "Cy"; case 3: return "Dan"; case 4: return "Ed"; - case 5: return "Fay"; + case 5: return "Flo"; case 6: return "Guy"; default: return "unknown"; } } +static const char* board_id_for_label(const String& label) { + if (label == "Amy") { + return "AMY"; + } + if (label == "Bob") { + return "BOB"; + } + if (label == "Cy") { + return "CY"; + } + if (label == "Dan") { + return "DAN"; + } + if (label == "Ed") { + return "ED"; + } + if (label == "Flo") { + return "FLO"; + } + if (label == "Guy") { + return "GUY"; + } + return label.c_str(); +} + +static bool is_local_name(const String& value) { + return value == NODE_LABEL || value == BOARD_ID; +} + +static String peer_label_from_name(const String& value) { + if (value == "AMY") { + return "Amy"; + } + if (value == "BOB") { + return "Bob"; + } + if (value == "CY") { + return "Cy"; + } + if (value == "DAN") { + return "Dan"; + } + if (value == "ED") { + return "Ed"; + } + if (value == "FLO") { + return "Flo"; + } + if (value == "GUY") { + return "Guy"; + } + return value; +} + static void IRAM_ATTR on_pps_edge() { last_pps_ms = millis(); } @@ -665,7 +715,6 @@ static void clear_peer_slot(uint8_t index) { peers[index].last_tx_ms = 0; peers[index].tx_iter = 0; peers[index].last_tx_second = 255; - peers[index].last_skip_second = 255; } static int ensure_peer_for_label(const String& label) { @@ -681,6 +730,11 @@ static int ensure_peer_for_label(const String& label) { } static String parse_sender_label(const String& text) { + const int says_pos = text.indexOf(" says "); + if (says_pos > 0) { + return text.substring(0, says_pos); + } + const int from_pos = text.indexOf("from "); if (from_pos < 0) { return ""; @@ -694,6 +748,16 @@ static String parse_sender_label(const String& text) { } static String parse_recipient_label(const String& text) { + const int says_hi_to_pos = text.indexOf(" says Hi to "); + if (says_hi_to_pos >= 0) { + const int start = says_hi_to_pos + 12; + int end = text.indexOf(' ', start); + if (end < 0) { + end = text.length(); + } + return text.substring(start, end); + } + const int to_pos = text.indexOf("to="); if (to_pos < 0) { return ""; @@ -707,14 +771,6 @@ static String parse_recipient_label(const String& text) { } static void attach_link_callbacks(RNS::Link& link) { - if (link) { - Serial.printf("APP LINK CALLBACKS: attach hash=%s status=%u initiator=%u\r\n", - link.hash().toHex().c_str(), - (unsigned)link.status(), - link.initiator() ? 1U : 0U); - } else { - Serial.println("APP LINK CALLBACKS: attach skipped null link"); - } link.set_packet_callback(on_link_packet); link.set_link_closed_callback(on_link_closed); } @@ -728,21 +784,13 @@ static void on_link_packet(const RNS::Bytes& data, const RNS::Packet& packet) { String text = data.toString().c_str(); String sender = parse_sender_label(text); String recipient = parse_recipient_label(text); - Serial.printf("APP RX LINK ENTER: link=%s status=%u initiator=%u peer_index=%d sender=%s recipient=%s text=%s\r\n", - packet.link() ? packet.link().hash().toHex().c_str() : "(none)", - packet.link() ? (unsigned)packet.link().status() : 255U, - packet.link() && packet.link().initiator() ? 1U : 0U, - peer_index, - sender.c_str(), - recipient.c_str(), - text.c_str()); - if (sender == NODE_LABEL || (recipient.length() > 0 && recipient != NODE_LABEL)) { - Serial.printf("RX LINK ignored: self_or_wrong_recipient text=%s\r\n", text.c_str()); + const String sender_label = peer_label_from_name(sender); + if (is_local_name(sender) || (recipient.length() > 0 && !is_local_name(recipient))) { return; } if (peer_index >= 0 && peers[peer_index].label.length() == 0 && - sender.length() > 0 && sender != NODE_LABEL) { - const int existing_index = find_peer_by_label(sender); + sender.length() > 0 && !is_local_name(sender)) { + const int existing_index = find_peer_by_label(sender_label); if (existing_index >= 0 && existing_index != peer_index) { if (packet.link()) { peers[existing_index].inbound_link = packet.link(); @@ -752,12 +800,12 @@ static void on_link_packet(const RNS::Bytes& data, const RNS::Packet& packet) { clear_peer_slot((uint8_t)peer_index); peer_index = existing_index; } else { - peers[peer_index].label = sender; + peers[peer_index].label = sender_label; } } if (peer_index < 0) { - if (sender.length() > 0 && sender != NODE_LABEL) { - peer_index = ensure_peer_for_label(sender); + if (sender.length() > 0 && !is_local_name(sender)) { + peer_index = ensure_peer_for_label(sender_label); if (peer_index >= 0 && packet.link()) { peers[peer_index].inbound_link = packet.link(); peers[peer_index].inbound_active = true; @@ -814,11 +862,6 @@ static void on_link_closed(RNS::Link& link) { static void on_outbound_link_established(RNS::Link& link) { int peer_index = find_peer_by_link_hash(link.hash()); - Serial.printf("APP LINK ESTABLISHED CB: direction=outbound hash=%s status=%u initiator=%u peer_index=%d\r\n", - link.hash().toHex().c_str(), - (unsigned)link.status(), - link.initiator() ? 1U : 0U, - peer_index); if (peer_index >= 0) { peers[peer_index].outbound_link = link; attach_link_callbacks(peers[peer_index].outbound_link); @@ -843,11 +886,6 @@ static void on_inbound_link_established(RNS::Link& link) { if (peer_index < 0) { peer_index = first_free_peer_slot(); } - Serial.printf("APP LINK ESTABLISHED CB: direction=inbound hash=%s status=%u initiator=%u peer_index=%d\r\n", - link.hash().toHex().c_str(), - (unsigned)link.status(), - link.initiator() ? 1U : 0U, - peer_index); if (peer_index >= 0) { peers[peer_index].inbound_link = link; attach_link_callbacks(peers[peer_index].inbound_link); @@ -922,11 +960,10 @@ static void print_config() { Serial.printf("LoRa: freq=%.1f BW=%.1f SF=%d CR=%d sync=0x%02x txp=%d\r\n", (double)LORA_FREQ_MHZ, (double)LORA_BW_KHZ, (int)LORA_SF, (int)LORA_CR, (int)LORA_SYNC_WORD, (int)LORA_TX_POWER_DBM); - Serial.printf("Sim: phy_envelope=%d phy_block_bob_cy=%d node_slot=%d rns_trace=%d linkfwd_delay_ms=%u transport=1\r\n", + Serial.printf("Sim: phy_envelope=%d phy_block_bob_cy=%d node_slot=%d rns_log=warning linkfwd_delay_ms=%u transport=1\r\n", (int)SIM_PHY_ENVELOPE, (int)SIM_PHY_BLOCK_BOB_CY, (int)NODE_SLOT_INDEX, - (int)EX205_RNS_TRACE, (unsigned)MR_LINKFWD_DELAY_MS); Serial.printf("Announce: startup=1 second=%lu repeat=%lu seconds\r\n", (unsigned long)ANNOUNCEMENT_2, @@ -1016,12 +1053,6 @@ static void maybe_open_link(const DateTime& rtc_now, bool have_rtc_now) { continue; } if (peer.outbound_active || (peer.outbound_link && peer.outbound_link.status() == RNS::Type::Link::ACTIVE)) { - if (!peer.outbound_active) { - Serial.printf("APP LINK STATE: outbound already active peer=%s hash=%s status=%u\r\n", - peer.label.c_str(), - peer.outbound_link.hash().toHex().c_str(), - (unsigned)peer.outbound_link.status()); - } peer.outbound_active = true; continue; } @@ -1151,11 +1182,7 @@ void setup() { Serial.println(); Serial.println("Exercise 205: sustained transport Links"); -#if EX205_RNS_TRACE - RNS::loglevel(RNS::LOG_TRACE); -#else - RNS::loglevel(RNS::LOG_NOTICE); -#endif + RNS::loglevel(RNS::LOG_WARNING); (void)tbeam_supreme::initPmuForPeripherals(pmu, &Serial); tbeam::DisplayConfig display_config; @@ -1196,8 +1223,7 @@ void loop() { maybe_send_scheduled_announce(); const uint8_t send_slot = node_send_slot_second(); - const uint8_t second_slot = (uint8_t)((send_slot + 30U) % 60U); - if (have_rtc_now && (rtc_now.second == send_slot || rtc_now.second == second_slot)) { + if (have_rtc_now && rtc_now.second == send_slot) { for (uint8_t i = 0; i < MAX_PEERS; ++i) { PeerState& peer = peers[i]; if (peer.label.length() == 0 || peer.last_tx_second == rtc_now.second) { @@ -1213,24 +1239,13 @@ void loop() { link = &peer.inbound_link; } if (!link) { - if (peer.last_skip_second != rtc_now.second) { - peer.last_skip_second = rtc_now.second; - Serial.printf("TX LINK SKIP: peer=%s outbound=%u outbound_status=%u inbound=%u inbound_status=%u attempted=%u active_out=%u active_in=%u\r\n", - peer.label.c_str(), - peer.outbound_link ? 1U : 0U, - peer.outbound_link ? (unsigned)peer.outbound_link.status() : 255U, - peer.inbound_link ? 1U : 0U, - peer.inbound_link ? (unsigned)peer.inbound_link.status() : 255U, - peer.outbound_attempted ? 1U : 0U, - peer.outbound_active ? 1U : 0U, - peer.inbound_active ? 1U : 0U); - } continue; } peer.last_tx_second = rtc_now.second; peer.last_tx_ms = now; - String message = String("Hi from ") + NODE_LABEL + " iter=" + String(peer.tx_iter++) + " to=" + peer.label; + const char* recipient = board_id_for_label(peer.label); + String message = String(BOARD_ID) + " says Hi to " + recipient + " iter=" + String(peer.tx_iter++); Serial.printf("TX LINK: %s via=%s hash=%s status=%u\r\n", message.c_str(), link == &peer.outbound_link ? "outbound" : "inbound",