Expand and harden full companion profiles
This commit is contained in:
1 parent
f2d4ffbb7e
commit
dc92ee61ef
44 files changed
+1522
-226
No files matched your search
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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, ¤t);
|
||||
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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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,4 +1,5 @@
|
||||
#include "ST7735Display.h"
|
||||
#include "../UsbLogging.h"
|
||||
#include <TFT_eSPI.h>
|
||||
|
||||
//#include <Fonts/GFXFF/FreeSans9pt7b.h>
|
||||
|
||||
Reference in new issue
Block a user