ESP32 NimBLE : Exemple minimal d'exécution de commandes personnalisées via BLE
Dans notre exemple précédent Minimal ESP32 NimBLE example of executing custom commands via BLE nous avons montré comment créer un service et une caractéristique BLE personnalisés en utilisant NimBLE sur l’ESP32. Cet exemple s’appuie sur le précédent en démontrant comment exécuter des commandes personnalisées via BLE.
Comment fonctionnent les commandes personnalisées
Le BLE ne connaît pas vraiment les commandes en tant que telles, il s’agit donc simplement d’une caractéristique personnalisée dans laquelle on peut écrire.
Dans cet exemple, nous utiliserons une seule caractéristique dans laquelle vous pouvez écrire une chaîne de caractères. Si la chaîne est RESTART, l’ESP32 redémarrera.
Notez qu’en dehors de CustomBLE.cpp (avec seulement quelques modifications par rapport à Minimal ESP32 NimBLE example of executing custom commands via BLE), tout le code est identique à l’exemple précédent et le code dupliqué est omis ici.
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";
// UUID du service et de la caractéristique personnalisés (UUID 128-bit générés aléatoirement)
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;
// Constantes de commande
#define RESTART_COMMAND "RESTART"
// Structure d'écouteur d'événements GAP
static struct ble_gap_event_listener gap_event_listener;
// Fonction d'accès à la caractéristique GATT pour les commandes
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, "Lecture de la caractéristique de commande - retour du statut");
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, "Écriture reçue sur la caractéristique de commande");
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, "Commande reçue : %s", buffer);
// Traiter la commande
if (strcmp(buffer, RESTART_COMMAND) == 0) {
ESP_LOGI(TAG, "Commande de redémarrage reçue - redémarrage de l'ESP32...");
// Ajouter un court délai pour permettre l'envoi de la réponse BLE
vTaskDelay(pdMS_TO_TICKS(100));
esp_restart();
} else {
ESP_LOGW(TAG, "Commande inconnue : %s", buffer);
}
}
}
return 0;
}
default: {
return BLE_ATT_ERR_UNLIKELY;
}
}
}
// Définition du service GATT
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 - fin des caractéristiques
}
},
},
{
0, // type - fin des services
}
};
// Gestionnaire d'événements GAP
static int custom_gap_event(struct ble_gap_event *event, void *arg) {
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
ESP_LOGI(TAG, "Connexion %s; statut=%d",
event->connect.status == 0 ? "établie" : "échouée",
event->connect.status);
if (event->connect.status == 0) {
conn_handle = event->connect.conn_handle;
}
break;
case BLE_GAP_EVENT_DISCONNECT:
ESP_LOGI(TAG, "Déconnexion; raison=%d", event->disconnect.reason);
conn_handle = BLE_HS_CONN_HANDLE_NONE;
break;
default:
break;
}
return 0;
}
void InitCustomBLE() {
int rc;
// Initialiser les services GATT
rc = ble_gatts_count_cfg(custom_gatt_svcs);
if (rc != 0) {
ESP_LOGE(TAG, "Échec du comptage des services GATT : %d", rc);
return;
}
rc = ble_gatts_add_svcs(custom_gatt_svcs);
if (rc != 0) {
ESP_LOGE(TAG, "Échec de l'ajout des services GATT : %d", rc);
return;
}
ESP_LOGI(TAG, "Service de commande NimBLE personnalisé initialisé");
}
void ExecuteBLECommand(const std::string& command) {
ESP_LOGI(TAG, "Exécution de la commande BLE : %s", command.c_str());
if (command == RESTART_COMMAND) {
ESP_LOGI(TAG, "Commande de redémarrage exécutée - redémarrage de l'ESP32...");
vTaskDelay(pdMS_TO_TICKS(100));
esp_restart();
} else {
ESP_LOGW(TAG, "Commande inconnue : %s", command.c_str());
}
}
std::string GetCommandStatus() {
return "Command interface ready. Supported commands: " RESTART_COMMAND;
}
void SetCustomBLEGapHandler() {
// Configurer l'écouteur d'événements GAP
gap_event_listener.fn = custom_gap_event;
gap_event_listener.arg = NULL;
ble_gap_event_listener_register(&gap_event_listener, custom_gap_event, NULL);
}Comment tester
Utilisez notre script depuis Python script to write BLE characteristics using Bleak pour écrire la commande RESTART dans la caractéristique.
Notez que puisque l’ESP32 redémarre immédiatement, l’écriture échouera, mais la commande sera tout de même exécutée.
python write_ble_characteristic.py 24:EC:4A:76:00:32 87654321-fedc-ba98-8765-4321fedcba98 RESTARTExemple de sortie
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.