Expand and harden full companion profiles

This commit is contained in:
mikecarper committed 2026-08-24 01:57:38 -07:00
1 parent f2d4ffbb7e
commit dc92ee61ef
44 files changed
+1522 -226

No files matched your search

+37
View File
@@ -3,6 +3,9 @@
#include <helpers/RemoteCliRequest.h>
#include <helpers/RemoteCliTimeout.h>
#include <Utils.h>
#if defined(ESP32_PLATFORM) && defined(BOARD_HAS_PSRAM)
#include <esp_heap_caps.h>
#endif
#ifndef SERVER_RESPONSE_DELAY
#define SERVER_RESPONSE_DELAY 300
@@ -19,6 +22,36 @@ bool BaseChatMesh::sendFloodScoped(const mesh::GroupChannel& channel, mesh::Pack
return sendFlood(pkt, delay_millis);
}
bool BaseChatMesh::initializeContactStorage() {
#if defined(ESP32_PLATFORM) && defined(BOARD_HAS_PSRAM)
const int requested_capacity = MAX_CONTACTS + MAX_ANON_CONTACTS;
if (contact_capacity >= requested_capacity) return true;
ContactInfo* expanded_contacts = static_cast<ContactInfo*>(
heap_caps_calloc(requested_capacity, sizeof(ContactInfo),
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
int* expanded_sort_array = static_cast<int*>(
heap_caps_calloc(requested_capacity, sizeof(int),
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
if (expanded_contacts == NULL || expanded_sort_array == NULL) {
if (expanded_contacts != NULL) heap_caps_free(expanded_contacts);
if (expanded_sort_array != NULL) heap_caps_free(expanded_sort_array);
MESH_DEBUG_PRINTLN(
"BaseChatMesh: PSRAM contact allocation failed; using %d slots",
contact_capacity - MAX_ANON_CONTACTS);
return false;
}
memcpy(expanded_contacts, contacts, sizeof(ContactInfo) * num_contacts);
contacts = expanded_contacts;
sort_array = expanded_sort_array;
contact_capacity = requested_capacity;
MESH_DEBUG_PRINTLN("BaseChatMesh: allocated %d contact slots in PSRAM",
MAX_CONTACTS);
#endif
return true;
}
mesh::Packet* BaseChatMesh::createSelfAdvert(const char* name) {
uint8_t app_data[MAX_ADVERT_DATA_SIZE];
uint8_t app_data_len;
@@ -86,7 +119,11 @@ ContactInfo* BaseChatMesh::allocateContactSlot(bool transient_only) {
return &contacts[oldest_idx];
}
} else {
#if defined(ESP32_PLATFORM) && defined(BOARD_HAS_PSRAM)
if (num_contacts < contact_capacity) {
#else
if (num_contacts < MAX_ANON_CONTACTS+MAX_CONTACTS) {
#endif
return &contacts[num_contacts++];
} else if (shouldOverwriteWhenFull()) {
// Find oldest non-favourite contact by oldest lastmod timestamp
+21 -1
View File
@@ -61,9 +61,23 @@ class BaseChatMesh : public mesh::Mesh {
friend class ContactsIterator;
#if defined(ESP32_PLATFORM) && defined(BOARD_HAS_PSRAM)
enum {
CONTACT_PSRAM_FALLBACK_REGULAR_SLOTS =
MAX_CONTACTS < 16 ? MAX_CONTACTS : 16,
CONTACT_PSRAM_FALLBACK_TOTAL_SLOTS =
MAX_ANON_CONTACTS + CONTACT_PSRAM_FALLBACK_REGULAR_SLOTS
};
ContactInfo* contacts;
ContactInfo contacts_fallback[CONTACT_PSRAM_FALLBACK_TOTAL_SLOTS];
int* sort_array;
int sort_array_fallback[CONTACT_PSRAM_FALLBACK_TOTAL_SLOTS];
int contact_capacity;
#else
ContactInfo contacts[MAX_CONTACTS+MAX_ANON_CONTACTS];
int num_contacts;
int sort_array[MAX_CONTACTS+MAX_ANON_CONTACTS];
#endif
int num_contacts;
int matching_peer_indexes[MAX_SEARCH_RESULTS];
unsigned long txt_send_timeout;
#ifdef MAX_GROUP_CHANNELS
@@ -81,6 +95,11 @@ protected:
BaseChatMesh(mesh::Radio& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::PacketManager& mgr, mesh::MeshTables& tables)
: mesh::Mesh(radio, ms, rng, rtc, mgr, tables)
{
#if defined(ESP32_PLATFORM) && defined(BOARD_HAS_PSRAM)
contacts = contacts_fallback;
sort_array = sort_array_fallback;
contact_capacity = CONTACT_PSRAM_FALLBACK_TOTAL_SLOTS;
#endif
resetContacts();
#ifdef MAX_GROUP_CHANNELS
@@ -93,6 +112,7 @@ protected:
}
void bootstrapRTCfromContacts();
bool initializeContactStorage();
void resetContacts() {
memset(contacts, 0, sizeof(contacts[0])*MAX_ANON_CONTACTS); // set all to have type = ADV_TYPE_NONE(0)
+2
View File
@@ -53,8 +53,10 @@ public:
#ifdef PIN_VBAT_READ
// battery read support
pinMode(PIN_VBAT_READ, INPUT);
#if ESP_ARDUINO_VERSION_MAJOR < 3
adcAttachPin(PIN_VBAT_READ);
#endif
#endif
#ifdef P_LORA_TX_LED
pinMode(P_LORA_TX_LED, OUTPUT);
+156 -8
View File
@@ -3,24 +3,116 @@
#if defined(ARDUINO)
#include <Arduino.h>
#include <atomic>
#include <new>
#if defined(MESH_DUAL_CDC_LOGGING)
#if !defined(NRF52_PLATFORM) || !defined(COMPANION_RADIO_FULL)
#error "MESH_DUAL_CDC_LOGGING is only supported by nRF52 Full Companion"
#if !defined(COMPANION_RADIO_FULL)
#error "MESH_DUAL_CDC_LOGGING is only supported by Full Companion"
#endif
#if !defined(CFG_TUD_CDC) || CFG_TUD_CDC < 2
#error "MESH_DUAL_CDC_LOGGING requires CFG_TUD_CDC >= 2"
#if defined(NRF52_PLATFORM)
#define MESH_NRF52_DUAL_CDC_LOGGING 1
#include <Adafruit_TinyUSB.h>
#if !defined(CFG_TUD_CDC) || CFG_TUD_CDC < 2
#error "MESH_DUAL_CDC_LOGGING requires CFG_TUD_CDC >= 2"
#endif
#elif defined(ESP32_PLATFORM) && defined(SOC_USB_OTG_SUPPORTED) && \
SOC_USB_OTG_SUPPORTED && !ARDUINO_USB_MODE
#define MESH_ESP32_DUAL_CDC_LOGGING 1
#include <USB.h>
#include <USBCDC.h>
#include <nvs.h>
#include <nvs_flash.h>
#if !defined(CONFIG_TINYUSB_CDC_MAX_PORTS) || \
CONFIG_TINYUSB_CDC_MAX_PORTS < 2
#error "MESH_DUAL_CDC_LOGGING requires two ESP32 TinyUSB CDC ports"
#endif
#else
#error "MESH_DUAL_CDC_LOGGING requires nRF52 or ESP32 native TinyUSB"
#endif
#include <Adafruit_TinyUSB.h>
#endif
namespace mesh {
static std::atomic<bool> usb_logging_enabled{true};
static std::atomic<bool> usb_logging_preference_known{false};
class NullUsbLoggingStream : public Stream {
public:
int available() override { return 0; }
int read() override { return -1; }
int peek() override { return -1; }
void flush() override {}
size_t write(uint8_t) override { return 1; }
size_t write(const uint8_t*, size_t size) override { return size; }
};
static NullUsbLoggingStream null_usb_logging_stream;
#if defined(MESH_DUAL_CDC_LOGGING)
static Adafruit_USBD_CDC dedicated_usb_logging_port;
static bool dedicated_usb_logging_port_configured = false;
static bool dedicated_usb_logging_port_started = false;
#if defined(MESH_NRF52_DUAL_CDC_LOGGING)
static Adafruit_USBD_CDC dedicated_usb_logging_port;
#elif defined(MESH_ESP32_DUAL_CDC_LOGGING)
static constexpr char USB_LOGGING_NVS_NAMESPACE[] = "mesh_usb";
static constexpr char USB_LOGGING_NVS_KEY[] = "log_port";
alignas(USBCDC) static uint8_t dedicated_usb_logging_port_storage[
sizeof(USBCDC)];
static USBCDC* dedicated_usb_logging_port = nullptr;
static bool readEsp32UsbLoggingBootPreference() {
// Missing or unreadable state must stay protocol-safe: Full Companion then
// exposes only its primary framed CDC interface. The normal preference load
// below mirrors an explicitly enabled setting for the following reboot.
if (nvs_flash_init() != ESP_OK) return false;
nvs_handle_t handle;
if (nvs_open(USB_LOGGING_NVS_NAMESPACE, NVS_READONLY, &handle) != ESP_OK) {
return false;
}
uint8_t value = 0;
const esp_err_t result = nvs_get_u8(handle, USB_LOGGING_NVS_KEY, &value);
nvs_close(handle);
return result == ESP_OK && value != 0;
}
static bool writeEsp32UsbLoggingBootPreference(bool enabled) {
if (nvs_flash_init() != ESP_OK) return false;
nvs_handle_t handle;
if (nvs_open(USB_LOGGING_NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) {
return false;
}
uint8_t current = 0;
esp_err_t result = nvs_get_u8(handle, USB_LOGGING_NVS_KEY, &current);
if (result == ESP_OK && current == (enabled ? 1 : 0)) {
nvs_close(handle);
return true;
}
result = nvs_set_u8(handle, USB_LOGGING_NVS_KEY, enabled ? 1 : 0);
if (result == ESP_OK) result = nvs_commit(handle);
nvs_close(handle);
return result == ESP_OK;
}
class Esp32UsbLoggingBootstrap {
public:
Esp32UsbLoggingBootstrap() {
const bool enabled = readEsp32UsbLoggingBootPreference();
usb_logging_enabled.store(enabled, std::memory_order_relaxed);
usb_logging_preference_known.store(true, std::memory_order_relaxed);
if (!enabled) return;
dedicated_usb_logging_port = new (dedicated_usb_logging_port_storage)
USBCDC(1);
dedicated_usb_logging_port_configured = true;
}
};
static Esp32UsbLoggingBootstrap esp32_usb_logging_bootstrap;
#endif
#endif
bool isUsbLoggingEnabled() {
@@ -29,15 +121,43 @@ bool isUsbLoggingEnabled() {
void setUsbLoggingEnabled(bool enabled) {
usb_logging_enabled.store(enabled, std::memory_order_relaxed);
usb_logging_preference_known.store(true, std::memory_order_relaxed);
}
bool saveUsbLoggingBootPreference(bool enabled) {
#if defined(MESH_ESP32_DUAL_CDC_LOGGING)
return writeEsp32UsbLoggingBootPreference(enabled);
#else
(void)enabled;
return true;
#endif
}
void beginUsbLoggingPort() {
#if defined(MESH_DUAL_CDC_LOGGING)
if (dedicated_usb_logging_port_started) return;
if (dedicated_usb_logging_port_started
|| !usb_logging_preference_known.load(std::memory_order_relaxed)
|| !isUsbLoggingEnabled()) {
return;
}
#if defined(MESH_ESP32_DUAL_CDC_LOGGING)
// ESP32 registers its descriptor before app_main starts native USB. If the
// saved boot mirror omitted CDC 1, enabling logging takes effect after the
// next reboot instead of trying to mutate a live USB descriptor.
if (dedicated_usb_logging_port == nullptr) return;
// Only the Companion interface may use the CDC control-line reboot gesture.
// A terminal opening or closing the diagnostics interface must never reboot
// the node or put it into the ROM downloader.
dedicated_usb_logging_port->enableReboot(false);
dedicated_usb_logging_port->begin(115200);
#elif defined(MESH_NRF52_DUAL_CDC_LOGGING)
dedicated_usb_logging_port.begin(115200);
dedicated_usb_logging_port_configured = true;
#endif
dedicated_usb_logging_port_started = true;
#if defined(MESH_NRF52_DUAL_CDC_LOGGING)
// The nRF52 core starts TinyUSB before setup(). If the host completed
// enumeration in that small window, reconnect once so it reads the expanded
// two-CDC descriptor. Usually enumeration has not completed and no reconnect
@@ -48,12 +168,24 @@ void beginUsbLoggingPort() {
TinyUSBDevice.attach();
}
#endif
#endif
}
Stream& usbLoggingPort() {
#if defined(MESH_DUAL_CDC_LOGGING)
return dedicated_usb_logging_port;
#if defined(MESH_NRF52_DUAL_CDC_LOGGING)
if (isUsbLoggingEnabled() && dedicated_usb_logging_port_started) {
return dedicated_usb_logging_port;
}
#elif defined(MESH_ESP32_DUAL_CDC_LOGGING)
if (isUsbLoggingEnabled() && dedicated_usb_logging_port_started
&& dedicated_usb_logging_port != nullptr) {
return *dedicated_usb_logging_port;
}
#endif
return null_usb_logging_stream;
#else
if (!isUsbLoggingEnabled()) return null_usb_logging_stream;
return Serial;
#endif
}
@@ -66,5 +198,21 @@ bool hasDedicatedUsbLoggingPort() {
#endif
}
bool isDedicatedUsbLoggingPortConfigured() {
#if defined(MESH_DUAL_CDC_LOGGING)
return dedicated_usb_logging_port_configured;
#else
return false;
#endif
}
bool usbLoggingInterfaceRestartRequired() {
#if defined(MESH_DUAL_CDC_LOGGING)
return dedicated_usb_logging_port_configured != isUsbLoggingEnabled();
#else
return false;
#endif
}
} // namespace mesh
#endif
+11 -4
View File
@@ -16,17 +16,24 @@ class Stream;
namespace mesh {
// Logging starts enabled on first boot. Roles with saved preferences restore
// the persisted setting after their preferences are loaded.
// Ordinary logging images start enabled. Dual-CDC Full Companion starts with
// logging disabled and restores its saved choice after preferences load.
bool isUsbLoggingEnabled();
void setUsbLoggingEnabled(bool enabled);
// ESP32 native USB starts before setup(), so its next-boot interface count is
// mirrored outside the normal role preferences. Other platforms need no
// mirror. Returns false only when that mirror could not be saved.
bool saveUsbLoggingBootPreference(bool enabled);
// Start the optional dedicated USB logging interface. Ordinary builds keep
// using Serial. nRF52 Full Companion builds expose a second CDC ACM interface
// so plaintext diagnostics never share the framed Companion stream.
// using Serial. Supported Full Companion builds expose a second CDC ACM
// interface so plaintext diagnostics never share the framed Companion stream.
void beginUsbLoggingPort();
Stream& usbLoggingPort();
bool hasDedicatedUsbLoggingPort();
bool isDedicatedUsbLoggingPortConfigured();
bool usbLoggingInterfaceRestartRequired();
} // namespace mesh
#endif
+6 -1
View File
@@ -16,7 +16,12 @@
namespace UserGpioPinPolicy {
inline bool isFirmwareReserved(uint8_t pin) {
static const int32_t reserved[] = {
// RadioLib 7 represents its disconnected-pin sentinel as unsigned
// 0xFFFFFFFF. Use a signed type wide enough for that value as well as the
// board definitions that use -1, then reject both forms with the 0..63
// range check below. A 32-bit signed initializer is a narrowing error under
// the GCC 14 toolchain used by Arduino-ESP32 3.x.
static const int64_t reserved[] = {
-1,
// LoRa radio, RF switches, FEMs, and activity indicators.
+6
View File
@@ -23,7 +23,13 @@ void ESPNowBridge::recv_cb(const uint8_t *mac, const uint8_t *data, int len) {
}
#endif
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
void ESPNowBridge::send_cb(const esp_now_send_info_t *info,
esp_now_send_status_t status) {
const uint8_t *mac = info != nullptr ? info->des_addr : nullptr;
#else
void ESPNowBridge::send_cb(const uint8_t *mac, esp_now_send_status_t status) {
#endif
if (_instance) {
_instance->onDataSent(mac, status);
}
+5
View File
@@ -48,7 +48,12 @@ private:
#else
static void recv_cb(const uint8_t *mac, const uint8_t *data, int len);
#endif
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
static void send_cb(const esp_now_send_info_t *info,
esp_now_send_status_t status);
#else
static void send_cb(const uint8_t *mac, esp_now_send_status_t status);
#endif
/**
* ESP-NOW Protocol Structure:
+18
View File
@@ -1,7 +1,11 @@
#include "ESPNOWRadio.h"
#include <esp_now.h>
#include <esp_idf_version.h>
#include <WiFi.h>
#include <esp_wifi.h>
#if ESP_ARDUINO_VERSION_MAJOR >= 3
#include <esp_mac.h>
#endif
static uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
static esp_now_peer_info_t peerInfo;
@@ -11,12 +15,26 @@ static uint8_t rx_buf[256];
static uint8_t last_rx_len = 0;
// callback when data is sent
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
static void OnDataSent(const esp_now_send_info_t *info,
esp_now_send_status_t status) {
(void)info;
#else
static void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
(void)mac_addr;
#endif
is_send_complete = true;
ESPNOW_DEBUG_PRINTLN("Send Status: %d", (int)status);
}
#if ESP_ARDUINO_VERSION_MAJOR >= 3
static void OnDataRecv(const esp_now_recv_info_t *info, const uint8_t *data,
int len) {
(void)info;
#else
static void OnDataRecv(const uint8_t *mac, const uint8_t *data, int len) {
(void)mac;
#endif
ESPNOW_DEBUG_PRINTLN("Recv: len = %d", len);
memcpy(rx_buf, data, len);
last_rx_len = len;
+156 -5
View File
@@ -1,7 +1,21 @@
#include "SerialBLEInterface.h"
#include "../CompanionFrameQueue.h"
#include "esp_mac.h"
#if defined(CONFIG_BLUEDROID_ENABLED)
#include "esp_gap_ble_api.h"
#endif
#if defined(CONFIG_NIMBLE_ENABLED)
#include <host/ble_store.h>
#endif
// Arduino 3.x relies on a library constructor to mark BLE as used before
// initArduino(), and that constructor ordering is not reliable with all link
// layouts/LTO combinations. A strong override keeps the controller memory
// available until SerialBLEInterface::begin(); Full Companion then reserves
// BLE controller/host memory before starting WiFi.
extern "C" bool bleInUse(void) {
return true;
}
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
@@ -12,7 +26,7 @@
#define ADVERT_RESTART_DELAY 1000 // millis
void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code) {
bool SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code) {
_pin_code = pin_code;
if (strcmp(name, "@@MAC") == 0) {
@@ -26,29 +40,73 @@ void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code
sprintf(dev_name, "%s%s", prefix, name);
// Create the BLE Device
#if defined(CONFIG_NIMBLE_ENABLED)
if (!BLEDevice::init(dev_name)) {
BLE_DEBUG_PRINTLN("BLEDevice::init failed");
return false;
}
#else
// Arduino-ESP32 2.x Bluedroid exposes a void init(). Its first observable
// failure is a null server below, which is handled without dereferencing it.
BLEDevice::init(dev_name);
#endif
BLEDevice::setSecurityCallbacks(this);
// ATT notifications consume three bytes of the negotiated MTU. Reserve
// that overhead so a MAX_FRAME_SIZE protocol frame fits without truncation.
BLEDevice::setMTU(MAX_FRAME_SIZE + 3);
BLESecurity sec;
#if defined(CONFIG_NIMBLE_ENABLED)
sec.setPassKey(true, pin_code);
sec.setAuthenticationMode(true, true, true);
#else
sec.setStaticPIN(pin_code);
sec.setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM_BOND);
#endif
//BLEDevice::setPower(ESP_PWR_LVL_N8);
// Create the BLE Server
pServer = BLEDevice::createServer();
if (pServer == NULL) {
BLE_DEBUG_PRINTLN("BLEDevice::createServer failed");
BLEDevice::deinit(false);
return false;
}
pServer->setCallbacks(this);
// Create the BLE Service
pService = pServer->createService(SERVICE_UUID);
if (pService == NULL) {
BLE_DEBUG_PRINTLN("BLEServer::createService failed");
BLEDevice::deinit(false);
pServer = NULL;
return false;
}
// Create a BLE Characteristic
pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY);
uint32_t tx_properties = BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY;
uint32_t rx_properties = BLECharacteristic::PROPERTY_WRITE;
#if defined(CONFIG_NIMBLE_ENABLED)
// NimBLE ignores setAccessPermissions(). Authentication requirements must
// be part of the characteristic properties instead.
tx_properties |= BLECharacteristic::PROPERTY_READ_AUTHEN;
rx_properties |= BLECharacteristic::PROPERTY_WRITE_AUTHEN;
#endif
pTxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_TX, tx_properties);
if (pTxCharacteristic == NULL) {
BLE_DEBUG_PRINTLN("BLEService::createCharacteristic(TX) failed");
BLEDevice::deinit(false);
pServer = NULL;
pService = NULL;
return false;
}
pTxCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM);
pTxCharacteristic->setCallbacks(this);
#if !defined(CONFIG_NIMBLE_ENABLED)
// NimBLE creates and owns the 0x2902 descriptor automatically.
pTxDescriptor = new BLE2902();
// Make notification setup start/finish pairing before the client begins its
// short device-info request timeout. The RX and TX characteristics already
@@ -57,12 +115,24 @@ void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code
pTxDescriptor->setAccessPermissions(
(esp_gatt_perm_t)(ESP_GATT_PERM_READ_ENC_MITM | ESP_GATT_PERM_WRITE_ENC_MITM));
pTxCharacteristic->addDescriptor(pTxDescriptor);
#endif
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
BLECharacteristic * pRxCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID_RX, rx_properties);
if (pRxCharacteristic == NULL) {
BLE_DEBUG_PRINTLN("BLEService::createCharacteristic(RX) failed");
BLEDevice::deinit(false);
pServer = NULL;
pService = NULL;
pTxCharacteristic = NULL;
pTxDescriptor = NULL;
return false;
}
pRxCharacteristic->setAccessPermissions(ESP_GATT_PERM_WRITE_ENC_MITM);
pRxCharacteristic->setCallbacks(this);
pServer->getAdvertising()->addServiceUUID(SERVICE_UUID);
return true;
}
// -------- BLESecurityCallbacks methods
@@ -89,6 +159,34 @@ bool SerialBLEInterface::onSecurityRequest() {
return true; // allow
}
#if defined(CONFIG_NIMBLE_ENABLED)
void SerialBLEInterface::onAuthenticationComplete(ble_gap_conn_desc* desc) {
const bool success = desc != NULL && desc->sec_state.encrypted &&
desc->sec_state.authenticated;
if (success) {
BLE_DEBUG_PRINTLN(" - SecurityCallback - Authentication Success");
deviceConnected = true;
} else {
BLE_DEBUG_PRINTLN(" - SecurityCallback - Authentication Failure");
if (desc != NULL) {
int remove_result = ble_store_util_delete_peer(&desc->peer_id_addr);
if (remove_result == 0) {
BLE_DEBUG_PRINTLN(" - SecurityCallback - Cleared failed peer bond");
} else {
BLE_DEBUG_PRINTLN(" - SecurityCallback - No peer bond cleared, err=%d",
remove_result);
}
}
deviceConnected = false;
if (desc != NULL) {
pServer->disconnect(desc->conn_handle);
}
scheduleAdvertisingRestart((uint32_t)millis());
}
}
#else
void SerialBLEInterface::onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl) {
if (cmpl.success) {
BLE_DEBUG_PRINTLN(" - SecurityCallback - Authentication Success");
@@ -112,12 +210,35 @@ void SerialBLEInterface::onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl) {
scheduleAdvertisingRestart((uint32_t)millis());
}
}
#endif
// -------- BLEServerCallbacks methods
void SerialBLEInterface::onConnect(BLEServer* pServer) {
}
#if defined(CONFIG_NIMBLE_ENABLED)
void SerialBLEInterface::onConnect(BLEServer* pServer, ble_gap_conn_desc* desc) {
BLE_DEBUG_PRINTLN("onConnect(), conn_id=%d, mtu=%d", desc->conn_handle,
pServer->getPeerMTU(desc->conn_handle));
last_conn_id = desc->conn_handle;
deviceConnected = false; // becomes usable only after authentication completes
oldDeviceConnected = false;
notifySucceeded = false;
notificationsEnabled.store(false, std::memory_order_release);
xQueueReset(recv_queue);
_tx_reset_pending.store(true, std::memory_order_release);
_adv_restart_pending = false;
}
void SerialBLEInterface::onMtuChanged(BLEServer* pServer,
ble_gap_conn_desc* desc,
uint16_t mtu) {
(void)pServer;
(void)desc;
BLE_DEBUG_PRINTLN("onMtuChanged(), mtu=%d", mtu);
}
#else
void SerialBLEInterface::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param) {
BLE_DEBUG_PRINTLN("onConnect(), conn_id=%d, mtu=%d", param->connect.conn_id, pServer->getPeerMTU(param->connect.conn_id));
last_conn_id = param->connect.conn_id;
@@ -136,11 +257,13 @@ void SerialBLEInterface::onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t
void SerialBLEInterface::onMtuChanged(BLEServer* pServer, esp_ble_gatts_cb_param_t* param) {
BLE_DEBUG_PRINTLN("onMtuChanged(), mtu=%d", pServer->getPeerMTU(param->mtu.conn_id));
}
#endif
void SerialBLEInterface::onDisconnect(BLEServer* pServer) {
BLE_DEBUG_PRINTLN("onDisconnect()");
deviceConnected = false;
notifySucceeded = false;
notificationsEnabled.store(false, std::memory_order_release);
xQueueReset(recv_queue);
_tx_reset_pending.store(true, std::memory_order_release);
if (pTxDescriptor != NULL) pTxDescriptor->setNotifications(false);
@@ -151,7 +274,15 @@ void SerialBLEInterface::onDisconnect(BLEServer* pServer) {
// -------- BLECharacteristicCallbacks methods
void SerialBLEInterface::onWrite(BLECharacteristic* pCharacteristic, esp_ble_gatts_cb_param_t* param) {
#if defined(CONFIG_NIMBLE_ENABLED)
void SerialBLEInterface::onWrite(BLECharacteristic* pCharacteristic,
ble_gap_conn_desc* desc) {
(void)desc;
#else
void SerialBLEInterface::onWrite(BLECharacteristic* pCharacteristic,
esp_ble_gatts_cb_param_t* param) {
(void)param;
#endif
if (_tx_disconnect_recovery.pending()) {
BLE_DEBUG_PRINTLN("onWrite(): dropping frame while BLE reconnect is pending");
return;
@@ -173,6 +304,19 @@ void SerialBLEInterface::onWrite(BLECharacteristic* pCharacteristic, esp_ble_gat
}
}
#if defined(CONFIG_NIMBLE_ENABLED)
void SerialBLEInterface::onSubscribe(BLECharacteristic* pCharacteristic,
ble_gap_conn_desc* desc,
uint16_t subValue) {
(void)desc;
if (pCharacteristic == pTxCharacteristic) {
// NimBLE uses bit 0 for notification subscriptions.
notificationsEnabled.store((subValue & 0x0001u) != 0,
std::memory_order_release);
}
}
#endif
void SerialBLEInterface::onStatus(BLECharacteristic* pCharacteristic, Status status, uint32_t code) {
(void)pCharacteristic;
notifySucceeded = status == SUCCESS_NOTIFY;
@@ -188,6 +332,7 @@ void SerialBLEInterface::clearBuffers() {
xQueueReset(recv_queue);
send_queue_len = 0;
notifySucceeded = false;
notificationsEnabled.store(false, std::memory_order_release);
_tx_stall_watchdog.reset();
_tx_disconnect_recovery.complete();
_tx_reset_pending.store(false, std::memory_order_release);
@@ -314,7 +459,13 @@ size_t SerialBLEInterface::checkRecvFrame(uint8_t dest[]) {
BLE_WRITE_MIN_INTERVAL) // space the writes apart
) {
const uint16_t peer_mtu = pServer->getPeerMTU(last_conn_id);
const bool notifications_ready = pTxDescriptor != NULL && pTxDescriptor->getNotifications();
#if defined(CONFIG_NIMBLE_ENABLED)
const bool notifications_ready =
notificationsEnabled.load(std::memory_order_acquire);
#else
const bool notifications_ready =
pTxDescriptor != NULL && pTxDescriptor->getNotifications();
#endif
const bool frame_fits = peer_mtu > 3 && send_queue[0].len <= peer_mtu - 3;
const bool delivery_required = mesh::companionFrameRequiresDelivery(
send_queue[0].buf, send_queue[0].len);
+18 -1
View File
@@ -19,6 +19,7 @@ class SerialBLEInterface : public BaseSerialInterface, BLESecurityCallbacks, BLE
bool deviceConnected;
bool oldDeviceConnected;
bool notifySucceeded;
std::atomic<bool> notificationsEnabled{false};
bool _isEnabled;
uint16_t last_conn_id;
uint32_t _pin_code;
@@ -54,16 +55,31 @@ protected:
void onPassKeyNotify(uint32_t pass_key) override;
bool onConfirmPIN(uint32_t pass_key) override;
bool onSecurityRequest() override;
#if defined(CONFIG_NIMBLE_ENABLED)
void onAuthenticationComplete(ble_gap_conn_desc* desc) override;
#else
void onAuthenticationComplete(esp_ble_auth_cmpl_t cmpl) override;
#endif
// BLEServerCallbacks methods
void onConnect(BLEServer* pServer) override;
#if defined(CONFIG_NIMBLE_ENABLED)
void onConnect(BLEServer* pServer, ble_gap_conn_desc* desc) override;
void onMtuChanged(BLEServer* pServer, ble_gap_conn_desc* desc, uint16_t mtu) override;
#else
void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param) override;
void onMtuChanged(BLEServer* pServer, esp_ble_gatts_cb_param_t* param) override;
#endif
void onDisconnect(BLEServer* pServer) override;
// BLECharacteristicCallbacks methods
#if defined(CONFIG_NIMBLE_ENABLED)
void onWrite(BLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) override;
void onSubscribe(BLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc,
uint16_t subValue) override;
#else
void onWrite(BLECharacteristic* pCharacteristic, esp_ble_gatts_cb_param_t* param) override;
#endif
void onStatus(BLECharacteristic* pCharacteristic, Status status, uint32_t code) override;
public:
@@ -75,6 +91,7 @@ public:
deviceConnected = false;
oldDeviceConnected = false;
notifySucceeded = false;
notificationsEnabled.store(false, std::memory_order_relaxed);
_adv_restart_started = 0;
_adv_restart_pending = false;
_isEnabled = false;
@@ -92,7 +109,7 @@ public:
* @param name IN/OUT - a name for the device (combined with prefix). If "@@MAC", is modified and returned
* @param pin_code the BLE security pin
*/
void begin(const char* prefix, char* name, uint32_t pin_code);
bool begin(const char* prefix, char* name, uint32_t pin_code);
// BaseSerialInterface methods
void enable() override;
+7 -2
View File
@@ -199,7 +199,7 @@ bool SerialBLEInterface::removeStoredBondForPeer(const char* cause) {
return true;
}
void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code) {
bool SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code) {
instance = this;
char charpin[20];
@@ -208,7 +208,11 @@ void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code
// If we want to control BLE LED ourselves, uncomment this:
// Bluefruit.autoConnLed(false);
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.begin();
if (!Bluefruit.begin()) {
instance = nullptr;
BLE_DEBUG_PRINTLN("Bluefruit.begin failed");
return false;
}
char dev_name[32+16];
if (strcmp(name, "@@MAC") == 0) {
@@ -275,6 +279,7 @@ void SerialBLEInterface::begin(const char* prefix, char* name, uint32_t pin_code
Bluefruit.Advertising.restartOnDisconnect(true);
return true;
}
void SerialBLEInterface::clearBuffers() {
+1 -1
View File
@@ -76,7 +76,7 @@ public:
* @param name IN/OUT - a name for the device (combined with prefix). If "@@MAC", is modified and returned
* @param pin_code the BLE security pin
*/
void begin(const char* prefix, char* name, uint32_t pin_code);
bool begin(const char* prefix, char* name, uint32_t pin_code);
void disconnect();
void enable() override;
+1
View File
@@ -1,4 +1,5 @@
#include "ST7735Display.h"
#include "../UsbLogging.h"
#include <TFT_eSPI.h>
//#include <Fonts/GFXFF/FreeSans9pt7b.h>