Minimální ESP32 NimBLE příklad vlastní BLE služby
V našem předchozím příspěvku ESP32 minimální BLE Device Information Service (DIS) příklad jsme ukázali, jak použít předdefinovanou DIS (Device Information Service) s balíčkem espressif/ble_services pomocí NimBLE jako backendu.
Na tomto základě, zde je jak vytvořit zcela vlastní BLE službu:
CustomBLE.hpp
#pragma once
#include <string>
/**
* @brief Initialize custom NimBLE service with a string characteristic
*/
void InitCustomBLE();
/**
* @brief Update the string value of the custom characteristic
* @param value The new string value to set
*/
void UpdateCustomString(const std::string& value);
/**
* @brief Get the current string value of the custom characteristic
* @return Current string value
*/
std::string GetCustomString();
/**
* @brief Set GAP event handler for connection management
* Call this before starting NimBLE host
*/
void SetCustomBLEGapHandler();CustomBLE.cpp
#include "CustomBLE.hpp"
#include "esp_log.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "host/ble_gatt.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include <string>
#include <cstring>
static const char *TAG = "CustomBLE";
// Custom service and characteristic UUIDs (randomly generated 128-bit UUIDs)
static const ble_uuid128_t custom_service_uuid =
BLE_UUID128_INIT(0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12,
0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12);
static const ble_uuid128_t custom_char_uuid =
BLE_UUID128_INIT(0x98, 0xBA, 0xDC, 0xFE, 0x21, 0x43, 0x65, 0x87,
0x98, 0xBA, 0xDC, 0xFE, 0x21, 0x43, 0x65, 0x87);
static std::string custom_string_value = "Hello, NimBLE!";
static uint16_t custom_char_handle;
static uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE;
// GAP event listener structure
static struct ble_gap_event_listener gap_event_listener;
// GATT characteristic access function
static int custom_char_access(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg) {
int rc;
switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR:
ESP_LOGI(TAG, "Custom characteristic read");
rc = os_mbuf_append(ctxt->om, custom_string_value.c_str(), custom_string_value.length());
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
case BLE_GATT_ACCESS_OP_WRITE_CHR: {
ESP_LOGI(TAG, "Custom characteristic write");
uint16_t om_len = OS_MBUF_PKTLEN(ctxt->om);
if (om_len > 0) {
char buffer[om_len + 1];
rc = ble_hs_mbuf_to_flat(ctxt->om, buffer, sizeof(buffer) - 1, NULL);
if (rc == 0) {
buffer[om_len] = '\0';
custom_string_value = std::string(buffer);
ESP_LOGI(TAG, "Custom string updated to: %s", custom_string_value.c_str());
}
}
return 0;
}
default:
return BLE_ATT_ERR_UNLIKELY;
}
}
// GATT service definition
static const struct ble_gatt_svc_def custom_gatt_svcs[] = {
{
BLE_GATT_SVC_TYPE_PRIMARY,
&custom_service_uuid.u,
NULL, // includes
(struct ble_gatt_chr_def[]) {
{
&custom_char_uuid.u,
custom_char_access,
NULL, // arg
NULL, // descriptors
BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_NOTIFY,
0, // min_key_size
&custom_char_handle,
NULL, // cpfd
},
{
NULL, // uuid - end of characteristics
}
},
},
{
0, // type - end of services
}
};
// GAP event handler
static int custom_gap_event(struct ble_gap_event *event, void *arg) {
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
ESP_LOGI(TAG, "Connection %s; status=%d",
event->connect.status == 0 ? "established" : "failed",
event->connect.status);
if (event->connect.status == 0) {
conn_handle = event->connect.conn_handle;
}
break;
case BLE_GAP_EVENT_DISCONNECT:
ESP_LOGI(TAG, "Disconnect; reason=%d", event->disconnect.reason);
conn_handle = BLE_HS_CONN_HANDLE_NONE;
break;
default:
break;
}
return 0;
}
void InitCustomBLE() {
int rc;
// Initialize GATT services
rc = ble_gatts_count_cfg(custom_gatt_svcs);
if (rc != 0) {
ESP_LOGE(TAG, "Failed to count GATT services: %d", rc);
return;
}
rc = ble_gatts_add_svcs(custom_gatt_svcs);
if (rc != 0) {
ESP_LOGE(TAG, "Failed to add GATT services: %d", rc);
return;
}
ESP_LOGI(TAG, "Custom NimBLE service initialized");
}
void UpdateCustomString(const std::string& value) {
custom_string_value = value;
ESP_LOGI(TAG, "Custom string value updated to: %s", custom_string_value.c_str());
// If connected, send notification
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
struct os_mbuf *om;
om = ble_hs_mbuf_from_flat(value.c_str(), value.length());
if (om != NULL) {
int rc = ble_gattc_notify_custom(conn_handle, custom_char_handle, om);
if (rc != 0) {
ESP_LOGE(TAG, "Failed to send notification: %d", rc);
}
}
}
}
std::string GetCustomString() {
return custom_string_value;
}
void SetCustomBLEGapHandler() {
// Set up the GAP event listener
gap_event_listener.fn = custom_gap_event;
gap_event_listener.arg = NULL;
ble_gap_event_listener_register(&gap_event_listener, custom_gap_event, NULL);
}Jak integrovat s InitBLE()
To se dobře integruje s BLE.cpp z našeho předchozího příkladu ESP32 minimální BLE Device Information Service (DIS) příklad. Zde je funkce InitBLE(), která inicializuje jak vlastní službu, tak Device Information Service.
Jediné, co zde musíte přidat, je
SetCustomBLEGapHandler();
InitCustomBLE(); // Initialize custom BLE service
po esp_nimble_init() a před spuštěním BLE hostitele přes esp_ble_conn_start().
Kompletní funkce InitBLE():
#include "CustomBLE.hpp"
/* ... */
void InitBLE(void)
{
esp_nimble_init();
GenerateDeviceName();
esp_ble_conn_config_t config;
strncpy((char*)config.device_name, device_name.c_str(), sizeof(config.device_name) - 1);
strncpy((char*)config.broadcast_data, "Metexon", sizeof(config.broadcast_data) - 1);
esp_err_t ret;
// Initialize NVS
esp_event_handler_register(BLE_CONN_MGR_EVENTS, ESP_EVENT_ANY_ID, app_ble_conn_event_handler, NULL);
esp_ble_conn_init(&config);
/**
* Initialize device information service (DIS)
*/
app_ble_dis_init();
SetCustomBLEGapHandler();
InitCustomBLE(); // Initialize custom BLE service
/**
* Start BLE (in separate thread)
*/
if (esp_ble_conn_start() != ESP_OK) {
esp_ble_conn_stop();
esp_ble_conn_deinit();
esp_event_handler_unregister(BLE_CONN_MGR_EVENTS, ESP_EVENT_ANY_ID, app_ble_conn_event_handler);
}
}Jak testovat
Použijte náš skript z Minimální Python skript pro výpis a čtení BLE charakteristik zařízení pomocí Pythonu (Bleak):
Částečný příklad výstupu
Service: 12345678-9abc-def0-1234-56789abcdef0
Description: Unknown
Handle: 1
Characteristics (1):
----------------------------------------------------------------------------
UUID: 87654321-fedc-ba98-8765-4321fedcba98
Description: Unknown
Handle: 2
Properties: read, write, notify
Value (string): Hello, NimBLE!