Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions src/mesh/PhoneAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ void PhoneAPI::handleStartConfig()
nodeInfoQueue.clear();
}
resetReadIndex();
configStartMsec = millis();
configHandshakeRestarted = false;
onConfigHandshakeStarted();
}

void PhoneAPI::close()
Expand Down Expand Up @@ -110,8 +113,12 @@ void PhoneAPI::close()
fromRadioNum = 0;
config_nonce = 0;
config_state = 0;
// Reset duplicate filter so each new connection starts clean
std::fill(std::begin(recentToRadioPacketIds), std::end(recentToRadioPacketIds), 0);
pauseBluetoothLogging = false;
heartbeatReceived = false;
configStartMsec = 0;
configHandshakeRestarted = false;
}
}

Expand Down Expand Up @@ -790,7 +797,35 @@ int PhoneAPI::onNotify(uint32_t newValue)
onNowHasData(newValue);
} else {
LOG_DEBUG("Client not yet interested in packets (state=%d)", state);
checkConfigHandshakeTimeout();
}

return timeout ? -1 : 0; // If we timed out, MeshService should stop iterating through observers as we just removed one
}

bool PhoneAPI::isConfigHandshakeActive() const
{
return !configHandshakeRestarted && state == STATE_SEND_MY_INFO && configStartMsec != 0;
}

uint32_t PhoneAPI::getConfigHandshakeElapsedMs() const
{
if (configStartMsec == 0)
return 0;
return millis() - configStartMsec;
}

bool PhoneAPI::checkConfigHandshakeTimeout()
{
if (!isConfigHandshakeActive())
return false;

uint32_t elapsedMs = getConfigHandshakeElapsedMs();
if (elapsedMs > kConfigHandshakeTimeoutMs) {
LOG_WARN("Config handshake stuck in state=%d for %u ms, forcing transport restart", state, elapsedMs);
configHandshakeRestarted = true;
onConfigHandshakeTimeout();
return true;
}
return false;
}
11 changes: 11 additions & 0 deletions src/mesh/PhoneAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ class PhoneAPI
/// Use to ensure that clients don't get confused about old messages from the radio
uint32_t config_nonce = 0;
uint32_t readIndex = 0;
uint32_t configStartMsec = 0;
bool configHandshakeRestarted = false;

std::vector<meshtastic_FileInfo> filesManifest = {};

Expand Down Expand Up @@ -138,6 +140,7 @@ class PhoneAPI
bool isConnected() { return state != STATE_SEND_NOTHING; }

protected:
static constexpr uint32_t kConfigHandshakeTimeoutMs = 3000;
/// Our fromradio packet while it is being assembled
meshtastic_FromRadio fromRadioScratch = {};

Expand All @@ -147,12 +150,20 @@ class PhoneAPI
/// Hookable to find out when connection changes
virtual void onConnectionChanged(bool connected) {}

/// Invoked if the config handshake stalls long enough that we want to drop the BLE link.
virtual void onConfigHandshakeTimeout() {}
virtual void onConfigHandshakeStarted() {}

/// If we haven't heard from the other side in a while then say not connected. Returns true if timeout occurred
bool checkConnectionTimeout();

/// Check the current underlying physical link to see if the client is currently connected
virtual bool checkIsConnected() = 0;

bool checkConfigHandshakeTimeout();
bool isConfigHandshakeActive() const;
uint32_t getConfigHandshakeElapsedMs() const;

/**
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
*/
Expand Down
60 changes: 49 additions & 11 deletions src/nimble/NimbleBluetooth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,39 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
protected:
virtual int32_t runOnce() override
{
std::lock_guard<std::mutex> guard(nimble_mutex);
if (queue_size > 0) {
for (uint8_t i = 0; i < queue_size; i++) {
handleToRadio(nimble_queue.at(i).data(), nimble_queue.at(i).length());
bool scheduledImmediate = false;
{
std::lock_guard<std::mutex> guard(nimble_mutex);
if (queue_size > 0) {
for (uint8_t i = 0; i < queue_size; i++) {
handleToRadio(nimble_queue.at(i).data(), nimble_queue.at(i).length());
}
LOG_DEBUG("Queue_size %u", queue_size);
queue_size = 0;
// Reset our timer so any newly queued work is handled right away.
setIntervalFromNow(0);
scheduledImmediate = true;
}
if (!hasChecked && phoneWants) {
// Pull fresh data while we're outside of the NimBLE callback context.
numBytes = getFromRadio(fromRadioBytes);
hasChecked = true;
// Make sure we wake immediately to publish the prefetched data.
setIntervalFromNow(0);
scheduledImmediate = true;
}
LOG_DEBUG("Queue_size %u", queue_size);
queue_size = 0;
}
if (!hasChecked && phoneWants) {
// Pull fresh data while we're outside of the NimBLE callback context.
numBytes = getFromRadio(fromRadioBytes);
hasChecked = true;

bool timedOut = checkConfigHandshakeTimeout();
if (!timedOut && !scheduledImmediate && isConfigHandshakeActive()) {
uint32_t elapsed = getConfigHandshakeElapsedMs();
uint32_t remaining = elapsed >= kConfigHandshakeTimeoutMs ? 1 : (kConfigHandshakeTimeoutMs - elapsed);
// Keep nudging the thread while the config handshake is in flight.
setIntervalFromNow(remaining);
}

// the run is triggered via NimbleBluetoothToRadioCallback and NimbleBluetoothFromRadioCallback
return INT32_MAX;
return RUN_SAME;
}
/**
* Subclasses can use this as a hook to provide custom notifications for their transport (i.e. bluetooth notifies)
Expand All @@ -92,6 +109,27 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
#endif
}

virtual void onConfigHandshakeStarted() override { setIntervalFromNow(kConfigHandshakeTimeoutMs); }

virtual void onConfigHandshakeTimeout() override
{
LOG_WARN("Config handshake stalled; restarting BLE connection");
if (!bleServer) {
return;
}
auto peers = bleServer->getPeerDevices();
if (peers.empty()) {
LOG_WARN("No BLE peers to disconnect during restart");
return;
}
for (auto connHandle : peers) {
int rc = bleServer->disconnect(connHandle);
if (rc != 0) {
LOG_WARN("Failed to disconnect BLE handle %u (rc=%d)", connHandle, rc);
}
}
}

/// Check the current underlying physical link to see if the client is currently connected
virtual bool checkIsConnected() { return bleServer && bleServer->getConnectedCount() > 0; }
};
Expand Down
Loading