Minimales ESP32 NimBLE-Beispiel für einen benutzerdefinierten BLE-Service
In unserem vorherigen Post ESP32 minimales BLE Device Information Service (DIS) Beispiel haben wir gezeigt, wie man den vordefinierten DIS (Device Information Service) mit dem espressif/ble_services-Paket unter Verwendung von NimBLE als Backend verwendet.
Darauf aufbauend, hier ist, wie man einen komplett benutzerdefinierten BLE-Service erstellt:
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);
}Integration mit InitBLE()
Dies integriert sich gut mit BLE.cpp aus unserem vorherigen Beispiel ESP32 minimales BLE Device Information Service (DIS) Beispiel. Hier ist die InitBLE()-Funktion, die sowohl den benutzerdefinierten Service als auch den Device Information Service initialisiert.
Das Einzige, was Sie hier hinzufügen müssen, ist
SetCustomBLEGapHandler();
InitCustomBLE(); // Initialize custom BLE service
nach esp_nimble_init() und vor dem Starten des BLE-Hosts über esp_ble_conn_start().
Vollständige InitBLE()-Funktion:
#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);
}
}Testen
Verwenden Sie unser Skript aus Minimales Python-Skript zum Auflisten & Lesen von BLE-Geräte-Charakteristiken mit Python (Bleak):
Partielle Beispielausgabe
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!