添加 USB 主机、BLE 和 Socket 数据端口驱动程序:
- 支持大容量存储类(MSC)的 USB 主机驱动程序 - BLE SPP(串行端口配置文件)客户端和服务器实现 - Socket(UDP/TCP)数据端口驱动程序 - 相关的 shell 接口用于配置和测试
This commit is contained in:
970
app/drivers/data_port/ble_spp/ble_spp_client.c
Normal file
970
app/drivers/data_port/ble_spp/ble_spp_client.c
Normal file
@@ -0,0 +1,970 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#include "esp_bt.h"
|
||||
#include "esp_bt_device.h"
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "esp_gattc_api.h"
|
||||
#include "esp_gatt_defs.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_gatt_common_api.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
|
||||
#include "drivers/data_port/ble_spp/ble_spp_client.h"
|
||||
#include "os/os.h"
|
||||
|
||||
#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF
|
||||
#define SYS_LOG_DOMAIN "GATTC_SPP"
|
||||
#define CONS_ABORT()
|
||||
#include "sys_log.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
os_pipe_t pipe_obj;
|
||||
} __ble_port_data_t;
|
||||
|
||||
static __ble_port_data_t s_data;
|
||||
static ble_client_status_t s_status;
|
||||
static ble_client_connect_cb s_connect_cb;
|
||||
static bool s_scan_start_en;
|
||||
static bool s_scan_start_flag;
|
||||
|
||||
#define SYS_LOG_BDA(BDA, SIZE) \
|
||||
do \
|
||||
{ \
|
||||
char buf[0x40]; \
|
||||
int len = 0; \
|
||||
for (int i = 0; i < SIZE - 1; i++) \
|
||||
{ \
|
||||
len += SYS_SPRINT(&buf[len], "%02X", BDA[i]); \
|
||||
if (len >= sizeof(buf) - 1) \
|
||||
{ \
|
||||
break; \
|
||||
} \
|
||||
if (i + 1 < SIZE) \
|
||||
{ \
|
||||
len += SYS_SPRINT(&buf[len], ":"); \
|
||||
} \
|
||||
} \
|
||||
buf[len] = '\0'; \
|
||||
SYS_LOG_DBG("%s", buf); \
|
||||
} while (0)
|
||||
|
||||
#define SYS_LOG_STR(STR, LEN) \
|
||||
do \
|
||||
{ \
|
||||
char buf[0x40]; \
|
||||
int len = sizeof(buf) - 1 < LEN ? sizeof(buf) - 1 : LEN; \
|
||||
memcpy(buf, STR, len); \
|
||||
buf[len] = '\0'; \
|
||||
SYS_LOG_DBG("%s", buf); \
|
||||
} while (0)
|
||||
|
||||
#define PROFILE_NUM 1
|
||||
#define PROFILE_APP_ID 0
|
||||
#define BT_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x"
|
||||
#define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]
|
||||
#define ESP_GATT_SPP_SERVICE_UUID 0xABF0
|
||||
#define SCAN_ALL_THE_TIME 0
|
||||
|
||||
struct gattc_profile_inst
|
||||
{
|
||||
esp_gattc_cb_t gattc_cb;
|
||||
uint16_t gattc_if;
|
||||
uint16_t app_id;
|
||||
uint16_t conn_id;
|
||||
uint16_t service_start_handle;
|
||||
uint16_t service_end_handle;
|
||||
uint16_t char_handle;
|
||||
esp_bd_addr_t remote_bda;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SPP_IDX_SVC,
|
||||
|
||||
SPP_IDX_SPP_DATA_RECV_VAL,
|
||||
|
||||
SPP_IDX_SPP_DATA_NTY_VAL,
|
||||
SPP_IDX_SPP_DATA_NTF_CFG,
|
||||
|
||||
SPP_IDX_SPP_COMMAND_VAL,
|
||||
|
||||
SPP_IDX_SPP_STATUS_VAL,
|
||||
SPP_IDX_SPP_STATUS_CFG,
|
||||
|
||||
#ifdef SUPPORT_HEARTBEAT
|
||||
SPP_IDX_SPP_HEARTBEAT_VAL,
|
||||
SPP_IDX_SPP_HEARTBEAT_CFG,
|
||||
#endif
|
||||
|
||||
SPP_IDX_NB,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 当 ble_client_start() 设置的回调函数为 NULL 时默认的回调函数
|
||||
*
|
||||
* @param status
|
||||
*/
|
||||
static void _cb_hdl_default(ble_client_status_t status)
|
||||
{
|
||||
static char *const str[] = {
|
||||
[BLE_CLIENT_STATUS_STOP] = "stop",
|
||||
[BLE_CLIENT_STATUS_SCANNING] = "scanning",
|
||||
[BLE_CLIENT_STATUS_CONNECTED] = "connected",
|
||||
[BLE_CLIENT_STATUS_DISCONNECTED] = "disconnected",
|
||||
};
|
||||
if (status < sizeof(str) / sizeof(str[0]) && str[status] != NULL)
|
||||
{
|
||||
SYS_LOG_DBG("connect status: %s", str[status]);
|
||||
}
|
||||
else
|
||||
{
|
||||
SYS_LOG_WRN("unknow status: %d", status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新连接状态并并执行由 ble_client_start() 设置的回调函数
|
||||
*
|
||||
* @param new_status 设置新的连接状态
|
||||
*/
|
||||
static void _port_cb(ble_client_status_t new_status)
|
||||
{
|
||||
if (s_status != new_status)
|
||||
{
|
||||
s_status = new_status;
|
||||
ble_client_connect_cb cb = s_connect_cb;
|
||||
if (cb)
|
||||
{
|
||||
cb(s_status);
|
||||
}
|
||||
else
|
||||
{
|
||||
_cb_hdl_default(s_status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启动扫描
|
||||
*
|
||||
* @param duration 单位为秒
|
||||
*/
|
||||
static void _gap_start(uint32_t duration)
|
||||
{
|
||||
if (s_scan_start_en && s_scan_start_flag)
|
||||
{
|
||||
if (s_status != BLE_CLIENT_STATUS_SCANNING)
|
||||
{
|
||||
esp_ble_gap_start_scanning(duration);
|
||||
_port_cb(BLE_CLIENT_STATUS_SCANNING);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_port_cb(BLE_CLIENT_STATUS_STOP);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 关闭扫描
|
||||
*/
|
||||
static void _gap_stop(void)
|
||||
{
|
||||
if (s_status == BLE_CLIENT_STATUS_SCANNING)
|
||||
{
|
||||
s_status = BLE_CLIENT_STATUS_STOP;
|
||||
esp_ble_gap_stop_scanning();
|
||||
}
|
||||
}
|
||||
|
||||
/// Declare static functions
|
||||
static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
|
||||
static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
||||
static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
||||
|
||||
/* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */
|
||||
static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = {
|
||||
[PROFILE_APP_ID] = {
|
||||
.gattc_cb = gattc_profile_event_handler,
|
||||
.gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
|
||||
},
|
||||
};
|
||||
|
||||
static esp_ble_scan_params_t ble_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = 0x50,
|
||||
.scan_window = 0x30,
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE};
|
||||
|
||||
static char *s_device_name;
|
||||
static bool is_connect = false;
|
||||
static uint16_t spp_conn_id = 0;
|
||||
static uint16_t spp_mtu_size = 23;
|
||||
static uint16_t cmd = 0;
|
||||
static uint16_t spp_srv_start_handle = 0;
|
||||
static uint16_t spp_srv_end_handle = 0;
|
||||
static uint16_t spp_gattc_if = 0xff;
|
||||
static char *notify_value_p = NULL;
|
||||
static int notify_value_offset = 0;
|
||||
static int notify_value_count = 0;
|
||||
static uint16_t count = SPP_IDX_NB;
|
||||
static esp_gattc_db_elem_t *db = NULL;
|
||||
static esp_ble_gap_cb_param_t scan_rst;
|
||||
static QueueHandle_t cmd_reg_queue = NULL;
|
||||
QueueHandle_t spp_uart_queue = NULL;
|
||||
|
||||
#ifdef SUPPORT_HEARTBEAT
|
||||
static uint8_t heartbeat_s[9] = {'E', 's', 'p', 'r', 'e', 's', 's', 'i', 'f'};
|
||||
static QueueHandle_t cmd_heartbeat_queue = NULL;
|
||||
#endif
|
||||
|
||||
static esp_bt_uuid_t spp_service_uuid = {
|
||||
.len = ESP_UUID_LEN_16,
|
||||
.uuid = {
|
||||
.uuid16 = ESP_GATT_SPP_SERVICE_UUID,
|
||||
},
|
||||
};
|
||||
|
||||
static void notify_event_handler(esp_ble_gattc_cb_param_t *p_data)
|
||||
{
|
||||
uint8_t handle = 0;
|
||||
|
||||
if (p_data->notify.is_notify == true)
|
||||
{
|
||||
SYS_LOG_DBG("+NOTIFY:handle = %d,length = %d ", p_data->notify.handle, p_data->notify.value_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
SYS_LOG_DBG("+INDICATE:handle = %d,length = %d ", p_data->notify.handle, p_data->notify.value_len);
|
||||
}
|
||||
handle = p_data->notify.handle;
|
||||
if (db == NULL)
|
||||
{
|
||||
SYS_LOG_ERR(" db is NULL");
|
||||
return;
|
||||
}
|
||||
if (handle == db[SPP_IDX_SPP_DATA_NTY_VAL].attribute_handle)
|
||||
{
|
||||
SYS_LOG_DUMP(p_data->notify.value, p_data->notify.value_len, 1, 0);
|
||||
os_pipe_fifo_fill(&s_data.pipe_obj, p_data->notify.value, p_data->notify.value_len);
|
||||
}
|
||||
else if (handle == ((db + SPP_IDX_SPP_STATUS_VAL)->attribute_handle))
|
||||
{
|
||||
SYS_LOG_STR((char *)p_data->notify.value, p_data->notify.value_len);
|
||||
// TODO:server notify status characteristic
|
||||
}
|
||||
else
|
||||
{
|
||||
SYS_LOG_STR((char *)p_data->notify.value, p_data->notify.value_len);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_gattc_srv_db(void)
|
||||
{
|
||||
is_connect = false;
|
||||
spp_gattc_if = 0xff;
|
||||
spp_conn_id = 0;
|
||||
spp_mtu_size = 23;
|
||||
cmd = 0;
|
||||
spp_srv_start_handle = 0;
|
||||
spp_srv_end_handle = 0;
|
||||
notify_value_p = NULL;
|
||||
notify_value_offset = 0;
|
||||
notify_value_count = 0;
|
||||
if (db)
|
||||
{
|
||||
free(db);
|
||||
db = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
|
||||
{
|
||||
uint8_t *adv_name = NULL;
|
||||
uint8_t adv_name_len = 0;
|
||||
esp_err_t err;
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
||||
{
|
||||
SYS_LOG_DBG("ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT");
|
||||
if ((err = param->scan_param_cmpl.status) != ESP_BT_STATUS_SUCCESS)
|
||||
{
|
||||
SYS_LOG_ERR("Scan param set failed: %s", esp_err_to_name(err));
|
||||
break;
|
||||
}
|
||||
s_scan_start_en = true;
|
||||
_gap_start(0xFFFF);
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
||||
// scan start complete event to indicate scan start successfully or failed
|
||||
if ((err = param->scan_start_cmpl.status) != ESP_BT_STATUS_SUCCESS)
|
||||
{
|
||||
SYS_LOG_ERR("ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: Scan start failed: %s", esp_err_to_name(err));
|
||||
break;
|
||||
}
|
||||
SYS_LOG_DBG("ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: Scan start successed. Target name: '%s'", s_device_name);
|
||||
break;
|
||||
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
|
||||
if ((err = param->scan_stop_cmpl.status) != ESP_BT_STATUS_SUCCESS)
|
||||
{
|
||||
SYS_LOG_ERR("ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: Scan stop failed: %s", esp_err_to_name(err));
|
||||
break;
|
||||
}
|
||||
if (is_connect == false)
|
||||
{
|
||||
esp_ble_gattc_open(gl_profile_tab[PROFILE_APP_ID].gattc_if, scan_rst.scan_rst.bda, scan_rst.scan_rst.ble_addr_type, true);
|
||||
SYS_LOG_DBG("ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: Scan stop successed");
|
||||
SYS_LOG_DBG("Connect to the remote device.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_port_cb(BLE_CLIENT_STATUS_STOP);
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
||||
{
|
||||
esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
|
||||
|
||||
static uint8_t con_scan_count;
|
||||
|
||||
switch (scan_result->scan_rst.search_evt)
|
||||
{
|
||||
case ESP_GAP_SEARCH_INQ_RES_EVT:
|
||||
adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
|
||||
if (s_device_name != NULL)
|
||||
{
|
||||
if (adv_name != NULL && strlen(s_device_name) == adv_name_len)
|
||||
{
|
||||
if (strncmp((char *)adv_name, s_device_name, adv_name_len) == 0)
|
||||
{
|
||||
memcpy(&(scan_rst), scan_result, sizeof(esp_ble_gap_cb_param_t));
|
||||
_gap_stop();
|
||||
SYS_LOG_DBG("");
|
||||
SYS_LOG_BDA(scan_result->scan_rst.bda, 6);
|
||||
SYS_LOG_DBG("Searched Adv Data Len %d, Scan Response Len %d", scan_result->scan_rst.adv_data_len, scan_result->scan_rst.scan_rsp_len);
|
||||
SYS_LOG_DBG("Searched Device Name Len %d", adv_name_len);
|
||||
SYS_LOG_STR(adv_name, adv_name_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_gap_stop();
|
||||
}
|
||||
if (con_scan_count++ == 100)
|
||||
{
|
||||
os_thread_sleep(100);
|
||||
}
|
||||
break;
|
||||
case ESP_GAP_SEARCH_INQ_CMPL_EVT:
|
||||
SYS_LOG_DBG("ESP_GAP_SEARCH_INQ_CMPL_EVT");
|
||||
break;
|
||||
default:
|
||||
SYS_LOG_DBG("scan_result->scan_rst.search_evt = %d", scan_result->scan_rst.search_evt);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
|
||||
if ((err = param->adv_stop_cmpl.status) != ESP_BT_STATUS_SUCCESS)
|
||||
{
|
||||
SYS_LOG_ERR("ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: Adv stop failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
else
|
||||
{
|
||||
SYS_LOG_DBG("ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: Stop adv successfully");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SYS_LOG_DBG("event = %d", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
|
||||
{
|
||||
SYS_LOG_DBG("EVT %d, gattc if %d", event, gattc_if);
|
||||
|
||||
/* If event is register event, store the gattc_if for each profile */
|
||||
if (event == ESP_GATTC_REG_EVT)
|
||||
{
|
||||
if (param->reg.status == ESP_GATT_OK)
|
||||
{
|
||||
gl_profile_tab[param->reg.app_id].gattc_if = gattc_if;
|
||||
}
|
||||
else
|
||||
{
|
||||
SYS_LOG_DBG("Reg app failed, app_id %04x, status %d", param->reg.app_id, param->reg.status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* If the gattc_if equal to profile A, call profile A cb handler,
|
||||
* so here call each profile's callback */
|
||||
do
|
||||
{
|
||||
int idx;
|
||||
for (idx = 0; idx < PROFILE_NUM; idx++)
|
||||
{
|
||||
if (gattc_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
|
||||
gattc_if == gl_profile_tab[idx].gattc_if)
|
||||
{
|
||||
if (gl_profile_tab[idx].gattc_cb)
|
||||
{
|
||||
gl_profile_tab[idx].gattc_cb(event, gattc_if, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (event == ESP_GATTC_CLOSE_EVT)
|
||||
{
|
||||
SYS_LOG_DBG("ESP_GATTC_CLOSE_EVT");
|
||||
if (s_scan_start_flag)
|
||||
{
|
||||
_port_cb(BLE_CLIENT_STATUS_DISCONNECTED);
|
||||
}
|
||||
_gap_start(SCAN_ALL_THE_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
|
||||
{
|
||||
esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case ESP_GATTC_REG_EVT:
|
||||
SYS_LOG_DBG("ESP_GATTC_REG_EVT: set scan params");
|
||||
esp_ble_gap_set_scan_params(&ble_scan_params);
|
||||
break;
|
||||
case ESP_GATTC_CONNECT_EVT:
|
||||
SYS_LOG_DBG("ESP_GATTC_CONNECT_EVT: conn_id=%d, gatt_if = %d", spp_conn_id, gattc_if);
|
||||
SYS_LOG_DBG("REMOTE BDA:");
|
||||
SYS_LOG_BDA(gl_profile_tab[PROFILE_APP_ID].remote_bda, sizeof(esp_bd_addr_t));
|
||||
spp_gattc_if = gattc_if;
|
||||
is_connect = true;
|
||||
spp_conn_id = p_data->connect.conn_id;
|
||||
memcpy(gl_profile_tab[PROFILE_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));
|
||||
esp_ble_gattc_search_service(spp_gattc_if, spp_conn_id, &spp_service_uuid);
|
||||
_port_cb(BLE_CLIENT_STATUS_CONNECTED);
|
||||
break;
|
||||
case ESP_GATTC_DISCONNECT_EVT:
|
||||
SYS_LOG_DBG("ESP_GATTC_DISCONNECT_EVT: disconnect");
|
||||
free_gattc_srv_db();
|
||||
break;
|
||||
case ESP_GATTC_SEARCH_RES_EVT:
|
||||
SYS_LOG_DBG("ESP_GATTC_SEARCH_RES_EVT: start_handle = %d, end_handle = %d, UUID:0x%04x", p_data->search_res.start_handle, p_data->search_res.end_handle, p_data->search_res.srvc_id.uuid.uuid.uuid16);
|
||||
spp_srv_start_handle = p_data->search_res.start_handle;
|
||||
spp_srv_end_handle = p_data->search_res.end_handle;
|
||||
break;
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT:
|
||||
SYS_LOG_DBG("ESP_GATTC_SEARCH_CMPL_EVT: conn_id = %x, status %d", spp_conn_id, p_data->search_cmpl.status);
|
||||
esp_ble_gattc_send_mtu_req(gattc_if, spp_conn_id);
|
||||
break;
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT:
|
||||
{
|
||||
if (p_data->reg_for_notify.status == ESP_GATT_OK)
|
||||
{
|
||||
SYS_LOG_DBG("ESP_GATTC_REG_FOR_NOTIFY_EVT: Index = %d,status = %d,handle = %d", cmd, p_data->reg_for_notify.status, p_data->reg_for_notify.handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
SYS_LOG_ERR("ESP_GATTC_REG_FOR_NOTIFY_EVT, status = %d", p_data->reg_for_notify.status);
|
||||
break;
|
||||
}
|
||||
uint16_t notify_en = 1;
|
||||
esp_ble_gattc_write_char_descr(
|
||||
spp_gattc_if,
|
||||
spp_conn_id,
|
||||
(db + cmd + 1)->attribute_handle,
|
||||
sizeof(notify_en),
|
||||
(uint8_t *)¬ify_en,
|
||||
ESP_GATT_WRITE_TYPE_RSP,
|
||||
ESP_GATT_AUTH_REQ_NONE);
|
||||
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT:
|
||||
SYS_LOG_DBG("ESP_GATTC_NOTIFY_EVT");
|
||||
notify_event_handler(p_data);
|
||||
break;
|
||||
case ESP_GATTC_READ_CHAR_EVT:
|
||||
SYS_LOG_DBG("ESP_GATTC_READ_CHAR_EVT");
|
||||
break;
|
||||
case ESP_GATTC_WRITE_CHAR_EVT:
|
||||
if (param->write.status == ESP_GATT_OK)
|
||||
{
|
||||
SYS_LOG_DBG("ESP_GATTC_WRITE_CHAR_EVT: status = %d, handle = %d", param->write.status, param->write.handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
SYS_LOG_ERR("ESP_GATTC_WRITE_CHAR_EVT, error status = %d", p_data->write.status);
|
||||
_gap_start(SCAN_ALL_THE_TIME);
|
||||
}
|
||||
break;
|
||||
case ESP_GATTC_PREP_WRITE_EVT:
|
||||
SYS_LOG_DBG("ESP_GATTC_PREP_WRITE_EVT");
|
||||
break;
|
||||
case ESP_GATTC_EXEC_EVT:
|
||||
SYS_LOG_DBG("ESP_GATTC_EXEC_EVT");
|
||||
break;
|
||||
case ESP_GATTC_WRITE_DESCR_EVT:
|
||||
if (p_data->write.status == ESP_GATT_OK)
|
||||
{
|
||||
SYS_LOG_DBG("ESP_GATTC_WRITE_DESCR_EVT: status =%d, handle = %d ", p_data->write.status, p_data->write.handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
SYS_LOG_ERR("ESP_GATTC_WRITE_DESCR_EVT, error status = %d", p_data->write.status);
|
||||
break;
|
||||
}
|
||||
switch (cmd)
|
||||
{
|
||||
case SPP_IDX_SPP_DATA_NTY_VAL:
|
||||
cmd = SPP_IDX_SPP_STATUS_VAL;
|
||||
xQueueSend(cmd_reg_queue, &cmd, 10 / portTICK_PERIOD_MS);
|
||||
break;
|
||||
case SPP_IDX_SPP_STATUS_VAL:
|
||||
#ifdef SUPPORT_HEARTBEAT
|
||||
cmd = SPP_IDX_SPP_HEARTBEAT_VAL;
|
||||
xQueueSend(cmd_reg_queue, &cmd, 10 / portTICK_PERIOD_MS);
|
||||
#endif
|
||||
break;
|
||||
#ifdef SUPPORT_HEARTBEAT
|
||||
case SPP_IDX_SPP_HEARTBEAT_VAL:
|
||||
xQueueSend(cmd_heartbeat_queue, &cmd, 10 / portTICK_PERIOD_MS);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
};
|
||||
break;
|
||||
case ESP_GATTC_CFG_MTU_EVT:
|
||||
if (p_data->cfg_mtu.status != ESP_OK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
SYS_LOG_DBG("ESP_GATTC_CFG_MTU_EVT: +MTU:%d", p_data->cfg_mtu.mtu);
|
||||
spp_mtu_size = p_data->cfg_mtu.mtu;
|
||||
|
||||
db = (esp_gattc_db_elem_t *)malloc(count * sizeof(esp_gattc_db_elem_t));
|
||||
if (db == NULL)
|
||||
{
|
||||
SYS_LOG_ERR("malloc db falied");
|
||||
break;
|
||||
}
|
||||
if (esp_ble_gattc_get_db(spp_gattc_if, spp_conn_id, spp_srv_start_handle, spp_srv_end_handle, db, &count) != ESP_GATT_OK)
|
||||
{
|
||||
SYS_LOG_ERR("get db falied");
|
||||
break;
|
||||
}
|
||||
if (count != SPP_IDX_NB)
|
||||
{
|
||||
SYS_LOG_ERR("get db count != SPP_IDX_NB, count = %d, SPP_IDX_NB = %d", count, SPP_IDX_NB);
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < SPP_IDX_NB; i++)
|
||||
{
|
||||
switch ((db + i)->type)
|
||||
{
|
||||
case ESP_GATT_DB_PRIMARY_SERVICE:
|
||||
SYS_LOG_DBG("attr_type = PRIMARY_SERVICE,attribute_handle=%d,start_handle=%d,end_handle=%d,properties=0x%x,uuid=0x%04x",
|
||||
(db + i)->attribute_handle, (db + i)->start_handle, (db + i)->end_handle, (db + i)->properties, (db + i)->uuid.uuid.uuid16);
|
||||
break;
|
||||
case ESP_GATT_DB_SECONDARY_SERVICE:
|
||||
SYS_LOG_DBG("attr_type = SECONDARY_SERVICE,attribute_handle=%d,start_handle=%d,end_handle=%d,properties=0x%x,uuid=0x%04x",
|
||||
(db + i)->attribute_handle, (db + i)->start_handle, (db + i)->end_handle, (db + i)->properties, (db + i)->uuid.uuid.uuid16);
|
||||
break;
|
||||
case ESP_GATT_DB_CHARACTERISTIC:
|
||||
SYS_LOG_DBG("attr_type = CHARACTERISTIC,attribute_handle=%d,start_handle=%d,end_handle=%d,properties=0x%x,uuid=0x%04x",
|
||||
(db + i)->attribute_handle, (db + i)->start_handle, (db + i)->end_handle, (db + i)->properties, (db + i)->uuid.uuid.uuid16);
|
||||
break;
|
||||
case ESP_GATT_DB_DESCRIPTOR:
|
||||
SYS_LOG_DBG("attr_type = DESCRIPTOR,attribute_handle=%d,start_handle=%d,end_handle=%d,properties=0x%x,uuid=0x%04x",
|
||||
(db + i)->attribute_handle, (db + i)->start_handle, (db + i)->end_handle, (db + i)->properties, (db + i)->uuid.uuid.uuid16);
|
||||
break;
|
||||
case ESP_GATT_DB_INCLUDED_SERVICE:
|
||||
SYS_LOG_DBG("attr_type = INCLUDED_SERVICE,attribute_handle=%d,start_handle=%d,end_handle=%d,properties=0x%x,uuid=0x%04x",
|
||||
(db + i)->attribute_handle, (db + i)->start_handle, (db + i)->end_handle, (db + i)->properties, (db + i)->uuid.uuid.uuid16);
|
||||
break;
|
||||
case ESP_GATT_DB_ALL:
|
||||
SYS_LOG_DBG("attr_type = ESP_GATT_DB_ALL,attribute_handle=%d,start_handle=%d,end_handle=%d,properties=0x%x,uuid=0x%04x",
|
||||
(db + i)->attribute_handle, (db + i)->start_handle, (db + i)->end_handle, (db + i)->properties, (db + i)->uuid.uuid.uuid16);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
cmd = SPP_IDX_SPP_DATA_NTY_VAL;
|
||||
xQueueSend(cmd_reg_queue, &cmd, 10 / portTICK_PERIOD_MS);
|
||||
break;
|
||||
case ESP_GATTC_SRVC_CHG_EVT:
|
||||
SYS_LOG_DBG("ESP_GATTC_SRVC_CHG_EVT");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void spp_client_reg_task(void *arg)
|
||||
{
|
||||
uint16_t cmd_id;
|
||||
for (;;)
|
||||
{
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
if (xQueueReceive(cmd_reg_queue, &cmd_id, portMAX_DELAY))
|
||||
{
|
||||
if (db != NULL)
|
||||
{
|
||||
if (cmd_id == SPP_IDX_SPP_DATA_NTY_VAL)
|
||||
{
|
||||
SYS_LOG_DBG("SPP_IDX_SPP_DATA_NTY_VAL: Index = %d,UUID = 0x%04x, handle = %d ", cmd_id, (db + SPP_IDX_SPP_DATA_NTY_VAL)->uuid.uuid.uuid16, (db + SPP_IDX_SPP_DATA_NTY_VAL)->attribute_handle);
|
||||
esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db + SPP_IDX_SPP_DATA_NTY_VAL)->attribute_handle);
|
||||
}
|
||||
else if (cmd_id == SPP_IDX_SPP_STATUS_VAL)
|
||||
{
|
||||
SYS_LOG_DBG("SPP_IDX_SPP_STATUS_VAL: Index = %d,UUID = 0x%04x, handle = %d ", cmd_id, (db + SPP_IDX_SPP_STATUS_VAL)->uuid.uuid.uuid16, (db + SPP_IDX_SPP_STATUS_VAL)->attribute_handle);
|
||||
esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db + SPP_IDX_SPP_STATUS_VAL)->attribute_handle);
|
||||
}
|
||||
#ifdef SUPPORT_HEARTBEAT
|
||||
else if (cmd_id == SPP_IDX_SPP_HEARTBEAT_VAL)
|
||||
{
|
||||
SYS_LOG_DBG("SPP_IDX_SPP_HEARTBEAT_VAL: Index = %d,UUID = 0x%04x, handle = %d ", cmd_id, (db + SPP_IDX_SPP_HEARTBEAT_VAL)->uuid.uuid.uuid16, (db + SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle);
|
||||
esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db + SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_HEARTBEAT
|
||||
void spp_heart_beat_task(void *arg)
|
||||
{
|
||||
uint16_t cmd_id;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
if (xQueueReceive(cmd_heartbeat_queue, &cmd_id, portMAX_DELAY))
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if ((is_connect == true) && (db != NULL) && ((db + SPP_IDX_SPP_HEARTBEAT_VAL)->properties & (ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_WRITE)))
|
||||
{
|
||||
esp_ble_gattc_write_char(spp_gattc_if,
|
||||
spp_conn_id,
|
||||
(db + SPP_IDX_SPP_HEARTBEAT_VAL)->attribute_handle,
|
||||
sizeof(heartbeat_s),
|
||||
(uint8_t *)heartbeat_s,
|
||||
ESP_GATT_WRITE_TYPE_RSP,
|
||||
ESP_GATT_AUTH_REQ_NONE);
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
else
|
||||
{
|
||||
SYS_LOG_DBG("disconnect");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void ble_client_appRegister(void)
|
||||
{
|
||||
esp_err_t status;
|
||||
char err_msg[20];
|
||||
|
||||
SYS_LOG_DBG("register callback");
|
||||
|
||||
// register the scan callback function to the gap module
|
||||
if ((status = esp_ble_gap_register_callback(esp_gap_cb)) != ESP_OK)
|
||||
{
|
||||
SYS_LOG_ERR("gap register error: %s", esp_err_to_name_r(status, err_msg, sizeof(err_msg)));
|
||||
return;
|
||||
}
|
||||
// register the callback function to the gattc module
|
||||
if ((status = esp_ble_gattc_register_callback(esp_gattc_cb)) != ESP_OK)
|
||||
{
|
||||
SYS_LOG_ERR("gattc register error: %s", esp_err_to_name_r(status, err_msg, sizeof(err_msg)));
|
||||
return;
|
||||
}
|
||||
esp_ble_gattc_app_register(PROFILE_APP_ID);
|
||||
|
||||
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(200);
|
||||
if (local_mtu_ret)
|
||||
{
|
||||
SYS_LOG_ERR("set local MTU failed: %s", esp_err_to_name_r(local_mtu_ret, err_msg, sizeof(err_msg)));
|
||||
}
|
||||
|
||||
cmd_reg_queue = xQueueCreate(10, sizeof(uint32_t));
|
||||
xTaskCreate(spp_client_reg_task, "spp_client_reg_task", 2048, NULL, 10, NULL);
|
||||
|
||||
#ifdef SUPPORT_HEARTBEAT
|
||||
cmd_heartbeat_queue = xQueueCreate(10, sizeof(uint32_t));
|
||||
xTaskCreate(spp_heart_beat_task, "spp_heart_beat_task", 2048, NULL, 10, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void _ble_spp_client_init(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
|
||||
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
|
||||
ret = esp_bt_controller_init(&bt_cfg);
|
||||
if (ret)
|
||||
{
|
||||
SYS_LOG_ERR("enable controller failed: %s", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
||||
if (ret)
|
||||
{
|
||||
SYS_LOG_ERR("enable controller failed: %s", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
SYS_LOG_DBG("init bluetooth");
|
||||
ret = esp_bluedroid_init();
|
||||
if (ret)
|
||||
{
|
||||
SYS_LOG_ERR("init bluetooth failed: %s", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
ret = esp_bluedroid_enable();
|
||||
if (ret)
|
||||
{
|
||||
SYS_LOG_ERR("enable bluetooth failed: %s", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ble_client_appRegister();
|
||||
}
|
||||
|
||||
/* port ------------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *tar_name;
|
||||
ble_client_connect_cb cb;
|
||||
} __ble_start_opt_t;
|
||||
|
||||
static sb_data_port_t s_port;
|
||||
|
||||
static void _port_init(sb_data_port_t *port);
|
||||
static void _port_uninit(sb_data_port_t *port);
|
||||
static void _port_start(sb_data_port_t *port, SB_DATA_PORT_RXMODE mode, const void *opt);
|
||||
static void _port_stop(sb_data_port_t *port);
|
||||
static int _port_write(sb_data_port_t *port, const void *data, uint32_t size, uint32_t wait_ms);
|
||||
static int _port_read(sb_data_port_t *port, void *data, uint32_t size, uint32_t wait_ms);
|
||||
static void *_port_get_recv_pipe(sb_data_port_t *port);
|
||||
|
||||
static sb_data_port_vtable_t const s_port_vtable = {
|
||||
.init = _port_init,
|
||||
.uninit = _port_uninit,
|
||||
.start = _port_start,
|
||||
.stop = _port_stop,
|
||||
.write = _port_write,
|
||||
.read = _port_read,
|
||||
};
|
||||
|
||||
static void _port_init(sb_data_port_t *port)
|
||||
{
|
||||
_ble_spp_client_init();
|
||||
}
|
||||
|
||||
static void _port_uninit(sb_data_port_t *port)
|
||||
{
|
||||
}
|
||||
|
||||
static void _port_start(sb_data_port_t *port, SB_DATA_PORT_RXMODE mode, const void *opt)
|
||||
{
|
||||
const __ble_start_opt_t *p = opt;
|
||||
|
||||
do
|
||||
{
|
||||
if (s_device_name)
|
||||
{
|
||||
if (is_connect != false)
|
||||
{
|
||||
ble_client_stop();
|
||||
}
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
if (is_connect == false)
|
||||
{
|
||||
os_thread_sleep(100);
|
||||
break;
|
||||
}
|
||||
os_thread_sleep(10);
|
||||
}
|
||||
|
||||
if (is_connect != false)
|
||||
{
|
||||
SYS_LOG_WRN("can not stop gap scanning!!");
|
||||
break;
|
||||
}
|
||||
|
||||
os_free(s_device_name);
|
||||
}
|
||||
|
||||
int len = strlen(p->tar_name);
|
||||
s_device_name = os_malloc(len + 1);
|
||||
if (s_device_name == NULL)
|
||||
{
|
||||
SYS_LOG_WRN("insufficient memory");
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy(s_device_name, p->tar_name);
|
||||
s_connect_cb = p->cb;
|
||||
|
||||
s_scan_start_flag = true;
|
||||
_gap_start(0xFFFF);
|
||||
|
||||
} while (0);
|
||||
}
|
||||
|
||||
static void _port_stop(sb_data_port_t *port)
|
||||
{
|
||||
if (s_scan_start_flag)
|
||||
{
|
||||
s_scan_start_flag = false;
|
||||
_gap_stop();
|
||||
if (is_connect == true)
|
||||
{
|
||||
esp_ble_gap_disconnect(gl_profile_tab[PROFILE_APP_ID].remote_bda);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int _port_write(sb_data_port_t *port, const void *data, uint32_t size, uint32_t wait_ms)
|
||||
{
|
||||
int wtotal = size;
|
||||
while (size)
|
||||
{
|
||||
int wsize = size;
|
||||
if (wsize > (1 << sizeof(uint16_t) * 2) - 1)
|
||||
{
|
||||
wsize = (1 << sizeof(uint16_t) * 2) - 1;
|
||||
}
|
||||
|
||||
if ((is_connect == true) && (db != NULL) && ((db + SPP_IDX_SPP_DATA_RECV_VAL)->properties & (ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_WRITE)))
|
||||
{
|
||||
if (esp_ble_gattc_write_char(spp_gattc_if,
|
||||
spp_conn_id,
|
||||
(db + SPP_IDX_SPP_DATA_RECV_VAL)->attribute_handle,
|
||||
wsize,
|
||||
(void *)data,
|
||||
ESP_GATT_WRITE_TYPE_NO_RSP,
|
||||
ESP_GATT_AUTH_REQ_NONE) != ESP_OK)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
size -= wsize;
|
||||
data = &((uint8_t *)data)[wsize];
|
||||
}
|
||||
return wtotal;
|
||||
}
|
||||
|
||||
static int _port_read(sb_data_port_t *port, void *data, uint32_t size, uint32_t wait_ms)
|
||||
{
|
||||
SYS_LOG_ERR("TODO");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *_port_get_recv_pipe(sb_data_port_t *port)
|
||||
{
|
||||
__ble_port_data_t *data = port->data;
|
||||
return &data->pipe_obj;
|
||||
}
|
||||
|
||||
sb_data_port_t *sb_ble_client_port_init(uint32_t buffer_size, int rx_task_priority)
|
||||
{
|
||||
/* 设置 s_port */
|
||||
s_port.vtable = &s_port_vtable;
|
||||
s_port.data = &s_data;
|
||||
|
||||
/* s_data::pipe_obj */
|
||||
if (os_pipe_is_valid(&s_data.pipe_obj))
|
||||
{
|
||||
SYS_LOG_WRN("ble initialize again!");
|
||||
return &s_port;
|
||||
}
|
||||
os_pipe_create(&s_data.pipe_obj, buffer_size);
|
||||
|
||||
s_port.vtable->init(&s_port);
|
||||
|
||||
return &s_port;
|
||||
}
|
||||
|
||||
sb_data_port_t *ble_client_port_bind(void)
|
||||
{
|
||||
return &s_port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 开始扫描指定的设备名并连接
|
||||
*
|
||||
* @param tar_name 目标设备名
|
||||
* @param cb 回调函数, NULL 值可用
|
||||
*/
|
||||
void ble_client_start(const char *tar_name, ble_client_connect_cb cb)
|
||||
{
|
||||
__ble_start_opt_t opt = {
|
||||
.tar_name = tar_name,
|
||||
.cb = cb,
|
||||
};
|
||||
s_port_vtable.start(&s_port, SB_DATA_PORT_RXMODE_AUTO_PULL_DATA, &opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 停止扫描,断开连接
|
||||
*/
|
||||
void ble_client_stop(void)
|
||||
{
|
||||
s_port_vtable.stop(&s_port);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前的链接状态
|
||||
*
|
||||
* @return ble_client_status_t
|
||||
*/
|
||||
ble_client_status_t ble_client_get_gap_status(void)
|
||||
{
|
||||
return s_status;
|
||||
}
|
||||
32
app/drivers/data_port/ble_spp/ble_spp_client.h
Normal file
32
app/drivers/data_port/ble_spp/ble_spp_client.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @file ble_spp_client.h
|
||||
* @author LokLiang
|
||||
* @brief BLE 主机接口(客户端)
|
||||
* @version 0.1
|
||||
* @date 2023-09-21
|
||||
*
|
||||
* @copyright Copyright (c) 2023
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "drivers/data_port/sb_data_port.h"
|
||||
#include "os/os.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
BLE_CLIENT_STATUS_STOP,
|
||||
BLE_CLIENT_STATUS_SCANNING,
|
||||
BLE_CLIENT_STATUS_CONNECTED,
|
||||
BLE_CLIENT_STATUS_DISCONNECTED,
|
||||
} ble_client_status_t;
|
||||
|
||||
typedef void (*ble_client_connect_cb)(ble_client_status_t);
|
||||
|
||||
sb_data_port_t *sb_ble_client_port_init(uint32_t buffer_size, int rx_task_priority);
|
||||
sb_data_port_t *ble_client_port_bind(void);
|
||||
|
||||
void ble_client_start(const char *tar_name, ble_client_connect_cb cb);
|
||||
void ble_client_stop(void);
|
||||
ble_client_status_t ble_client_get_gap_status(void);
|
||||
136
app/drivers/data_port/ble_spp/ble_spp_client_shell.c
Normal file
136
app/drivers/data_port/ble_spp/ble_spp_client_shell.c
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "ble_spp_client_shell.h"
|
||||
#include "ble_spp_client.h"
|
||||
#include "shell/sh.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "drivers/data_port/sb_data_port.h"
|
||||
|
||||
static sh_cp_param_t const s_param_profile[] = {
|
||||
{"\"SpeedyBee F405 Mini\"", "SpeedyBee F405 Mini"},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
static void _ble_connect_cb(ble_client_status_t status);
|
||||
|
||||
SH_CMD_FN(_start);
|
||||
SH_CMD_FN(_stop);
|
||||
SH_CMD_FN(_status);
|
||||
SH_CMD_FN(_write);
|
||||
|
||||
SH_CMD_CP_FN(_start_cp);
|
||||
|
||||
SH_DEF_SUB_CMD(
|
||||
sub_ble,
|
||||
SH_SETUP_CMD("start", "开始扫描指定的设备名并连接 <设备名>", _start, _start_cp), //
|
||||
SH_SETUP_CMD("stop", "停止扫描,断开连接", _stop, NULL), //
|
||||
SH_SETUP_CMD("status", "获取当前连接状态", _status, NULL), //
|
||||
SH_SETUP_CMD("write", "发送数据 <数值|字符串>", _write, NULL), //
|
||||
);
|
||||
|
||||
SH_DEF_CMD(
|
||||
_register_cmd_ble,
|
||||
SH_SETUP_CMD("ble", "操作 BLE 透传数据接口", NULL, sub_ble), //
|
||||
);
|
||||
|
||||
void ble_client_shell_register(void)
|
||||
{
|
||||
sh_register_cmd(&_register_cmd_ble);
|
||||
}
|
||||
|
||||
void ble_client_shell_unregister(void)
|
||||
{
|
||||
sh_unregister_cmd(&_register_cmd_ble);
|
||||
}
|
||||
|
||||
static void _ble_connect_cb(ble_client_status_t status)
|
||||
{
|
||||
sh_echo(&g_uart_handle_vt100, "\r\n");
|
||||
_status(&g_uart_handle_vt100, 0, NULL);
|
||||
}
|
||||
|
||||
SH_CMD_FN(_start)
|
||||
{
|
||||
if (argc < 1)
|
||||
{
|
||||
sh_echo(sh_hdl, "缺少参数\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
sh_echo(sh_hdl, "参数中有空格。如果目标设备名中包含空格,请使用引号表示\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ble_client_start(argv[0], _ble_connect_cb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SH_CMD_FN(_stop)
|
||||
{
|
||||
ble_client_stop();
|
||||
os_thread_sleep(100);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SH_CMD_FN(_status)
|
||||
{
|
||||
static char *const str[] = {
|
||||
[BLE_CLIENT_STATUS_STOP] = "stop",
|
||||
[BLE_CLIENT_STATUS_SCANNING] = "scanning",
|
||||
[BLE_CLIENT_STATUS_CONNECTED] = "connected",
|
||||
[BLE_CLIENT_STATUS_DISCONNECTED] = "disconnected",
|
||||
};
|
||||
ble_client_status_t status = ble_client_get_gap_status();
|
||||
if (status < sizeof(str) / sizeof(str[0]) && str[status] != NULL)
|
||||
{
|
||||
sh_echo(sh_hdl, "connect status: %s\r\n", str[status]);
|
||||
}
|
||||
else
|
||||
{
|
||||
sh_echo(sh_hdl, "unknow status: %d\r\n", status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SH_CMD_FN(_write)
|
||||
{
|
||||
int ret = -1;
|
||||
if (argc < 1)
|
||||
{
|
||||
sh_echo(sh_hdl, "缺少参数\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sh_parse_t pv = sh_parse_value(argv[0]);
|
||||
switch (pv.type)
|
||||
{
|
||||
case _PARSE_TYPE_STRING: // 字符串
|
||||
ret = sb_data_port_write(ble_client_port_bind(), pv.value.val_string, strlen(pv.value.val_string), 0);
|
||||
break;
|
||||
case _PARSE_TYPE_INTEGER: // 带符号整型
|
||||
ret = sb_data_port_write(ble_client_port_bind(), &pv.value.val_integer, sizeof(pv.value.val_integer), 0);
|
||||
break;
|
||||
case _PARSE_TYPE_UNSIGNED: // 无符号整型
|
||||
ret = sb_data_port_write(ble_client_port_bind(), &pv.value.val_unsigned, sizeof(pv.value.val_unsigned), 0);
|
||||
break;
|
||||
case _PARSE_TYPE_FLOAT: // 浮点
|
||||
ret = sb_data_port_write(ble_client_port_bind(), &pv.value.val_float, sizeof(pv.value.val_float), 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_thread_sleep(50);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SH_CMD_CP_FN(_start_cp)
|
||||
{
|
||||
if (argc + flag == 1)
|
||||
{
|
||||
sh_completion_param(sh_hdl, s_param_profile);
|
||||
}
|
||||
}
|
||||
4
app/drivers/data_port/ble_spp/ble_spp_client_shell.h
Normal file
4
app/drivers/data_port/ble_spp/ble_spp_client_shell.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void ble_client_shell_register(void);
|
||||
void ble_client_shell_unregister(void);
|
||||
1196
app/drivers/data_port/ble_spp/ble_spp_server.c
Normal file
1196
app/drivers/data_port/ble_spp/ble_spp_server.c
Normal file
File diff suppressed because it is too large
Load Diff
50
app/drivers/data_port/ble_spp/ble_spp_server.h
Normal file
50
app/drivers/data_port/ble_spp/ble_spp_server.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @file ble_spp_server.h
|
||||
* @author LokLiang
|
||||
* @brief BLE 从机接口(服务端)
|
||||
* @version 0.1
|
||||
* @date 2023-11-01
|
||||
*
|
||||
* @copyright Copyright (c) 2023
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "drivers/data_port/sb_data_port.h"
|
||||
#include "os/os.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
BLE_SERVER_STATUS_STOP,
|
||||
BLE_SERVER_STATUS_CONNECTED,
|
||||
BLE_SERVER_STATUS_DISCONNECTED,
|
||||
} ble_server_status_t;
|
||||
|
||||
typedef void (*ble_server_gen_manufacturer_data_cb)(uint8_t manufacturer_data[20]);
|
||||
typedef void (*ble_server_connect_cb)(ble_server_status_t);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *device_name; // 设备名
|
||||
ble_server_gen_manufacturer_data_cb manufacturer_data; // 回调函数,生成 manufacturer[20]
|
||||
ble_server_connect_cb connect_cb; // 回调函数, NULL 值可用
|
||||
} ble_server_init_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t buffer_size; // 指定接收缓存长度
|
||||
os_work_t *wkup_work_hdl; // 当接收非空时唤醒的工作项, NULL 值可用
|
||||
} ble_server_param_t;
|
||||
|
||||
int sb_ble_server_port_init(ble_server_init_t *init_param);
|
||||
|
||||
sb_data_port_t *ble_server_port_bind_cmd(uint32_t buffer_size, os_work_t *wkup_work_hdl);
|
||||
sb_data_port_t *ble_server_port_bind_val(uint32_t buffer_size, os_work_t *wkup_work_hdl);
|
||||
void ble_server_port_unbind_cmd(sb_data_port_t *port);
|
||||
void ble_server_port_unbind_val(sb_data_port_t *port);
|
||||
|
||||
ble_server_status_t ble_server_get_gap_status(void);
|
||||
uint32_t ble_server_get_mtu(void);
|
||||
|
||||
const uint8_t *ble_server_get_mac(void);
|
||||
49
app/drivers/data_port/ble_spp/ble_spp_server_define.h
Normal file
49
app/drivers/data_port/ble_spp/ble_spp_server_define.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* DEFINES
|
||||
****************************************************************************************
|
||||
*/
|
||||
//#define SUPPORT_HEARTBEAT
|
||||
//#define SPP_DEBUG_MODE
|
||||
|
||||
#define SPP_DATA_MAX_LEN (512)
|
||||
#define SPP_CMD_MAX_LEN (512)
|
||||
#define SPP_STATUS_MAX_LEN (512)
|
||||
#define SPP_DATA_BUFF_MAX_LEN (2*1024)
|
||||
///Attributes State Machine
|
||||
enum
|
||||
{
|
||||
SPP_IDX_SVC,
|
||||
|
||||
SPP_IDX_SPP_DATA_RECV_CHAR,
|
||||
SPP_IDX_SPP_DATA_RECV_VAL,
|
||||
|
||||
SPP_IDX_SPP_DATA_NOTIFY_CHAR,
|
||||
SPP_IDX_SPP_DATA_NTY_VAL,
|
||||
SPP_IDX_SPP_DATA_NTF_CFG,
|
||||
|
||||
SPP_IDX_SPP_COMMAND_CHAR,
|
||||
SPP_IDX_SPP_COMMAND_VAL,
|
||||
|
||||
SPP_IDX_SPP_STATUS_CHAR,
|
||||
SPP_IDX_SPP_STATUS_VAL,
|
||||
SPP_IDX_SPP_STATUS_CFG,
|
||||
|
||||
#ifdef SUPPORT_HEARTBEAT
|
||||
SPP_IDX_SPP_HEARTBEAT_CHAR,
|
||||
SPP_IDX_SPP_HEARTBEAT_VAL,
|
||||
SPP_IDX_SPP_HEARTBEAT_CFG,
|
||||
#endif
|
||||
|
||||
SPP_IDX_NB,
|
||||
};
|
||||
334
app/drivers/data_port/ble_spp/ble_spp_server_shell.c
Normal file
334
app/drivers/data_port/ble_spp/ble_spp_server_shell.c
Normal file
@@ -0,0 +1,334 @@
|
||||
#include "ble_spp_server_shell.h"
|
||||
#include "ble_spp_server.h"
|
||||
#include "shell/sh.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "os/os.h"
|
||||
|
||||
#include "drivers/data_port/sb_data_port.h"
|
||||
|
||||
#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF
|
||||
#define SYS_LOG_DOMAIN "SH"
|
||||
#include "sys_log.h"
|
||||
|
||||
static void _sb_manufacturer_encode(uint8_t manufacturer_data[20]);
|
||||
static void _ble_connect_cb(ble_server_status_t status);
|
||||
static void _work_speed_handler(void *arg);
|
||||
static void _work_read_handler(void *arg);
|
||||
static int _write_process(sb_data_port_t *data_port, uint32_t size);
|
||||
|
||||
static struct
|
||||
{
|
||||
sb_data_port_t *port_ble_cmd;
|
||||
sb_data_port_t *port_ble_val;
|
||||
os_work_t s_work_hdl_cmd;
|
||||
os_work_t s_work_hdl_val;
|
||||
os_work_t s_speed_work_hdl;
|
||||
int s_size_total;
|
||||
int s_recv_total_cmd;
|
||||
int s_recv_total_val;
|
||||
} s_cm;
|
||||
|
||||
SH_CMD_FN(_start);
|
||||
SH_CMD_FN(_stop);
|
||||
SH_CMD_FN(_status);
|
||||
SH_CMD_FN(_write_cmd);
|
||||
SH_CMD_FN(_write_val);
|
||||
|
||||
SH_DEF_SUB_CMD(
|
||||
sub_ble,
|
||||
SH_SETUP_CMD("start", "开始广播 <设备名>", _start, NULL), //
|
||||
SH_SETUP_CMD("stop", "断开连接并停止广播", _stop, NULL), //
|
||||
SH_SETUP_CMD("status", "获取当前连接状态", _status, NULL), //
|
||||
SH_SETUP_CMD("write-cmd", "发送数据到命令通道 <数值|字符串>", _write_cmd, NULL), //
|
||||
SH_SETUP_CMD("write-val", "发送数据到数据通道 <数值|字符串>", _write_val, NULL), //
|
||||
);
|
||||
|
||||
SH_DEF_CMD(
|
||||
_register_cmd_ble,
|
||||
SH_SETUP_CMD("ble", "操作 BLE 透传数据接口", NULL, sub_ble), //
|
||||
);
|
||||
|
||||
void ble_server_shell_register(void)
|
||||
{
|
||||
sh_register_cmd(&_register_cmd_ble);
|
||||
|
||||
ble_server_init_t ble_init_param = {
|
||||
.device_name = "12345", // 设备名
|
||||
.manufacturer_data = _sb_manufacturer_encode, // 回调函数,生成 manufacturer[20]
|
||||
.connect_cb = _ble_connect_cb, // 回调函数, NULL 值可用
|
||||
};
|
||||
sb_ble_server_port_init(&ble_init_param);
|
||||
s_cm.port_ble_cmd = ble_server_port_bind_cmd(0x2000, &s_cm.s_work_hdl_cmd);
|
||||
s_cm.port_ble_val = ble_server_port_bind_val(0x400, &s_cm.s_work_hdl_val);
|
||||
|
||||
sb_data_port_start(s_cm.port_ble_cmd);
|
||||
sb_data_port_start(s_cm.port_ble_val);
|
||||
|
||||
os_work_create(&s_cm.s_work_hdl_cmd, "work-read", _work_read_handler, s_cm.port_ble_cmd, OS_PRIORITY_LOWEST);
|
||||
os_work_submit(default_os_work_q_hdl, &s_cm.s_work_hdl_cmd, 0);
|
||||
|
||||
os_work_create(&s_cm.s_work_hdl_val, "work-read", _work_read_handler, s_cm.port_ble_val, OS_PRIORITY_LOWEST);
|
||||
os_work_submit(default_os_work_q_hdl, &s_cm.s_work_hdl_val, 0);
|
||||
|
||||
os_work_create(&s_cm.s_speed_work_hdl, "work-speed", _work_speed_handler, NULL, OS_PRIORITY_LOWEST);
|
||||
os_work_submit(default_os_work_q_hdl, &s_cm.s_speed_work_hdl, 0);
|
||||
}
|
||||
|
||||
void ble_server_shell_unregister(void)
|
||||
{
|
||||
sh_unregister_cmd(&_register_cmd_ble);
|
||||
}
|
||||
|
||||
static void _sb_manufacturer_encode(uint8_t manufacturer_data[20])
|
||||
{
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
manufacturer_data[i] = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void _ble_connect_cb(ble_server_status_t status)
|
||||
{
|
||||
sh_echo(&g_uart_handle_vt100, "\r\n");
|
||||
_status(&g_uart_handle_vt100, 0, NULL);
|
||||
}
|
||||
|
||||
static void _work_speed_handler(void *arg)
|
||||
{
|
||||
static os_time_t last_time;
|
||||
os_time_t curr_time = os_get_sys_time();
|
||||
int time_cost = curr_time - last_time;
|
||||
|
||||
os_work_later(1000);
|
||||
|
||||
time_cost += !time_cost;
|
||||
if (sb_data_port_is_started(s_cm.port_ble_cmd))
|
||||
{
|
||||
if (s_cm.s_size_total)
|
||||
{
|
||||
int result = s_cm.s_size_total * 1000 / time_cost;
|
||||
s_cm.s_recv_total_cmd += s_cm.s_size_total;
|
||||
SYS_LOG_INF("cmd trans speed: %2u.%02u KiB/S, recv total: %d bytes", result / 0x400, result % 0x400 * 100 / 0x400, s_cm.s_recv_total_cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_cm.s_recv_total_cmd = 0;
|
||||
}
|
||||
s_cm.s_size_total = 0;
|
||||
}
|
||||
|
||||
if (sb_data_port_is_started(s_cm.port_ble_val))
|
||||
{
|
||||
if (s_cm.s_size_total)
|
||||
{
|
||||
int result = s_cm.s_size_total * 1000 / time_cost;
|
||||
s_cm.s_recv_total_val += s_cm.s_size_total;
|
||||
SYS_LOG_INF("val trans speed: %2u.%02u KiB/S, recv total: %d bytes", result / 0x400, result % 0x400 * 100 / 0x400, s_cm.s_recv_total_val);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_cm.s_recv_total_val = 0;
|
||||
}
|
||||
s_cm.s_size_total = 0;
|
||||
}
|
||||
|
||||
last_time = curr_time;
|
||||
}
|
||||
|
||||
static void _work_read_handler(void *arg)
|
||||
{
|
||||
static uint8_t val_log = '0';
|
||||
|
||||
while (sb_data_port_is_started(arg))
|
||||
{
|
||||
uint8_t buf[0x100];
|
||||
int r_size = sb_data_port_read(arg, buf, sizeof(buf), 0);
|
||||
if (r_size <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_cm.s_size_total += r_size;
|
||||
for (int i = 0; i < r_size; i++)
|
||||
{
|
||||
if (buf[i] != val_log)
|
||||
{
|
||||
SYS_LOG_WRN("buf[i]: %d != val_log: %d", buf[i], val_log);
|
||||
val_log = buf[i];
|
||||
}
|
||||
val_log = '0' + (val_log - '0' + 1) % 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SH_CMD_FN(_start)
|
||||
{
|
||||
if (argc < 1)
|
||||
{
|
||||
sh_echo(sh_hdl, "缺少参数\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
sh_echo(sh_hdl, "参数中有空格。如果目标设备名中包含空格,请使用引号表示\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sb_data_port_start(s_cm.port_ble_cmd);
|
||||
sb_data_port_start(s_cm.port_ble_val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SH_CMD_FN(_stop)
|
||||
{
|
||||
sb_data_port_stop(s_cm.port_ble_cmd);
|
||||
sb_data_port_stop(s_cm.port_ble_val);
|
||||
os_thread_sleep(100);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SH_CMD_FN(_status)
|
||||
{
|
||||
static char *const str[] = {
|
||||
[BLE_SERVER_STATUS_STOP] = "stop",
|
||||
[BLE_SERVER_STATUS_CONNECTED] = "connected",
|
||||
[BLE_SERVER_STATUS_DISCONNECTED] = "disconnected",
|
||||
};
|
||||
ble_server_status_t status = ble_server_get_gap_status();
|
||||
if (status < sizeof(str) / sizeof(str[0]) && str[status] != NULL)
|
||||
{
|
||||
sh_echo(sh_hdl, "connect status: %s\r\n", str[status]);
|
||||
}
|
||||
else
|
||||
{
|
||||
sh_echo(sh_hdl, "unknow status: %d\r\n", status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
SH_CMD_FN(_write_cmd)
|
||||
{
|
||||
int ret = -1;
|
||||
if (argc < 1)
|
||||
{
|
||||
sh_echo(sh_hdl, "缺少参数\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sh_parse_t pv = sh_parse_value(argv[0]);
|
||||
switch (pv.type)
|
||||
{
|
||||
case _PARSE_TYPE_STRING: // 字符串
|
||||
ret = sb_data_port_write(s_cm.port_ble_cmd, pv.value.val_string, strlen(pv.value.val_string), 0);
|
||||
break;
|
||||
case _PARSE_TYPE_INTEGER: // 带符号整型
|
||||
ret = sb_data_port_write(s_cm.port_ble_cmd, &pv.value.val_integer, sizeof(pv.value.val_integer), 0);
|
||||
break;
|
||||
case _PARSE_TYPE_UNSIGNED: // 无符号整型
|
||||
{
|
||||
ret = _write_process(s_cm.port_ble_cmd, pv.value.val_unsigned);
|
||||
break;
|
||||
}
|
||||
case _PARSE_TYPE_FLOAT: // 浮点
|
||||
ret = sb_data_port_write(s_cm.port_ble_cmd, &pv.value.val_float, sizeof(pv.value.val_float), 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_thread_sleep(50);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SH_CMD_FN(_write_val)
|
||||
{
|
||||
int ret = -1;
|
||||
if (argc < 1)
|
||||
{
|
||||
sh_echo(sh_hdl, "缺少参数\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sh_parse_t pv = sh_parse_value(argv[0]);
|
||||
switch (pv.type)
|
||||
{
|
||||
case _PARSE_TYPE_STRING: // 字符串
|
||||
ret = sb_data_port_write(s_cm.port_ble_val, pv.value.val_string, strlen(pv.value.val_string), 0);
|
||||
break;
|
||||
case _PARSE_TYPE_INTEGER: // 带符号整型
|
||||
ret = sb_data_port_write(s_cm.port_ble_val, &pv.value.val_integer, sizeof(pv.value.val_integer), 0);
|
||||
break;
|
||||
case _PARSE_TYPE_UNSIGNED: // 无符号整型
|
||||
{
|
||||
ret = _write_process(s_cm.port_ble_val, pv.value.val_unsigned);
|
||||
break;
|
||||
}
|
||||
case _PARSE_TYPE_FLOAT: // 浮点
|
||||
ret = sb_data_port_write(s_cm.port_ble_val, &pv.value.val_float, sizeof(pv.value.val_float), 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_thread_sleep(50);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _write_process(sb_data_port_t *data_port, uint32_t size)
|
||||
{
|
||||
int ret = -1;
|
||||
char start_char = '0';
|
||||
int w_total = 0;
|
||||
|
||||
while (size)
|
||||
{
|
||||
char buf[0x200];
|
||||
int mtu = ble_server_get_mtu();
|
||||
int w_size = size;
|
||||
|
||||
if (w_size > mtu)
|
||||
{
|
||||
w_size = mtu;
|
||||
}
|
||||
|
||||
for (int i = 0; i < w_size; i++)
|
||||
{
|
||||
buf[i] = start_char;
|
||||
start_char = '0' + (start_char - '0' + 1) % 10;
|
||||
}
|
||||
|
||||
ret = sb_data_port_write(data_port, &buf, w_size, 0);
|
||||
if (ret <= 0)
|
||||
{
|
||||
for (int r = 0; r < 3; r++)
|
||||
{
|
||||
ret = sb_data_port_write(data_port, &buf, w_size, 0);
|
||||
SYS_LOG_WRN("write success = %u, retry %d", w_total, r + 1);
|
||||
if (ret > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
os_thread_sleep(50 * (r + 1));
|
||||
}
|
||||
if (ret <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
os_thread_sleep(20);
|
||||
}
|
||||
|
||||
size -= w_size;
|
||||
w_total += w_size;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
4
app/drivers/data_port/ble_spp/ble_spp_server_shell.h
Normal file
4
app/drivers/data_port/ble_spp/ble_spp_server_shell.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
void ble_server_shell_register(void);
|
||||
void ble_server_shell_unregister(void);
|
||||
Reference in New Issue
Block a user