From 8a2c0a60d50b12348b4f0ecd264e628a54106729 Mon Sep 17 00:00:00 2001 From: Peter Siegmund Date: Sat, 20 Sep 2025 20:26:23 +0200 Subject: [PATCH] add BLE bonding Signed-off-by: Peter Siegmund --- firmware/components/light/light.c | 6 +- firmware/components/light/outdoor.c | 32 +- .../persistence/include/persistence.h | 1 + firmware/components/persistence/persistence.c | 154 +++++++++ .../remote_control/include/remote_control.h | 12 + .../remote_control/include/uart_service.h | 3 +- .../components/remote_control/light_service.c | 13 +- .../remote_control/remote_control.c | 295 ++++++++++++++++-- .../components/remote_control/uart_service.c | 37 ++- firmware/main/Kconfig.projbuild | 25 ++ firmware/main/main.c | 8 - firmware/partitions.csv | 7 +- firmware/sdkconfig.defaults | 4 +- 13 files changed, 524 insertions(+), 73 deletions(-) diff --git a/firmware/components/light/light.c b/firmware/components/light/light.c index e6098ec..066a46e 100644 --- a/firmware/components/light/light.c +++ b/firmware/components/light/light.c @@ -14,7 +14,11 @@ esp_err_t wled_init(void) led_strip_config_t strip_config = {.strip_gpio_num = CONFIG_WLED_DIN_PIN, .max_leds = led_matrix.size, .led_model = LED_MODEL_WS2812, - .color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, +#if CONFIG_WLED_WITH_WHITE + .color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRBW, +#else + .color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_RGB, +#endif .flags = { .invert_out = false, }}; diff --git a/firmware/components/light/outdoor.c b/firmware/components/light/outdoor.c index 88bf408..18e00f3 100644 --- a/firmware/components/light/outdoor.c +++ b/firmware/components/light/outdoor.c @@ -7,26 +7,21 @@ static const char *TAG = "outdoor"; -// Timer resolution (10 bit = 1024 steps) -#define LEDC_RESOLUTION LEDC_TIMER_10_BIT +#define LEDC_RESOLUTION LEDC_TIMER_10_BIT // Timer resolution (10 bit = 1024 steps) #define MAX_DUTY 1023 -// Constant brightness for the "normal state" (approx. 90%) -#define NORMAL_DUTY (MAX_DUTY * 0.9) +#define NORMAL_DUTY (MAX_DUTY * 0.9) // 90% brightness -// Parameters for flickering -#define FLICKER_CHANCE 5 // 5% chance of flickering per cycle +#define FLICKER_CHANCE 2 // 2% chance of flickering per cycle #define FLICKER_COUNT 8 // Number of brightness changes during a flicker TaskHandle_t outdoor_task_handle = NULL; void outdoor_task(void *pvParameters) { - ESP_LOGI(TAG, "Simulation of a defective light bulb started."); int led_pin = *(int *)pvParameters; - // 1. LEDC timer configuration ledc_timer_config_t ledc_timer = {.speed_mode = LEDC_LOW_SPEED_MODE, .timer_num = LEDC_TIMER_0, .duty_resolution = LEDC_RESOLUTION, @@ -34,7 +29,6 @@ void outdoor_task(void *pvParameters) .clk_cfg = LEDC_AUTO_CLK}; ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); - // 2. LEDC channel configuration ledc_channel_config_t ledc_channel = {.speed_mode = LEDC_LOW_SPEED_MODE, .channel = LEDC_CHANNEL_0, .timer_sel = LEDC_TIMER_0, @@ -44,40 +38,38 @@ void outdoor_task(void *pvParameters) .hpoint = 0}; ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); - // 3. Main loop with flicker logic while (1) { - // First, set the LED to its normal brightness ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, NORMAL_DUTY); ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0); - // Random trigger for flickering - uint32_t random_val = esp_random() % 100; // Random number between 0 and 99 + uint32_t random_val = esp_random() % 100; if (random_val < FLICKER_CHANCE) { - // Start flicker sequence for (int i = 0; i < FLICKER_COUNT; i++) { - // Set a random, lower brightness (e.g., 30-70% of normal brightness) uint32_t flicker_duty = (NORMAL_DUTY * 0.3) + (esp_random() % (uint32_t)(NORMAL_DUTY * 0.4)); ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, flicker_duty); ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0); - // Short, random delay for irregular flickering vTaskDelay(pdMS_TO_TICKS(20 + (esp_random() % 50))); } } - // A fixed delay in normal operation vTaskDelay(pdMS_TO_TICKS(100)); } } esp_err_t outdoor_start(void) { - static const int led_pin = 13; - xTaskCreate(outdoor_task, "outdoor_task", 2048, (void *)&led_pin, 5, &outdoor_task_handle); + ESP_LOGI(TAG, "Simulation of a defective light bulb started."); + + static const int led_left_pin = CONFIG_LED_PIN_LEFT; + xTaskCreate(outdoor_task, "outdoor_task_left", 2048, (void *)&led_left_pin, 5, &outdoor_task_handle); + + static const int led_right_pin = CONFIG_LED_PIN_RIGHT; + xTaskCreate(outdoor_task, "outdoor_task_right", 2048, (void *)&led_right_pin, 5, &outdoor_task_handle); return ESP_OK; } @@ -93,4 +85,4 @@ esp_err_t outdoor_stop(void) outdoor_task_handle = NULL; return ESP_OK; -} \ No newline at end of file +} diff --git a/firmware/components/persistence/include/persistence.h b/firmware/components/persistence/include/persistence.h index ed5ad6d..a6fbaab 100644 --- a/firmware/components/persistence/include/persistence.h +++ b/firmware/components/persistence/include/persistence.h @@ -3,6 +3,7 @@ typedef enum { VALUE_TYPE_STRING, + VALUE_TYPE_INT8, VALUE_TYPE_INT32, } persistence_value_type_t; diff --git a/firmware/components/persistence/persistence.c b/firmware/components/persistence/persistence.c index 69ed527..defcfd6 100644 --- a/firmware/components/persistence/persistence.c +++ b/firmware/components/persistence/persistence.c @@ -10,6 +10,149 @@ static const char *TAG = "persistence"; static nvs_handle_t persistence_handle; static SemaphoreHandle_t persistence_mutex; +#include "esp_log.h" +#include "nvs.h" +#include "nvs_flash.h" + +static const char *nvs_type_to_str(nvs_type_t type) +{ + switch (type) + { + case NVS_TYPE_U8: + return "U8"; + case NVS_TYPE_I8: + return "I8"; + case NVS_TYPE_U16: + return "U16"; + case NVS_TYPE_I16: + return "I16"; + case NVS_TYPE_U32: + return "U32"; + case NVS_TYPE_I32: + return "I32"; + case NVS_TYPE_U64: + return "U64"; + case NVS_TYPE_I64: + return "I64"; + case NVS_TYPE_STR: + return "STR"; + case NVS_TYPE_BLOB: + return "BLOB"; + case NVS_TYPE_ANY: + return "ANY"; + default: + return "UNKNOWN"; + } +} + +void display_nvs_value(const char *namespace_name, const char *key, nvs_type_t type) +{ + nvs_handle_t handle; + esp_err_t err = nvs_open(namespace_name, NVS_READONLY, &handle); + if (err != ESP_OK) + { + return; + } + + switch (type) + { + case NVS_TYPE_I8: { + int8_t value; + if (nvs_get_i8(handle, key, &value) == ESP_OK) + { + ESP_LOGI(TAG, " -> Value (I8): %d", value); + } + break; + } + case NVS_TYPE_I32: { + int32_t value; + if (nvs_get_i32(handle, key, &value) == ESP_OK) + { + ESP_LOGI(TAG, " -> Value (I32): %d", value); + } + break; + } + case NVS_TYPE_STR: { + size_t length = 0; + nvs_get_str(handle, key, NULL, &length); + if (length > 0) + { + char *value = malloc(length); + if (nvs_get_str(handle, key, value, &length) == ESP_OK) + { + ESP_LOGI(TAG, " -> Value (STR): %s", value); + } + free(value); + } + break; + } + case NVS_TYPE_BLOB: { + size_t length = 0; + nvs_get_blob(handle, key, NULL, &length); + if (length > 0) + { + ESP_LOGI(TAG, " -> Value (BLOB): %d bytes", length); + + // Optional: Erste Bytes als Hex anzeigen + uint8_t *blob = malloc(length); + if (nvs_get_blob(handle, key, blob, &length) == ESP_OK) + { + ESP_LOG_BUFFER_HEX_LEVEL(TAG, blob, MIN(length, 32), ESP_LOG_INFO); + } + free(blob); + } + break; + } + default: + break; + } + + nvs_close(handle); +} + +static void list_all_nvs_entries(void) +{ + ESP_LOGI(TAG, "========== NVS ENTRIES =========="); + + nvs_iterator_t it = NULL; + esp_err_t err; + + // Iterator für alle Namespaces und Keys erstellen + err = nvs_entry_find(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_ANY, &it); + + while (err == ESP_OK) + { + nvs_entry_info_t info; + nvs_entry_info(it, &info); + + ESP_LOGI(TAG, "Namespace: %-16s | Key: %-16s | Type: %s", info.namespace_name, info.key, + nvs_type_to_str(info.type)); + + // Optional: Wert anzeigen + display_nvs_value(info.namespace_name, info.key, info.type); + + err = nvs_entry_next(&it); + } + + nvs_release_iterator(it); + ESP_LOGI(TAG, "=================================="); +} + +static void check_nvs_stats(void) +{ + nvs_stats_t nvs_stats; + esp_err_t err = nvs_get_stats(NULL, &nvs_stats); + if (err == ESP_OK) + { + ESP_LOGI(TAG, "NVS: Used entries = %d, Free entries = %d, Total entries = %d", nvs_stats.used_entries, + nvs_stats.free_entries, nvs_stats.total_entries); + + size_t used_kb = (nvs_stats.used_entries * 32) / 1024; // Grobe Schätzung + size_t free_kb = (nvs_stats.free_entries * 32) / 1024; + ESP_LOGI(TAG, "NVS: ~%d KB used, ~%d KB free", used_kb, free_kb); + } +} + void persistence_init(const char *namespace_name) { esp_err_t ret = nvs_flash_init(); @@ -20,6 +163,9 @@ void persistence_init(const char *namespace_name) } ESP_ERROR_CHECK(ret); + list_all_nvs_entries(); + check_nvs_stats(); + ESP_ERROR_CHECK(nvs_open(namespace_name, NVS_READWRITE, &persistence_handle)); persistence_mutex = xSemaphoreCreateMutex(); @@ -43,6 +189,10 @@ void persistence_save(persistence_value_type_t value_type, const char *key, cons err = nvs_set_str(persistence_handle, key, (char *)value); break; + case VALUE_TYPE_INT8: + err = nvs_set_i8(persistence_handle, key, *(int8_t *)value); + break; + case VALUE_TYPE_INT32: err = nvs_set_i32(persistence_handle, key, *(int32_t *)value); break; @@ -80,6 +230,10 @@ void *persistence_load(persistence_value_type_t value_type, const char *key, voi err = nvs_get_str(persistence_handle, key, (char *)out, NULL); break; + case VALUE_TYPE_INT8: + err = nvs_get_i8(persistence_handle, key, (int8_t *)out); + break; + case VALUE_TYPE_INT32: err = nvs_get_i32(persistence_handle, key, (int32_t *)out); break; diff --git a/firmware/components/remote_control/include/remote_control.h b/firmware/components/remote_control/include/remote_control.h index ac994c8..af85e96 100644 --- a/firmware/components/remote_control/include/remote_control.h +++ b/firmware/components/remote_control/include/remote_control.h @@ -1,3 +1,15 @@ #pragma once +#include "host/ble_hs.h" +#include "sdkconfig.h" + +typedef struct +{ + uint16_t conn_handle; + bool is_connected; +} ble_connection_t; + +extern ble_connection_t g_connections[CONFIG_BT_NIMBLE_MAX_CONNECTIONS]; + void remote_control_init(void); +bool is_any_device_connected(void); diff --git a/firmware/components/remote_control/include/uart_service.h b/firmware/components/remote_control/include/uart_service.h index 6f30150..d4c694e 100644 --- a/firmware/components/remote_control/include/uart_service.h +++ b/firmware/components/remote_control/include/uart_service.h @@ -14,8 +14,7 @@ extern const ble_uuid128_t gatt_svr_chr_uart_rx_uuid; // TX Characteristic UUID: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E extern const ble_uuid128_t gatt_svr_chr_uart_tx_uuid; -extern uint16_t conn_handle; -extern uint16_t tx_chr_val_handle; +extern uint16_t tx_chr_val_handle; // This is still needed as it's set once by the stack int gatt_svr_chr_uart_access(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); void send_ble_data(const char *data); diff --git a/firmware/components/remote_control/light_service.c b/firmware/components/remote_control/light_service.c index 9156760..9f43738 100644 --- a/firmware/components/remote_control/light_service.c +++ b/firmware/components/remote_control/light_service.c @@ -3,6 +3,7 @@ #include "persistence.h" static uint8_t g_beacon_enabled = 0; +static int8_t g_led_value = 0; /// Characteristic Callbacks int gatt_svr_chr_light_led_access(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, @@ -14,13 +15,18 @@ int gatt_svr_chr_light_led_access(uint16_t conn_handle, uint16_t attr_handle, st os_mbuf_append(ctxt->om, data, strlen(data)); return 0; } + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) + { + int8_t led_value = 0; + persistence_load(VALUE_TYPE_INT8, "LED_VALUE", &led_value); + } return BLE_ATT_ERR_UNLIKELY; } int gatt_svr_chr_light_beacon_access(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { - persistence_load(VALUE_TYPE_INT32, "BEACON_ENABLED", &g_beacon_enabled); + if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { return os_mbuf_append(ctxt->om, &g_beacon_enabled, sizeof(g_beacon_enabled)) == 0 @@ -29,6 +35,9 @@ int gatt_svr_chr_light_beacon_access(uint16_t conn_handle, uint16_t attr_handle, } if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + int8_t beacon_enabled = 0; + persistence_load(VALUE_TYPE_INT8, "BEACON_ENABLED", &beacon_enabled); + // it has to be 1 Byte (0 or 1) if (OS_MBUF_PKTLEN(ctxt->om) != 1) { @@ -56,7 +65,7 @@ int gatt_svr_chr_light_beacon_access(uint16_t conn_handle, uint16_t attr_handle, { beacon_stop(); } - persistence_save(VALUE_TYPE_INT32, "BEACON_ENABLED", &g_beacon_enabled); + persistence_save(VALUE_TYPE_INT8, "BEACON_ENABLED", &g_beacon_enabled); return 0; } return BLE_ATT_ERR_UNLIKELY; diff --git a/firmware/components/remote_control/remote_control.c b/firmware/components/remote_control/remote_control.c index a277952..3d4dbca 100644 --- a/firmware/components/remote_control/remote_control.c +++ b/firmware/components/remote_control/remote_control.c @@ -5,6 +5,7 @@ #include "esp_event.h" #include "esp_log.h" +#include "esp_mac.h" #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" #include "freertos/task.h" @@ -21,6 +22,8 @@ #include "services/gap/ble_svc_gap.h" #include "services/gatt/ble_svc_gatt.h" +void ble_store_config_init(void); + static const char *TAG = "remote_control"; static const ble_uuid16_t gatt_svr_svc_device_uuid = BLE_UUID16_INIT(0x180A); @@ -29,6 +32,29 @@ static const ble_uuid16_t gatt_svr_svc_settings_uuid = BLE_UUID16_INIT(0xA999); uint8_t ble_addr_type; +ble_connection_t g_connections[CONFIG_BT_NIMBLE_MAX_CONNECTIONS]; + +static void init_connection_pool() +{ + for (int i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) + { + g_connections[i].conn_handle = BLE_HS_CONN_HANDLE_NONE; + g_connections[i].is_connected = false; + } +} + +bool is_any_device_connected(void) +{ + for (int i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) + { + if (g_connections[i].is_connected) + { + return true; + } + } + return false; +} + static void ble_app_advertise(void); // Descriptors for the Beacon Characteristic @@ -121,14 +147,16 @@ static const struct ble_gatt_svc_def gatt_svcs[] = { { // Beacon Characteristic .uuid = BLE_UUID16_DECLARE(0xBEA0), - .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + .flags = + BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC, .access_cb = gatt_svr_chr_light_beacon_access, .descriptors = beacon_char_desc, }, { // LED Characteristic .uuid = BLE_UUID16_DECLARE(0xF037), - .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + .flags = + BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC, .access_cb = gatt_svr_chr_light_led_access, .descriptors = led_char_desc, }, @@ -165,23 +193,174 @@ static const struct ble_gatt_svc_def gatt_svcs[] = { }, {0}}; +inline static void format_addr(char *addr_str, uint8_t addr[]) +{ + sprintf(addr_str, "%02X:%02X:%02X:%02X:%02X:%02X", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); +} + +static void print_conn_desc(struct ble_gap_conn_desc *desc) +{ + /* Local variables */ + char addr_str[18] = {0}; + + /* Connection handle */ + ESP_LOGI(TAG, "connection handle: %d", desc->conn_handle); + + /* Local ID address */ + format_addr(addr_str, desc->our_id_addr.val); + ESP_LOGI(TAG, "device id address: type=%d, value=%s", desc->our_id_addr.type, addr_str); + + /* Peer ID address */ + format_addr(addr_str, desc->peer_id_addr.val); + ESP_LOGI(TAG, "peer id address: type=%d, value=%s", desc->peer_id_addr.type, addr_str); + + /* Connection info */ + ESP_LOGI(TAG, + "conn_itvl=%d, conn_latency=%d, supervision_timeout=%d, " + "encrypted=%d, authenticated=%d, bonded=%d\n", + desc->conn_itvl, desc->conn_latency, desc->supervision_timeout, desc->sec_state.encrypted, + desc->sec_state.authenticated, desc->sec_state.bonded); +} + // BLE event handling static int ble_gap_event(struct ble_gap_event *event, void *arg) { + esp_err_t rc; + struct ble_gap_conn_desc desc; + switch (event->type) { case BLE_GAP_EVENT_CONNECT: - ESP_LOGI(TAG, "Connection established; status=%d", event->connect.status); - conn_handle = event->connect.conn_handle; - ESP_LOGI(TAG, "Connection handle: %d", conn_handle); - break; + /* A new connection was established or a connection attempt failed. */ + ESP_LOGI(TAG, "connection %s; status=%d", event->connect.status == 0 ? "established" : "failed", + event->connect.status); + + /* Connection succeeded */ + if (event->connect.status == 0) + { + bool found_slot = false; + for (int i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) + { + if (!g_connections[i].is_connected) + { + g_connections[i].conn_handle = event->connect.conn_handle; + g_connections[i].is_connected = true; + found_slot = true; + ESP_LOGI(TAG, "Connection stored in slot %d", i); + break; + } + } + if (!found_slot) + { + ESP_LOGW(TAG, "No free connection slot available!"); + } + + /* Check connection handle */ + rc = ble_gap_conn_find(event->connect.conn_handle, &desc); + if (rc != 0) + { + } + + print_conn_desc(&desc); + + /* Try to update connection parameters */ + struct ble_gap_upd_params params = {.itvl_min = desc.conn_itvl, + .itvl_max = desc.conn_itvl, + .latency = 3, + .supervision_timeout = desc.supervision_timeout}; + rc = ble_gap_update_params(event->connect.conn_handle, ¶ms); + if (rc != 0) + { + ESP_LOGE(TAG, "failed to update connection parameters, error code: %d", rc); + return rc; + } + } + /* Connection failed, restart advertising */ + else + { + ble_app_advertise(); + } + return rc; case BLE_GAP_EVENT_DISCONNECT: ESP_LOGI(TAG, "Disconnected; reason=%d", event->disconnect.reason); - conn_handle = 0; - ble_app_advertise(); + for (int i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) + { + if (g_connections[i].conn_handle == event->disconnect.conn.conn_handle) + { + g_connections[i].is_connected = false; + g_connections[i].conn_handle = BLE_HS_CONN_HANDLE_NONE; + ESP_LOGI(TAG, "Connection from slot %d removed", i); + ble_app_advertise(); // Restart advertising to allow new connections + break; + } + } break; + case BLE_GAP_EVENT_PASSKEY_ACTION: + ESP_LOGI(TAG, "Passkey action required: %d", event->passkey.params.action); + struct ble_sm_io pkey = {0}; + + switch (event->passkey.params.action) + { + case BLE_SM_IOACT_DISP: + pkey.action = BLE_SM_IOACT_DISP; + pkey.passkey = CONFIG_BONDING_PASSPHRASE; + ESP_LOGI(TAG, "Displaying passkey: %06d", pkey.passkey); + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); + if (rc != 0) + { + ESP_LOGE(TAG, "failed to inject security manager io, error code: %d", rc); + return rc; + } + break; + + default: + ESP_LOGE(TAG, "Unknown passkey action: %d", event->passkey.params.action); + return 0; + } + break; + + case BLE_GAP_EVENT_ENC_CHANGE: + ESP_LOGI(TAG, "Encryption change event; status=%d", event->enc_change.status); + + if (event->enc_change.status != 0) + { + ESP_LOGW(TAG, "Encryption failed with status %d", event->enc_change.status); + + rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc); + if (rc == 0) + { + char addr_str[18] = {0}; + format_addr(addr_str, desc.peer_id_addr.val); + ESP_LOGI(TAG, "Deleting bond for peer: %s", addr_str); + + ble_store_util_delete_peer(&desc.peer_id_addr); + + ble_gap_terminate(event->enc_change.conn_handle, BLE_ERR_REM_USER_CONN_TERM); + } + } + else + { + ESP_LOGI(TAG, "Encryption successfully established"); + } + break; + + case BLE_GAP_EVENT_REPEAT_PAIRING: + ESP_LOGI(TAG, "Repeat pairing requested"); + + rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc); + if (rc == 0) + { + char addr_str[18] = {0}; + format_addr(addr_str, desc.peer_id_addr.val); + ESP_LOGI(TAG, "Deleting old bond for specific peer: %s", addr_str); + + ble_store_util_delete_peer(&desc.peer_id_addr); + } + + return BLE_GAP_REPEAT_PAIRING_RETRY; + case BLE_GAP_EVENT_ADV_COMPLETE: ESP_LOGI(TAG, "Advertising complete"); ble_app_advertise(); @@ -254,15 +433,29 @@ static void ble_app_advertise(void) } } -// The application -static void ble_app_on_sync(void) +static void on_stack_reset(int reason) { + /* On reset, print reset reason to console */ + ESP_LOGI(TAG, "nimble stack reset, reset reason: %d", reason); +} + +static void on_stack_sync(void) +{ + esp_err_t ret; uint8_t ble_addr[6] = {0}; - int ret = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, ble_addr, NULL); + + /* Figure out address to use while advertising (no privacy for now) */ + ret = ble_hs_id_infer_auto(0, &ble_addr_type); + if (ret != 0) + { + ESP_LOGE(TAG, "error determining address type; rc=%d", ret); + return; + } + + ret = ble_hs_id_copy_addr(ble_addr_type, ble_addr, NULL); if (ret != 0) { ESP_LOGE(TAG, "Failed to get BLE MAC address (err: %d)", ret); - return; } char formatted_name[32]; @@ -270,26 +463,88 @@ static void ble_app_on_sync(void) ble_svc_gap_device_name_set(formatted_name); // Start Advertising - ble_hs_id_infer_auto(0, &ble_addr_type); // Determines the best address type automatically ble_app_advertise(); } +static esp_err_t gatt_svc_init(void) +{ + esp_err_t ret; + ble_svc_gatt_init(); + + ret = ble_gatts_count_cfg(gatt_svcs); + if (ret != ESP_OK) + { + return ret; + } + + ret = ble_gatts_add_svcs(gatt_svcs); + if (ret != ESP_OK) + { + return ret; + } + + return ESP_OK; +} + +static esp_err_t gap_init(void) +{ + ble_svc_gap_init(); + + return ESP_OK; +} + // The infinite task static void host_task(void *param) { nimble_port_run(); // This function will return only when nimble_port_stop() is executed } +static void nimble_host_config_init(void) +{ + // callbacks + ble_hs_cfg.reset_cb = on_stack_reset; + ble_hs_cfg.sync_cb = on_stack_sync; + + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_ONLY; + ble_hs_cfg.sm_bonding = 1; + ble_hs_cfg.sm_mitm = 1; + ble_hs_cfg.sm_our_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID; + ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID; + + // Initialize BLE store configuration + ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + + ble_store_config_init(); +} + void remote_control_init(void) { - nimble_port_init(); - ble_svc_gap_init(); - ble_svc_gatt_init(); - ble_gatts_count_cfg(gatt_svcs); - ble_gatts_add_svcs(gatt_svcs); + esp_err_t ret; - // Callback for synchronization - ble_hs_cfg.sync_cb = ble_app_on_sync; + ret = nimble_port_init(); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "Failed to initialize nimble stack (err: %s)", esp_err_to_name(ret)); + return; + } + + init_connection_pool(); + + ret = gap_init(); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "Failed to initialize GAP service (err: %s)", esp_err_to_name(ret)); + return; + } + + ret = gatt_svc_init(); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "Failed to initialize GATT server (err: %s)", esp_err_to_name(ret)); + return; + } + + nimble_host_config_init(); nimble_port_freertos_init(host_task); // Start BLE host task diff --git a/firmware/components/remote_control/uart_service.c b/firmware/components/remote_control/uart_service.c index 5f6e9f8..87a8312 100644 --- a/firmware/components/remote_control/uart_service.c +++ b/firmware/components/remote_control/uart_service.c @@ -1,4 +1,7 @@ #include "include/uart_service.h" +#include "esp_log.h" +#include "include/remote_control.h" +#include "sdkconfig.h" static const char *TAG = "uart_service"; @@ -14,7 +17,6 @@ const ble_uuid128_t gatt_svr_chr_uart_rx_uuid = const ble_uuid128_t gatt_svr_chr_uart_tx_uuid = BLE_UUID128_INIT(0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x03, 0x00, 0x40, 0x6E); -uint16_t conn_handle; uint16_t tx_chr_val_handle; // Callback function for GATT events (read/write on characteristics) @@ -51,19 +53,26 @@ int gatt_svr_chr_uart_access(uint16_t conn_handle, uint16_t attr_handle, struct // Function to send data via the TX characteristic void send_ble_data(const char *data) { - if (conn_handle != 0) - { // Only send when connected - struct os_mbuf *om = ble_hs_mbuf_from_flat(data, strlen(data)); - if (om) + ESP_LOGI(TAG, "Preparing to send data: %s", data); + + struct os_mbuf *om; + + for (int i = 0; i < CONFIG_BT_NIMBLE_MAX_CONNECTIONS; i++) + { + if (g_connections[i].is_connected) { - int rc = ble_gatts_notify_custom(conn_handle, tx_chr_val_handle, om); - if (rc == 0) + om = ble_hs_mbuf_from_flat(data, strlen(data)); + if (om) { - ESP_LOGI(TAG, "Sent data: %s", data); - } - else - { - ESP_LOGE(TAG, "Error sending data: %d", rc); + int rc = ble_gatts_notify_custom(g_connections[i].conn_handle, tx_chr_val_handle, om); + if (rc == 0) + { + ESP_LOGI(TAG, "Sent data to conn_handle %d: %s", g_connections[i].conn_handle, data); + } + else if (rc != BLE_HS_ENOTCONN) // Ignore "not connected" errors if a device just disconnected + { + ESP_LOGE(TAG, "Error sending data to conn_handle %d: %d", g_connections[i].conn_handle, rc); + } } } } @@ -76,11 +85,11 @@ void uart_tx_task(void *param) while (1) { vTaskDelay(pdMS_TO_TICKS(2000)); - if (conn_handle != 0) + if (is_any_device_connected()) { ESP_LOGI(TAG, "Sending data over BLE UART TX"); sprintf(buffer, "Hello World #%d", count++); send_ble_data(buffer); } } -} \ No newline at end of file +} diff --git a/firmware/main/Kconfig.projbuild b/firmware/main/Kconfig.projbuild index bf8387d..4c5dfe3 100644 --- a/firmware/main/Kconfig.projbuild +++ b/firmware/main/Kconfig.projbuild @@ -15,4 +15,29 @@ menu "Warnemuende Lighthouse" int default 1 if WLED_USE_DMA default 0 if !WLED_USE_DMA + + config WLED_WITH_WHITE + bool "WLED with White Channel" + default y + help + Use a WLED strip with a white channel (e.g. WS2812B RGBW). + + config LED_PIN_LEFT + int "LED Left Pin" + default 11 + help + The pin of the LED for the left side. + + config LED_PIN_RIGHT + int "LED Right Pin" + default 12 + help + The pin of the LED for the right side. + + config BONDING_PASSPHRASE + int "Bonding Passphrase" + default 123456 + help + The passphrase for the BLE bonding. + endmenu diff --git a/firmware/main/main.c b/firmware/main/main.c index 9f6c307..331df32 100644 --- a/firmware/main/main.c +++ b/firmware/main/main.c @@ -25,14 +25,6 @@ void app_main(void) printf("Failed to initialize beacon"); return; } - /// start beacon service - uint8_t beacon_enabled = 1; - persistence_load(VALUE_TYPE_INT32, "BEACON_ENABLED", &beacon_enabled); - if (beacon_enabled && beacon_start() != ESP_OK) - { - printf("Failed to start beacon"); - return; - } /// start outdoor light service if (outdoor_start() != ESP_OK) diff --git a/firmware/partitions.csv b/firmware/partitions.csv index 2d9cedc..3ced78c 100644 --- a/firmware/partitions.csv +++ b/firmware/partitions.csv @@ -1,6 +1,5 @@ # Name , Type , SubType , Offset , Size , Flags -nvs , data , nvs , 0x9000 , 20k , -otadata , data , ota , 0xe000 , 8k , -app0 , app , ota_0 , 0x10000 , 1792k , -app1 , app , ota_1 , , 1792k , +nvs , data , nvs , 0x9000 , 24k , +phy_init , data , phy , , 4k , +factory , app , factory , 0x10000 , 3584K , coredump , data , coredump , , 64k , diff --git a/firmware/sdkconfig.defaults b/firmware/sdkconfig.defaults index 69ac3e4..42fa2bf 100755 --- a/firmware/sdkconfig.defaults +++ b/firmware/sdkconfig.defaults @@ -3,10 +3,10 @@ CONFIG_BT_ENABLED=y CONFIG_BT_NIMBLE_ENABLED=y # NimBLE Options -CONFIG_BT_NIMBLE_SECURITY_ENABLE=n -CONFIG_BT_NIMBLE_SVC_GAP_DEVICE_NAME="lighthouse" +CONFIG_BT_NIMBLE_SECURITY_ENABLE=y CONFIG_BT_NIMBLE_NVS_PERSIST=y CONFIG_BT_NIMBLE_SMP_ID_RESET=y +CONFIG_NIMBLE_CRYPTO_STACK_MBEDTLS=y # Flash Size CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y