Minimales ESP32 NimBLE-Beispiel für die Ausführung benutzerdefinierter Befehle über BLE
In unserem vorherigen Beispiel Minimales ESP32 NimBLE-Beispiel für die Ausführung benutzerdefinierter Befehle über BLE haben wir gezeigt, wie man einen benutzerdefinierten BLE-Service und eine Charakteristik mit NimBLE auf dem ESP32 erstellt. Dieses Beispiel baut darauf auf und zeigt, wie man benutzerdefinierte Befehle über BLE ausführt.
Wie benutzerdefinierte Befehle funktionieren
BLE kennt eigentlich keine Befehle als solche, also ist es einfach eine benutzerdefinierte Charakteristik, auf die wir schreiben können.
In diesem Beispiel werden wir eine einzige Charakteristik verwenden, auf die du einen String schreiben kannst. Wenn der String RESTART ist, wird der ESP32 neu starten.
Beachte, dass außer der CustomBLE.cpp (mit nur wenigen Änderungen gegenüber Minimales ESP32 NimBLE-Beispiel für die Ausführung benutzerdefinierter Befehle über BLE) der gesamte Code derselbe wie im vorherigen Beispiel ist und doppelter Code hier weggelassen wird.
CustomBLE.cpp
#include "CustomBLE.hpp"
#include "esp_log.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.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";
// Benutzerdefinierte Service- und Charakteristik-UUIDs (zufällig generierte 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 command_char_uuid =
BLE_UUID128_INIT(0x98, 0xBA, 0xDC, 0xFE, 0x21, 0x43, 0x65, 0x87,
0x98, 0xBA, 0xDC, 0xFE, 0x21, 0x43, 0x65, 0x87);
static uint16_t command_char_handle;
static uint16_t conn_handle = BLE_HS_CONN_HANDLE_NONE;
// Befehlskonstanten
#define RESTART_COMMAND "RESTART"
// GAP Event-Listener-Struktur
static struct ble_gap_event_listener gap_event_listener;
// GATT Charakteristik-Zugriffsfunktion für Befehle
static int command_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, "Command characteristic read - returning status");
const char* status_msg = "Command interface ready";
rc = os_mbuf_append(ctxt->om, status_msg, strlen(status_msg));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
case BLE_GATT_ACCESS_OP_WRITE_CHR: {
ESP_LOGI(TAG, "Command characteristic write received");
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';
ESP_LOGI(TAG, "Received command: %s", buffer);
// Befehl verarbeiten
if (strcmp(buffer, RESTART_COMMAND) == 0) {
ESP_LOGI(TAG, "Restart command received - restarting ESP32...");
// Kleine Verzögerung hinzufügen, damit die BLE-Antwort gesendet werden kann
vTaskDelay(pdMS_TO_TICKS(100));
esp_restart();
} else {
ESP_LOGW(TAG, "Unknown command: %s", buffer);
}
}
}
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[]) {
{
&command_char_uuid.u,
command_char_access,
NULL, // arg
NULL, // Deskriptoren
BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
0, // min_key_size
&command_char_handle,
NULL, // cpfd
},
{
NULL, // uuid - Ende der Charakteristiken
}
},
},
{
0, // type - Ende der 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;
// GATT Services initialisieren
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 command service initialized");
}
void ExecuteBLECommand(const std::string& command) {
ESP_LOGI(TAG, "Executing BLE command: %s", command.c_str());
if (command == RESTART_COMMAND) {
ESP_LOGI(TAG, "Restart command executed - restarting ESP32...");
vTaskDelay(pdMS_TO_TICKS(100));
esp_restart();
} else {
ESP_LOGW(TAG, "Unknown command: %s", command.c_str());
}
}
std::string GetCommandStatus() {
return "Command interface ready. Supported commands: " RESTART_COMMAND;
}
void SetCustomBLEGapHandler() {
// GAP Event-Listener einrichten
gap_event_listener.fn = custom_gap_event;
gap_event_listener.arg = NULL;
ble_gap_event_listener_register(&gap_event_listener, custom_gap_event, NULL);
}Testen
Verwende unser Skript aus Python-Skript zum Schreiben von BLE-Charakteristiken mit Bleak, um den Befehl RESTART auf die Charakteristik zu schreiben.
Beachte, dass der ESP32 sofort neu starten wird, der Schreibvorgang wird fehlschlagen, aber der Befehl wird trotzdem ausgeführt.
python write_ble_characteristic.py 24:EC:4A:76:00:32 87654321-fedc-ba98-8765-4321fedcba98 RESTARTBeispielausgabe
Using MAC address: 24:EC:4A:76:00:32
Characteristic UUID: 87654321-fedc-ba98-8765-4321fedcba98
Value: RESTART (utf-8)
Attempting to connect to 24:EC:4A:76:00:32 ...
Successfully connected to 24:EC:4A:76:00:32
Connected at: 2025-08-04 03:07:02
Writing to characteristic 87654321-fedc-ba98-8765-4321fedcba98 ...
Failed to write value: [org.bluez.Error.Failed] Operation failed with ATT error: 0x0e (Unlikely Error)
Write operation failed.