低功耗SDK接入
方案介绍
如果设备为长上电设备,不需要关注此章节内容。
对于低功耗 IOT 设备,RTCX 提供了以下接入方案,用户可以根据自己设备特性,选择适合自己的方案。
信媒分离方案(建议使用)
在某些场景下(比如智能门锁),期望在不启动视频主控芯片的情况下,也能实现一些非视频类的简单操作,比如物模型的参数配置、以及消息上报 等。此种情况下可以将 IoTSDK和StreamSDK分离,IoTSDK运行在wifi芯片上,在主控休眠的时候也可以处理服务器下发的信令操作。StreamSDK 运行在视频主控芯片上,需要唤醒主控之后才能工作。
此种模式下,对客户端APP来说可以把设备看做长上电设备,需要操作设备的时候,直接发送相关的请求即可,由IoTSDK判断是否需要唤醒StreamSDK。主控启动之后不需要再建立信令连接,可以加快远程唤醒出图的速度。
分离方案如下图所示:
注意,在信令流媒体分离的场景下,使用者需要通过SPI/SDIO总线或者其他方式将 IoTSDK 和StreamSDK回调的数据转发到另一端,注意转发的时候要保证数据的正确性,不能出现丢包或者数据错乱的现象。
数据交换说明:
- wifi芯片IoTSDK 回调函数 xs_msgCallback 回调的数据,通过总线传到主控芯片,然后应用层调用 StreamSDK的 xs_stream_inputIotMsg 接口将数据塞入
- 主控芯片StreamSDK 回调函数 xs_streamMsgCallback 回调的数据,通过总线传到 wifi 芯片,然后应用层调用 IoTSDK 的 xs_iot_handleExternalMsg 接口将数据塞入
设备休眠
当设备需要进入休眠时,需要调用下面的接口通知SDK:
- StreamSDK:xs_stream_sleep 接口,主控休眠之前需要调用,SDK 内部会停止推流相关任务,此接口为阻塞接口,调用完毕之后设备可以进入休眠
- IoTSDK:xs_iot_notifyHostStatus 接口:主控芯片休眠之后,wifi 芯片上需要调用此接口通知 IoTSDK,唤醒的时候不需要通知(sdk 内部通过消息处理)
/**
* @brief 用于告知 主控芯片(streamsdk运行的芯片) 的运行状态
* 注意:如果iotsdk和streamsdk运行在同一个芯片,不需要调用此函数
* 如果运行在不同芯片,在streamsdk所在芯片休眠后,需要调用此接口通知 iotsdk 主控已休眠 (XS_IOT_HOST_SLEEP)
* XS_IOT_HOST_ACTIVE 不需要用户设置,在 streamsdk启动后,会通过消息接口内部主动通知
* @param status 主控运行状态,包括唤醒和休眠两个状态
*/
xs_int32 xs_iot_notifyHostStatus(xs_hostStatus_en status);
远程唤醒
当wifi芯片上的IoTSDK 收到直播回放等流媒体相关请求时,如果StreamSdk没有启动,会触发唤醒的回调如下:
/*回调函数列表结构体,不需要的回调可以设置为null*/
typedef struct {
xs_wakeupHostCallback wakeupCb; /*唤醒主控streamsdk的回调,iotsdk和streamsdk运行在不同芯片上的时候,会收到此回调*/
}xs_iot_callbackFun_st;
收到回调后,设备需要唤醒主控,并将IoTSDK 回调(xs_msgCallback )出来的数据转发到StreamSDK
心跳保活方案
某些Wifi或者4G芯片不支持用户自定义编程,只能设置保活报文和唤醒报文信息,可以采用此方案。
此模式下,IoTSDK和StreamSDK都运行在视频主控芯片上,休眠的时候,wifi芯片和平台建立一条简单TCP长连接,定期发送心跳包维持保活,APP远程唤醒设备的时候,平台会下发唤醒包。
相关操作流程如下:
- 初始化的时候,初始化参数 workMode == XS_IOT_WORK_MODE_LOWPOWER_KEEPLIVE,示例代码如下:
int initSdk()
{
/*初始化iotsdk回调函数指针*/
xs_iot_callbackFun_st cbFun;
initIotCallbackFun(&cbFun);
/*设置初始化参数*/
xs_iotInitParam_st initParam;
memset(&initParam, 0, sizeof(initParam));
initParam.workMode = XS_IOT_WORK_MODE_LOWPOWER_KEEPLIVE;
... //设置其他参数
/*初始化iotsdk*/
ret = xs_iot_init(&cbFun, &initParam);
return ret;
}
- SDK登录成功之后,触发保活信息的回 调,设备需要保存下来
/**
* @brief 保活服务器地址信息回调,workMode == XS_IOT_WORK_MODE_LOWPOWER_KEEPLIVE 的模式才会有此回调
* @param [IN] keepLiveInfo: 保活信息
* @return 0:成功, 其他值:错误码
* @note 建议厂商将保活信息保存下来,如果收到SDK回调的,就将本地的覆盖掉。因为不是每次启动SDK都会有此回调,只有登录服务器成功后才有有此回调
*/
typedef xs_int32(*xs_keepliveCallback)(const xs_iotKeepLiveInfo_st* keepLiveInfo);
typedef struct {
xs_uint32 heartbeatSeconds; /**< 低功耗设备休眠之后,发往服务器的心 跳包间隔(秒)*/
xs_char* heartbeatData; /**< 低功耗设备休眠之后,按照指定间隔发送的心跳包内容 */
xs_uint32 hearbeatDataLen; /**< 心跳包内容长度,最长不会超过20字节 */
xs_char* wakeupData; /**< 唤醒包内容,客户端远程唤醒时,保活服务器发往低功耗设备 */
xs_uint32 wakeupDataLen; /**< 唤醒包内容长度,最长不会超过20字节*/
xs_iotKeepLiveServer_st* serverList[MAX_SERVER_COUNT]; //**< 保活服务器地址和端口列表,用冒号分割,${host}:${port}的格式,host可能是IP地址(IPV4或IPV6),也可能是域名,设备根据自己当前的协议栈选择适合自己的保活服务器*/
xs_uint32 serverCount; /**< 保活服务器的个数,目前最多4个 */
} xs_iotKeepLiveInfo_st;
设备休眠
设备要进入休眠之前,主控调用StreamSDK:xs_stream_sleep 接口通知SDK 内部停止推流相关业务,此接口为阻塞接口,调用完毕之后设备可以进入休眠
休眠后,wifi芯片按照SDK下发的参数和保活服务器维持连接,并定时发送心跳,建立连接和保活的流程可以参考 StreamSdk Demo中的 xs_demo_keeplive.c 文件
远程唤醒
当APP远程唤醒时,wifi上的保活连接会收到前面下发的保活报文(wakeupData),wifi上匹配成功,即可唤醒主控设备
代理方案
此方案不太推荐,建议优先采用信媒分离的方案。
注意:如果设备为低功耗设备,但是不支持保活和远程唤醒,不需要集成此部分 SDK。
前置条件:设备已完成主控芯片上 IoTSDK 和 StreamSDK 的接入,将音视频接入了 RTCX 平台。在此基础上,可以接入低功耗代理相关的 SDK。
低功耗 SDK 分为两部分,两者互相依赖,缺一不可
-
LowPwrSdk:运⾏在 WiFi 模组上,维持和服务器之间的 SSL 保活连接,以及通过wifi和主控之间的总线和 ProxySDK 进行交互
-
ProxySDK:运⾏在主控芯片上, 运⾏环境和 IoTSDK/StreamSDK 保持⼀致,采用本地 socket 代理的方式,将主控和服务器之间的信令数据转发给 LowPwrSdk,然后再由 LowPwrSdk 发送给服务器
两者之间的关系如下图所示,两个 SDK 都提供了消息回调接口和消息输入接口,需要应用层将数据在主控芯片和 wifi 芯片之间进行数据传输
ProxySDK
ProxySDK 运行在主控芯片,主要实现了本地连接的代理功能,并处理和 wifi 模组之间交互的数据。
API 接口
API 接口如下:
函数名 | 说明 |
---|---|
xs_proxy_start | 启动代理 SDK |
xs_proxy_stop | 停止代理 SDK |
xs_proxy_inputData | 应用层收到 wifi 芯片 lowpowerSdk 回调的数据(xs_lpw_send_callback),调用此接口将数据传给 ProxySDK |
接口定义说明:
typedef struct
{
xs_deviceInfo_st deviceInfo; //设备信息四元组
xs_proxy_sendCallback sendCb; //发送数据的回调函数
}xs_proxy_t;
/**
* @brief: 启 动代理SDK
* @param: init: 初始化配置参数
* @return: < 0, 初始化失败
* == 0,初始化成功
*
*/
xs_int32 xs_proxy_start(xs_proxy_t *init);
/**
* @brief: 停止代理SDK
*
**/
xs_void xs_proxy_stop();
/**
*@brief 应用层收到wifi芯片 lowpowerSdk 回调的数据(xs_lpw_send_callback),调用此接口将数据传给ProxySDK
* @param: data: 数据内容
* @param: datalen: 数据长度
* @return: < 0, 失败
* == 0,成功
*/
xs_int32 xs_proxy_inputData(xs_char *data,xs_int32 datalen);
回调函数接口:
函数名 | 说明 |
---|---|
xs_proxy_sendCallback | 发送数据的回调函数,应用层收到此回调,直接将数据通过传给 wifi 模组,wifi 模组将数据塞到 LowPowerSDK 的 xs_lpw_api_data_input 函数 |
/**
* @brief: 发送数据的回调函数,应用层收到此回调,直接将数据通过传给wifi模组,wifi模组将数据塞到LowPowerSDK 的 xs_lpw_api_data_input函数
* @param: data: 数据内容
* @param: datalen: 数据长度
* @return: < 0, 失败
* == 0,成功
*/
typedef xs_int32 (*xs_proxy_sendCallback)(xs_char *data, xs_int32 datalen);
demo 示例
SDK 提供的打包文件中包含 ProxySDK 的 demo,代码流程如下:
- 调用 xs_proxy_start 接口设置初始化参数,启动 sdk
- 启动一个 udp 通道,用于模拟和 wifi 芯片上 LowPwrSDK 的数据交互
- 回调函数处理,处理数据的转发
// xs_proxy 需要发送给 xs_lowpowerSdk 的数据
static xs_int32 proxySendCb(xs_char *data, xs_int32 len)
{
printf("#########proxy send data to lowPwr, length:%d, data:%s\n", len, data+15);
int left = len;
int offset=0;
while(left > 0){
int sendlen = left > 300 ? 300:left;
//demo中采用udp channel发送到对端
test_channel_sendto_lowpower(data+offset,sendlen);
offset += sendlen;
left -= 300;
}
return 0;
}
/*xs_proxy收到的从 xs_lowpowerSdk 发过来的数据 */
static int recvLowPwrData(char *data,int32_t len)
{
printf("#########proxy recv data from lowPwr, length:%d\n", len);
return xs_proxy_inputData(data,len);
}
xs_int32 testIotProxyStart(const xs_deviceInfo_st* devInfo)
{
/*起一个udp channel模拟wifi模组和主控之间的数据收发*/
test_channel_init(recvLowPwrData);
sleep(1); //此处sleep 1秒是为了等待测试程序中 udp 线程启动成功
xs_proxy_t init_cfg={0};
init_cfg.deviceInfo = *devInfo;
init_cfg.sendCb = proxySendCb;
int32_t ret = xs_proxy_start(&init_cfg);
if (ret != XSIOT_EC_SUCCESS){
printf("xs_proxy_start failed, ret=%d\n",ret);
}
printf("#######xs_proxy_start, ret=%d\n", ret);
return ret;
}
LowPwrSDK
LowPwrSDK 运行在 wifi 芯片,主要维护和信令服务器之间的 SSL 保活链接。纯 C 代码实现,依赖 mbedtls 库。需要和 ProxySDK 配合使用。
-
demo
- lowPwrSDK 的 demo,指导开发人员快速接入
-
include
- lowPwrSDK 的头文件
-
lib
- lowPwrSDK 静态库
-
hal
- 硬件系统函数适配层函数接口,需要开发者根据不同的芯片实现不同的接口
out/
├── demo
│ ├── lpw_test.c
│ ├── udp_channel.c
│ └── udp_channel.h
├── hal
│ ├── xs_lpw_hal.h
│ ├── xs_lpw_hal_linux.c
│ ├── xs_lpw_hal_liteos.c
│ ├── xs_mbedtls_hal.c
│ └── xs_mbedtls_hal.h
├── include
│ ├── xs_lpw_api.h
│ └── xs_lpw_data_types.h
└── lib
└── liblowPwrSdk.a
API 接口
API 列表如下:
函数名 | 说明 |
---|---|
xs_lpw_api_init | 初始化接口,传入初始化参数 |
xs_lpw_api_deinit | 反初始化接口 |
xs_lpw_connect | 建立和服务器的连接,此函数调用之后,内部会触发 XS_COMM_EVENT_WEEKUP 的回调唤醒主控设备,主控设备 ProxySDK 会将服务器地址信息地址传递过来 |
xs_lpw_disconnect | 断开连接 |
xs_proxy_inputData | 主控端 ProxySDK 回调的数据,通过该接口通知给 LowPwrSDK,该接口为⾮阻塞类型接⼝,对栈空间消耗很少。 |
xs_lpw_api_status_set | 用于告知 主控芯片 的运行状态 主控 ProxySDK 运行后,自动通知 LowPwrSDK(通过 xs_lpw_api_data_input 接口),但是 主控 ProxySDK 退出后,需要由用户去设置 XS_COMM_HOST_SLEEP XS_COMM_HOST_ACTIVE 不需要用户设置,注意主控休眠的时候,一定要通知 LowPwrSDK,否则内部判断状态错误可能会导致出现问题 |
相关数据结构定义
typedef struct
{
/* data */
xs_lpw_send_callback send_callback; // 调用本地通信总线接口,把数据流发送给 主控端的ProxySDK
xs_lpw_event_callback event_callback;// 事件通知
int buf_size; //本地缓存数据的大小,空间大小必须是 2 的 n 次方。
int wakeup; //唤醒关键词, 支持按位或的方式
int heartbeat_interval_s; // 心跳包间隔,单位是秒,建议大于 60s
}xs_lpw_init_t;
/**
* @brief LowPwrSDK 初始化接口
*/
int xs_lpw_api_init(xs_lpw_init_t *init);
/**
* @brief LowPwrSDK 反初始化接口
*/
void xs_lpw_api_deinit();
/**
* @brief 建立和服务器的连接
* @note 此函数调用之后, 内部会触发 XS_COMM_EVENT_WEEKUP 的回调唤醒主控设备,主控设备ProxySDK会将服务器地址信息地址传递过来
*/
int xs_lpw_connect();
/**
* @brief 断开连接
*/
int xs_lpw_disconnect();
/**
* @brief 主控端ProxySDK回调的数据,通过该接口通知给 LowPwrSDK
* @param data 数据内容
* @param len 数据长度
*/
int xs_lpw_api_data_input(char *data,int len);
/**
* @brief 用于告知 主控芯片 的运行状态,主控ProxySDK 运行后,自动通知 xs_lpw(通过 xs_lpw_api_data_input接口).
* 但是 主控ProxySDK 退出后,需要由用户去设置XS_COMM_HOST_SLEEP。
* XS_COMM_HOST_ACTIVE 不需要用户设置。
* @param status 主控运行状态,包括唤醒和休眠两个状态
*/
void xs_lpw_api_status_set(xs_lpw_status_t status);
回调函数接口:
函数名 | 说明 |
---|---|
xs_lpw_send_callback | 发送数据的回调函数,应用层收到此回调,直接将数据通过传给 wifi 模组,wifi 模组将数据塞到 LowPowerSDK 的 xs_lpw_api_data_input 函数 |
xs_lpw_event_callback | 内部事件的回调函数,用于通知唤醒主控设备,以及回调信令通道在线状态 |
/*
* @brief: 发送数据的回调函数,应用层收到此回调,直接将数据通过传给主控芯片,主控芯片将数据塞到 ProxySDK 的 xs_proxy_inputData 函数
* @param: data: 数据内容
* @param: datalen: 数据长度
* @return: < 0, 初始化失败
* == 0,初始化成功
*
* */
typedef int (*xs_lpw_send_callback)(char *data, int datalen);
/*
* @brief: 内部事件的回调函数,用于通知唤醒主控设备,以及回调信令通道在线状态
* @param: event: 事件类型
* @param: data: 事件附带的数据信息,目前为NULL
* @param: datalen: 事件附带的数据信息,目前为 0
* @return: < 0, 初始化失败
* == 0,初始化成功
*
* */
typedef int (*xs_lpw_event_callback)(xs_lpw_event_t event, char* data, int datalen);
demo 示例
SDK 提供的打包文件中包含 LowPwrSDK 的 demo,代码流程如下:
- 启动一个 udp 通道,用于模拟和主控芯片上 ProxySDK 的数据交互
- 调用 xs_lpw_api_init 接口设置初始化参数,启动 sdk
- 调用 xs_lpw_connect 启动连接,SDK 会回调
/*udp通道收到主控发过来的数据,塞给LowPwrSdk*/
static int channle_recv_callback_handle(char *data,int32_t len)
{
return xs_lpw_api_data_input(data,len);
}
/*LowPwrSdk回调出来的数据,通过udp通道发送到主控芯片*/
static int send_callback_handle(char *data,int32_t len)
{
int left = len;
int offset=0;
while(left > 0){
int sendlen = left > 900 ? 900:left;
test_channel_sendto_host(data+offset,sendlen);
offset += sendlen;
left -= 900;
}
return 0;
}
/*LowPwrSdk事件回调*/
static int event_callback(xs_lpw_event_t event, char* data, int datalen )
{
switch (event)
{
case XS_COMM_EVENT_WEEKUP:
printf("[lpw_demo]================================XS_COMM_EVENT_WEEKUP=======================\n");
/*需要唤醒主控芯片*/
break;
case XS_COMM_EVENT_CONNECTED:
printf("[lpw_demo]==========================device online ===================================\n");
break;
case XS_COMM_EVENT_DISCONNECTED:
printf("[lpw_demo]==========================device offline ===================================\n");
break;
default:
break;
}
return 0;
}
int main()
{
int ret =0;
/*启动udp channel,模拟和主控芯片之间的数据收发*/
test_channel_init(channle_recv_callback_handle);
/*设置初始化参数*/
xs_lpw_init_t init_cfg;
memset(&init_cfg,0,sizeof(init_cfg));
init_cfg.send_callback = send_callback_handle;
init_cfg.event_callback = event_callback;
init_cfg.buf_size = 8*1024;
init_cfg.heartbeat_interval_s = 90;
init_cfg.wakeup = XS_LPW_WAKEUP_UPGRADE | XS_LPW_WAKEUP_PLAY;
ret = xs_lpw_api_init(&init_cfg);
if (ret != XS_LPW_SUCCESS){
printf("[lpw_demo]init failed, ret=%d\n",ret);
goto CLEAN_UP;
}
/*启动连接*/
ret = xs_lpw_connect();
while(1)
{
int cmd=0;
scanf("%d",&cmd);
switch(cmd)
{
case 1: //模拟主控进入休眠
{
printf("[lpw_demo]kill host now\n");
system("[lpw_demo]killall -9 iotDemo");
xs_lpw_api_status_set(XS_COMM_HOST_SLEEP); //通知sdk主控进入休眠
break;
}
}
usleep(100*1000);
}
CLEAN_UP:
xs_lpw_disconnect();
xs_lpw_api_deinit();
return 0;
}
其他流程 说明
低功耗保活设备除了需要集成 ProxySDK 和 LowPwrSDK 之外,在集成 IoTSDK 和 StreamSDK 的时候需要注意以下几点:
-
IoTSDK
- xs_iot_init 初始化接口中,需要指定设备工作模式为 XS_IOT_WORK_MODE_LOWPOWER_PROXY
-
StreamSDK
- SDK 提供了 xs_stream_sleep 接口,设备在休眠之前需要调用,SDK 内部会停止相关任务,此接口为阻塞接口,调用完毕之后设备可以进入休眠
休眠机制
休眠时机由设备侧自行控制,SDK内部目前提供了有人观看和无人观看的回调,设备侧可以结合此状态综合判断。
/**
* @brief 播放状态回调(包含直播和SD卡回放)
*
* @param [IN] devId: 设备ID标识
* @param [IN] isPlay: 是否有人播放
* @return void
*/
typedef xs_void (*xs_playStatusCb)(xs_int32 devId, xs_bool isPlay);