Some checks failed
ESP-IDF Build / build (esp32, latest) (push) Failing after 46s
ESP-IDF Build / build (esp32, release-v5.4) (push) Failing after 46s
ESP-IDF Build / build (esp32, release-v5.5) (push) Failing after 45s
ESP-IDF Build / build (esp32c3, latest) (push) Failing after 45s
ESP-IDF Build / build (esp32c3, release-v5.4) (push) Failing after 45s
ESP-IDF Build / build (esp32c3, release-v5.5) (push) Failing after 45s
ESP-IDF Build / build (esp32c5, latest) (push) Failing after 45s
ESP-IDF Build / build (esp32c5, release-v5.4) (push) Failing after 45s
ESP-IDF Build / build (esp32c5, release-v5.5) (push) Failing after 45s
ESP-IDF Build / build (esp32c6, latest) (push) Failing after 45s
ESP-IDF Build / build (esp32c6, release-v5.4) (push) Failing after 45s
ESP-IDF Build / build (esp32c6, release-v5.5) (push) Failing after 45s
ESP-IDF Build / build (esp32h2, latest) (push) Failing after 46s
ESP-IDF Build / build (esp32h2, release-v5.4) (push) Failing after 45s
ESP-IDF Build / build (esp32h2, release-v5.5) (push) Failing after 45s
ESP-IDF Build / build (esp32p4, latest) (push) Failing after 45s
ESP-IDF Build / build (esp32p4, release-v5.4) (push) Failing after 45s
ESP-IDF Build / build (esp32p4, release-v5.5) (push) Failing after 52s
ESP-IDF Build / build (esp32s3, latest) (push) Failing after 56s
ESP-IDF Build / build (esp32s3, release-v5.4) (push) Failing after 46s
ESP-IDF Build / build (esp32s3, release-v5.5) (push) Failing after 45s
Signed-off-by: Peter Siegmund <mars3142@users.noreply.github.com>
553 lines
17 KiB
C
553 lines
17 KiB
C
#include "include/remote_control.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#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"
|
|
#include "host/ble_hs.h"
|
|
#include "host/ble_sm.h"
|
|
#include "host/ble_uuid.h"
|
|
#include "include/char_desc.h"
|
|
#include "include/device_service.h"
|
|
#include "include/light_service.h"
|
|
#include "include/uart_service.h"
|
|
#include "nimble/nimble_port.h"
|
|
#include "nimble/nimble_port_freertos.h"
|
|
#include "sdkconfig.h"
|
|
#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);
|
|
static const ble_uuid16_t gatt_svr_svc_light_uuid = BLE_UUID16_INIT(0xA000);
|
|
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
|
|
static struct ble_gatt_dsc_def beacon_char_desc[] = {
|
|
{
|
|
// User Description Descriptor
|
|
.uuid = BLE_UUID16_DECLARE(0x2901),
|
|
.att_flags = BLE_ATT_F_READ,
|
|
.access_cb = gatt_svr_desc_beacon_user_desc_access,
|
|
},
|
|
{
|
|
// Presentation Format Descriptor
|
|
.uuid = BLE_UUID16_DECLARE(0x2904),
|
|
.att_flags = BLE_ATT_F_READ,
|
|
.access_cb = gatt_svr_desc_presentation_bool_access,
|
|
},
|
|
{
|
|
// Valid Range Descriptor
|
|
.uuid = BLE_UUID16_DECLARE(0x2906),
|
|
.att_flags = BLE_ATT_F_READ,
|
|
.access_cb = gatt_svr_desc_valid_range_bool_access,
|
|
},
|
|
{0},
|
|
};
|
|
|
|
// Descriptors for the LED Characteristic
|
|
static struct ble_gatt_dsc_def led_char_desc[] = {
|
|
{
|
|
// User Description Descriptor
|
|
.uuid = BLE_UUID16_DECLARE(0x2901),
|
|
.att_flags = BLE_ATT_F_READ,
|
|
.access_cb = gatt_svr_desc_led_user_desc_access,
|
|
},
|
|
{
|
|
// Presentation Format Descriptor
|
|
.uuid = BLE_UUID16_DECLARE(0x2904),
|
|
.att_flags = BLE_ATT_F_READ,
|
|
.access_cb = gatt_svr_desc_presentation_bool_access,
|
|
},
|
|
{
|
|
// Valid Range Descriptor
|
|
.uuid = BLE_UUID16_DECLARE(0x2906),
|
|
.att_flags = BLE_ATT_F_READ,
|
|
.access_cb = gatt_svr_desc_valid_range_bool_access,
|
|
},
|
|
{0},
|
|
};
|
|
|
|
// Array of pointers to service definitions
|
|
static const struct ble_gatt_svc_def gatt_svcs[] = {
|
|
{
|
|
// Device Information Service
|
|
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
|
.uuid = &gatt_svr_svc_device_uuid.u,
|
|
.characteristics =
|
|
(struct ble_gatt_chr_def[]){
|
|
{
|
|
// Manufacturer String
|
|
.uuid = BLE_UUID16_DECLARE(0x2A29),
|
|
.flags = BLE_GATT_CHR_F_READ,
|
|
.access_cb = gatt_svr_chr_device_manufacturer_access,
|
|
},
|
|
{
|
|
// Hardware Revision String
|
|
.uuid = BLE_UUID16_DECLARE(0x2A27),
|
|
.flags = BLE_GATT_CHR_F_READ,
|
|
.access_cb = gatt_svr_chr_device_hardware_access,
|
|
},
|
|
{
|
|
// Firmware Revision String
|
|
.uuid = BLE_UUID16_DECLARE(0x2A26),
|
|
.flags = BLE_GATT_CHR_F_READ,
|
|
.access_cb = gatt_svr_chr_device_firmware_access,
|
|
},
|
|
{
|
|
// Device Name
|
|
.uuid = BLE_UUID16_DECLARE(0x2A00),
|
|
.flags = BLE_GATT_CHR_F_READ,
|
|
.access_cb = gatt_svr_chr_device_name_access,
|
|
},
|
|
{0},
|
|
},
|
|
},
|
|
{
|
|
// Light Service
|
|
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
|
.uuid = &gatt_svr_svc_light_uuid.u,
|
|
.characteristics =
|
|
(struct ble_gatt_chr_def[]){
|
|
{
|
|
// Beacon Characteristic
|
|
.uuid = BLE_UUID16_DECLARE(0xBEA0),
|
|
.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 | BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_WRITE_ENC,
|
|
.access_cb = gatt_svr_chr_light_led_access,
|
|
.descriptors = led_char_desc,
|
|
},
|
|
{0},
|
|
},
|
|
},
|
|
{
|
|
// UART Service
|
|
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
|
.uuid = &gatt_svr_svc_uart_uuid.u,
|
|
.characteristics =
|
|
(struct ble_gatt_chr_def[]){
|
|
{
|
|
// Characteristic: RX (Receiving of data)
|
|
.uuid = &gatt_svr_chr_uart_rx_uuid.u,
|
|
.access_cb = gatt_svr_chr_uart_access,
|
|
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_NO_RSP,
|
|
},
|
|
{
|
|
// Characteristic: TX (Sending of data)
|
|
.uuid = &gatt_svr_chr_uart_tx_uuid.u,
|
|
.access_cb = gatt_svr_chr_uart_access,
|
|
.val_handle = &tx_chr_val_handle,
|
|
.flags = BLE_GATT_CHR_F_NOTIFY,
|
|
},
|
|
{0},
|
|
},
|
|
},
|
|
// Settings Service (empty for now)
|
|
{
|
|
.type = BLE_GATT_SVC_TYPE_PRIMARY,
|
|
.uuid = &gatt_svr_svc_settings_uuid.u,
|
|
.characteristics = (struct ble_gatt_chr_def[]){{0}},
|
|
},
|
|
{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:
|
|
/* 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);
|
|
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();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Define the BLE connection
|
|
static void ble_app_advertise(void)
|
|
{
|
|
int ret;
|
|
|
|
// GAP - advertising definition
|
|
struct ble_hs_adv_fields fields;
|
|
memset(&fields, 0, sizeof(fields));
|
|
uint8_t mfg_data[] = {0xDE, 0xC0, 0x05, 0x10, 0x20, 0x25};
|
|
static const ble_uuid16_t services[] = {gatt_svr_svc_device_uuid, gatt_svr_svc_light_uuid,
|
|
gatt_svr_svc_settings_uuid};
|
|
|
|
fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
|
|
fields.uuids16 = services;
|
|
fields.num_uuids16 = sizeof(services) / sizeof(services[0]);
|
|
fields.uuids16_is_complete = 1;
|
|
fields.mfg_data = mfg_data;
|
|
fields.mfg_data_len = sizeof(mfg_data);
|
|
|
|
ret = ble_gap_adv_set_fields(&fields);
|
|
if (ret != 0)
|
|
{
|
|
ESP_LOGE(TAG, "Failed to set advertising data (err: %d)", ret);
|
|
return;
|
|
}
|
|
|
|
// 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
|
|
ret = ble_gap_adv_start(ble_addr_type, NULL, BLE_HS_FOREVER, &adv_params, ble_gap_event, NULL);
|
|
if (ret != 0)
|
|
{
|
|
ESP_LOGE(TAG, "Advertising failed to start (err %d)", ret);
|
|
return;
|
|
}
|
|
|
|
// --- Configure Scan Response Data (SCAN_RSP) ---
|
|
struct ble_hs_adv_fields scan_rsp_fields;
|
|
memset(&scan_rsp_fields, 0, sizeof(scan_rsp_fields));
|
|
|
|
// Get the device name
|
|
const char *device_name;
|
|
device_name = ble_svc_gap_device_name();
|
|
scan_rsp_fields.name = (uint8_t *)device_name;
|
|
scan_rsp_fields.name_len = strlen(device_name);
|
|
scan_rsp_fields.name_is_complete = 1;
|
|
|
|
// Optionally, add TX power level to scan response
|
|
scan_rsp_fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
|
scan_rsp_fields.tx_pwr_lvl_is_present = 1;
|
|
|
|
ret = ble_gap_adv_rsp_set_fields(&scan_rsp_fields);
|
|
if (ret != 0)
|
|
{
|
|
ESP_LOGE(TAG, "Error setting scan response data; rc=%d", ret);
|
|
return;
|
|
}
|
|
}
|
|
|
|
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};
|
|
|
|
/* 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);
|
|
}
|
|
|
|
char formatted_name[32];
|
|
snprintf(formatted_name, sizeof(formatted_name), "Lighthouse %02X%02X", ble_addr[4], ble_addr[5]);
|
|
ble_svc_gap_device_name_set(formatted_name);
|
|
|
|
// Start Advertising
|
|
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)
|
|
{
|
|
esp_err_t ret;
|
|
|
|
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
|
|
|
|
xTaskCreate(uart_tx_task, "uart_tx", 2048, NULL, 1, NULL);
|
|
}
|