From dc6078b29651dc3dc26e8037ca43b40601af04da Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Mon, 2 Feb 2026 15:30:54 -0700 Subject: [PATCH] Fix firmware for ESP-IDF v5.3 and hardware-verified operation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Switch UART0 for protocol I/O (USB bridge), disable ESP-IDF console - Wire all 21 command handlers into dispatch table (was only 4) - Add configure command handler (name, io_cap, device_class) - Add bt_classic_is_enabled()/bt_ble_is_enabled() for live status - Fix cJSON_False misuse in get_status (type constant, not boolean) - Fix esp_bt_gap_set_cod() to use esp_bt_cod_t bitfield struct - Fix auth_cmpl.auth_mode → lk_type for ESP-IDF v5.3 - Replace deprecated esp_bt_dev_set_device_name with stack-specific API - Remove unused bytes_to_hex, obsolete kconfig symbols - Use large partition table (1.5MB) for dual-mode BT stack Verified on ESP32-D0WD-V3 rev 3.1, /dev/ttyUSB4, all commands tested. --- firmware/main/bt_ble.c | 17 ++++--- firmware/main/bt_ble.h | 4 ++ firmware/main/bt_classic.c | 24 +++++++-- firmware/main/bt_classic.h | 8 ++- firmware/main/cmd_dispatcher.c | 91 +++++++++++++++++++++++++++------- firmware/main/personas.c | 3 +- firmware/main/protocol.h | 2 +- firmware/main/uart_handler.c | 15 +++--- firmware/sdkconfig.defaults | 12 ++--- 9 files changed, 127 insertions(+), 49 deletions(-) diff --git a/firmware/main/bt_ble.c b/firmware/main/bt_ble.c index 17f91d0..dd6f8a3 100644 --- a/firmware/main/bt_ble.c +++ b/firmware/main/bt_ble.c @@ -114,14 +114,6 @@ static int hex_to_bytes(const char *hex, uint8_t *out, int max_len) return len; } -static void bytes_to_hex(const uint8_t *data, int len, char *out) -{ - for (int i = 0; i < len; i++) { - sprintf(out + i * 2, "%02x", data[i]); - } - out[len * 2] = '\0'; -} - static uint8_t properties_from_json(cJSON *arr) { uint8_t props = 0; @@ -424,6 +416,15 @@ static void gatts_event_handler(esp_gatts_cb_event_t event, } } +/* ------------------------------------------------------------------ */ +/* State query */ +/* ------------------------------------------------------------------ */ + +bool bt_ble_is_enabled(void) +{ + return s_ble.enabled; +} + /* ------------------------------------------------------------------ */ /* Command handlers */ /* ------------------------------------------------------------------ */ diff --git a/firmware/main/bt_ble.h b/firmware/main/bt_ble.h index 13b56e9..6027570 100644 --- a/firmware/main/bt_ble.h +++ b/firmware/main/bt_ble.h @@ -1,10 +1,14 @@ #pragma once +#include #include "cJSON.h" /* Initialize BLE subsystem (Bluedroid GATTS) */ void bt_ble_init(void); +/* State query */ +bool bt_ble_is_enabled(void); + /* Command handlers (called from cmd_dispatcher) */ void cmd_ble_enable(const char *id, cJSON *params); void cmd_ble_disable(const char *id, cJSON *params); diff --git a/firmware/main/bt_classic.c b/firmware/main/bt_classic.c index edc2988..f9673af 100644 --- a/firmware/main/bt_classic.c +++ b/firmware/main/bt_classic.c @@ -105,9 +105,9 @@ static void gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) bd_addr_to_str(param->auth_cmpl.bda, addr_str); bool ok = (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS); - ESP_LOGI(TAG, "auth_cmpl: %s %s (mode=%d)", + ESP_LOGI(TAG, "auth_cmpl: %s %s (lk_type=%d)", addr_str, ok ? "success" : "FAIL", - param->auth_cmpl.auth_mode); + param->auth_cmpl.lk_type); event_report_pair_complete(addr_str, ok); @@ -583,6 +583,15 @@ void cmd_classic_set_ssp_mode(const char *id, cJSON *params) uart_send_response(id, STATUS_OK, data); } +/* ------------------------------------------------------------------ */ +/* State query */ +/* ------------------------------------------------------------------ */ + +bool bt_classic_is_enabled(void) +{ + return classic_state.enabled; +} + /* ------------------------------------------------------------------ */ /* Configure helpers (called from the configure command handler) */ /* ------------------------------------------------------------------ */ @@ -605,11 +614,16 @@ void bt_classic_set_io_cap(const char *io_cap_str) } } -void bt_classic_set_device_class(uint32_t cod) +void bt_classic_set_device_class(uint32_t cod_raw) { - esp_err_t err = esp_bt_gap_set_cod(cod, ESP_BT_SET_COD_ALL); + esp_bt_cod_t cod; + memset(&cod, 0, sizeof(cod)); + cod.minor = (cod_raw >> 2) & 0x3F; + cod.major = (cod_raw >> 8) & 0x1F; + cod.service = (cod_raw >> 13) & 0x7FF; + esp_err_t err = esp_bt_gap_set_cod(cod, ESP_BT_SET_COD_MAJOR_MINOR | ESP_BT_SET_COD_SERVICE_CLASS); if (err == ESP_OK) { - ESP_LOGI(TAG, "CoD set to 0x%06" PRIx32, cod); + ESP_LOGI(TAG, "CoD set to 0x%06" PRIx32, cod_raw); } else { ESP_LOGE(TAG, "set_cod failed: %s", esp_err_to_name(err)); } diff --git a/firmware/main/bt_classic.h b/firmware/main/bt_classic.h index 062f414..674471f 100644 --- a/firmware/main/bt_classic.h +++ b/firmware/main/bt_classic.h @@ -3,6 +3,9 @@ */ #pragma once + +#include +#include #include "cJSON.h" void bt_classic_init(void); @@ -14,6 +17,9 @@ void cmd_classic_set_discoverable(const char *id, cJSON *params); void cmd_classic_pair_respond(const char *id, cJSON *params); void cmd_classic_set_ssp_mode(const char *id, cJSON *params); +/* State query */ +bool bt_classic_is_enabled(void); + /* Called by configure command to set IO capabilities */ void bt_classic_set_io_cap(const char *io_cap_str); -void bt_classic_set_device_class(uint32_t cod); +void bt_classic_set_device_class(uint32_t cod_raw); diff --git a/firmware/main/cmd_dispatcher.c b/firmware/main/cmd_dispatcher.c index edce37a..12c49c0 100644 --- a/firmware/main/cmd_dispatcher.c +++ b/firmware/main/cmd_dispatcher.c @@ -1,13 +1,16 @@ #include "cmd_dispatcher.h" #include "protocol.h" #include "uart_handler.h" +#include "bt_classic.h" +#include "bt_ble.h" +#include "personas.h" #include "esp_system.h" #include "esp_chip_info.h" #include "esp_timer.h" #include "esp_log.h" #include "esp_mac.h" -#include "esp_bt.h" +#include "esp_gap_bt_api.h" #include "cJSON.h" #include @@ -23,11 +26,6 @@ typedef struct { cmd_handler_t handler; } cmd_entry_t; -/* Forward declarations for handlers in other modules */ -extern void bt_classic_register_commands(void); -extern void bt_ble_register_commands(void); -extern void personas_register_commands(void); - /* --- System command handlers --- */ static void handle_ping(const char *id, cJSON *params) @@ -97,30 +95,87 @@ static void handle_get_status(const char *id, cJSON *params) cJSON *data = cJSON_CreateObject(); cJSON_AddNumberToObject(data, "uptime_ms", (double)uptime_ms); cJSON_AddNumberToObject(data, "free_heap", (double)esp_get_free_heap_size()); - cJSON_AddBoolToObject(data, "bt_enabled", cJSON_False); - cJSON_AddBoolToObject(data, "ble_enabled", cJSON_False); + cJSON_AddBoolToObject(data, "bt_enabled", bt_classic_is_enabled()); + cJSON_AddBoolToObject(data, "ble_enabled", bt_ble_is_enabled()); uart_send_response(id, STATUS_OK, data); } +/* --- Configure command: sets device name, IO capabilities, CoD --- */ + +static void handle_configure(const char *id, cJSON *params) +{ + if (!params) { + cJSON *err = cJSON_CreateObject(); + cJSON_AddStringToObject(err, "error", "params required"); + uart_send_response(id, STATUS_ERROR, err); + return; + } + + cJSON *applied = cJSON_CreateObject(); + + cJSON *name = cJSON_GetObjectItem(params, "name"); + if (name && cJSON_IsString(name)) { + esp_bt_gap_set_device_name(name->valuestring); + cJSON_AddStringToObject(applied, "name", name->valuestring); + } + + cJSON *io_cap = cJSON_GetObjectItem(params, "io_cap"); + if (io_cap && cJSON_IsString(io_cap)) { + bt_classic_set_io_cap(io_cap->valuestring); + cJSON_AddStringToObject(applied, "io_cap", io_cap->valuestring); + } + + cJSON *device_class = cJSON_GetObjectItem(params, "device_class"); + if (device_class && cJSON_IsNumber(device_class)) { + bt_classic_set_device_class((uint32_t)device_class->valuedouble); + cJSON_AddNumberToObject(applied, "device_class", device_class->valuedouble); + } + + uart_send_response(id, STATUS_OK, applied); +} + /* --- Command table --- */ static const cmd_entry_t cmd_table[] = { - { CMD_PING, handle_ping }, - { CMD_RESET, handle_reset }, - { CMD_GET_INFO, handle_get_info }, - { CMD_GET_STATUS, handle_get_status }, + /* System */ + { CMD_PING, handle_ping }, + { CMD_RESET, handle_reset }, + { CMD_GET_INFO, handle_get_info }, + { CMD_GET_STATUS, handle_get_status }, + + /* Configuration */ + { CMD_CONFIGURE, handle_configure }, + { CMD_LOAD_PERSONA, cmd_load_persona }, + { CMD_LIST_PERSONAS, cmd_list_personas }, + + /* Classic BT */ + { CMD_CLASSIC_ENABLE, cmd_classic_enable }, + { CMD_CLASSIC_DISABLE, cmd_classic_disable }, + { CMD_CLASSIC_SET_DISCOVERABLE,cmd_classic_set_discoverable }, + { CMD_CLASSIC_PAIR_RESPOND, cmd_classic_pair_respond }, + { CMD_CLASSIC_SET_SSP_MODE, cmd_classic_set_ssp_mode }, + + /* BLE */ + { CMD_BLE_ENABLE, cmd_ble_enable }, + { CMD_BLE_DISABLE, cmd_ble_disable }, + { CMD_BLE_ADVERTISE, cmd_ble_advertise }, + { CMD_BLE_SET_ADV_DATA, cmd_ble_set_adv_data }, + + /* GATT */ + { CMD_GATT_ADD_SERVICE, cmd_gatt_add_service }, + { CMD_GATT_ADD_CHARACTERISTIC, cmd_gatt_add_characteristic }, + { CMD_GATT_SET_VALUE, cmd_gatt_set_value }, + { CMD_GATT_NOTIFY, cmd_gatt_notify }, + { CMD_GATT_CLEAR, cmd_gatt_clear }, + { NULL, NULL } /* sentinel */ }; void cmd_dispatcher_init(void) { - ESP_LOGI(TAG, "command dispatcher ready"); - - /* Future: these will register their own handlers into the table */ - /* bt_classic_register_commands(); */ - /* bt_ble_register_commands(); */ - /* personas_register_commands(); */ + ESP_LOGI(TAG, "command dispatcher ready (%d commands)", + (int)(sizeof(cmd_table) / sizeof(cmd_table[0])) - 1); } void cmd_dispatch(const char *id, const char *cmd, cJSON *params) diff --git a/firmware/main/personas.c b/firmware/main/personas.c index d51057e..085145b 100644 --- a/firmware/main/personas.c +++ b/firmware/main/personas.c @@ -14,6 +14,7 @@ #include "bt_ble.h" #include "esp_bt_device.h" +#include "esp_gap_bt_api.h" #include "esp_gap_ble_api.h" #include "esp_log.h" #include "cJSON.h" @@ -174,7 +175,7 @@ void cmd_load_persona(const char *id, cJSON *params) /* --- Set device name on both stacks --- */ if (p->classic_enabled) { - esp_bt_dev_set_device_name(p->device_name); + esp_bt_gap_set_device_name(p->device_name); } if (p->ble_enabled) { esp_ble_gap_set_device_name(p->device_name); diff --git a/firmware/main/protocol.h b/firmware/main/protocol.h index 135a3ab..ba36bf8 100644 --- a/firmware/main/protocol.h +++ b/firmware/main/protocol.h @@ -3,7 +3,7 @@ #include "driver/uart.h" /* UART configuration */ -#define PROTO_UART_NUM UART_NUM_1 +#define PROTO_UART_NUM UART_NUM_0 #define PROTO_BAUD_RATE 115200 #define PROTO_TX_BUF_SIZE 4096 #define PROTO_RX_BUF_SIZE 4096 diff --git a/firmware/main/uart_handler.c b/firmware/main/uart_handler.c index 73b0c60..d52cd11 100644 --- a/firmware/main/uart_handler.c +++ b/firmware/main/uart_handler.c @@ -14,9 +14,11 @@ static const char *TAG = "uart"; static SemaphoreHandle_t tx_mutex; -/* Pin assignments -- keep UART0 free for ESP-IDF console/logging */ -#define UART_TX_PIN GPIO_NUM_4 -#define UART_RX_PIN GPIO_NUM_5 +/* + * Use UART0 — the dev board's USB bridge connects here. + * ESP-IDF console is disabled (CONFIG_ESP_CONSOLE_NONE=y) so we own UART0. + * Default UART0 pins (TX=GPIO1, RX=GPIO3) route through the USB bridge. + */ void uart_handler_init(void) { @@ -30,8 +32,7 @@ void uart_handler_init(void) }; ESP_ERROR_CHECK(uart_param_config(PROTO_UART_NUM, &cfg)); - ESP_ERROR_CHECK(uart_set_pin(PROTO_UART_NUM, UART_TX_PIN, UART_RX_PIN, - UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + /* UART0 default pins (TX=1, RX=3) — no set_pin needed */ ESP_ERROR_CHECK(uart_driver_install(PROTO_UART_NUM, PROTO_RX_BUF_SIZE, PROTO_TX_BUF_SIZE, 0, NULL, 0)); @@ -39,8 +40,8 @@ void uart_handler_init(void) tx_mutex = xSemaphoreCreateMutex(); assert(tx_mutex); - ESP_LOGI(TAG, "UART%d ready (TX=%d RX=%d @ %d baud)", - PROTO_UART_NUM, UART_TX_PIN, UART_RX_PIN, PROTO_BAUD_RATE); + ESP_LOGI(TAG, "UART%d ready (USB bridge @ %d baud)", + PROTO_UART_NUM, PROTO_BAUD_RATE); } char *uart_read_line(void) diff --git a/firmware/sdkconfig.defaults b/firmware/sdkconfig.defaults index 4f241b6..51c20cf 100644 --- a/firmware/sdkconfig.defaults +++ b/firmware/sdkconfig.defaults @@ -5,7 +5,6 @@ CONFIG_BT_CLASSIC_ENABLED=y CONFIG_BT_BLE_ENABLED=y CONFIG_BT_A2DP_ENABLE=n CONFIG_BT_SPP_ENABLED=y -CONFIG_BT_SSP_ENABLED=y # GAP & GATTS CONFIG_BT_GATTS_ENABLE=y @@ -14,22 +13,19 @@ CONFIG_BT_GATTC_ENABLE=n # Bluetooth controller CONFIG_BTDM_CTRL_MODE_BTDM=y -# UART -CONFIG_ESP_CONSOLE_UART_NUM=0 +# UART — disable ESP-IDF console so our protocol handler owns UART0 +CONFIG_ESP_CONSOLE_NONE=y # Stack sizes (Bluetooth needs generous stacks) CONFIG_BT_BTU_TASK_STACK_SIZE=8192 CONFIG_BTDM_CTRL_HCI_MODE_VHCI=y -# NVS (for bonding storage) -CONFIG_NVS_ENABLED=y - # Logging — keep it reasonable CONFIG_LOG_DEFAULT_LEVEL_INFO=y CONFIG_LOG_MAXIMUM_LEVEL_DEBUG=y -# Partition table -CONFIG_PARTITION_TABLE_SINGLE_APP=y +# Partition table — large (1.5MB app) needed for dual-mode BT stack +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y # FreeRTOS CONFIG_FREERTOS_HZ=1000