Ryan Malloy dc6078b296 Fix firmware for ESP-IDF v5.3 and hardware-verified operation
- 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.
2026-02-02 15:30:54 -07:00

138 lines
3.4 KiB
C

#include "uart_handler.h"
#include "protocol.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "cJSON.h"
#include <string.h>
#include <stdlib.h>
static const char *TAG = "uart";
static SemaphoreHandle_t tx_mutex;
/*
* 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)
{
uart_config_t cfg = {
.baud_rate = PROTO_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
ESP_ERROR_CHECK(uart_param_config(PROTO_UART_NUM, &cfg));
/* 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));
tx_mutex = xSemaphoreCreateMutex();
assert(tx_mutex);
ESP_LOGI(TAG, "UART%d ready (USB bridge @ %d baud)",
PROTO_UART_NUM, PROTO_BAUD_RATE);
}
char *uart_read_line(void)
{
static char buf[PROTO_MAX_LINE];
int pos = 0;
for (;;) {
uint8_t byte;
int n = uart_read_bytes(PROTO_UART_NUM, &byte, 1, portMAX_DELAY);
if (n <= 0) {
continue;
}
if (byte == '\n') {
if (pos == 0) {
continue; /* skip empty lines */
}
buf[pos] = '\0';
return strdup(buf);
}
if (byte == '\r') {
continue; /* ignore carriage returns */
}
if (pos < PROTO_MAX_LINE - 1) {
buf[pos++] = (char)byte;
} else {
/* line too long -- discard and reset */
ESP_LOGE(TAG, "line overflow (%d bytes), discarding", pos);
pos = 0;
}
}
}
void uart_send_line(const char *json_line)
{
if (!json_line) {
return;
}
xSemaphoreTake(tx_mutex, portMAX_DELAY);
size_t len = strlen(json_line);
uart_write_bytes(PROTO_UART_NUM, json_line, len);
uart_write_bytes(PROTO_UART_NUM, "\n", 1);
xSemaphoreGive(tx_mutex);
}
void uart_send_response(const char *id, const char *status, cJSON *data)
{
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "type", MSG_TYPE_RESP);
cJSON_AddStringToObject(root, "id", id);
cJSON_AddStringToObject(root, "status", status);
if (data) {
cJSON_AddItemToObject(root, "data", data);
} else {
cJSON_AddObjectToObject(root, "data");
}
char *out = cJSON_PrintUnformatted(root);
uart_send_line(out);
free(out);
cJSON_Delete(root);
}
void uart_send_event(const char *event_name, cJSON *data)
{
int64_t ts_ms = esp_timer_get_time() / 1000;
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "type", MSG_TYPE_EVENT);
cJSON_AddStringToObject(root, "event", event_name);
if (data) {
cJSON_AddItemToObject(root, "data", data);
} else {
cJSON_AddObjectToObject(root, "data");
}
cJSON_AddNumberToObject(root, "ts", (double)ts_ms);
char *out = cJSON_PrintUnformatted(root);
uart_send_line(out);
free(out);
cJSON_Delete(root);
}