687 lines
20 KiB
C
687 lines
20 KiB
C
/**
|
|
* @file control.c
|
|
* @brief Control module implementation with BLE, NVS, and OTA
|
|
*/
|
|
|
|
#include "control.h"
|
|
#include "led.h"
|
|
#include "rcsignal.h"
|
|
#include "animation.h"
|
|
|
|
#include "esp_log.h"
|
|
#include "esp_system.h"
|
|
#include "nvs_flash.h"
|
|
#include "nvs.h"
|
|
#include "esp_timer.h"
|
|
#include "esp_ota_ops.h"
|
|
#include "esp_http_server.h"
|
|
|
|
#include "esp_bt.h"
|
|
#include "esp_gap_ble_api.h"
|
|
#include "esp_gatts_api.h"
|
|
#include "esp_bt_main.h"
|
|
#include "esp_gatt_common_api.h"
|
|
|
|
#include <string.h>
|
|
|
|
static const char *TAG = "CONTROL";
|
|
|
|
#define NVS_NAMESPACE "led_ctrl"
|
|
#define CONFIG_MAGIC 0xDEADBEEF
|
|
#define DEFAULT_NUM_LEDS_A 44
|
|
#define DEFAULT_NUM_LEDS_B 44
|
|
|
|
// BLE Configuration
|
|
#define GATTS_SERVICE_UUID 0x00FF
|
|
#define GATTS_CHAR_UUID_CONFIG 0xFF01
|
|
#define GATTS_CHAR_UUID_MODE 0xFF02
|
|
#define GATTS_CHAR_UUID_PWM 0xFF03
|
|
#define GATTS_CHAR_UUID_OTA 0xFF04
|
|
#define GATTS_NUM_HANDLE_TEST 8
|
|
|
|
#define DEVICE_NAME "LED-Controller"
|
|
#define ADV_CONFIG_FLAG (1 << 0)
|
|
#define SCAN_RSP_CONFIG_FLAG (1 << 1)
|
|
|
|
// Global state
|
|
static controller_config_t current_config = {
|
|
.led_pin_strip_a = -1,
|
|
.led_pin_strip_b = -1,
|
|
.pwm_pin = -1,
|
|
.ble_timeout = BLE_TIMEOUT_NEVER,
|
|
.magic = CONFIG_MAGIC};
|
|
|
|
static bool ble_enabled = true;
|
|
static uint8_t current_animation_mode = 0;
|
|
static esp_timer_handle_t ble_timeout_timer = NULL;
|
|
static bool ble_connected = false;
|
|
|
|
// OTA state
|
|
static const esp_partition_t *update_partition = NULL;
|
|
static esp_ota_handle_t update_handle = 0;
|
|
static size_t ota_bytes_written = 0;
|
|
|
|
// BLE variables
|
|
static uint8_t adv_config_done = 0;
|
|
static uint16_t gatts_if_global = ESP_GATT_IF_NONE;
|
|
static uint16_t conn_id_global = 0;
|
|
static uint16_t service_handle = 0;
|
|
|
|
// BLE advertising parameters
|
|
static esp_ble_adv_params_t adv_params = {
|
|
.adv_int_min = 0x20,
|
|
.adv_int_max = 0x40,
|
|
.adv_type = ADV_TYPE_IND,
|
|
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
|
.channel_map = ADV_CHNL_ALL,
|
|
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
|
};
|
|
|
|
// Characteristic handles
|
|
static struct
|
|
{
|
|
uint16_t config_handle;
|
|
uint16_t mode_handle;
|
|
uint16_t pwm_handle;
|
|
uint16_t ota_handle;
|
|
} char_handles = {0};
|
|
|
|
// Forward declarations
|
|
static void ble_timeout_callback(void *arg);
|
|
static void on_mode_change(uint8_t new_mode);
|
|
|
|
// NVS Functions
|
|
static esp_err_t load_config_from_nvs(void)
|
|
{
|
|
nvs_handle_t nvs_handle;
|
|
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
|
|
if (err != ESP_OK)
|
|
{
|
|
ESP_LOGW(TAG, "NVS not found, using defaults");
|
|
return ESP_ERR_NOT_FOUND;
|
|
}
|
|
|
|
size_t required_size = sizeof(controller_config_t);
|
|
err = nvs_get_blob(nvs_handle, "config", ¤t_config, &required_size);
|
|
nvs_close(nvs_handle);
|
|
|
|
if (err != ESP_OK || current_config.magic != CONFIG_MAGIC)
|
|
{
|
|
ESP_LOGW(TAG, "Invalid config in NVS, using defaults");
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "Loaded config from NVS");
|
|
ESP_LOGI(TAG, " Strip A: GPIO%d", current_config.led_pin_strip_a);
|
|
ESP_LOGI(TAG, " Strip B: GPIO%d", current_config.led_pin_strip_b);
|
|
ESP_LOGI(TAG, " PWM Pin: GPIO%d", current_config.pwm_pin);
|
|
ESP_LOGI(TAG, " BLE Timeout: %d", current_config.ble_timeout);
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t save_config_to_nvs(void)
|
|
{
|
|
nvs_handle_t nvs_handle;
|
|
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
|
|
if (err != ESP_OK)
|
|
{
|
|
return err;
|
|
}
|
|
|
|
current_config.magic = CONFIG_MAGIC;
|
|
err = nvs_set_blob(nvs_handle, "config", ¤t_config, sizeof(controller_config_t));
|
|
if (err == ESP_OK)
|
|
{
|
|
err = nvs_commit(nvs_handle);
|
|
}
|
|
|
|
nvs_close(nvs_handle);
|
|
|
|
if (err == ESP_OK)
|
|
{
|
|
ESP_LOGI(TAG, "Config saved to NVS");
|
|
}
|
|
else
|
|
{
|
|
ESP_LOGE(TAG, "Failed to save config: %s", esp_err_to_name(err));
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
esp_err_t control_reset_config(void)
|
|
{
|
|
current_config.led_pin_strip_a = -1;
|
|
current_config.led_pin_strip_b = -1;
|
|
current_config.pwm_pin = -1;
|
|
current_config.ble_timeout = BLE_TIMEOUT_NEVER;
|
|
current_config.magic = CONFIG_MAGIC;
|
|
|
|
return save_config_to_nvs();
|
|
}
|
|
|
|
const controller_config_t *control_get_config(void)
|
|
{
|
|
return ¤t_config;
|
|
}
|
|
|
|
esp_err_t control_update_config(const controller_config_t *config)
|
|
{
|
|
if (!config)
|
|
{
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
// Reinitialize if pins changed
|
|
bool pins_changed = (current_config.led_pin_strip_a != config->led_pin_strip_a) ||
|
|
(current_config.led_pin_strip_b != config->led_pin_strip_b) ||
|
|
(current_config.pwm_pin != config->pwm_pin);
|
|
|
|
memcpy(¤t_config, config, sizeof(controller_config_t));
|
|
esp_err_t err = save_config_to_nvs();
|
|
|
|
if (err == ESP_OK && pins_changed)
|
|
{
|
|
ESP_LOGI(TAG, "Restarting to apply new pin configuration...");
|
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
|
esp_restart();
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
// BLE timeout handling
|
|
static void ble_timeout_callback(void *arg)
|
|
{
|
|
if (!ble_connected)
|
|
{
|
|
ESP_LOGI(TAG, "BLE timeout reached, disabling BLE");
|
|
control_disable_ble();
|
|
}
|
|
}
|
|
|
|
static void start_ble_timeout(void)
|
|
{
|
|
if (current_config.ble_timeout == BLE_TIMEOUT_NEVER)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ble_timeout_timer == NULL)
|
|
{
|
|
esp_timer_create_args_t timer_args = {
|
|
.callback = ble_timeout_callback,
|
|
.name = "ble_timeout"};
|
|
esp_timer_create(&timer_args, &ble_timeout_timer);
|
|
}
|
|
|
|
esp_timer_stop(ble_timeout_timer);
|
|
esp_timer_start_once(ble_timeout_timer, (uint64_t)current_config.ble_timeout * 1000000ULL);
|
|
ESP_LOGI(TAG, "BLE timeout started: %d seconds", current_config.ble_timeout);
|
|
}
|
|
|
|
void control_disable_ble(void)
|
|
{
|
|
if (!ble_enabled)
|
|
return;
|
|
|
|
ble_enabled = false;
|
|
|
|
if (ble_timeout_timer)
|
|
{
|
|
esp_timer_stop(ble_timeout_timer);
|
|
}
|
|
|
|
// Stop BLE advertising
|
|
esp_ble_gap_stop_advertising();
|
|
|
|
ESP_LOGI(TAG, "BLE disabled");
|
|
}
|
|
|
|
bool control_is_ble_enabled(void)
|
|
{
|
|
return ble_enabled;
|
|
}
|
|
|
|
// Animation mode change callback
|
|
static void on_mode_change(uint8_t new_mode)
|
|
{
|
|
current_animation_mode = new_mode;
|
|
animation_set_mode((animation_mode_t)new_mode);
|
|
}
|
|
|
|
void control_set_animation_mode(uint8_t mode)
|
|
{
|
|
if (mode >= ANIM_MODE_COUNT)
|
|
{
|
|
mode = 0;
|
|
}
|
|
on_mode_change(mode);
|
|
}
|
|
|
|
uint8_t control_get_animation_mode(void)
|
|
{
|
|
return current_animation_mode;
|
|
}
|
|
|
|
void control_emulate_pwm_pulse(void)
|
|
{
|
|
rcsignal_trigger_mode_change();
|
|
}
|
|
|
|
// Embedded web files (will be linked)
|
|
extern const uint8_t index_html_start[] asm("_binary_index_html_start");
|
|
extern const uint8_t index_html_end[] asm("_binary_index_html_end");
|
|
extern const uint8_t app_js_start[] asm("_binary_app_js_start");
|
|
extern const uint8_t app_js_end[] asm("_binary_app_js_end");
|
|
extern const uint8_t style_css_start[] asm("_binary_style_css_start");
|
|
extern const uint8_t style_css_end[] asm("_binary_style_css_end");
|
|
extern const uint8_t favicon_ico_start[] asm("_binary_favicon_ico_start");
|
|
extern const uint8_t favicon_ico_end[] asm("_binary_favicon_ico_end");
|
|
|
|
// BLE GAP event handler
|
|
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
|
|
{
|
|
ESP_LOGI(TAG, "gap_event_handler() event: %i\n", event);
|
|
switch (event)
|
|
{
|
|
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
|
|
adv_config_done &= (~ADV_CONFIG_FLAG);
|
|
if (adv_config_done == 0)
|
|
{
|
|
esp_ble_gap_start_advertising(&adv_params);
|
|
}
|
|
break;
|
|
|
|
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
|
|
adv_config_done &= (~SCAN_RSP_CONFIG_FLAG);
|
|
if (adv_config_done == 0)
|
|
{
|
|
esp_ble_gap_start_advertising(&adv_params);
|
|
}
|
|
break;
|
|
|
|
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
|
|
if (param->adv_start_cmpl.status == ESP_BT_STATUS_SUCCESS)
|
|
{
|
|
ESP_LOGI(TAG, "BLE advertising started");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// BLE GATTS event handler
|
|
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
|
|
{
|
|
switch (event)
|
|
{
|
|
case ESP_GATTS_REG_EVT:
|
|
ESP_LOGI(TAG, "GATTS register, status %d, app_id %d", param->reg.status, param->reg.app_id);
|
|
gatts_if_global = gatts_if;
|
|
|
|
// Set device name
|
|
esp_ble_gap_set_device_name(DEVICE_NAME);
|
|
|
|
// Config advertising data
|
|
esp_ble_adv_data_t adv_data = {
|
|
.set_scan_rsp = false,
|
|
.include_name = true,
|
|
.include_txpower = true,
|
|
.min_interval = 0x0006,
|
|
.max_interval = 0x0010,
|
|
.appearance = 0x00,
|
|
.manufacturer_len = 0,
|
|
.p_manufacturer_data = NULL,
|
|
.service_data_len = 0,
|
|
.p_service_data = NULL,
|
|
.service_uuid_len = sizeof(uint16_t),
|
|
.p_service_uuid = (uint8_t[]){GATTS_SERVICE_UUID & 0xFF, (GATTS_SERVICE_UUID >> 8) & 0xFF},
|
|
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
|
|
};
|
|
esp_ble_gap_config_adv_data(&adv_data);
|
|
adv_config_done |= ADV_CONFIG_FLAG;
|
|
|
|
// Create service
|
|
esp_gatt_srvc_id_t service_id = {
|
|
.is_primary = true,
|
|
.id.inst_id = 0,
|
|
.id.uuid.len = ESP_UUID_LEN_16,
|
|
.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID,
|
|
};
|
|
esp_ble_gatts_create_service(gatts_if, &service_id, GATTS_NUM_HANDLE_TEST);
|
|
break;
|
|
|
|
case ESP_GATTS_CREATE_EVT:
|
|
ESP_LOGI(TAG, "CREATE_SERVICE_EVT, status %d, service_handle %d", param->create.status, param->create.service_handle);
|
|
service_handle = param->create.service_handle;
|
|
|
|
esp_ble_gatts_start_service(service_handle);
|
|
|
|
// Add characteristics
|
|
esp_bt_uuid_t char_uuid;
|
|
char_uuid.len = ESP_UUID_LEN_16;
|
|
|
|
// Config characteristic
|
|
char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_CONFIG;
|
|
esp_ble_gatts_add_char(service_handle, &char_uuid,
|
|
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
|
|
ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE,
|
|
NULL, NULL);
|
|
|
|
// Mode characteristic
|
|
char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_MODE;
|
|
esp_ble_gatts_add_char(service_handle, &char_uuid,
|
|
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
|
|
ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY,
|
|
NULL, NULL);
|
|
|
|
// PWM emulation characteristic
|
|
char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_PWM;
|
|
esp_ble_gatts_add_char(service_handle, &char_uuid,
|
|
ESP_GATT_PERM_WRITE,
|
|
ESP_GATT_CHAR_PROP_BIT_WRITE,
|
|
NULL, NULL);
|
|
|
|
// OTA characteristic
|
|
char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_OTA;
|
|
esp_ble_gatts_add_char(service_handle, &char_uuid,
|
|
ESP_GATT_PERM_WRITE,
|
|
ESP_GATT_CHAR_PROP_BIT_WRITE,
|
|
NULL, NULL);
|
|
break;
|
|
|
|
case ESP_GATTS_ADD_CHAR_EVT:
|
|
ESP_LOGI(TAG, "ADD_CHAR_EVT, status %d, char_handle %d", param->add_char.status, param->add_char.attr_handle);
|
|
|
|
// Store handles
|
|
if (param->add_char.char_uuid.uuid.uuid16 == GATTS_CHAR_UUID_CONFIG)
|
|
{
|
|
char_handles.config_handle = param->add_char.attr_handle;
|
|
}
|
|
else if (param->add_char.char_uuid.uuid.uuid16 == GATTS_CHAR_UUID_MODE)
|
|
{
|
|
char_handles.mode_handle = param->add_char.attr_handle;
|
|
}
|
|
else if (param->add_char.char_uuid.uuid.uuid16 == GATTS_CHAR_UUID_PWM)
|
|
{
|
|
char_handles.pwm_handle = param->add_char.attr_handle;
|
|
}
|
|
else if (param->add_char.char_uuid.uuid.uuid16 == GATTS_CHAR_UUID_OTA)
|
|
{
|
|
char_handles.ota_handle = param->add_char.attr_handle;
|
|
}
|
|
break;
|
|
|
|
case ESP_GATTS_CONNECT_EVT:
|
|
ESP_LOGI(TAG, "BLE device connected");
|
|
conn_id_global = param->connect.conn_id;
|
|
ble_connected = true;
|
|
|
|
// Stop timeout timer when connected
|
|
if (ble_timeout_timer)
|
|
{
|
|
esp_timer_stop(ble_timeout_timer);
|
|
}
|
|
break;
|
|
|
|
case ESP_GATTS_DISCONNECT_EVT:
|
|
ESP_LOGI(TAG, "BLE device disconnected");
|
|
ble_connected = false;
|
|
|
|
// Restart advertising and timeout
|
|
if (ble_enabled)
|
|
{
|
|
esp_ble_gap_start_advertising(&adv_params);
|
|
start_ble_timeout();
|
|
}
|
|
break;
|
|
|
|
case ESP_GATTS_READ_EVT:
|
|
ESP_LOGI(TAG, "GATTS_READ_EVT, handle %d", param->read.handle);
|
|
|
|
esp_gatt_rsp_t rsp;
|
|
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
|
|
rsp.attr_value.handle = param->read.handle;
|
|
|
|
if (param->read.handle == char_handles.config_handle)
|
|
{
|
|
rsp.attr_value.len = sizeof(controller_config_t);
|
|
memcpy(rsp.attr_value.value, ¤t_config, sizeof(controller_config_t));
|
|
}
|
|
else if (param->read.handle == char_handles.mode_handle)
|
|
{
|
|
rsp.attr_value.len = 1;
|
|
rsp.attr_value.value[0] = current_animation_mode;
|
|
}
|
|
|
|
esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id,
|
|
ESP_GATT_OK, &rsp);
|
|
break;
|
|
|
|
case ESP_GATTS_WRITE_EVT:
|
|
ESP_LOGI(TAG, "GATTS_WRITE_EVT, handle %d, len %d", param->write.handle, param->write.len);
|
|
|
|
if (param->write.handle == char_handles.config_handle)
|
|
{
|
|
// Update configuration
|
|
if (param->write.len == sizeof(controller_config_t))
|
|
{
|
|
controller_config_t new_config;
|
|
memcpy(&new_config, param->write.value, sizeof(controller_config_t));
|
|
control_update_config(&new_config);
|
|
}
|
|
}
|
|
else if (param->write.handle == char_handles.mode_handle)
|
|
{
|
|
// Set animation mode
|
|
if (param->write.len == 1)
|
|
{
|
|
control_set_animation_mode(param->write.value[0]);
|
|
}
|
|
}
|
|
else if (param->write.handle == char_handles.pwm_handle)
|
|
{
|
|
// Emulate PWM pulse
|
|
control_emulate_pwm_pulse();
|
|
}
|
|
else if (param->write.handle == char_handles.ota_handle)
|
|
{
|
|
// Handle OTA data
|
|
if (ota_bytes_written == 0)
|
|
{
|
|
// First packet - start OTA
|
|
ESP_LOGI(TAG, "Starting OTA update...");
|
|
update_partition = esp_ota_get_next_update_partition(NULL);
|
|
if (update_partition == NULL)
|
|
{
|
|
ESP_LOGE(TAG, "No OTA partition found");
|
|
break;
|
|
}
|
|
|
|
esp_err_t err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle);
|
|
if (err != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "OTA begin failed: %s", esp_err_to_name(err));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Write OTA data
|
|
esp_err_t err = esp_ota_write(update_handle, param->write.value, param->write.len);
|
|
if (err != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "OTA write failed: %s", esp_err_to_name(err));
|
|
esp_ota_abort(update_handle);
|
|
ota_bytes_written = 0;
|
|
break;
|
|
}
|
|
|
|
ota_bytes_written += param->write.len;
|
|
ESP_LOGI(TAG, "OTA progress: %d bytes", ota_bytes_written);
|
|
|
|
// Check if this is the last packet (indicated by packet size < MTU)
|
|
if (param->write.len < 512)
|
|
{
|
|
ESP_LOGI(TAG, "OTA complete, total bytes: %d", ota_bytes_written);
|
|
|
|
err = esp_ota_end(update_handle);
|
|
if (err != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "OTA end failed: %s", esp_err_to_name(err));
|
|
break;
|
|
}
|
|
|
|
err = esp_ota_set_boot_partition(update_partition);
|
|
if (err != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "OTA set boot partition failed: %s", esp_err_to_name(err));
|
|
break;
|
|
}
|
|
|
|
// Reset configuration
|
|
control_reset_config();
|
|
|
|
ESP_LOGI(TAG, "OTA successful, restarting...");
|
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
|
esp_restart();
|
|
}
|
|
}
|
|
|
|
if (!param->write.is_prep)
|
|
{
|
|
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id,
|
|
ESP_GATT_OK, NULL);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Initialize BLE
|
|
static esp_err_t init_ble(void)
|
|
{
|
|
if (!ble_enabled)
|
|
{
|
|
ESP_LOGI(TAG, "BLE disabled by configuration");
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t ret;
|
|
|
|
// Initialize BT controller
|
|
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
|
ret = esp_bt_controller_init(&bt_cfg);
|
|
if (ret)
|
|
{
|
|
ESP_LOGE(TAG, "BT controller init failed: %s", esp_err_to_name(ret));
|
|
return ret;
|
|
}
|
|
|
|
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
|
if (ret)
|
|
{
|
|
ESP_LOGE(TAG, "BT controller enable failed: %s", esp_err_to_name(ret));
|
|
return ret;
|
|
}
|
|
|
|
ret = esp_bluedroid_init();
|
|
if (ret)
|
|
{
|
|
ESP_LOGE(TAG, "Bluedroid init failed: %s", esp_err_to_name(ret));
|
|
return ret;
|
|
}
|
|
|
|
ret = esp_bluedroid_enable();
|
|
if (ret)
|
|
{
|
|
ESP_LOGE(TAG, "Bluedroid enable failed: %s", esp_err_to_name(ret));
|
|
return ret;
|
|
}
|
|
|
|
// Register callbacks
|
|
esp_ble_gatts_register_callback(gatts_event_handler);
|
|
esp_ble_gap_register_callback(gap_event_handler);
|
|
esp_ble_gatts_app_register(0);
|
|
|
|
// Set MTU
|
|
esp_ble_gatt_set_local_mtu(517);
|
|
|
|
// Start timeout timer
|
|
start_ble_timeout();
|
|
|
|
esp_ble_gatts_app_register(0);
|
|
|
|
vTaskDelay(pdMS_TO_TICKS(100));
|
|
esp_ble_gap_start_advertising(&adv_params);
|
|
|
|
ESP_LOGI(TAG, "BLE initialized");
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
// Main initialization
|
|
esp_err_t control_init(void)
|
|
{
|
|
esp_err_t ret;
|
|
|
|
ESP_LOGI(TAG, "Initializing LED Controller...");
|
|
|
|
// Initialize NVS
|
|
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);
|
|
|
|
// Load configuration
|
|
load_config_from_nvs();
|
|
|
|
// Initialize LED strips
|
|
ret = led_init(current_config.led_pin_strip_a, current_config.led_pin_strip_b,
|
|
DEFAULT_NUM_LEDS_A, DEFAULT_NUM_LEDS_B);
|
|
if (ret != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "LED init failed: %s", esp_err_to_name(ret));
|
|
return ret;
|
|
}
|
|
|
|
// Initialize animation system
|
|
ret = animation_init();
|
|
if (ret != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "Animation init failed: %s", esp_err_to_name(ret));
|
|
return ret;
|
|
}
|
|
|
|
// Initialize RC signal
|
|
ret = rcsignal_init(current_config.pwm_pin);
|
|
if (ret != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "RC signal init failed: %s", esp_err_to_name(ret));
|
|
return ret;
|
|
}
|
|
|
|
// Register mode change callback
|
|
rcsignal_register_callback(on_mode_change);
|
|
|
|
// Initialize BLE
|
|
ret = init_ble();
|
|
if (ret != ESP_OK)
|
|
{
|
|
ESP_LOGE(TAG, "BLE init failed: %s", esp_err_to_name(ret));
|
|
return ret;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "Control system initialized successfully");
|
|
|
|
return ESP_OK;
|
|
}
|