Refactor BLE initialization and integrate NVS handling

Refactored BLE service initialization to improve functionality and organization, including added GATT service definitions and event handling. Integrated NVS initialization into the main app to ensure proper storage BLE configurations. Cleaned up deprecated code and optimized dependencies in CMakeLists.

Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
2025-05-20 22:34:52 +02:00
parent c407e3f015
commit 4e3666bcd6
4 changed files with 174 additions and 116 deletions

View File

@@ -2,5 +2,4 @@ idf_component_register(SRCS "remote_control.c"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
PRIV_REQUIRES PRIV_REQUIRES
bt bt
nvs_flash
) )

View File

@@ -1,147 +1,195 @@
#include <stdio.h> #include <stdio.h>
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_event.h"
#include "esp_log.h" #include "esp_log.h"
#include "nvs_flash.h"
// NimBLE-Header
#include "esp_nimble_hci.h" #include "esp_nimble_hci.h"
#include "nimble/nimble_port.h" #include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h" #include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h" #include "host/ble_hs.h"
#include "host/util/util.h" #include "host/ble_sm.h"
#include "services/gap/ble_svc_gap.h" #include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include "sdkconfig.h"
// Tag für Logging
static const char *TAG = "remote_control"; static const char *TAG = "remote_control";
static const char *DEVICE_NAME = "Miniature Town";
#define DEVICE_NAME "Miniature Town"
uint8_t ble_addr_type; uint8_t ble_addr_type;
/** void ble_app_advertise(void);
* Konfiguriert und startet das BLE-Advertising.
*/ // Write data to ESP32 defined as server
static void ble_app_advertise(void) static int device_write(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{ {
struct ble_gap_adv_params adv_params; // printf("Data from the client: %.*s\n", ctxt->om->om_len, ctxt->om->om_data);
struct ble_hs_adv_fields fields;
int rc;
// Fülle die Advertising-Felder char *data = (char *)ctxt->om->om_data;
memset(&fields, 0, sizeof(fields)); printf("%d\n", strcmp(data, (char *)"LIGHT ON") == 0);
if (strcmp(data, (char *)"LIGHT ON\0") == 0)
// Flags:
// - BLE_HS_ADV_F_DISC_GEN: General discoverable mode
// - BLE_HS_ADV_F_BREDR_UNSUP: BR/EDR (Classic Bluetooth) nicht unterstützt
fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
// Device Name
// Stelle sicher, dass der Name + Overhead in das Advertising-Paket passt.
// Bei Bedarf muss der Name gekürzt werden oder in Scan Response platziert werden.
fields.name = (uint8_t *)DEVICE_NAME;
fields.name_len = strlen(DEVICE_NAME);
fields.name_is_complete = 1; // Der Name ist vollständig im Advertising-Paket
// Setze die Advertising-Daten
rc = ble_gap_adv_set_fields(&fields);
if (rc != 0)
{ {
ESP_LOGE(TAG, "Fehler beim Setzen der Advertising-Daten; rc=%d", rc); printf("LIGHT ON\n");
return; }
else if (strcmp(data, (char *)"LIGHT OFF\0") == 0)
{
printf("LIGHT OFF\n");
}
else if (strcmp(data, (char *)"FAN ON\0") == 0)
{
printf("FAN ON\n");
}
else if (strcmp(data, (char *)"FAN OFF\0") == 0)
{
printf("FAN OFF\n");
}
else
{
printf("Data from the client: %.*s\n", ctxt->om->om_len, ctxt->om->om_data);
} }
// Konfiguriere die Advertising-Parameter return 0;
memset(&adv_params, 0, sizeof(adv_params));
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; // Undirected connectable
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; // General discoverable
// Starte das Advertising
// Parameter:
// - own_addr_type: Typ der eigenen Adresse (wird von ble_hs_id_infer_auto gesetzt)
// - peer_addr: NULL für undirected advertising
// - duration_ms: BLE_HS_FOREVER für kontinuierliches Advertising
// - adv_params: Die oben konfigurierten Parameter
// - cb: Callback-Funktion bei GAP-Events (z.B. Verbindung, Trennung)
// - cb_arg: Argument für die Callback-Funktion
rc = ble_gap_adv_start(ble_addr_type, NULL, BLE_HS_FOREVER, &adv_params, NULL, NULL);
if (rc != 0)
{
ESP_LOGE(TAG, "Fehler beim Starten des Advertising; rc=%d", rc);
return;
}
ESP_LOGI(TAG, "Advertising gestartet. Gerät sollte sichtbar sein als '%s'", DEVICE_NAME);
} }
/** // Read data from ESP32 defined as server
* Callback-Funktion, die aufgerufen wird, wenn der BLE-Host-Stack synchronisiert (bereit) ist. static int device_read(uint16_t con_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
*/
static void ble_app_on_sync(void)
{ {
int rc; char *data = "Data from the server";
os_mbuf_append(ctxt->om, data, strlen(data));
// Setze eine zufällige statische Adresse oder eine öffentliche Adresse return 0;
// Hier verwenden wir eine zufällige statische Adresse als Beispiel
rc = ble_hs_id_infer_auto(0, &ble_addr_type);
if (rc != 0)
{
ESP_LOGE(TAG, "Fehler beim Bestimmen der Adresse; rc=%d", rc);
return;
} }
// Die Adresse wurde gesetzt, starte Advertising static int device_name_read(uint16_t onn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{
os_mbuf_append(ctxt->om, DEVICE_NAME, strlen(DEVICE_NAME));
return 0;
}
static int model_number_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{
char *model_number = "Model Number";
os_mbuf_append(ctxt->om, model_number, strlen(model_number));
return 0;
}
static int manufacturer_read(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg)
{
char *manufacturer = "mars3142";
os_mbuf_append(ctxt->om, manufacturer, strlen(manufacturer));
return 0;
}
// Array of pointers to other service definitions
static const struct ble_gatt_svc_def gatt_svcs[] = {
{
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(0x180A),
.characteristics = (struct ble_gatt_chr_def[]){
{.uuid = BLE_UUID16_DECLARE(0x2A00), .flags = BLE_GATT_CHR_F_READ, .access_cb = device_name_read},
{.uuid = BLE_UUID16_DECLARE(0x2A24), .flags = BLE_GATT_CHR_F_READ, .access_cb = model_number_read},
{.uuid = BLE_UUID16_DECLARE(0x2A29), .flags = BLE_GATT_CHR_F_READ, .access_cb = manufacturer_read},
{0}},
},
{.type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = BLE_UUID16_DECLARE(0x180), .characteristics = (struct ble_gatt_chr_def[]){{.uuid = BLE_UUID16_DECLARE(0xFEF4), .flags = BLE_GATT_CHR_F_READ, .access_cb = device_read}, {.uuid = BLE_UUID16_DECLARE(0xDEAD), .flags = BLE_GATT_CHR_F_WRITE, .access_cb = device_write}, {0}}},
{0}};
// BLE event handling
static int ble_gap_event(struct ble_gap_event *event, void *arg)
{
switch (event->type)
{
case BLE_GAP_EVENT_CONNECT:
ESP_LOGI(TAG, "BLE GAP EVENT CONNECT %s", event->connect.status == 0 ? "OK!" : "FAILED!");
if (event->connect.status == 0)
{
// Start security pairing without disconnecting
int ret = ble_gap_security_initiate(event->connect.conn_handle);
ESP_LOGI(TAG, "BLE GAP SECURITY INITIATE %s", ret == 0 ? "OK!" : "FAILED!");
}
else
{
// Re-advertise if connection failed
ble_app_advertise(); ble_app_advertise();
} }
break;
/** case BLE_GAP_EVENT_DISCONNECT:
* Host-Task für NimBLE. ESP_LOGI(TAG, "BLE GAP EVENT DISCONNECTED");
*/ // Re-advertise after disconnection
void ble_host_task(void *param) ble_app_advertise();
break;
case BLE_GAP_EVENT_ADV_COMPLETE:
ESP_LOGI(TAG, "BLE GAP EVENT ADV COMPLETE");
// Re-advertise to continue accepting new clients
ble_app_advertise();
break;
case BLE_GAP_EVENT_ENC_CHANGE:
if (event->enc_change.status == 0)
{ {
ESP_LOGI(TAG, "BLE Host Task gestartet"); ESP_LOGI(TAG, "Encryption enabled for connection");
// Dieser Aufruf blockiert, bis nimble_port_stop() aufgerufen wird }
nimble_port_run(); else
{
ESP_LOGE(TAG, "Failed to enable encryption, status=%d", event->enc_change.status);
}
break;
nimble_port_freertos_deinit(); default:
break;
}
return 0;
}
// Define the BLE connection
void ble_app_advertise(void)
{
// GAP - device name definition
struct ble_hs_adv_fields fields;
const char *device_name;
memset(&fields, 0, sizeof(fields));
device_name = ble_svc_gap_device_name(); // Read the BLE device name
fields.name = (uint8_t *)device_name;
fields.name_len = strlen(device_name);
fields.name_is_complete = 1;
ble_gap_adv_set_fields(&fields);
// GAP - device connectivity definition
struct ble_gap_adv_params adv_params;
memset(&adv_params, 0, sizeof(adv_params));
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; // connectable or non-connectable
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; // discoverable or non-discoverable
ble_gap_adv_start(ble_addr_type, NULL, BLE_HS_FOREVER, &adv_params, ble_gap_event, NULL);
}
// The application
void ble_app_on_sync(void)
{
ble_hs_id_infer_auto(0, &ble_addr_type); // Determines the best address type automatically
ble_app_advertise(); // Define the BLE connection
}
// The infinite task
void host_task(void *param)
{
nimble_port_run(); // This function will return only when nimble_port_stop() is executed
} }
void ble_init(void *args) void ble_init(void *args)
{ {
int rc; nimble_port_init(); // 3 - Initialize the host stack
ble_svc_gap_device_name_set(DEVICE_NAME); // 4 - Initialize NimBLE configuration - server name
ble_svc_gap_init(); // 4 - Initialize NimBLE configuration - gap service
ble_svc_gatt_init(); // 4 - Initialize NimBLE configuration - gatt service
ble_gatts_count_cfg(gatt_svcs); // 4 - Initialize NimBLE configuration - config gatt services
ble_gatts_add_svcs(gatt_svcs); // 4 - Initialize NimBLE configuration - queues gatt services.
ble_hs_cfg.sync_cb = ble_app_on_sync; // 5 - Initialize application
// Initialisiere NVS (Non-Volatile Storage) // Configure security settings
// Dies ist für BLE erforderlich, um Controller-Konfigurationen zu speichern ble_hs_cfg.sm_bonding = 1; // Enable bonding
esp_err_t ret = nvs_flash_init(); ble_hs_cfg.sm_sc = 0; // Enable Secure Connections (LE SC)
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ENC; // Encryption key distribution
{ ble_hs_cfg.sm_their_key_dist = BLE_SM_PAIR_KEY_DIST_ENC;
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init(); nimble_port_freertos_init(host_task); // Run the host task
}
ESP_ERROR_CHECK(ret);
// Initialisiere den NimBLE-Port (FreeRTOS spezifisch)
nimble_port_init();
// Konfiguriere den BLE-Host-Stack:
// - Setze den Callback für "on_sync" (wenn der Stack bereit ist)
// - Setze den Callback für "on_reset" (wenn der Controller zurückgesetzt wird)
ble_hs_cfg.sync_cb = ble_app_on_sync;
ble_hs_cfg.reset_cb = NULL; // Hier nicht explizit benötigt für einfaches Advertising
// Optional: Setze den Gerätenamen direkt im GAP Service.
// Dies ist eine andere Stelle als das Advertising-Paket, aber gute Praxis.
rc = ble_svc_gap_device_name_set(DEVICE_NAME);
assert(rc == 0);
// Initialisiere das NimBLE HCI Layer
// ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init()); // Für ESP32, ESP32-C3, ESP32-S3 (kombinierter Host & Controller)
// Für ESP32-C2, ESP32-C6, ESP32-H2, die VHCI verwenden, wird eine andere Initialisierung benötigt.
// Aber für typische ESP32 ist dies korrekt.
// Starte den NimBLE Host Task in einem eigenen Thread.
// Die Priorität und Stack-Größe können angepasst werden.
// NIMBLE_STACK_SIZE ist in nimble_port_freertos.h definiert
nimble_port_freertos_init(ble_host_task);
ESP_LOGI(TAG, "BLE App Main abgeschlossen. BLE Host Task läuft.");
} }

View File

@@ -3,4 +3,5 @@ idf_component_register(SRCS "main.c"
PRIV_REQUIRES PRIV_REQUIRES
led_matrix led_matrix
remote_control remote_control
nvs_flash
) )

View File

@@ -3,9 +3,19 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "nvs_flash.h"
void app_main(void) void app_main(void)
{ {
// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
xTaskCreatePinnedToCore(led_matrix_init, "led_matrix", configMINIMAL_STACK_SIZE * 2, NULL, 5, NULL, 1); xTaskCreatePinnedToCore(led_matrix_init, "led_matrix", configMINIMAL_STACK_SIZE * 2, NULL, 5, NULL, 1);
ble_init(NULL); ble_init(NULL);
} }