mirror of
https://github.com/meshtastic/firmware.git
synced 2025-10-27 15:02:41 +00:00
Merge branch 'develop' into mqtt_buffer_protection
This commit is contained in:
commit
b1e00613d4
@ -72,6 +72,7 @@ void PhoneAPI::handleStartConfig()
|
|||||||
|
|
||||||
LOG_INFO("Start API client config");
|
LOG_INFO("Start API client config");
|
||||||
nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos
|
nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos
|
||||||
|
nodeInfoQueue.clear();
|
||||||
resetReadIndex();
|
resetReadIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +95,7 @@ void PhoneAPI::close()
|
|||||||
fromRadioScratch = {};
|
fromRadioScratch = {};
|
||||||
toRadioScratch = {};
|
toRadioScratch = {};
|
||||||
nodeInfoForPhone = {};
|
nodeInfoForPhone = {};
|
||||||
|
nodeInfoQueue.clear();
|
||||||
packetForPhone = NULL;
|
packetForPhone = NULL;
|
||||||
filesManifest.clear();
|
filesManifest.clear();
|
||||||
fromRadioNum = 0;
|
fromRadioNum = 0;
|
||||||
@ -431,17 +433,25 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_SEND_OTHER_NODEINFOS: {
|
case STATE_SEND_OTHER_NODEINFOS: {
|
||||||
|
LOG_DEBUG("Send known nodes");
|
||||||
|
if (nodeInfoForPhone.num == 0 && !nodeInfoQueue.empty()) {
|
||||||
|
// Serve the next cached node without re-reading from the DB iterator.
|
||||||
|
nodeInfoForPhone = nodeInfoQueue.front();
|
||||||
|
nodeInfoQueue.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
if (nodeInfoForPhone.num != 0) {
|
if (nodeInfoForPhone.num != 0) {
|
||||||
// Just in case we stored a different user.id in the past, but should never happen going forward
|
// Just in case we stored a different user.id in the past, but should never happen going forward
|
||||||
sprintf(nodeInfoForPhone.user.id, "!%08x", nodeInfoForPhone.num);
|
sprintf(nodeInfoForPhone.user.id, "!%08x", nodeInfoForPhone.num);
|
||||||
LOG_INFO("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s", nodeInfoForPhone.num, nodeInfoForPhone.last_heard,
|
LOG_DEBUG("nodeinfo: num=0x%x, lastseen=%u, id=%s, name=%s", nodeInfoForPhone.num, nodeInfoForPhone.last_heard,
|
||||||
nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name);
|
nodeInfoForPhone.user.id, nodeInfoForPhone.user.long_name);
|
||||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_node_info_tag;
|
||||||
fromRadioScratch.node_info = nodeInfoForPhone;
|
fromRadioScratch.node_info = nodeInfoForPhone;
|
||||||
// Stay in current state until done sending nodeinfos
|
nodeInfoForPhone = {};
|
||||||
nodeInfoForPhone.num = 0; // We just consumed a nodeinfo, will need a new one next time
|
prefetchNodeInfos();
|
||||||
} else {
|
} else {
|
||||||
LOG_DEBUG("Done sending nodeinfo");
|
LOG_DEBUG("Done sending nodeinfo");
|
||||||
|
nodeInfoQueue.clear();
|
||||||
state = STATE_SEND_FILEMANIFEST;
|
state = STATE_SEND_FILEMANIFEST;
|
||||||
// Go ahead and send that ID right now
|
// Go ahead and send that ID right now
|
||||||
return getFromRadio(buf);
|
return getFromRadio(buf);
|
||||||
@ -545,6 +555,30 @@ void PhoneAPI::releaseQueueStatusPhonePacket()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhoneAPI::prefetchNodeInfos()
|
||||||
|
{
|
||||||
|
bool added = false;
|
||||||
|
// Keep the queue topped up so BLE reads stay responsive even if DB fetches take a moment.
|
||||||
|
while (nodeInfoQueue.size() < kNodePrefetchDepth) {
|
||||||
|
auto nextNode = nodeDB->readNextMeshNode(readIndex);
|
||||||
|
if (!nextNode)
|
||||||
|
break;
|
||||||
|
|
||||||
|
auto info = TypeConversions::ConvertToNodeInfo(nextNode);
|
||||||
|
bool isUs = info.num == nodeDB->getNodeNum();
|
||||||
|
info.hops_away = isUs ? 0 : info.hops_away;
|
||||||
|
info.last_heard = isUs ? getValidTime(RTCQualityFromNet) : info.last_heard;
|
||||||
|
info.snr = isUs ? 0 : info.snr;
|
||||||
|
info.via_mqtt = isUs ? false : info.via_mqtt;
|
||||||
|
info.is_favorite = info.is_favorite || isUs;
|
||||||
|
nodeInfoQueue.push_back(info);
|
||||||
|
added = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (added)
|
||||||
|
onNowHasData(0);
|
||||||
|
}
|
||||||
|
|
||||||
void PhoneAPI::releaseMqttClientProxyPhonePacket()
|
void PhoneAPI::releaseMqttClientProxyPhonePacket()
|
||||||
{
|
{
|
||||||
if (mqttClientProxyMessageForPhone) {
|
if (mqttClientProxyMessageForPhone) {
|
||||||
@ -581,20 +615,8 @@ bool PhoneAPI::available()
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case STATE_SEND_OTHER_NODEINFOS:
|
case STATE_SEND_OTHER_NODEINFOS:
|
||||||
if (nodeInfoForPhone.num == 0) {
|
if (nodeInfoQueue.empty())
|
||||||
auto nextNode = nodeDB->readNextMeshNode(readIndex);
|
prefetchNodeInfos();
|
||||||
if (nextNode) {
|
|
||||||
nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(nextNode);
|
|
||||||
bool isUs = nodeInfoForPhone.num == nodeDB->getNodeNum();
|
|
||||||
nodeInfoForPhone.hops_away = isUs ? 0 : nodeInfoForPhone.hops_away;
|
|
||||||
nodeInfoForPhone.last_heard = isUs ? getValidTime(RTCQualityFromNet) : nodeInfoForPhone.last_heard;
|
|
||||||
nodeInfoForPhone.snr = isUs ? 0 : nodeInfoForPhone.snr;
|
|
||||||
nodeInfoForPhone.via_mqtt = isUs ? false : nodeInfoForPhone.via_mqtt;
|
|
||||||
nodeInfoForPhone.is_favorite = nodeInfoForPhone.is_favorite || isUs; // Our node is always a favorite
|
|
||||||
|
|
||||||
onNowHasData(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true; // Always say we have something, because we might need to advance our state machine
|
return true; // Always say we have something, because we might need to advance our state machine
|
||||||
case STATE_SEND_PACKETS: {
|
case STATE_SEND_PACKETS: {
|
||||||
if (!queueStatusPacketForPhone)
|
if (!queueStatusPacketForPhone)
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "mesh-pb-constants.h"
|
#include "mesh-pb-constants.h"
|
||||||
#include "meshtastic/portnums.pb.h"
|
#include "meshtastic/portnums.pb.h"
|
||||||
|
#include <deque>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@ -79,6 +80,10 @@ class PhoneAPI
|
|||||||
|
|
||||||
/// We temporarily keep the nodeInfo here between the call to available and getFromRadio
|
/// We temporarily keep the nodeInfo here between the call to available and getFromRadio
|
||||||
meshtastic_NodeInfo nodeInfoForPhone = meshtastic_NodeInfo_init_default;
|
meshtastic_NodeInfo nodeInfoForPhone = meshtastic_NodeInfo_init_default;
|
||||||
|
// Prefetched node info entries ready for immediate transmission to the phone.
|
||||||
|
std::deque<meshtastic_NodeInfo> nodeInfoQueue;
|
||||||
|
// Tunable size of the node info cache so we can keep BLE reads non-blocking.
|
||||||
|
static constexpr size_t kNodePrefetchDepth = 4;
|
||||||
|
|
||||||
meshtastic_ToRadio toRadioScratch = {
|
meshtastic_ToRadio toRadioScratch = {
|
||||||
0}; // this is a static scratch object, any data must be copied elsewhere before returning
|
0}; // this is a static scratch object, any data must be copied elsewhere before returning
|
||||||
@ -158,6 +163,8 @@ class PhoneAPI
|
|||||||
|
|
||||||
void releaseQueueStatusPhonePacket();
|
void releaseQueueStatusPhonePacket();
|
||||||
|
|
||||||
|
void prefetchNodeInfos();
|
||||||
|
|
||||||
void releaseMqttClientProxyPhonePacket();
|
void releaseMqttClientProxyPhonePacket();
|
||||||
|
|
||||||
void releaseClientNotification();
|
void releaseClientNotification();
|
||||||
|
|||||||
@ -48,6 +48,8 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
|||||||
uint8_t queue_size = 0;
|
uint8_t queue_size = 0;
|
||||||
uint8_t fromRadioBytes[meshtastic_FromRadio_size] = {0};
|
uint8_t fromRadioBytes[meshtastic_FromRadio_size] = {0};
|
||||||
size_t numBytes = 0;
|
size_t numBytes = 0;
|
||||||
|
bool hasChecked = false;
|
||||||
|
bool phoneWants = false;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual int32_t runOnce() override
|
virtual int32_t runOnce() override
|
||||||
@ -60,7 +62,11 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
|||||||
LOG_DEBUG("Queue_size %u", queue_size);
|
LOG_DEBUG("Queue_size %u", queue_size);
|
||||||
queue_size = 0;
|
queue_size = 0;
|
||||||
}
|
}
|
||||||
// Note: phoneWants/hasChecked logic removed since onRead() handles getFromRadio() directly
|
if (!hasChecked && phoneWants) {
|
||||||
|
// Pull fresh data while we're outside of the NimBLE callback context.
|
||||||
|
numBytes = getFromRadio(fromRadioBytes);
|
||||||
|
hasChecked = true;
|
||||||
|
}
|
||||||
|
|
||||||
// the run is triggered via NimbleBluetoothToRadioCallback and NimbleBluetoothFromRadioCallback
|
// the run is triggered via NimbleBluetoothToRadioCallback and NimbleBluetoothFromRadioCallback
|
||||||
return INT32_MAX;
|
return INT32_MAX;
|
||||||
@ -117,6 +123,8 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
|
|||||||
bluetoothPhoneAPI->queue_size++;
|
bluetoothPhoneAPI->queue_size++;
|
||||||
bluetoothPhoneAPI->setIntervalFromNow(0);
|
bluetoothPhoneAPI->setIntervalFromNow(0);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
LOG_DEBUG("Drop duplicate ToRadio packet (%u bytes)", val.length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -129,17 +137,32 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
|
|||||||
virtual void onRead(NimBLECharacteristic *pCharacteristic)
|
virtual void onRead(NimBLECharacteristic *pCharacteristic)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
bluetoothPhoneAPI->phoneWants = true;
|
||||||
|
bluetoothPhoneAPI->setIntervalFromNow(0);
|
||||||
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->nimble_mutex);
|
std::lock_guard<std::mutex> guard(bluetoothPhoneAPI->nimble_mutex);
|
||||||
|
|
||||||
// Get fresh data immediately when client reads
|
if (!bluetoothPhoneAPI->hasChecked) {
|
||||||
bluetoothPhoneAPI->numBytes = bluetoothPhoneAPI->getFromRadio(bluetoothPhoneAPI->fromRadioBytes);
|
// Fetch payload on demand; prefetch keeps this fast for the first read.
|
||||||
|
bluetoothPhoneAPI->numBytes = bluetoothPhoneAPI->getFromRadio(bluetoothPhoneAPI->fromRadioBytes);
|
||||||
|
bluetoothPhoneAPI->hasChecked = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Set the characteristic value with whatever data we have
|
|
||||||
pCharacteristic->setValue(bluetoothPhoneAPI->fromRadioBytes, bluetoothPhoneAPI->numBytes);
|
pCharacteristic->setValue(bluetoothPhoneAPI->fromRadioBytes, bluetoothPhoneAPI->numBytes);
|
||||||
|
|
||||||
|
if (bluetoothPhoneAPI->numBytes != 0) {
|
||||||
|
#ifdef NIMBLE_TWO
|
||||||
|
// Notify immediately so subscribed clients see the packet without an extra read.
|
||||||
|
pCharacteristic->notify(bluetoothPhoneAPI->fromRadioBytes, bluetoothPhoneAPI->numBytes, BLE_HS_CONN_HANDLE_NONE);
|
||||||
|
#else
|
||||||
|
pCharacteristic->notify();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
if (bluetoothPhoneAPI->numBytes != 0) // if we did send something, queue it up right away to reload
|
if (bluetoothPhoneAPI->numBytes != 0) // if we did send something, queue it up right away to reload
|
||||||
bluetoothPhoneAPI->setIntervalFromNow(0);
|
bluetoothPhoneAPI->setIntervalFromNow(0);
|
||||||
bluetoothPhoneAPI->numBytes = 0;
|
bluetoothPhoneAPI->numBytes = 0;
|
||||||
|
bluetoothPhoneAPI->hasChecked = false;
|
||||||
|
bluetoothPhoneAPI->phoneWants = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -271,6 +294,8 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
|||||||
bluetoothPhoneAPI->close();
|
bluetoothPhoneAPI->close();
|
||||||
bluetoothPhoneAPI->numBytes = 0;
|
bluetoothPhoneAPI->numBytes = 0;
|
||||||
bluetoothPhoneAPI->queue_size = 0;
|
bluetoothPhoneAPI->queue_size = 0;
|
||||||
|
bluetoothPhoneAPI->hasChecked = false;
|
||||||
|
bluetoothPhoneAPI->phoneWants = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the last ToRadio packet buffer to avoid rejecting first packet from new connection
|
// Clear the last ToRadio packet buffer to avoid rejecting first packet from new connection
|
||||||
@ -278,6 +303,15 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
|||||||
#ifdef NIMBLE_TWO
|
#ifdef NIMBLE_TWO
|
||||||
// Restart Advertising
|
// Restart Advertising
|
||||||
ble->startAdvertising();
|
ble->startAdvertising();
|
||||||
|
#else
|
||||||
|
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||||
|
if (!pAdvertising->start(0)) {
|
||||||
|
if (pAdvertising->isAdvertising()) {
|
||||||
|
LOG_DEBUG("BLE advertising already running");
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("BLE failed to restart advertising");
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -401,15 +435,18 @@ void NimbleBluetooth::setupService()
|
|||||||
// Define the characteristics that the app is looking for
|
// Define the characteristics that the app is looking for
|
||||||
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
||||||
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE);
|
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE);
|
||||||
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ);
|
// Allow notifications so phones can stream FromRadio without polling.
|
||||||
|
FromRadioCharacteristic =
|
||||||
|
bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY);
|
||||||
fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
|
fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
|
||||||
logRadioCharacteristic =
|
logRadioCharacteristic =
|
||||||
bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ, 512U);
|
bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ, 512U);
|
||||||
} else {
|
} else {
|
||||||
ToRadioCharacteristic = bleService->createCharacteristic(
|
ToRadioCharacteristic = bleService->createCharacteristic(
|
||||||
TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC);
|
TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC);
|
||||||
FromRadioCharacteristic = bleService->createCharacteristic(
|
FromRadioCharacteristic =
|
||||||
FROMRADIO_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN |
|
||||||
|
NIMBLE_PROPERTY::READ_ENC | NIMBLE_PROPERTY::NOTIFY);
|
||||||
fromNumCharacteristic =
|
fromNumCharacteristic =
|
||||||
bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
|
bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
|
||||||
NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user