offline queue bump, lossless message sync via ble

This commit is contained in:
liquidraver
2026-02-28 21:49:58 +01:00
parent bb5aca423c
commit b62214ad3e
5 changed files with 77 additions and 7 deletions
+2 -1
View File
@@ -120,9 +120,10 @@ config ZEPHCORE_MAX_CHANNELS
config ZEPHCORE_OFFLINE_QUEUE_SIZE
int "Offline message queue size"
default 16
default 256
help
Size of the offline message queue for incoming messages.
Arduino MeshCore uses 256 for both T1000-E and Wio Tracker.
config ZEPHCORE_ACK_TABLE_SIZE
int "ACK tracking table size"
+15
View File
@@ -121,6 +121,7 @@ static bool dle_requested;
/* Advertising state */
static bool adv_switching = false;
static bool adv_is_slow = false;
static bool adv_post_disconnect = false; /* skip fast advert after disconnect */
/* Runtime BLE passkey */
static uint32_t ble_passkey = CONFIG_ZEPHCORE_BLE_PASSKEY;
@@ -340,6 +341,10 @@ static void disconnected(struct bt_conn *conn, uint8_t reason)
k_work_cancel_delayable(&tx_drain_work);
k_work_cancel_delayable(&overflow_retry_work);
/* Skip fast advertising on reconnect — go straight to slow.
* Prevents tight reconnect flapping loops on flaky links. */
adv_post_disconnect = true;
/* Notify main of BLE disconnection */
if (ble_cbs && ble_cbs->on_disconnected) {
ble_cbs->on_disconnected();
@@ -354,6 +359,16 @@ static void recycled(void)
adv_switching, adv_is_slow);
return;
}
if (adv_post_disconnect) {
/* After disconnect, skip fast advertising — go straight to slow.
* Prevents rapid reconnect flapping on flaky BLE links. */
adv_post_disconnect = false;
LOG_INF("post-disconnect: starting slow advertising directly");
adv_slow_work_fn(NULL);
return;
}
LOG_DBG("restart advertising");
start_adv();
}
+29 -3
View File
@@ -158,6 +158,7 @@ CompanionMesh::CompanionMesh(mesh::Radio &radio, mesh::MillisecondClock &ms, mes
_offline_queue_head = 0;
_offline_queue_tail = 0;
_offline_queue_count = 0;
_sync_pending = false;
memset(_ack_table, 0, sizeof(_ack_table));
_ack_next_overwrite = 0;
memset(_advert_paths, 0, sizeof(_advert_paths));
@@ -392,6 +393,22 @@ bool CompanionMesh::dequeueOfflineMessage(uint8_t *dest, size_t &len)
return true;
}
bool CompanionMesh::peekOfflineMessage(uint8_t *dest, size_t &len)
{
if (_offline_queue_count == 0) return false;
QueuedFrame *f = &_offline_queue[_offline_queue_head];
len = f->len;
memcpy(dest, f->buf, len);
return true; /* head NOT advanced — message stays in queue */
}
void CompanionMesh::confirmOfflineMessage()
{
if (_offline_queue_count == 0) return;
_offline_queue_head = (_offline_queue_head + 1) % OFFLINE_QUEUE_SIZE;
_offline_queue_count--;
}
void CompanionMesh::resetContactIterator()
{
if (_contact_iter_active) {
@@ -1677,12 +1694,21 @@ bool CompanionMesh::handleProtocolFrame(const uint8_t *data, size_t len)
}
case CMD_SYNC_NEXT_MESSAGE: {
LOG_INF("CMD_SYNC_NEXT_MESSAGE: queue_count=%d", _offline_queue_count);
LOG_INF("CMD_SYNC_NEXT_MESSAGE: queue_count=%d pending=%d",
_offline_queue_count, _sync_pending);
/* Phone asking for next = implicit ACK for the previously-peeked message */
if (_sync_pending) {
confirmOfflineMessage();
_sync_pending = false;
}
uint8_t buf[MAX_FRAME_SIZE];
size_t msg_len;
if (dequeueOfflineMessage(buf, msg_len)) {
LOG_INF("CMD_SYNC_NEXT_MESSAGE: dequeued msg_len=%u type=0x%02x", (unsigned)msg_len, buf[0]);
if (peekOfflineMessage(buf, msg_len)) {
LOG_INF("CMD_SYNC_NEXT_MESSAGE: peeked msg_len=%u type=0x%02x", (unsigned)msg_len, buf[0]);
writeFrame(buf, msg_len);
_sync_pending = true; /* will be confirmed on next request or lost on disconnect */
} else {
LOG_INF("CMD_SYNC_NEXT_MESSAGE: queue empty, sending NO_MORE_MSGS");
uint8_t rsp[] = { PACKET_NO_MORE_MSGS };
+16
View File
@@ -162,9 +162,22 @@ public:
/**
* Reset contact iterator (call when new command received).
* Sends PACKET_CONTACT_END if iteration was in progress.
*/
void resetContactIterator();
/**
* Cancel contact iteration silently (no frame sent).
* Call on BLE disconnect there's nobody to send CONTACT_END to.
*/
void cancelContactIterator() { _contact_iter_active = false; }
/**
* Cancel pending message sync. Un-ACKed message stays in queue.
* Call on BLE disconnect so the message is re-sent on reconnect.
*/
void cancelSyncPending() { _sync_pending = false; }
/**
* Get BLE device name for advertising.
*/
@@ -270,6 +283,7 @@ private:
int _offline_queue_head;
int _offline_queue_tail;
int _offline_queue_count;
bool _sync_pending; /* true = last peeked message not yet ACKed by phone */
/* ACK tracking table */
struct AckEntry {
@@ -328,6 +342,8 @@ private:
void queueOfflineMessage(const uint8_t *data, size_t len);
bool dequeueOfflineMessage(uint8_t *dest, size_t &len);
bool peekOfflineMessage(uint8_t *dest, size_t &len);
void confirmOfflineMessage();
void queueContactMessage(const ContactInfo &contact, mesh::Packet *pkt,
uint8_t txt_type, uint32_t sender_timestamp, const uint8_t *extra, int extra_len, const char *text);
+15 -3
View File
@@ -141,11 +141,21 @@ static void ble_on_connected(void)
ui_notify(UI_EVENT_BLE_CONNECTED);
}
/* BLE disconnected callback — notify UI */
/* BLE disconnected callback — clean up state and notify UI */
static void ble_on_disconnected(void)
{
#if IS_ENABLED(CONFIG_LOG)
zephcore_usb_companion_reset_rx();
#endif
#ifdef ZEPHCORE_LORA
/* Silently cancel any in-progress contact iteration.
* Without this, reconnect triggers resetContactIterator() which sends
* a stale PACKET_CONTACT_END to the NEW connection, confusing the
* phone's sync state machine. */
companion_mesh_ptr->cancelContactIterator();
/* Reset message sync — un-ACKed peeked message stays in queue
* and will be re-sent on next CMD_SYNC_NEXT_MESSAGE. */
companion_mesh_ptr->cancelSyncPending();
#endif
ui_notify(UI_EVENT_BLE_DISCONNECTED);
}
@@ -205,8 +215,10 @@ static void rx_process_work_fn(struct k_work *work)
/* Process all queued frames */
while (k_msgq_get(zephcore_ble_get_recv_queue(), &f, K_NO_WAIT) == 0) {
#ifdef ZEPHCORE_LORA
/* Reset contact iterator when new command received */
companion_mesh_ptr->resetContactIterator();
/* handleProtocolFrame() resets the contact iterator internally
* (line 1229) for any non-CMD_GET_CONTACTS command no need
* to call resetContactIterator() here. Doing so sent a stale
* PACKET_CONTACT_END before the command was even processed. */
if (!companion_mesh_ptr->handleProtocolFrame(f.buf, f.len)) {
LOG_DBG("rx_process: unknown cmd 0x%02x len=%u", f.buf[0], (unsigned)f.len);
uint8_t err_rsp[] = { 0x01, 0x01 }; /* PACKET_ERROR, ERR_UNSUPPORTED */