上两篇我们查了一下SLE server和client端的demo,怎么运行的,收发应该调用那些接口已经清楚了,就可以开始做丢包及连接稳定性测试了
测试方案
server端代码不用动,client端连接server后,创建一个TASK,定时用SLE向server发数据,并计数,发送到最大次数后,停止发送,摧毁task。
然后提取双方日志,查看中途是否有断连、收发次数是否有缺少
1.client端代码修改
1.1 发送函数
参照之前找到的发送函数“sle_uart_client_read_int_handler”,写一个我自己用的发送函数,我把他放在sle_uart.c中
void my_sle_send_data(const char *buffer, uint16_t length)
{
ssapc_write_param_t *sle_uart_send_param = get_g_sle_uart_send_param();
uint16_t g_sle_uart_conn_id = get_g_sle_uart_conn_id();
sle_uart_send_param->data_len = length;
sle_uart_send_param->data = (uint8_t *)buffer;
ssapc_write_req(0, g_sle_uart_conn_id, sle_uart_send_param);
}
然后需要声明一下函数,但是我没有找到在sle_uart.h,那就放到sle_uart_client.h中(虽然这样不太合适,但是我有点想偷懒,不想创建一个sle_uart.h)
void my_sle_send_data(const char *buffer, uint16_t length);
1.2 发送TASK的创建及摧毁函数
定时发送我选择创建一个task,专门用于发送。SDK的OS使用的是Lite OS,我之前没有用过,但是看了一下demo中串口TASK的创建代码,感觉和FreeRTOS差不多,而且SDK还套了一层OSAL,使用起来还是很简单的
1.2.1创建发送task函数
#define SLE_SEND_TASK_PRIO 28
#define SLE_SEND_TASK_STACK_SIZE 0x1200
osal_task *send_task_handle = NULL;
static void create_send_task(void)
{
send_task_handle = osal_kthread_create((osal_kthread_handler)send_task, 0, "sendTask",
SLE_SEND_TASK_STACK_SIZE);
if (send_task_handle != NULL)
{
osal_kthread_set_priority(send_task_handle, SLE_SEND_TASK_PRIO);
}
}
这里会创建一个TASK,名字叫“sendTask”
配置其stack的大小:SLE_SEND_TASK_STACK_SIZE
TASK实际干活的函数:send_task
如果创建成功,send_task_handle就会被赋值,那我们就可以拿着他去设置task的优先级为:SLE_SEND_TASK_PRIO
1.2.2摧毁发送task函数
static void destroy_send_task(void)
{
osal_kthread_destroy(send_task_handle, 0);
}
摧毁函数很简单,就只要调用这一个函数即可,第一个参数时刚才我们创建task时得到的handle,第二个参数我没太理解,SDK的原文是如下
* @param stop_flag [in] Indicates whether the current thread exits. If the value of stop_flag is 0,
* The current thread does not exit. The stop flag is not 0.
指示当前线程是否退出,0:退出;非0:不退出。 我不太能理解,摧毁task不是就应该停止了吗?为什么还能摧毁task但是不退出线程?
1.3 实际干活的发送函数
#define SLE_SEND_CNT_MAX 10000
uint8_t g_sle_connect_state = 0;
static void *send_task(const char *arg)
{
int send_cnt = 1;
unused(arg);
char data[34] = {"send test data, send cnt = 000000"};
/* delay 5s,保证发现SSAP特征值等业务跑完 */
osal_msleep(1000 * 5);
while (1)
{
osal_printk("send task running\r\n");
/* 检查一下是否连上 */
if (g_sle_connect_state == 1)
{
snprintf(data, sizeof(data), "send test data, send cnt = %06d", send_cnt);
osal_printk("send cnt = %06d\r\n", send_cnt);
my_sle_send_data(data, sizeof(data) - 1);
send_cnt++;
}
/* delay 500ms */
osal_msleep(500);
/* 判断是否发送到max */
if (send_cnt > SLE_SEND_CNT_MAX)
{
osal_printk("******send test data over******\r\n");
destroy_send_task();
}
}
return NULL;
}
当发送task创建后,这个函数就会被运行,进来后我先delay一会儿。主要是为了保证交换MTU、发现特征值等操作完成,因为我会在连接成功后去创建task。之后就定时发送数据,每发送一次send_cnt就会+1,他会体现在日志和发送的数据中,便于后续分析日志。还会判断是否断连、是否发送到max次数。
如果断连我就不会再发送(这儿我不摧毁task,在断连的cb中我会摧毁task)
如果发送到max次数,我就会摧毁task
1.4 创建/摧毁发送task函数调用及连接状态标志位置位
我会在配对成功的cb中创建task。在连接成功cb中置连接标志位。(星闪连接有2步,连接+配对,那么我就认为配对成功才算是双方真正的连上了)
在断连cb中摧毁task并清除连接标志位。
/**
* @brief SLE client pair完成回调
* @param[in] conn_id 连接 ID。
* @param[in] addr 地址。
* @param[in] status 执行结果错误码。
* [url=home.php?mod=space&uid=784970]@return[/url] none
*/
void sle_uart_client_sample_pair_complete_cbk(uint16_t conn_id, const sle_addr_t *addr, errcode_t status)
{
osal_printk("%s pair complete conn_id:%d, addr:%02x***%02x%02x\n", SLE_UART_CLIENT_LOG, conn_id,
addr->addr[0], addr->addr[4], addr->addr[5]);
if (status == 0)
{
ssap_exchange_info_t info = {0};
info.mtu_size = SLE_MTU_SIZE_DEFAULT;
info.version = 1;
/* 请求交换SSAP信息 */
ssapc_exchange_info_req(0, g_sle_uart_conn_id, &info);
create_send_task();
}
}
/**
* @brief SLE client 连接状态改变
* @param[in] conn_id 连接 ID。
* @param[in] addr 地址。
* @param[in] conn_state 连接状态 { [url=home.php?mod=space&uid=1064992]@ref[/url] sle_acb_state_t }。
* @param[in] pair_state 配对状态 { @ref sle_pair_state_t }。
* @param[in] disc_reason 断链原因 { @ref sle_disc_reason_t }。
* @return none
*/
static void sle_uart_client_sample_connect_state_changed_cbk(uint16_t conn_id, const sle_addr_t *addr,
sle_acb_state_t conn_state, sle_pair_state_t pair_state,
sle_disc_reason_t disc_reason)
{
unused(addr);
unused(pair_state);
osal_printk("%s conn state changed disc_reason:0x%x\r\n", SLE_UART_CLIENT_LOG, disc_reason);
g_sle_uart_conn_id = conn_id;
/* 已经连上 */
if (conn_state == SLE_ACB_STATE_CONNECTED)
{
g_sle_connect_state = 1;
osal_printk("%s SLE_ACB_STATE_CONNECTED\r\n", SLE_UART_CLIENT_LOG);
/* 配对状态是未配对 */
if (pair_state == SLE_PAIR_NONE)
{
/* 开始配对 */
sle_pair_remote_device(&g_sle_uart_remote_addr);
}
#ifdef CONFIG_SAMPLE_SUPPORT_LOW_LATENCY_TYPE
sle_uart_client_sample_set_phy_param();
osal_msleep(SLE_UART_TASK_DELAY_MS);
sle_low_latency_rx_enable();
sle_low_latency_set(get_g_sle_uart_conn_id(), true, SLE_UART_LOW_LATENCY_2K);
osal_printk("%s sle_low_latency_rx_enable \r\n", SLE_UART_CLIENT_LOG); //这句话应该在这儿,不应该放外面
#endif
}
/* 未连接(还能有未连接?怎么会有这种状态?) */
else if (conn_state == SLE_ACB_STATE_NONE)
{
osal_printk("%s SLE_ACB_STATE_NONE\r\n", SLE_UART_CLIENT_LOG);
}
/* 断连 */
else if (conn_state == SLE_ACB_STATE_DISCONNECTED)
{
g_sle_connect_state = 0;
destroy_send_task();
osal_printk("%s SLE_ACB_STATE_DISCONNECTED\r\n", SLE_UART_CLIENT_LOG);
/* 移除配对设备 */
sle_remove_paired_remote_device(&g_sle_uart_remote_addr);
/* 再次开启SCAN */
sle_uart_start_scan();
}
/* 其他未知情况 */
else
{
osal_printk("%s status error\r\n", SLE_UART_CLIENT_LOG);
}
}
好了,代码的修改到此就完成了。我把整个代码仓放到github上了,https://github.com/BUYITAO/StarLink_BearPi-Pico-H2821
2.测试及结果
server端先上电,然后client上电。之后代码会自动连上,自动发数据。我代码中设置的是500ms发送一次,总共发送1W次。等着他跑完看日志即可。
测试环境:正常办公环境(环境中大约有50多个BLE设备在ADV,连接的BLE数量未知,但数量也不会太少,这个环境对于BLE是会有一点影响,不过我们是SLE,应该没啥影响)。板卡距离:1m
测试结果:未断连,为丢包。(日志附件)
短距离不丢包很正常
仓鼠老师,我下载您文中github链接的代码仓,搜索不到my_sle_send_data、create_send_task……等等这些函数和相关的变量、定义啊,请问是后来修改了吗
另外,创建发送task函数时,这个函数应该创建在哪里呢?
引用: 晚风吹散 发表于 2024-10-22 14:00 仓鼠老师,我下载您文中github链接的代码仓,搜索不到my_sle_send_data、create_send_task…… ...
你下载下来后要把当前分支置到最新的一次提交,默认拉下来应该是在首次提交的那个位置上
引用: 不爱胡萝卜的仓鼠 发表于 2024-10-22 14:19 你下载下来后要把当前分支置到最新的一次提交,默认拉下来应该是在首次提交的那个位置上
好的,非常感谢!
另外想咨询您几个问题,不胜感激:
1. 海思Hispark IDE中,当我在系统配置 - application 中,切换了Server sample / Client sample后,需要重新编译一遍吗?
2. 您有遇到过,sle_uart.c里,“#if defined(CONFIG_SAMPLE_SUPPORT_SLE_UART_SERVER)”下的内容,和 “#elif defined(CONFIG_SAMPLE_SUPPORT_SLE_UART_CLIENT)”下的内容,全部都是暗色的情况吗?
IDE似乎默认这两个define都没有生效,故而全部置灰(但烧录的程序可以正常运行)。这给我的代码阅读、修改带来了很大困扰。
引用: 晚风吹散 发表于 2024-10-23 17:09 另外想咨询您几个问题,不胜感激: 1. 海思Hispark IDE中,当我在系统配置 - application 中,切换了Ser ...
需要重新编译,他本质上就是设置编译脚本中的define,为了避免这个值在某个.h中有使用,最好是清除全编一次。
第二个问题,也是由上面的事情导致的,他这个define是没有明确的值的,只有在编译时才会从编译脚本中得到,当vscode读.c.h文件时他是无法得到define的具体值,那他就按默认值来了,所以他显示的和你配置的会不一致
引用: 不爱胡萝卜的仓鼠 发表于 2024-10-25 10:03 需要重新编译,他本质上就是设置编译脚本中的define,为了避免这个值在某个.h中有使用,最好是清除全编一 ...
好的,再次感谢您