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 Sie einen String schreiben können. Wenn der String RESTART ist, wird der ESP32 neu starten.

Beachten Sie, 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

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";

// 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 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;

// Command constants
#define RESTART_COMMAND "RESTART"

// GAP event listener structure
static struct ble_gap_event_listener gap_event_listener;

// GATT characteristic access function for commands
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);
                    // Process the command
                    if (strcmp(buffer, RESTART_COMMAND) == 0) {
                        ESP_LOGI(TAG, "Restart command received - restarting ESP32...");
                        // Add a small delay to allow the BLE response to be sent
                        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, // descriptors
                BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE,
                0, // min_key_size
                &command_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 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() {
    // 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);
}

Testen

Verwenden Sie unser Skript aus Python-Skript zum Schreiben von BLE-Charakteristiken mit Bleak, um den Befehl RESTART auf die Charakteristik zu schreiben.

Beachten Sie, dass der ESP32 sofort neu starten wird, der Schreibvorgang wird fehlschlagen, aber der Befehl wird trotzdem ausgeführt.

ble_write_test.sh
python write_ble_characteristic.py 24:EC:4A:76:00:32 87654321-fedc-ba98-8765-4321fedcba98 RESTART

Beispielausgabe

ble_write_test_output.txt
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.

Check out similar posts by category: Bluetooth, ESP32, ESP-IDF