不爱胡萝卜的仓鼠 发表于 2024-8-21 00:24

[BearPi-Pico H2821]测评 ④SLE server源码解读

本帖最后由 不爱胡萝卜的仓鼠 于 2024-8-21 00:24 编辑

<p>今天我们来简单的分析一下SEL server demo的源码,看看他流程上会调用哪些东西,关键的广播数据、服务、特征值、收发数据是对应哪些函数。</p>

<p>&nbsp;</p>

<p>因为时间和篇幅有限,今天的解读会比较粗糙,不会讲的很细。具体细节还是建议大家自己花时间去阅读,我这个还是一个浅层的引导。下面贴出来的代码中部分是我自己加的,有的可能写的不是很好,请见谅</p>

<p>&nbsp;</p>

<p>首先了解一下SDK,整个SDK是跑OS的,用的是LiteOS。</p>

<p>我们这个demo的源码在如下路径</p>

<div style="text-align: center;"></div>

<p>其中&ldquo;sel_uart.c&rdquo;是server和client公用的,server自己的代码放在&ldquo;sel_uart_cliebt&rdquo;、&quot;sle_uart_server&quot;文件夹中</p>

<p>&nbsp;</p>

<p>我们再来回顾一下server demo的大致流程。模块上电-&gt;注册各种回调-&gt;开启SLE-&gt;配置adv、server等-&gt;开始adv-&gt;等待client连接-&gt;连上后触发连接cb、交换MTU等-&gt;与client通讯</p>

<p>&nbsp;</p>

<p>代码首先会进入&ldquo;sel_uart.c&rdquo;中的&ldquo;sle_uart_entry&rdquo;函数,代码如下<code class="language-cpp"> </code></p>

<pre>
<code class="language-cpp">/**
* @brief                SEL UART入口函数(可以认为是SDK正常运行后就会调用的函数)
* @param        none
* @return      none
*/
static void sle_uart_entry(void)
{
    osal_task *task_handle = NULL;

    /* 配置时钟,MCU时钟频率配置为64M */
    if (uapi_clock_control(CLOCK_CONTROL_FREQ_LEVEL_CONFIG, CLOCK_FREQ_LEVEL_HIGH) == ERRCODE_SUCC)
    {
      osal_printk("Clock config succ.\r\n");
    }
    else
    {
      osal_printk("Clock config fail.\r\n");
    }

    /* 锁定任务调度(为了避免别的task调度,干扰下面TASK的创建?) */
    osal_kthread_lock();
#if defined(CONFIG_SAMPLE_SUPPORT_SLE_UART_SERVER)
    /* 注册sel server部分的回调函数 */
    sle_dev_register_cbks();
    /* 创建server demo的task */
    task_handle = osal_kthread_create((osal_kthread_handler)sle_uart_server_task, 0, "SLEUartServerTask",
                                    SLE_UART_TASK_STACK_SIZE);
#elif defined(CONFIG_SAMPLE_SUPPORT_SLE_UART_CLIENT)
    /* 注册sel client部分的回调函数 */
    sle_uart_client_sample_dev_cbk_register();
    /* 创建client demo的task */
    task_handle = osal_kthread_create((osal_kthread_handler)sle_uart_client_task, 0, "SLEUartDongleTask",
                                    SLE_UART_TASK_STACK_SIZE);
#endif /* CONFIG_SAMPLE_SUPPORT_SLE_UART_CLIENT */
   
    if (task_handle != NULL)
    {
      /* 如果TASK创建成功,设置task优先级 */
      osal_kthread_set_priority(task_handle, SLE_UART_TASK_PRIO);
    }
    else
    {
      osal_printk("sel uart task create fail!!!\r\n");
    }
    /* 解锁任务调度 */
    osal_kthread_unlock();
}
</code></pre>

<p>这里会配置时钟,然后注册SEL会使用的CB,然后创建串口TASK(我们现在主要关注SLE,所以串口部分我就不讲了)</p>

<p>&nbsp;</p>

<p>对于server demo,我们进入sle_dev_register_cbks();看一下</p>

<pre>
<code class="language-cpp">/**
* @brief                注册SEL的回调函数
* @param        none
* @return      errcode_t
*/
errcode_t sle_dev_register_cbks(void)
{
    errcode_t ret = 0;
    sle_dev_manager_callbacks_t dev_mgr_cbks = {0};
    dev_mgr_cbks.sle_power_on_cb = sle_power_on_cbk;
    dev_mgr_cbks.sle_enable_cb = sle_enable_cbk;
    ret = sle_dev_manager_register_callbacks(&amp;dev_mgr_cbks);
    if (ret != ERRCODE_SLE_SUCCESS)
    {
      sample_at_log_print("%s sle_dev_register_cbks,register_callbacks fail :%x\r\n",
            SLE_UART_SERVER_LOG, ret);
      return ret;
    }
#if (CORE_NUMS &lt; 2)
    /* SLE使能 */
    enable_sle();
#endif
    return ERRCODE_SLE_SUCCESS;
}</code></pre>

<p>这里会注册SLE管理的回调函数,然后使能SLE</p>

<p>&ldquo;sle_dev_manager_callbacks_t&rdquo;结构体里有3个回调:SLE上电回调、SLE使能回调、SLE去使能回调。具体内容如下</p>

<pre>
<code class="language-cpp">/**
* @if Eng
* @brief Struct of SLE device announce callback function.
* @else
* @brief SLE 设备公开回调函数接口定义。
* @endif
*/
typedef struct {
    sle_power_on_callback sle_power_on_cb;                  /*!&lt; @if Eng SLE device power on callback.
                                                               @else   SLE设备上电回调函数。 @endif */
    sle_enable_callback sle_enable_cb;                      /*!&lt; @if Eng SLE stack enable callback.
                                                               @else   SLE协议栈使能回调函数。 @endif */
    sle_disable_callback sle_disable_cb;                  /*!&lt; @if Eng SLE stack disable callback.
                                                               @else   SLE协议栈去使能回调函数。 @endif */
} sle_dev_manager_callbacks_t;
</code></pre>

<p>我们的这个代码中只注册了前2个,因为没有关闭,所以去使能就不注册了</p>

<p>&nbsp;</p>

<p>设置完后,首先会进入power on的回调,这里面又回去使能一次SLE(和上面重复了,我个人觉得,在这里enable会更好,上面的可以去掉。不过重复调用貌似也没什么问题)</p>

<pre>
<code class="language-cpp">/**
* @brief                SEL上电回调
* @param        status:状态,具体含义未知
* @return      none
*/
static void sle_power_on_cbk(uint8_t status)
{
    sample_at_log_print("sle power on: %d\r\n", status);
    enable_sle();
}</code></pre>

<p>当SLE enable完成后,会进入enbale cb</p>

<pre>
<code class="language-cpp">/**
* @brief                SEL使能回调
* @param        status:状态,具体含义未知
* @return      none
*/
static void sle_enable_cbk(uint8_t status)
{
    sample_at_log_print("sle enable: %d\r\n", status);
    sle_enable_server_cbk();
}</code></pre>

<p>这里面会调用&ldquo;sle_enable_server_cbk()&rdquo;函数,去开启server</p>

<pre>
<code class="language-cpp">/**
* @brief                server使能回调
* @param        none
* @return      none
*/
errcode_t sle_enable_server_cbk(void)
{
    errcode_t ret;

    /* 添加服务 */
    ret = sle_uart_server_add();
    if (ret != ERRCODE_SLE_SUCCESS)
    {
      sample_at_log_print("%s sle_uart_server_init,sle_uart_server_add fail :%x\r\n", SLE_UART_SERVER_LOG, ret);
      return ret;
    }

    /* 初始化ADV */
    ret = sle_uart_server_adv_init();
    if (ret != ERRCODE_SLE_SUCCESS)
    {
      sample_at_log_print("%s sle_uart_server_init,sle_uart_server_adv_init fail :%x\r\n", SLE_UART_SERVER_LOG, ret);
      return ret;
    }
    return ERRCODE_SLE_SUCCESS;
}</code></pre>

<p>这里面会两件事,分别是添加服务,然后是初始化ADV。这个函数运行完后,整个server就可以认为初始化完成了,处于ADV状态,等待client发现并连接了</p>

<p>&nbsp;</p>

<p>添加服务中主要添加的就是SSAP服务,用于数据透传,里面主要有4个步骤,注册SSAP服务,会得到一个服务ID,后面添加UUID、添加特征值时用得到。然后就是添加UUID、添加特征值,最后开启服务。代码如下</p>

<pre>
<code class="language-cpp">/**
* @brief                添加服务
* @param        none
* @return      none
*/
static errcode_t sle_uart_server_add(void)
{
    errcode_t ret;
    sle_uuid_t app_uuid = {0};

    sample_at_log_print("%s sle uart add service in\r\n", SLE_UART_SERVER_LOG);
    app_uuid.len = sizeof(g_sle_uuid_app_uuid);
    if (memcpy_s(app_uuid.uuid, app_uuid.len, g_sle_uuid_app_uuid, sizeof(g_sle_uuid_app_uuid)) != EOK)
    {
      return ERRCODE_SLE_FAIL;
    }
    /* 注册SSAP服务,用于透传数据。注册成功会得到g_server_id,后面添加UUID啥的需要 */
    ssaps_register_server(&amp;app_uuid, &amp;g_server_id);

    /* 添加服务UUID */
    if (sle_uuid_server_service_add() != ERRCODE_SLE_SUCCESS)
    {
      ssaps_unregister_server(g_server_id);
      return ERRCODE_SLE_FAIL;
    }

    /* 添加特征值、特征值描述符 */
    if (sle_uuid_server_property_add() != ERRCODE_SLE_SUCCESS)
    {
      ssaps_unregister_server(g_server_id);
      return ERRCODE_SLE_FAIL;
    }

    sample_at_log_print("%s sle uart add service, server_id:%x, service_handle:%x, property_handle:%x\r\n",
      SLE_UART_SERVER_LOG, g_server_id, g_service_handle, g_property_handle);
   
    /* 开启SSAP服务 */
    ret = ssaps_start_service(g_server_id, g_service_handle);
    if (ret != ERRCODE_SLE_SUCCESS)
    {
      sample_at_log_print("%s sle uart add service fail, ret:%x\r\n", SLE_UART_SERVER_LOG, ret);
      return ERRCODE_SLE_FAIL;
    }
    sample_at_log_print("%s sle uart add service out\r\n", SLE_UART_SERVER_LOG);
    return ERRCODE_SLE_SUCCESS;
}</code></pre>

<p>&nbsp;</p>

<p>初始化ADV中,有三个步骤,配置ADV参数,配置ADV内容,开启ADV,代码如下</p>

<pre>
<code class="language-cpp">/**
* @brief                server端adv初始化
* @param        none
* @return      errcode_t
*/
errcode_t sle_uart_server_adv_init(void)
{
    errcode_t ret;
    /* 设置ADV参数 */
    sle_set_default_announce_param();
    /* 设置ADV data */
    sle_set_default_announce_data();

    /* 开始ADV */
    ret = sle_start_announce(SLE_ADV_HANDLE_DEFAULT);
    if (ret != ERRCODE_SLE_SUCCESS) {
      sample_at_log_print("%s sle_uart_server_adv_init,sle_start_announce fail :%x\r\n", SLE_UART_SERVER_LOG, ret);
      return ret;
    }
    return ERRCODE_SLE_SUCCESS;
}</code></pre>

<p>&nbsp;</p>

<p>sle_set_default_announce_param中主要是配置ADV的参数,例如广播间隔、广播模式、地址类型等等</p>

<pre>
<code class="language-cpp">/**
* @brief                设置adv参数
* @param        none
* @return      none
*/
static int sle_set_default_announce_param(void)
{
    errno_t ret;
    sle_announce_param_t param = {0};
    uint8_t index;
    unsigned char local_addr = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    param.announce_mode = SLE_ANNOUNCE_MODE_CONNECTABLE_SCANABLE;
    param.announce_handle = SLE_ADV_HANDLE_DEFAULT;
    param.announce_gt_role = SLE_ANNOUNCE_ROLE_T_CAN_NEGO;
    param.announce_level = SLE_ANNOUNCE_LEVEL_NORMAL;
    param.announce_channel_map = SLE_ADV_CHANNEL_MAP_DEFAULT;
    param.announce_interval_min = SLE_ADV_INTERVAL_MIN_DEFAULT;
    param.announce_interval_max = SLE_ADV_INTERVAL_MAX_DEFAULT;
    param.conn_interval_min = SLE_CONN_INTV_MIN_DEFAULT;
    param.conn_interval_max = SLE_CONN_INTV_MAX_DEFAULT;
    param.conn_max_latency = SLE_CONN_MAX_LATENCY;
    param.conn_supervision_timeout = SLE_CONN_SUPERVISION_TIMEOUT_DEFAULT;
    param.own_addr.type = 0;
    ret = memcpy_s(param.own_addr.addr, SLE_ADDR_LEN, local_addr, SLE_ADDR_LEN);
    if (ret != EOK)
    {
      sample_at_log_print("%s sle_set_default_announce_param data memcpy fail\r\n", SLE_UART_SERVER_LOG);
      return 0;
    }
    sample_at_log_print("%s sle_uart_local addr: ", SLE_UART_SERVER_LOG);
    for (index = 0; index &lt; SLE_ADDR_LEN; index++)
    {
      sample_at_log_print("0x%02x ", param.own_addr.addr);
    }
    sample_at_log_print("\r\n");
    return sle_set_announce_param(param.announce_handle, &amp;param);
}
</code></pre>

<p>&nbsp;</p>

<p>sle_set_default_announce_data中会组装adv数据、scan rsp数据,并设置</p>

<pre>
<code class="language-cpp">/**
* @brief                设置adv data
* @param        none
* @return      none
*/
static int sle_set_default_announce_data(void)
{
    errcode_t ret;
    uint8_t announce_data_len = 0;
    uint8_t seek_data_len = 0;
    sle_announce_data_t data = {0};
    uint8_t adv_handle = SLE_ADV_HANDLE_DEFAULT;
    uint8_t announce_data = {0};
    uint8_t seek_rsp_data = {0};
    uint8_t data_index = 0;

    /* 组装ADV数据 */
    announce_data_len = sle_set_adv_data(announce_data);
    data.announce_data = announce_data;
    data.announce_data_len = announce_data_len;

    sample_at_log_print("%s data.announce_data_len = %d\r\n", SLE_UART_SERVER_LOG, data.announce_data_len);
    sample_at_log_print("%s data.announce_data: ", SLE_UART_SERVER_LOG);
    for (data_index = 0; data_index&lt;data.announce_data_len; data_index++) {
      sample_at_log_print("0x%02x ", data.announce_data);
    }
    sample_at_log_print("\r\n");

    /* 组装scan rsp数据 */
    seek_data_len = sle_set_scan_response_data(seek_rsp_data);
    data.seek_rsp_data = seek_rsp_data;
    data.seek_rsp_data_len = seek_data_len;

    sample_at_log_print("%s data.seek_rsp_data_len = %d\r\n", SLE_UART_SERVER_LOG, data.seek_rsp_data_len);
    sample_at_log_print("%s data.seek_rsp_data: ", SLE_UART_SERVER_LOG);
    for (data_index = 0; data_index&lt;data.seek_rsp_data_len; data_index++) {
      sample_at_log_print("0x%02x ", data.seek_rsp_data);
    }
    sample_at_log_print("\r\n");

    /* 设置adv数据 */
    ret = sle_set_announce_data(adv_handle, &amp;data);
    if (ret == ERRCODE_SLE_SUCCESS) {
      sample_at_log_print("%s set announce data success.\r\n", SLE_UART_SERVER_LOG);
    } else {
      sample_at_log_print("%s set adv param fail.\r\n", SLE_UART_SERVER_LOG);
    }
    return ERRCODE_SLE_SUCCESS;
}
</code></pre>

<p>&nbsp;</p>

<p>之后我们中断关注的就是连接、断连回调,还有接收数据的回调。还有如何发数据的接口</p>

<p>&nbsp;</p>

<p>在SLE中连接有2个步骤,第一步是connect,第二步是pair。但是目前我还没有理解这两个有什么区别,并且为什么有一步pair?</p>

<p>connect会进入&ldquo;sle_connect_state_changed_cbk&rdquo;,断连也是会进去这里</p>

<pre>
<code class="language-cpp">/**
* @brief                SEL连接状态改变回调
* @param        conn_id:连接ID
* @param        addr:mac 地址
* @param        conn_state:连接状态
* @param        pair_state:配对状态
* @param        disc_reason:断开原因
* @return      none
*/
static void sle_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)
{
    uint8_t sle_connect_state[] = "sle_dis_connect";
    sample_at_log_print("%s connect state changed callback conn_id:0x%02x, conn_state:0x%x, pair_state:0x%x, \
      disc_reason:0x%x\r\n", SLE_UART_SERVER_LOG,conn_id, conn_state, pair_state, disc_reason);
    sample_at_log_print("%s connect state changed callback addr:%02x:**:**:**:%02x:%02x\r\n", SLE_UART_SERVER_LOG,
      addr-&gt;addr, addr-&gt;addr);
    if (conn_state == SLE_ACB_STATE_CONNECTED)
    {
      g_sle_conn_hdl = conn_id;
#ifdef CONFIG_SAMPLE_SUPPORT_LOW_LATENCY_TYPE
      sle_low_latency_tx_enable();
      osal_printk("%s sle_low_latency_tx_enable \r\n", SLE_UART_SERVER_LOG);
#endif
    }
    else if (conn_state == SLE_ACB_STATE_DISCONNECTED)
    {
      g_sle_conn_hdl = 0;
      g_sle_pair_hdl = 0;
      if (g_sle_uart_server_msg_queue != NULL)
      {
            g_sle_uart_server_msg_queue(sle_connect_state, sizeof(sle_connect_state));
      }
    }
}
</code></pre>

<p>&nbsp;</p>

<p>配对完成回调如下</p>

<pre>
<code class="language-cpp">/**
* @brief                SEL配对完成回调
* @param        conn_id:连接ID
* @param        addr:mac 地址
* @param        errcode_t:错误码
* @return      none
*/
static void sle_pair_complete_cbk(uint16_t conn_id, const sle_addr_t *addr, errcode_t status)
{
    sample_at_log_print("%s pair complete conn_id:%02x, status:%x\r\n", SLE_UART_SERVER_LOG,
      conn_id, status);
    sample_at_log_print("%s pair complete addr:%02x:**:**:**:%02x:%02x\r\n", SLE_UART_SERVER_LOG,
      addr-&gt;addr, addr-&gt;addr);
    g_sle_pair_hdl = conn_id + 1;
    ssap_exchange_info_t parameter = { 0 };
    parameter.mtu_size = 520;
    parameter.version = 1;
    ssaps_set_info(g_server_id, &amp;parameter);
}
</code></pre>

<p>&nbsp;</p>

<p>接收数据回调:</p>

<p>接收数据 是一个注册函数,在如下函数中注册</p>

<pre>
<code class="language-cpp">static errcode_t sle_ssaps_register_cbks(ssaps_read_request_callback ssaps_read_callback, ssaps_write_request_callback
    ssaps_write_callback)
{
    errcode_t ret;
    ssaps_callbacks_t ssaps_cbk = {0};
    ssaps_cbk.add_service_cb = ssaps_add_service_cbk;
    ssaps_cbk.add_property_cb = ssaps_add_property_cbk;
    ssaps_cbk.add_descriptor_cb = ssaps_add_descriptor_cbk;
    ssaps_cbk.start_service_cb = ssaps_start_service_cbk;
    ssaps_cbk.delete_all_service_cb = ssaps_delete_all_service_cbk;
    ssaps_cbk.mtu_changed_cb = ssaps_mtu_changed_cbk;
    ssaps_cbk.read_request_cb = ssaps_read_callback;
    ssaps_cbk.write_request_cb = ssaps_write_callback;
    ret = ssaps_register_callbacks(&amp;ssaps_cbk);
    if (ret != ERRCODE_SLE_SUCCESS)
    {
      sample_at_log_print("%s sle_ssaps_register_cbks,ssaps_register_callbacks fail :%x\r\n", SLE_UART_SERVER_LOG,
            ret);
      return ret;
    }
    return ERRCODE_SLE_SUCCESS;
}
</code></pre>

<p>就是ssaps_cbk.write_request_cb = ssaps_write_callback;&nbsp; &nbsp;SSAP服务如果收到数据会触发write_request_cb。那么真正工作的函数是谁呢?就要看看sle_ssaps_register_cbks被谁调用了,因为write的函数会传进来。他会被sle_uart_server_init调用,而他又会被sle_uart_server_task调用。最终真正处理收到数据的是ssaps_server_write_request_cbk,代码如下</p>

<pre>
<code class="language-cpp">static void ssaps_server_write_request_cbk(uint8_t server_id, uint16_t conn_id, ssaps_req_write_cb_t *write_cb_para,
    errcode_t status)
{
    osal_printk("%s ssaps write request callback cbk server_id:%x, conn_id:%x, handle:%x, status:%x\r\n",
      SLE_UART_SERVER_LOG, server_id, conn_id, write_cb_para-&gt;handle, status);
    if ((write_cb_para-&gt;length &gt; 0) &amp;&amp; write_cb_para-&gt;value)
    {
      uapi_uart_write(CONFIG_SLE_UART_BUS, (uint8_t *)write_cb_para-&gt;value, write_cb_para-&gt;length, 0);
    }
}
</code></pre>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>发送数据回调:</p>

<p>发送函数可以从串口接收入手开始找,在sle_uart_server_task中会注册串口接收回调函数</p>

<div style="text-align: center;"></div>

<p>串口收到数据后,会给sle去透传,那么很容易就找到了发送函数:sle_uart_server_send_report_by_handle</p>

<pre>
<code class="language-cpp">static void sle_uart_server_read_int_handler(const void *buffer, uint16_t length, bool error)
{
    unused(error);
    if (sle_uart_client_is_connected())
    {
#ifdef CONFIG_SAMPLE_SUPPORT_LOW_LATENCY_TYPE
    g_buff_data_valid = 1;
    g_uart_buff_len = 0;
    (void)memcpy_s(g_buff, SLE_UART_SERVER_SEND_BUFF_MAX_LEN, buffer, length);
    g_uart_buff_len = length;
#else
    sle_uart_server_send_report_by_handle(buffer, length);
#endif
    }
    else
    {
      osal_printk("%s sle client is not connected! \r\n", SLE_UART_SERVER_LOG);
    }
}
</code></pre>

<p>sle_uart_server_send_report_by_handle源码如下,真正的发送函数是ssaps_notify_indicate。这里我又有一个疑惑了,怎么notify和indicate和在一起了?</p>

<pre>
<code class="language-cpp">/* device通过handle向host发送数据:report */
errcode_t sle_uart_server_send_report_by_handle(const uint8_t *data, uint16_t len)
{
    ssaps_ntf_ind_t param = {0};
    uint8_t receive_buf = { 0 }; /* max receive length. */
    param.handle = g_property_handle;
    param.type = SSAP_PROPERTY_TYPE_VALUE;
    param.value = receive_buf;
    param.value_len = len;
    if (memcpy_s(param.value, param.value_len, data, len) != EOK)
    {
      return ERRCODE_SLE_FAIL;
    }
    return ssaps_notify_indicate(g_server_id, g_sle_conn_hdl, &amp;param);
}</code></pre>

<p>这个是通过handle发送数据,还有一个通过UUID发送数据的函数,就在他的上面,代码如下</p>

<pre>
<code class="language-cpp">/* device通过uuid向host发送数据:report */
errcode_t sle_uart_server_send_report_by_uuid(const uint8_t *data, uint8_t len)
{
    errcode_t ret;
    ssaps_ntf_ind_by_uuid_t param = {0};
    param.type = SSAP_PROPERTY_TYPE_VALUE;
    param.start_handle = g_service_handle;
    param.end_handle = g_property_handle;
    param.value_len = len;
    param.value = (uint8_t *)osal_vmalloc(len);
    if (param.value == NULL)
    {
      sample_at_log_print("%s send report new fail\r\n", SLE_UART_SERVER_LOG);
      return ERRCODE_SLE_FAIL;
    }
    if (memcpy_s(param.value, param.value_len, data, len) != EOK)
    {
      sample_at_log_print("%s send input report memcpy fail\r\n", SLE_UART_SERVER_LOG);
      osal_vfree(param.value);
      return ERRCODE_SLE_FAIL;
    }
    sle_uuid_setu2(SLE_UUID_SERVER_NTF_REPORT, &amp;param.uuid);
    ret = ssaps_notify_indicate_by_uuid(g_server_id, g_sle_conn_hdl, &amp;param);
    if (ret != ERRCODE_SLE_SUCCESS)
    {
      sample_at_log_print("%s sle_uart_server_send_report_by_uuid,ssaps_notify_indicate_by_uuid fail :%x\r\n",
            SLE_UART_SERVER_LOG, ret);
      osal_vfree(param.value);
      return ret;
    }
    osal_vfree(param.value);
    return ERRCODE_SLE_SUCCESS;
}
</code></pre>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>现在server demo基本上理清楚了,关键的几个设置、回调函数都知道了。还有一些其他的函数和回调不是非常重要,可以后续自己慢慢去看。</p>

<p>&nbsp;</p>

<p>下一篇我们来看看client的代码。等两边的代码基本都熟悉后,我们就可以改造一下,做一下数据收发丢包和连接稳定性测试了</p>

萝卜嘞 发表于 2024-9-5 16:15

<p>学习到了</p>
页: [1]
查看完整版本: [BearPi-Pico H2821]测评 ④SLE server源码解读