系列文章目录

星闪开发之Server-Client 指令交互控制红灯亮灭的全流程解析(SLE_LED详解)


在这里插入图片描述

文章目录

  • 系列文章目录
  • 前言
  • 一、项目地址
  • 二、客户端
    • 1.SLE_LED_Client\inc\SLE_LED_Client.h
    • 2.SLE_LED_Client\src\SLE_LED_Client.c
      • 头文件与依赖管理
      • 宏定义与全局变量
      • LED 控制功能
      • 回调函数(扫描使能,开始,停止,结果)
      • 设备扫描与连接流程
      • 服务发现与通信协议
      • 任务启动与系统集成
      • 客户端总结
  • 三、服务端
    • 1.SLE_LED_Server\inc\SLE_LED_Server.h
    • 2.SLE_LED_Server\inc\SLE_LED_Server_adv.h
      • 广播数据结构设计
      • 广播信道配置
      • 广播数据类型枚举
      • 广播初始化接口
    • 3.SLE_LED_Server\src\SLE_LED_Server.c
      • LED控制任务
      • 服务器初始化流程
      • 服务与属性创建
      • 客户端交互处理
      • 通知发送机制
      • 连接状态管理
    • 4.SLE_LED_Server\src\SLE_LED_Server_adv.c
      • 广播数据配置
      • 扫描响应数据配置
      • 设备地址与名称设置
      • 广播参数初始化
      • 回调函数与广播控制
  • 四、从代码库拿的代码跑不通(解决)
  • 总结


前言

之前有粉丝想要计蒙写一篇关于sle_led样例的详细解析文章,于是有了这篇文章,如果拿到代码库中的项目跑不通请直接跳转至第四部分。


一、项目地址

基于海思WS63项目库中的sle_led项目

二、客户端

客户端中共两个文件。分别是SLE_LED_Client.h,SLE_LED_Client.c

1.SLE_LED_Client\inc\SLE_LED_Client.h

/*** @defgroup* @ingroup* @{*/
// LED_MODULE LED控制模块
#ifndef SLE_LED_CLIENT_H   //头文件保护宏开始,防止重复包含6。SLE_LED_CLIENT_H是自定义的宏名称,通常与文件名对应。
#define SLE_LED_CLIENT_H   //定义上述宏,标记该头文件已被包含#endif //头文件保护宏结束标记

这段代码是一个 LED 控制模块的头文件框架,采用了标准的头文件保护机制来防止重复包含。

头文件保护宏的作用:头文件保护宏(也称为 Include Guard)是 C/C++ 中防止头文件被重复包含的标准技术。当多个源文件包含同一个头文件时,可能会导致函数或变量的重复定义错误。使用保护宏可以确保头文件内容只被编译一次。

2.SLE_LED_Client\src\SLE_LED_Client.c

实现了一个基于 SLE协议的 LED 客户端控制系统,主要功能是通过 SLE 协议与远程设备通信并控制本地 LED 灯。整个系统采用模块化设计,包含设备扫描、连接管理、服务发现和 LED 控制等核心模块。

代码特点:
安全机制:使用memcpy_s等安全函数防止缓冲区溢出
资源管理:动态分配内存(osal_vmalloc)并及时释放(osal_vfree)
硬件抽象:通过pinctrl.h和gpio.h封装底层硬件操作
协议栈集成:深度集成 SLE 协议栈,处理扫描、连接、服务发现全流程
调试支持:通过PRINT宏输出详细日志,便于问题定位

头文件与依赖管理

#include "securec.h"               // 安全函数库(如memcpy_s)
#include "sle_device_discovery.h"  // SLE设备发现相关API
#include "sle_connection_manager.h"// SLE连接管理
#include "sle_ssap_client.h"       // SSAP(服务发现协议)客户端
#include "../inc/SLE_LED_Client.h" // 自定义LED客户端头文件
#include "soc_osal.h"              // 操作系统抽象层(如线程、锁)
#include "app_init.h"              // 应用初始化
#include "common_def.h"            // 通用定义(如错误码)
#include "debug_print.h"           // 调试打印
#include "pinctrl.h"               // 引脚控制
#include "gpio.h"                  // GPIO操作

依赖多个底层库,涵盖安全操作、协议栈、硬件控制和系统接口,自定义头文件SLE_LED_Client.h用于声明 LED 控制相关接口。

宏定义与全局变量

//宏定义
#define SLE_MTU_SIZE_DEFAULT 300       // 默认MTU(最大传输单元)大小
#define SLE_SEEK_INTERVAL_DEFAULT 100  // 扫描间隔(单位可能是ms)
#define SLE_SEEK_WINDOW_DEFAULT 100    // 扫描窗口
#define UUID_16BIT_LEN 2               // 16位UUID长度
#define UUID_128BIT_LEN 16             // 128位UUID长度//全局变量
static sle_announce_seek_callbacks_t g_seek_cbk = {0};//扫描回调函数组static sle_connection_callbacks_t g_connect_cbk = {0};//连接回调函数组static ssapc_callbacks_t g_ssapc_cbk = {0};//SSAP回调函数组static sle_addr_t g_remote_addr = {0};//远程设备地址

宏定义配置了通信参数(MTU、扫描间隔等),全局变量存储协议回调函数和连接状态,便于跨函数访问。


LED 控制功能

//LED控制函数
static void example_turn_onoff_led(pin_t pin, gpio_level_t level)
{uapi_pin_set_mode(pin, HAL_PIO_FUNC_GPIO);// 配置引脚为GPIO模式uapi_gpio_set_dir(pin, GPIO_DIRECTION_OUTPUT);// 设置为输出模式uapi_gpio_set_val(pin, level);// 设置电平(高/低)
}
//LED通知回调
static void example_led_notification_cbk(uint8_t client_id, uint16_t conn_id, ssapc_handle_value_t *data)
{// 解析数据并控制LED(如"RLED_ON"控制红色LED)// 支持"RLED_ON", "RLED_OFF", "YLED_ON", "YLED_OFF", "GLED_ON", "GLED_OFF"命令if (data->data_len == strlen("RLED_ON") && data->data[0] == 'R' && data->data[1] == 'L' && data->data[2] == 'E' &&data->data[3] == 'D' && data->data[4] == '_' && data->data[5] == 'O' && data->data[6] == 'N') {example_turn_onoff_led(GPIO_07, GPIO_LEVEL_HIGH);}if (data->data_len == strlen("RLED_OFF") && data->data[0] == 'R' && data->data[1] == 'L' && data->data[2] == 'E' &&data->data[3] == 'D' && data->data[4] == '_' && data->data[5] == 'O' && data->data[6] == 'F' &&data->data[7] == 'F') {example_turn_onoff_led(GPIO_07, GPIO_LEVEL_LOW);}if (data->data_len == strlen("YLED_ON") && data->data[0] == 'Y' && data->data[1] == 'L' && data->data[2] == 'E' &&data->data[3] == 'D' && data->data[4] == '_' && data->data[5] == 'O' && data->data[6] == 'N') {example_turn_onoff_led(GPIO_10, GPIO_LEVEL_HIGH);}if (data->data_len == strlen("YLED_OFF") && data->data[0] == 'Y' && data->data[1] == 'L' && data->data[2] == 'E' &&data->data[3] == 'D' && data->data[4] == '_' && data->data[5] == 'O' && data->data[6] == 'F' &&data->data[7] == 'F') {example_turn_onoff_led(GPIO_10, GPIO_LEVEL_LOW);}if (data->data_len == strlen("GLED_ON") && data->data[0] == 'G' && data->data[1] == 'L' && data->data[2] == 'E' &&data->data[3] == 'D' && data->data[4] == '_' && data->data[5] == 'O' && data->data[6] == 'N') {example_turn_onoff_led(GPIO_11, GPIO_LEVEL_HIGH);}if (data->data_len == strlen("GLED_OFF") && data->data[0] == 'G' && data->data[1] == 'L' && data->data[2] == 'E' &&data->data[3] == 'D' && data->data[4] == '_' && data->data[5] == 'O' && data->data[6] == 'F' &&data->data[7] == 'F') {example_turn_onoff_led(GPIO_11, GPIO_LEVEL_LOW);}//将LED状态写回服务器/* 将本端(Cient)的LED等状态, 通过写请求发送给对端(Server) */ssapc_write_param_t param = {0};param.handle = g_find_service_result.start_hdl;param.type = SSAP_PROPERTY_TYPE_VALUE;param.data_len = data->data_len;param.data = osal_vmalloc(param.data_len);if (param.data == NULL) {PRINT("[SLE Client] write req mem fail\r\n");return;}if (memcpy_s(param.data, param.data_len, data, data->data_len) != EOK) {PRINT("[SLE Client] write req memcpy fail\r\n");osal_vfree(param.data);return;}
// ssapc_write_req(client_id, conn_id, &param);  发送写请求if (ssapc_write_req(client_id, conn_id, &param) != ERRCODE_SUCC) {PRINT("[SLE Client] write req fail\r\n");osal_vfree(param.data);return;}osal_vfree(param.data);return;
}

通过 GPIO 接口直接控制硬件引脚,实现 LED 开关。
支持三种颜色 LED(红、黄、绿),分别对应 GPIO_07、GPIO_10、GPIO_11。
接收到远程通知后,不仅控制本地 LED,还会将状态回传给服务器。

回调函数(扫描使能,开始,停止,结果)

//扫描使能回调
static void example_sle_enable_cbk(errcode_t status)
{if (status == ERRCODE_SUCC) {example_sle_start_scan();}
}
//扫描开始回调
static void example_sle_seek_enable_cbk(errcode_t status)
{if (status == ERRCODE_SUCC) {return;}
}
//扫描停止回调
static void example_sle_seek_disable_cbk(errcode_t status)
{if (status == ERRCODE_SUCC) {sle_connect_remote_device(&g_remote_addr);// 连接设备}
}
//定义了一个静态全局数组 g_sle_expected_addr,用于存储预期的SLE设备地址  
static uint8_t g_sle_expected_addr[SLE_ADDR_LEN] = {0x04, 0x01, 0x06, 0x08, 0x06, 0x03};
// static uint8_t g_sle_expected_addr[SLE_ADDR_LEN] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};//扫描结果回调,找到特定设备后停止扫描
//触发扫描的调用流程--扫描结果处理  如果找到目标设备(地址匹配),则停止扫描。
static void example_sle_seek_result_info_cbk(sle_seek_result_info_t *seek_result_data)
{if (seek_result_data == NULL) {//调试输出PRINT("[SLE Client] seek result seek_result_data is NULL\r\n");return;}if (memcmp((void *)seek_result_data->addr.addr, (void *)g_sle_expected_addr, SLE_ADDR_LEN) == 0) {PRINT("[SLE Client] seek result find expected addr:%02x***%02x%02x\r\n", seek_result_data->addr.addr[0],seek_result_data->addr.addr[4], seek_result_data->addr.addr[5]);(void)memcpy_s(&g_remote_addr, sizeof(sle_addr_t), &seek_result_data->addr, sizeof(sle_addr_t));sle_stop_seek();// 停止扫描}
}

设备扫描与连接流程

// 扫描参数配置与启动
static void example_sle_start_scan(void) {sle_seek_param_t param;param.seek_interval[0] = SLE_SEEK_INTERVAL_DEFAULT;param.seek_window[0] = SLE_SEEK_WINDOW_DEFAULT;sle_set_seek_param(&param);sle_start_seek(); // 启动扫描
}// 扫描结果处理(匹配目标设备地址)
static void example_sle_seek_result_info_cbk(...) {if (memcmp(发现地址, 预期地址, SLE_ADDR_LEN) == 0) {memcpy_s(保存远程地址, 发现地址);sle_stop_seek(); // 找到目标后停止扫描}
}// 连接状态处理
static void example_sle_connect_state_changed_cbk(...) {if (conn_state == 已连接) {if (未配对) sle_pair_remote_device(发起配对);g_conn_id = conn_id; // 保存连接ID}
}

扫描阶段:按预设参数(间隔、窗口)搜索设备,匹配到预设地址g_sle_expected_addr后停止。
连接阶段:建立连接后自动发起配对,并保存连接 ID 用于后续通信。

服务发现与通信协议

// SSAP协议交换信息回调
static void example_sle_exchange_info_cbk(...) {if (状态成功) {ssapc_find_structure_param_t find_param;find_param.type = SSAP_FIND_TYPE_PRIMARY_SERVICE;ssapc_find_structure(查找主服务);}
}// 服务发现结果处理
static void example_sle_find_structure_cbk(...) {if (状态成功) {g_find_service_result.start_hdl = service->start_hdl;memcpy_s(保存服务UUID, service->uuid);}
}// 处理远程通知(核心LED控制入口)
static void example_sle_notification_cbk(...) {example_led_notification_cbk(client_id, conn_id, data); // 调用LED控制函数
}

通过 SSAP 协议发现远程服务,获取服务句柄和 UUID
注册通知回调example_sle_notification_cbk,当收到远程命令时触发 LED 控制

任务启动与系统集成

// 主任务函数
static int example_sle_led_client_task(...) {osal_msleep(5000); // 等待SLE初始化// 注册各类回调函数example_sle_seek_cbk_register();example_sle_connect_cbk_register();example_sle_ssapc_cbk_register();// 启用SLE功能if (enable_sle() != 成功) return -1;return 0;
}// 任务创建入口
static void example_sle_led_client_entry(void) {osal_task *task_handle = osal_kthread_create(example_sle_led_client_task, "SLELedClientTask", SLE_LED_CLI_STACK_SIZE);if (task_handle) {osal_kthread_set_priority(task_handle, SLE_LED_CLI_TASK_PRIO);osal_kfree(task_handle);}
}app_run(example_sle_led_client_entry); // 启动应用

客户端总结

核心工作流程:

  1. 系统启动:创建 SLE LED 客户端任务,等待协议栈初始化
  2. 注册回调:设置扫描、连接、SSAP 协议的各类回调函数
  3. 设备扫描:按预设参数搜索设备,匹配到目标地址后停止
  4. 建立连接:与目标设备配对,获取连接 ID
  5. 服务发现:通过 SSAP协议发现远程服务,获取操作句柄
  6. 命令交互:接收远程通知(如 “RLED_ON”),控制本地 LED 并回传状态

三、服务端

1.SLE_LED_Server\inc\SLE_LED_Server.h

SLE协议栈的 LED 服务器头文件框架,主要定义了服务相关的 UUID 和基础结构,使用标准的头文件保护宏防止重复包含。

//使用 #ifndef、#define 和 #endif 防止头文件被重复包含,避免编译错误
/*** @defgroup* @ingroup* @{*/
#ifndef SLE_LED_SERVER_H
#define SLE_LED_SERVER_H
//引入了 sle_ssap_server.h,可能是SLE协议栈中与SSAP(服务发现协议)服务器相关的定义。
#include "sle_ssap_server.h"//定义了三个UUID(通用唯一标识符),用于标识服务器中的服务、通知报告和属性。
/* Service UUID */
#define SLE_UUID_SERVER_SERVICE 0xABCD  //标识LED服务器提供的服务。客户端通过此UUID发现服务器。/* Notify Repoert UUID */
#define SLE_UUID_SERVER_NTF_REPORT 0x1122  //标识服务器发送的通知或报告。客户端可以订阅此UUID以接收状态更新(如LED开关状态)。/* Property UUID */
#define SLE_UUID_SERVER_PROPERTY 0x3344  //标识服务器中的属性。客户端可以读取或写入此UUID对应的属性(如控制LED开关)。#endif//服务发现
//客户端通过 SLE_UUID_SERVER_SERVICE 发现服务器提供的LED控制服务。
//通知订阅
//客户端订阅 SLE_UUID_SERVER_NTF_REPORT,接收服务器推送的LED状态变更通知。
//属性操作
//客户端通过 SLE_UUID_SERVER_PROPERTY 读取或设置LED状态(如发送"RLED_ON"命令)。

服务 UUID (0xABCD):标识 LED 服务器提供的整体服务,客户端通过此 UUID 发现服务器
通知报告 UUID (0x1122):用于服务器主动推送通知(如 LED 状态变化)
属性 UUID (0x3344):用于客户端读写 LED 控制属性(如开关状态)

2.SLE_LED_Server\inc\SLE_LED_Server_adv.h

定义了 SLE 协议中 LED 服务器的广播功能,包含数据结构、信道配置和初始化接口。

广播数据结构设计

//广播通用数据结构
struct sle_adv_common_value {uint8_t length;  // 数据长度uint8_t type;    // 数据类型uint8_t value;   // 数据值
};// 用途:表示广播数据的基本结构,包含长度、类型和值。
// 字段解析:
// length:广播数据的长度。
// type:广播数据的类型(如设备名称、服务UUID等)。
// value:广播数据的具体内容。

广播信道配置

//广播信道映射枚举
typedef enum {SLE_ADV_CHANNEL_MAP_77 = 0x01,   // 信道77SLE_ADV_CHANNEL_MAP_78 = 0x02,   // 信道78SLE_ADV_CHANNEL_MAP_79 = 0x04,   // 信道79SLE_ADV_CHANNEL_MAP_DEFAULT = 0x07 // 默认信道(77、78、79)
} sle_adv_channel_map;
// 用途:定义广播使用的信道映射。
// 字段解析:
// 每个枚举值对应一个信道(77、78、79)或默认信道组合。

广播数据类型枚举

//广播数据类型枚举
typedef enum {SLE_ADV_DATA_TYPE_DISCOVERY_LEVEL = 0x01,                         /*!< 发现等级 */SLE_ADV_DATA_TYPE_ACCESS_MODE = 0x02,                             /*!< 接入层能力 */SLE_ADV_DATA_TYPE_SERVICE_DATA_16BIT_UUID = 0x03,                 /*!< 标准服务数据信息 */SLE_ADV_DATA_TYPE_SERVICE_DATA_128BIT_UUID = 0x04,                /*!< 自定义服务数据信息 */SLE_ADV_DATA_TYPE_COMPLETE_LIST_OF_16BIT_SERVICE_UUIDS = 0x05,    /*!< 完整标准服务标识列表 */SLE_ADV_DATA_TYPE_COMPLETE_LIST_OF_128BIT_SERVICE_UUIDS = 0x06,   /*!< 完整自定义服务标识列表 */SLE_ADV_DATA_TYPE_INCOMPLETE_LIST_OF_16BIT_SERVICE_UUIDS = 0x07,  /*!< 部分标准服务标识列表 */SLE_ADV_DATA_TYPE_INCOMPLETE_LIST_OF_128BIT_SERVICE_UUIDS = 0x08, /*!< 部分自定义服务标识列表 */SLE_ADV_DATA_TYPE_SERVICE_STRUCTURE_HASH_VALUE = 0x09,            /*!< 服务结构散列值 */SLE_ADV_DATA_TYPE_SHORTENED_LOCAL_NAME = 0x0A,                    /*!< 设备缩写本地名称 */SLE_ADV_DATA_TYPE_COMPLETE_LOCAL_NAME = 0x0B,                     /*!< 设备完整本地名称 */SLE_ADV_DATA_TYPE_TX_POWER_LEVEL = 0x0C,                          /*!< 广播发送功率 */SLE_ADV_DATA_TYPE_SLB_COMMUNICATION_DOMAIN = 0x0D,                /*!< SLB通信域域名 */SLE_ADV_DATA_TYPE_SLB_MEDIA_ACCESS_LAYER_ID = 0x0E,               /*!< SLB媒体接入层标识 */SLE_ADV_DATA_TYPE_EXTENDED = 0xFE,                                /*!< 数据类型扩展 */SLE_ADV_DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF               /*!< 厂商自定义信息 */
} sle_adv_data_type;// 用途:定义广播数据的类型,涵盖标准服务、设备名称、功率等级等。
// 字段解析:
// 包括发现等级、服务UUID、设备名称、功率等级等广播数据类型。
// 0xFF 保留给厂商自定义数据。

广播初始化接口

//广播初始化函数
errcode_t example_sle_server_adv_init(void);

3.SLE_LED_Server\src\SLE_LED_Server.c

实现了一个基于 SLE协议的 LED 服务器应用,主要用于控制不同类型 LED 的开关状态。该应用支持设备发现、连接建立、服务发布以及 LED 状态的远程控制,适用于物联网设备中的 LED 管理场景。
关键数据结构

  • 枚举类型example_control_led_type_t定义LED控制操作(开关主板LED、灯板RGB灯等)
  • 全局变量:存储服务句柄、连接ID、属性句柄等关键信息
  • UUID定义:使用16位UUID标识服务和属性

核心模块划分

  1. SLE协议栈接口:通过sle_common.hsle_ssap_server.h等头文件与SLE协议栈交互
  2. 任务管理:使用CMSIS-RTOS v2进行多任务管理,创建LED控制任务和服务器主任务
  3. LED控制逻辑:定义多种LED操作类型,并实现周期性控制逻辑
  4. 服务与属性管理:创建SLE服务和属性,处理读写请求
  5. 连接管理:处理设备连接状态变化和配对完成事件

LED控制任务

static int example_led_control_task(const char *arg)
{example_control_led_type_t last_led_operation = EXAMPLE_CONTORL_LED_LEDBOARD_GLED_OFF;PRINT("[SLE Server] start led control task\r\n");while (1){(void)osal_msleep(500); if (last_led_operation == EXAMPLE_CONTORL_LED_LEDBOARD_GLED_OFF) {// 发送打开红色LED命令uint8_t write_req_data[] = {'R', 'L', 'E', 'D', '_', 'O', 'N'};example_sle_server_send_notify_by_handle(write_req_data, sizeof(write_req_data));last_led_operation = EXAMPLE_CONTORL_LED_LEDBOARD_RLED_ON;} // 其他LED状态切换逻辑(省略部分代码)}return 0;
}
  • 周期性控制:每500ms切换一次LED状态,按红→黄→绿的顺序循环控制
  • 命令发送:通过example_sle_server_send_notify_by_handle发送通知到客户端
  • 状态管理:使用枚举变量last_led_operation跟踪上一次操作状态

服务器初始化流程

static int example_sle_led_server_task(const char *arg)
{(void)osal_msleep(5000); // 等待SLE初始化// 使能SLE协议栈if (enable_sle() != ERRCODE_SUCC) return -1;// 注册连接管理回调if (example_sle_conn_register_cbks() != ERRCODE_SUCC) return -1;// 注册SSAP服务器回调if (example_sle_ssaps_register_cbks() != ERRCODE_SUCC) return -1;// 添加服务和属性if (example_sle_server_add() != ERRCODE_SUCC) return -1;// 启动设备广播if (example_sle_server_adv_init() != ERRCODE_SUCC) return -1;return 0;
}
  • 初始化顺序:延时等待→使能SLE→注册回调→创建服务→启动广播
  • 关键函数
    • enable_sle():初始化SLE协议栈
    • example_sle_server_add():创建服务和属性
    • example_sle_server_adv_init():启动设备发现广播

服务与属性创建

static errcode_t example_sle_server_add(void)
{// 注册服务器ssaps_register_server(&app_uuid, &g_server_id);// 添加服务if (example_sle_server_service_add() != ERRCODE_SUCC) return ERRCODE_FAIL;// 添加属性if (example_sle_server_property_add() != ERRCODE_SUCC) return ERRCODE_FAIL;// 启动服务return ssaps_start_service(g_server_id, g_service_handle);
}
  • UUID设置:使用16位UUID标识服务(SLE_UUID_SERVER_SERVICE)和属性(SLE_UUID_SERVER_PROPERTY
  • 属性权限:设置为可读可写(SSAP_PERMISSION_READ | SSAP_PERMISSION_WRITE
  • 描述符添加:为属性添加描述符,定义通知操作权限

客户端交互处理

static void example_ssaps_write_request_cbk(uint8_t server_id, uint16_t conn_id, ssaps_req_write_cb_t *write_cb_para, errcode_t status)
{if (status == ERRCODE_SUCC) {example_print_led_state(write_cb_para);}
}static void example_print_led_state(ssaps_req_write_cb_t *write_cb_para)
{// 解析"LED_ON"和"LED_OFF"命令并打印状态if (write_cb_para->length == strlen("LED_ON") && memcmp(write_cb_para->value, "LED_ON", 6) == 0) {PRINT("[SLE Server] client main board led is on.\r\n");}// 其他命令解析逻辑(省略)
}
  • 写请求处理:接收客户端发送的LED控制命令
  • 命令解析:通过字符串匹配识别"LED_ON"、"LED_OFF"等命令
  • 状态反馈:通过PRINT函数输出LED状态到调试日志

通知发送机制

static errcode_t example_sle_server_send_notify_by_handle(const uint8_t *data, uint8_t len)
{ssaps_ntf_ind_t param = {0};param.handle = g_property_handle;param.value = osal_vmalloc(len);param.value_len = len;// 内存拷贝if (memcpy_s(param.value, len, data, len) != EOK) return ERRCODE_MEMCPY;// 发送通知if (ssaps_notify_indicate(g_server_id, g_conn_id, &param) != ERRCODE_SUCC) {osal_vfree(param.value);return ERRCODE_FAIL;}osal_vfree(param.value);return ERRCODE_SUCC;
}
  • 通知结构:使用ssaps_ntf_ind_t结构封装通知数据
  • 内存管理:动态分配内存存储通知数据,发送后释放
  • 协议接口:通过ssaps_notify_indicate函数发送通知到客户端

连接状态管理

static void example_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)
{PRINT("[SLE Server] connect state changed conn_id:0x%02x, conn_state:0x%x\r\n", conn_id, conn_state);g_conn_id = conn_id;
}static void example_sle_pair_complete_cbk(uint16_t conn_id, const sle_addr_t *addr, errcode_t status)
{PRINT("[SLE Server] pair complete conn_id:0x%02x, status:0x%x\r\n", conn_id, status);if (status == ERRCODE_SUCC) {example_led_control_entry(); // 配对成功后启动LED控制任务}
}
  • 状态回调:监听连接状态变化和配对完成事件
  • 任务启动:配对成功后启动LED控制任务
  • 连接ID管理:使用全局变量g_conn_id存储当前连接ID

4.SLE_LED_Server\src\SLE_LED_Server_adv.c

实现了SLE的广播功能,主要用于设备发现和连接建立。通过配置广播参数、设置广播数据和扫描响应数据,使服务器能够在网络中被其他设备发现,并支持连接请求。代码包含地址设置、名称配置、广播参数初始化及回调函数注册等完整流程。

广播数据配置

// 设置设备名称到广播数据
static uint16_t example_sle_set_adv_local_name(uint8_t *adv_data, uint16_t max_len) {uint8_t local_name_len = strlen((char *)sle_local_name);adv_data[0] = local_name_len + 1;          // 长度字段(含类型字节)adv_data[1] = SLE_ADV_DATA_TYPE_COMPLETE_LOCAL_NAME; // 数据类型memcpy_s(&adv_data[2], max_len - 2, sle_local_name, local_name_len);return local_name_len + 2;
}// 配置广播数据(发现等级、接入模式)
static uint16_t example_sle_set_adv_data(uint8_t *adv_data) {// 发现等级配置struct sle_adv_common_value adv_disc_level = {.length = sizeof(struct sle_adv_common_value) - 1,.type = SLE_ADV_DATA_TYPE_DISCOVERY_LEVEL,.value = SLE_ANNOUNCE_LEVEL_NORMAL};memcpy_s(adv_data, SLE_ADV_DATA_LEN_MAX, &adv_disc_level, sizeof(adv_disc_level));// 接入模式配置struct sle_adv_common_value adv_access_mode = {.length = sizeof(struct sle_adv_common_value) - 1,.type = SLE_ADV_DATA_TYPE_ACCESS_MODE,.value = 0};memcpy_s(&adv_data[sizeof(adv_disc_level)], SLE_ADV_DATA_LEN_MAX - sizeof(adv_disc_level), &adv_access_mode, sizeof(adv_access_mode));return sizeof(adv_disc_level) + sizeof(adv_access_mode);
}
  • 数据结构:使用struct sle_adv_common_value定义广播数据项,包含长度、类型和值
  • 名称设置:遵循广播数据格式(长度+类型+内容),名称为"SLE_LED_SERVER"
  • 发现等级:设置为SLE_ANNOUNCE_LEVEL_NORMAL(普通发现等级)
  • 接入模式:值为0(具体含义由协议定义)

扫描响应数据配置

// 配置扫描响应数据(功率等级、设备名称)
static uint16_t example_sle_set_scan_response_data(uint8_t *scan_rsp_data) {uint16_t idx = 0;// 发送功率配置struct sle_adv_common_value tx_power_level = {.length = sizeof(struct sle_adv_common_value) - 1,.type = SLE_ADV_DATA_TYPE_TX_POWER_LEVEL,.value = SLE_ADV_TX_POWER  // 功率值10};memcpy_s(scan_rsp_data, SLE_ADV_DATA_LEN_MAX, &tx_power_level, sizeof(tx_power_level));idx += sizeof(tx_power_level);// 设备名称配置(复用广播数据中的名称设置函数)idx += example_sle_set_adv_local_name(&scan_rsp_data[idx], SLE_ADV_DATA_LEN_MAX - idx);return idx;
}
  • 扫描响应作用:当设备被扫描时,返回包含功率和名称的响应数据
  • 功率等级:设置为SLE_ADV_TX_POWER=10,影响信号覆盖范围
  • 数据复用:扫描响应中的名称设置复用广播数据的名称配置函数

设备地址与名称设置

// 本地地址设置(固定地址:0x04, 0x01, 0x06, 0x08, 0x06, 0x03)
static void example_sle_set_addr(void) {uint8_t g_sle_local_addr[SLE_ADDR_LEN] = {0x04, 0x01, 0x06, 0x08, 0x06, 0x03};sle_addr_t sle_addr = {0};sle_addr.type = 0;memcpy_s(sle_addr.addr, SLE_ADDR_LEN, g_sle_local_addr, SLE_ADDR_LEN);sle_set_local_addr(&sle_addr);
}// 本地名称设置("sle_led_server")
static void example_sle_set_name(void) {uint8_t g_local_device_name[] = {'s', 'l', 'e', '_', 'l', 'e', 'd', '_', 's', 'e', 'r', 'v', 'e', 'r'};sle_set_local_name(g_local_device_name, sizeof(g_local_device_name));
}
  • 地址格式:6字节固定地址,可能为厂商自定义格式
  • 名称规范:使用小写字母和下划线,符合设备命名惯例
  • 协议接口:通过sle_set_local_addrsle_set_local_name设置基础信息

广播参数初始化

// 设置广播参数(间隔、超时等)
static errcode_t example_sle_set_default_announce_param(void) {sle_announce_param_t param = {.announce_mode = SLE_ANNOUNCE_MODE_CONNECTABLE_SCANABLE,  // 可连接可扫描模式.announce_handle = SLE_ADV_HANDLE_DEFAULT,                // 广播句柄.announce_gt_role = SLE_ANNOUNCE_ROLE_T_CAN_NEGO,         // 角色可协商.announce_level = SLE_ANNOUNCE_LEVEL_NORMAL,              // 普通广播等级.announce_channel_map = SLE_ADV_CHANNEL_MAP_DEFAULT,      // 默认信道映射.announce_interval_min = SLE_ADV_INTERVAL_MIN_DEFAULT,    // 最小广播间隔25ms.announce_interval_max = SLE_ADV_INTERVAL_MAX_DEFAULT,    // 最大广播间隔25ms.conn_interval_min = SLE_CONN_INTV_MIN_DEFAULT,           // 最小连接间隔12.5ms.conn_interval_max = SLE_CONN_INTV_MAX_DEFAULT,           // 最大连接间隔12.5ms.conn_max_latency = SLE_CONN_MAX_LATENCY,                 // 最大连接延迟4990ms.conn_supervision_timeout = SLE_CONN_SUPERVISION_TIMEOUT_DEFAULT // 超时时间5000ms};memcpy_s(param.own_addr.addr, SLE_ADDR_LEN, g_sle_local_addr, SLE_ADDR_LEN);return sle_set_announce_param(param.announce_handle, &param);
}
  • 广播模式SLE_ANNOUNCE_MODE_CONNECTABLE_SCANABLE表示支持连接和扫描
  • 间隔配置:广播间隔固定为25ms,连接间隔固定为12.5ms
  • 超时机制:连接监控超时5000ms,超过则断开连接
  • 信道映射:使用默认信道,适应标准网络环境

回调函数与广播控制

// 广播状态回调函数
void example_sle_announce_enable_cbk(uint32_t announce_id, errcode_t status) {PRINT("[SLE Adv] 广播启用,ID:%02x,状态:%02x\r\n", announce_id, status);
}void example_sle_announce_disable_cbk(uint32_t announce_id, errcode_t status) {PRINT("[SLE Adv] 广播禁用,ID:%02x,状态:%02x\r\n", announce_id, status);
}// 注册回调函数
void example_sle_announce_register_cbks(void) {sle_announce_seek_callbacks_t seek_cbks = {.announce_enable_cb = example_sle_announce_enable_cbk,.announce_disable_cb = example_sle_announce_disable_cbk,.announce_terminal_cb = example_sle_announce_terminal_cbk,.sle_enable_cb = example_sle_enable_cbk};sle_announce_seek_register_callbacks(&seek_cbks);
}// 广播初始化主函数
errcode_t example_sle_server_adv_init(void) {example_sle_announce_register_cbks();       // 注册回调example_sle_set_default_announce_param();   // 设置广播参数example_sle_set_default_announce_data();    // 设置广播数据example_sle_set_addr();                     // 设置设备地址example_sle_set_name();                     // 设置设备名称sle_start_announce(SLE_ADV_HANDLE_DEFAULT); // 启动广播return ERRCODE_SUCC;
}
  • 回调机制:监听广播启用、禁用、终止和SLE启用事件
  • 初始化流程:注册回调→设置参数→配置数据→地址名称→启动广播
  • 广播控制:通过sle_start_announce启动广播,使用默认句柄1

四、从代码库拿的代码跑不通(解决)

将SLE_LED_Server\src\SLE_LED_Server.c文件中的example_sle_server_property_add方法内部

    descriptor.permissions = SSAP_PERMISSION_READ | SSAP_PERMISSION_WRITE;descriptor.value = osal_vmalloc(sizeof(ntf_value));

修改为:

    descriptor.permissions = SSAP_PERMISSION_READ | SSAP_PERMISSION_WRITE;descriptor.operate_indication = SSAP_OPERATE_INDICATION_BIT_READ | SSAP_OPERATE_INDICATION_BIT_WRITE;descriptor.type = SSAP_DESCRIPTOR_USER_DESCRIPTION;descriptor.value = ntf_value;descriptor.value_len = sizeof(ntf_value);

修改后的方法如下:

static errcode_t example_sle_server_property_add(void)
{errcode_t ret = ERRCODE_FAIL;ssaps_property_info_t property = {0};ssaps_desc_info_t descriptor = {0};uint8_t ntf_value[] = {0x01, 0x0};property.permissions = SSAP_PERMISSION_READ | SSAP_PERMISSION_WRITE;example_sle_uuid_setu2(SLE_UUID_SERVER_PROPERTY, &property.uuid);property.value = osal_vmalloc(sizeof(g_sle_property_value));if (property.value == NULL) {PRINT("[SLE Server] sle property mem fail\r\n");return ERRCODE_MALLOC;}if (memcpy_s(property.value, sizeof(g_sle_property_value), g_sle_property_value, sizeof(g_sle_property_value)) !=EOK) {osal_vfree(property.value);PRINT("[SLE Server] sle property mem cpy fail\r\n");return ERRCODE_MEMCPY;}ret = ssaps_add_property_sync(g_server_id, g_service_handle, &property, &g_property_handle);if (ret != ERRCODE_SUCC) {PRINT("[SLE Server] sle uuid add property fail, ret:0x%x\r\n", ret);osal_vfree(property.value);return ERRCODE_FAIL;}PRINT("[SLE Server] sle uuid add property property_handle: %u\r\n", g_property_handle);//仓库的代码// descriptor.permissions = SSAP_PERMISSION_READ | SSAP_PERMISSION_WRITE;// descriptor.value = osal_vmalloc(sizeof(ntf_value));//新的代码descriptor.permissions = SSAP_PERMISSION_READ | SSAP_PERMISSION_WRITE;descriptor.operate_indication = SSAP_OPERATE_INDICATION_BIT_READ | SSAP_OPERATE_INDICATION_BIT_WRITE;descriptor.type = SSAP_DESCRIPTOR_USER_DESCRIPTION;descriptor.value = ntf_value;descriptor.value_len = sizeof(ntf_value);if (descriptor.value == NULL) {PRINT("[SLE Server] sle descriptor mem fail\r\n");osal_vfree(property.value);return ERRCODE_MALLOC;}if (memcpy_s(descriptor.value, sizeof(ntf_value), ntf_value, sizeof(ntf_value)) != EOK) {PRINT("[SLE Server] sle descriptor mem cpy fail\r\n");osal_vfree(property.value);osal_vfree(descriptor.value);return ERRCODE_MEMCPY;}ret = ssaps_add_descriptor_sync(g_server_id, g_service_handle, g_property_handle, &descriptor);if (ret != ERRCODE_SUCC) {PRINT("[SLE Server] sle uuid add descriptor fail, ret:0x%x\r\n", ret);osal_vfree(property.value);osal_vfree(descriptor.value);return ERRCODE_FAIL;}osal_vfree(property.value);osal_vfree(descriptor.value);return ERRCODE_SUCC;
}

差异主要体现在内存管理方式、描述符字段配置上。

总结

个人分析,仅供参考。如有疑问请留言或者联系计蒙。下一篇文章计划–写一个关于OLED的星闪互联案例。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/908209.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/908209.shtml
英文地址,请注明出处:http://en.pswp.cn/news/908209.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux shell练习题

Shell 1. 判断~/bigdata.txt 是否存在&#xff0c;若已存在则打印出”该文件已存在“&#xff0c;如不存在&#xff0c;则输出打印&#xff1a;”该文件不存在“ if [ -f ./bigdata.txt ];then echo "文件存在" else echo "文件不存在" fi2. 判断~/bigd…

Linux基本指令(三)

接上之前的文章&#xff0c;咱继续分享Linux的基本指令&#xff0c;Linux指令比较多&#xff0c;很难全部记住需要做笔记对常用的指令进行记录&#xff0c;方便以后复习查找&#xff0c;做笔记也可以对知识理解更加深刻。 目录 时间相关指令 date显示 时间戳 cal指令 ​编…

WebRTC中sdp多媒体会话协议报文详细解读

sdp介绍 在WebRTC&#xff08;Web实时通信&#xff09;中&#xff0c;SDP&#xff08;Session Description Protocol&#xff09;是用来描述和协商多媒体会话的协议。它定义了会话的参数和媒体流的信息&#xff0c;如音视频编码格式、传输方式、网络地址等。SDP是WebRTC中一个…

【MySQL】 约束

一、约束的定义 MySQL 约束是用于限制表中数据的规则&#xff0c;确保数据的 准确性 和 一致性 。约束可以在创建表时定义&#xff0c;也可以在表创建后通过修改表结构添加。 二、常见的约束类型 2.1 NOT NULL 非空约束 加了非空约束的列不能为 NULL 值&#xff0c;如果可以…

【.net core】【watercloud】树形组件combotree导入及调用

源码下载:combotree: 基于layui及zTree的树下拉框组件 链接中提供了组件的基本使用方法 框架修改内容 1.文件导入&#xff08;路径可更具自身情况自行设定&#xff09; 解压后将文件夹放在图示路径下&#xff0c;修改文件夹名称为combotree 2.设置路径&#xff08;设置layu…

ES101系列07 | 分布式系统和分页

本篇文章主要讲解 ElasticSearch 中分布式系统的概念&#xff0c;包括节点、分片和并发控制等&#xff0c;同时还会提到分页遍历和深度遍历问题的解决方案。 节点 节点是一个 ElasticSearch 示例 其本质就是一个 Java 进程一个机器上可以运行多个示例但生产环境推荐只运行一个…

CppCon 2015 学习:3D Face Tracking and Reconstruction using Modern C++

1. 3D面部追踪和重建是什么&#xff1f; 3D面部追踪&#xff08;3D Face Tracking&#xff09;&#xff1a; 实时检测并追踪人脸在三维空间中的位置和姿态&#xff08;如转头、点头、表情变化等&#xff09;&#xff0c;通常基于摄像头捕获的视频帧。3D面部重建&#xff08;3D…

代码中的问题及解决方法

目录 YOLOX1. AttributeError: VOCDetection object has no attribute cache2. ValueError: operands could not be broadcast together with shapes (8,5) (0,)3. windows远程查看服务器的tensorboard4. AttributeError: int object has no attribute numel YOLOX 1. Attribu…

【JVM】Java类加载机制

【JVM】Java类加载机制 什么是类加载&#xff1f; 在 Java 的世界里&#xff0c;每一个类或接口在经过编译后&#xff0c;都会生成对应的 .class 字节码文件。 所谓类加载机制&#xff0c;就是 JVM 将这些 .class 文件中的二进制数据加载到内存中&#xff0c;并对其进行校验…

vue的监听属性watch的详解

文章目录 1. 监听属性 watch2. 常规用法3. 监听对象和route变化4. 使用场景 1. 监听属性 watch watch 是一个对象&#xff0c;键是需要观察的表达式&#xff0c;用于观察 Vue 实例上的一个表达式或者一个函数计算结果的变化。回调函数的参数是新值和旧值。值也可以是方法名&am…

如何在 Ubuntu 24.04 服务器上安装 Apache Solr

Apache Solr 是一个免费、开源的搜索平台&#xff0c;广泛应用于实时索引。其强大的可扩展性和容错能力使其在高流量互联网场景下表现优异。 Solr 基于 Java 开发&#xff0c;提供了分布式索引、复制、负载均衡及自动故障转移和恢复等功能。 本教程将指导您如何在 Ubuntu 24.…

Linux内核中TCP三次握手的实现机制详解

TCP三次握手是建立可靠网络连接的核心过程,其在内核中的实现涉及复杂的协议栈协作。本文将深入分析Linux内核中三次握手的实现机制,涵盖客户端与服务端的分工、关键函数调用、协议号验证及数据包处理流程。 一、三次握手的整体流程 三次握手分为三个阶段,客户端与服务端通过…

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…

优化 Spring Boot API 性能:利用 GZIP 压缩处理大型有效载荷

引言 在构建需要处理和传输大量数据的API服务时&#xff0c;响应时间是一个关键的性能指标。一个常见的场景是&#xff0c;即使后端逻辑和数据库查询已得到充分优化&#xff0c;当API端点返回大型数据集&#xff08;例如&#xff0c;数千条记录的列表&#xff09;时&#xff0…

【WPF】WPF 项目实战:构建一个可增删、排序的光源类型管理界面(含源码)

&#x1f4a1;WPF 项目实战&#xff1a;构建一个可增删、排序的光源类型管理界面&#xff08;含源码&#xff09; 在实际的图像处理项目中&#xff0c;我们经常需要对“光源类型”进行筛选或管理。今天我们来一步步构建一个实用的 WPF 界面&#xff0c;实现以下功能&#xff1…

C++23 已弃用特性

文章目录 1. std::aligned_storage 与 std::aligned_union1.1 特性介绍1.2 被弃用的原因1.3 替代方案 2. std::numeric_limits::has_denorm2.1 特性介绍2.2 被弃用的原因 3. 总结 C23 已弃用特性包括&#xff1a;std::aligned_storage、std::aligned_union 与 std::numeric_lim…

十三、【核心功能篇】测试计划管理:组织和编排测试用例

【核心功能篇】测试计划管理:组织和编排测试用例 前言准备工作第一部分:后端实现 (Django)1. 定义 `TestPlan` 模型2. 生成并应用数据库迁移3. 创建 `TestPlanSerializer`4. 创建 `TestPlanViewSet`5. 注册路由6. 注册到 Django Admin第二部分:前端实现 (Vue3)1. 创建 `Test…

STM32最小CLion开发环境

文章目录 1 必须文件2 工具链3 CLion 全局配置4 CLion 新项目配置ST-Link 调试 5 点亮 LED6 分析 elf 文件7 项目模板 1 必须文件 ST 提供的头文件支持 MDK-ARM, GCC, IAR 3种编译器, 下面采用 GCC 编译器 Arm GNU Toolchain Downloads – Arm Developer 或 安装包版 调试器服…

核函数:解锁支持向量机的强大能力

在机器学习的世界中&#xff0c;支持向量机&#xff08;SVM&#xff09;是一种强大的分类算法&#xff0c;而核函数则是其背后的“魔法”&#xff0c;让 SVM 能够处理复杂的非线性问题。今天&#xff0c;我们就来深入探讨核函数的奥秘&#xff0c;看看它们是如何帮助 SVM 在高维…

【Go-6】数据结构与集合

6. 数据结构与集合 数据结构是编程中用于组织和存储数据的方式&#xff0c;直接影响程序的效率和性能。Go语言提供了多种内置的数据结构&#xff0c;如数组、切片、Map和结构体&#xff0c;支持不同类型的数据管理和操作。本章将详细介绍Go语言中的主要数据结构与集合&#xf…