跳到主要内容

低功耗SDK接入

更新时间:2025-04-25 19:03:58

方案介绍

如果设备为长上电设备,不需要关注此章节内容。

对于低功耗 IOT 设备,RTCX 提供了以下接入方案,用户可以根据自己设备特性,选择适合自己的方案。

信媒分离方案(建议使用)

​ 在某些场景下(比如智能门锁),期望在不启动视频主控芯片的情况下,也能实现一些非视频类的简单操作,比如物模型的参数配置、以及消息上报等。此种情况下可以将 IoTSDK和StreamSDK分离,IoTSDK运行在wifi芯片上,在主控休眠的时候也可以处理服务器下发的信令操作。StreamSDK 运行在视频主控芯片上,需要唤醒主控之后才能工作。

​ 此种模式下,对客户端APP来说可以把设备看做长上电设备,需要操作设备的时候,直接发送相关的请求即可,由IoTSDK判断是否需要唤醒StreamSDK。主控启动之后不需要再建立信令连接,可以加快远程唤醒出图的速度。

分离方案如下图所示:

图片无法显示

注意,在信令流媒体分离的场景下,使用者需要通过SPI/SDIO总线或者其他方式将 IoTSDK 和StreamSDK回调的数据转发到另一端,注意转发的时候要保证数据的正确性,不能出现丢包或者数据错乱的现象。

数据交换说明:

  1. wifi芯片IoTSDK 回调函数 xs_msgCallback 回调的数据,通过总线传到主控芯片,然后应用层调用 StreamSDK的 xs_stream_inputIotMsg 接口将数据塞入
  2. 主控芯片StreamSDK 回调函数 xs_streamMsgCallback 回调的数据,通过总线传到 wifi 芯片,然后应用层调用 IoTSDK 的 xs_iot_handleExternalMsg 接口将数据塞入

设备休眠

当设备需要进入休眠时,需要调用下面的接口通知SDK:

  1. StreamSDK:xs_stream_sleep 接口,主控休眠之前需要调用,SDK 内部会停止推流相关任务,此接口为阻塞接口,调用完毕之后设备可以进入休眠
  2. 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远程唤醒设备的时候,平台会下发唤醒包。

相关操作流程如下:

  1. 初始化的时候,初始化参数 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;
}
  1. 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 分为两部分,两者互相依赖,缺一不可

  1. LowPwrSdk:运⾏在 WiFi 模组上,维持和服务器之间的 SSL 保活连接,以及通过wifi和主控之间的总线和 ProxySDK 进行交互

  2. 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,代码流程如下:

  1. 调用 xs_proxy_start 接口设置初始化参数,启动 sdk
  2. 启动一个 udp 通道,用于模拟和 wifi 芯片上 LowPwrSDK 的数据交互
  3. 回调函数处理,处理数据的转发
// 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 配合使用。

  1. demo

    1. lowPwrSDK 的 demo,指导开发人员快速接入
  2. include

    1. lowPwrSDK 的头文件
  3. lib

    1. lowPwrSDK 静态库
  4. hal

    1. 硬件系统函数适配层函数接口,需要开发者根据不同的芯片实现不同的接口
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,代码流程如下:

  1. 启动一个 udp 通道,用于模拟和主控芯片上 ProxySDK 的数据交互
  2. 调用 xs_lpw_api_init 接口设置初始化参数,启动 sdk
  3. 调用 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 的时候需要注意以下几点:

  1. IoTSDK

    1. xs_iot_init 初始化接口中,需要指定设备工作模式为 XS_IOT_WORK_MODE_LOWPOWER_PROXY
  2. StreamSDK

    1. 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);