5.x SDK 开发指南

更新时间:2024-11-20 08:51:25下载pdf

本文介绍了涂鸦 IPC 嵌入式 SDK 的开发流程,从运行 SDK Demo 开始,将开发中所需要的资料串联。您可根据实际需要进行具体功能的开发,可查看对应方案的开发说明,适用于快速入门。

名词解释

名词 释义
配网 建立设备端、手机 App、云端之间的通信链路。
PID/Productkey 即产品 ID(Product ID),用来标示某一类产品,同一种类型的 IPC 设备共享同一个产品 ID。PID 关联了产品具有的功能点。
UUID 即通用唯一识别码(Universally Unique Identifier),由一台机器上生成的数字,保证所有机器都具有唯一性。
AUTHKEY 设备的授权值,在 涂鸦开发者平台 上已注册能够使用云服务的服务码。
P2P ID 点对点服务 ID,3.0.0 版本以上 SDK 不需要填写 P2P ID,云端自动进行分配。
Token 二维码配网过程中,由云服务器生成的标识码,10 分钟有效期。
DP Data Point,设备与服务器数据交互的功能点。
Chromecast 谷歌电视棒或谷歌 Home Hub 实时视频传输服务。
Echo Show 亚马逊音响实时视频传输服务。
IFTTT 与其他品牌商建立设备联动服务。
OTA 设备在线升级功能。

SDK 目录

SDK 文件导航目录如下:

.
└── tuya_ipc_sdk
    ├── apps						//APP 相关,暂空
    ├── platforms					//暂空
    ├── sdk				                //SDK 文件
    │	├── include
    │   │	├── cJSON.h   			        //cJSON 相关
    │   │	├── tuya_cloud_base_defs.h 		//涂鸦开发者平台的基本定义
    │   │	├── tuya_cloud_com_defs.h  	        //涂鸦开发者平台的通用定义
    │   │	├── tuya_cloud_error_code.h 	        //Tuya SDK 错误的定义
    │   │	├── tuya_cloud_types.h  		//类型定义
    │   │	├── tuya_cloud_wifi_defs.h 		//Wi-Fi utils 的定义
    │   │	├── codec
    │   │	│	└── tuya_g711_utils.h  		//G.711 相关
    │   │	├── tuya_ipc_api.h  			//API 接口相关
    │   │	├── tuya_ipc_cloud_storage.h  	        //云存储相关
    │   │	├── tuya_ipc_img_defs.h  		//IPC 图像库基本定义
    │   │	├── tuya_ipc_img_proc.h  		//IPC 图像 API 相关
    │   │	├── tuya_ipc_media.h  			//IPC 媒体相关
    │   │	├── tuya_ipc_p2p.h  			//IPC P2P 相关
    │   │	├── tuya_ipc_ptz.h  			//IPC PTZ 相关
    │   │	├── tuya_ipc_qrcode_proc.h  	        //二维码相关
    │   │	├── tuya_ipc_skill.h  			//IPC 能力相关
    │   │	├── tuya_ipc_stream_storage.h 	        //IPC 本地存储相关
    │   │	├── tuya_ipc_video_msg.h  		//IPC 视频消息相关
    │   │	├── tuya_ipc_video_proc.h  		//IPC 视频检测相关
    │   │	├── tuya_ipc_webrtc.h  			//IPC web拉流相关
    │   │	├── tuya_ring_buffer.h  		//ringbuffer 相关
    │   │	├── uni_network.h  	                //网络相关
    │   │	├── ty_wifi_mgnt.h  			//Wi-Fi 接口相关
    │   │	└── ...
    │	└── libs
    │		├── libtuya_iot.a
    │   	└── libtuya_iot.a.stripped
    ├── bulid_app.sh					//忽略,参照 demo 编译方法
    ├── CHANGELOG.md				        //说明文档
    └── README.md					//说明文档

业务流程

获取到 SDK 后,您首先需要进行 SDK 服务注册。注册操作通过 SDK 配网模块建立,服务通道建立成功后,用户可以将设备的音视频信息按照格式要求送入 SDK 开辟的缓存中,至此用户无需关心后续的操作。

可通过 App 端操控设备。基于原始的的音视频数据,SDK 结合手机端 App 可以实现设备的存储功能、点对点传输服务以及其他的第三方接入功能,例如:Echo Show 和 Chromecast 接入功能。

SDK 架构

5.x SDK 开发指南

Demo 流程

您可以根据以下流程,快速体验涂鸦 IPC SDK 功能。

  1. 登录 涂鸦开发者平台 创建产品。
  2. 获取 IPC SDK 包。
  3. 获取 tuya_ipc_demo
  4. 在 App Store 等应用市场下载智能生活 app。
  5. 在 Linux 环境下做简单适配。
  6. 运行 Demo,手机移动端直观体验。

第一步:创建产品

  1. 登录 涂鸦开发者平台

  2. 单击 创建产品

    5.x SDK 开发指南

  3. 选择 摄像机 > 云台摄像机

    5.x SDK 开发指南

  4. 选择智能化方式并勾选自定义方案。

    5.x SDK 开发指南

  5. 输入产品名称和型号,选择设备类型,并单击 创建产品

    5.x SDK 开发指南

  6. 添加需要的 DP。

    5.x SDK 开发指南

  7. 单击 设备交互

    5.x SDK 开发指南

  8. 下拉页面,选择对应模板。

    5.x SDK 开发指南

  9. 单击 硬件开发,选择芯片平台和模组名称。

    5.x SDK 开发指南

  10. 确定后弹出如下界面,单击 免费领取

    5.x SDK 开发指南

  11. 选择交付方式,提交订单。

    5.x SDK 开发指南

  12. 单击订单详情。

    5.x SDK 开发指南

  13. 下载授权码清单。

    5.x SDK 开发指南

至此,产品创建成功,并获得 2 组试用的授权信息。

第二步:获取 SDK

获取 SDK。如果未能找到您需要的 SDK,请联系您的涂鸦技术支持工程师。

第三步:运行 Demo

  1. 下载 demo 代码。

    5.x SDK 开发指南

  2. 解压 SDK,调整视频文件路径。

    解压SDK

    将解压后的 SDK 中的 SDK 文件夹(包含 includelib 文件夹),复制到 demo_for_ipc_5.x 文件夹下。

    调整视频文件路径

    • 方式一(无需改动代码):

      将下载的 demo 文件中的 demo_resource 复制到 demo_for_ipc_5.x 文件夹下,并且重命名为 resource

      在运行时,指定文件位置为当前文件夹即可,-r 后即为视频文件地址。

      ./tuya_ipc_demo -m 2 -p PID -u UUID -a AUTHKEY -r "./" -t "TOKEN"
      
    • 方式二(需要改动代码):

      demo_src 文件夹下的 user_main.c 文件中,跳转至 359 行。

      strcpy(s_raw_path, "/tmp"); //Path where demo resources locates
      

      在此处修改视频文件所在路径。

      代码中视频文件名默认为 resource,可以在代码中进行修改,也可将文件夹名改为 resource 后调整路径。

  3. 编译生成可执行文件。

    在终端,进入 demo_for_ipc_5.x 文件夹,执行指令:

    #make APP_NAME=demo
    

    当编译完成并显示 [Build APP Finish],即为编译完成。

    可执行文件生成位置:demo_for_ipc_5.x/output/ 文件下,[tuya_ipc_demo] 为输出的可执行文件。

  4. 使用手机下载安装智能生活 App,打开 App,单击添加设备。

    5.x SDK 开发指南
  5. 选择 安防监控 品类 > 智能摄像机(Wi-Fi)

    5.x SDK 开发指南
  6. 勾选 确认指示灯在快闪或听到提示音,然后单击 下一步

    5.x SDK 开发指南
  7. 输入 Wi-Fi 名称和密码。

    此处连接的 Wi-Fi 需是 2.4 GHz。

    5.x SDK 开发指南
  8. 生成和识别二维码。

    将二维码进行截屏,打开微信,使用 扫一扫 功能来扫描二维码图片。

    此处不要单击 听到提示音 按钮,在第 11 步时再单击。

    5.x SDK 开发指南
  9. 在获取到的数据中,提取 token。

    获取到的数据:{"p":"test","s":"test","t":"AYYBJt2OGvY***"}

    t 后的参数即为 Token,有效期:10 分钟。10分钟后 token 失效,需要按照上诉流程重新获取。

  10. 在 output/ 文件下执行命令,运行可执行程序。

    #在终端输入以下指令
    #./tuya_ipc_demo -m 2 -p PID -u UUID -a AUTHKEY -r "./" -t "TOKEN"
    
    #注:-m 为配网模式的选择[0,2]。0 为快联配网,1为 AP 配网,2为 debug 配网。PID、UUID 和 AUTHKEY 是通过此前创建产品时获取到的,使用自己获取到的进行替换。-r 为视频文件所在位置,此处为当前文件夹。token 是通过扫码二维码解析获取到的。
    
  11. 在执行可执行程序的同时,单击手机 App 上的 听到提示音 按钮。

    5.x SDK 开发指南
  12. 等待一段时间即可添加成功。

    单击添加成功的设备,即可成功获取视频流。如果没有视频显示,则表示之前的 demo_resource 文件没有设置正确,请至第 2 步检查是否操作正确。

    5.x SDK 开发指南

    在终端中输入下列指令,可以模拟体验 IPC 功能:

    指令 指令说明
    start 模拟移动侦测事件触发
    stop 模拟停止移动侦测事件
    status 获取设备激活状态
    bell 模拟门铃事件上报

    例如虚拟设备执行过程中,执行:

    #start
    

    可以在消息中心看到一条移动侦测的上报。

    5.x SDK 开发指南

通过上述步骤可以快速体验 IPC SDK 功能。

应用开发

  • 您需要在 demo_src/user_main.c 文件中,分别修改 DB 文件、OTA 文件、本地录像文件的保存路径,宏定义如下:

    DB 文件中保存设备的配网信息,需要保存至掉电不丢失的路径下。

    DB 文件与本地录像文件保存路径需要检查结尾是否有 /

    #define IPC_APP_STORAGE_PATH "/tmp/"
    #define IPC_APP_UPGRADE_FILE "/tmp/upgrade.file"
    #define IPC_APP_SD_BASE_PATH "/tmp/"
    
  • PID、UUID、AUTHKEY 的传入:

    • 方式一:(建议运行 demo 时使用此方法)

      #./tuya_ipc_demo -m 2 -p PID -u UUID -a AUTHKEY -r "./" -t "TOKEN"
      #替换其中的PID、UUID 和 AUTHKEY 即可,在运行 demo 的快速体验 > 运行demo 第 10 步介绍了其他参数。
      
    • 方式二:(建议开发时使用此方法)

      #CHAR_T s_ipc_pid[64]="tuya_pid";			//pid
      #CHAR_T s_ipc_uuid[64]="tuya_uuid";			//uuid
      #CHAR_T s_ipc_authkey[64]="tuya_authkey";	//authkey
      #将 PID、UUID 和 AUTHKEY 替换即可,可以写入到 flash 文件中保存,掉电不丢失
      
  • 修改版本号的宏定义:

    版本号必需按照格式:xx.xx.xx 进行填写,不超过 20 位字符。

    #define IPC_APP_VERSION "1.2.3"
    

至此,SDK 基本参数已完成填写,进入配网模式。

SDK 配网开发

涂鸦 SDK 搭建了一套完整的、用以引导设备配网的函数框架,本章节阐述配网的流程以及各框架函数的用途。建议您按照文档介绍,在对应的框架函数下,实现各功能点的具体操作。

Wi-Fi 快联配网

5.x SDK 开发指南

开发流程

  • 在 main 主函数中找到参数 mode,选择:WIFI_INIT_AUTO 配网模式,token 参数填 NULL

    CHAR_T token[30] = {0};	//token--二维码配网过程中,由云服务器生成的标识码,10min有效期
    WIFI_INIT_MODE_E mode = WIFI_INIT_AUTO;	//mode--配网模式选择
    
  • 以下接口需要您实现。具体实现可以参考 demo。

    //Wi-Fi模块模式的获取接口,默认模式为 sniffer 模式
    OPERATE_RET tuya_adapter_wifi_get_work_mode(OUT WF_WK_MD_E *mode)
    //扫描全部信道并将扫描到的信息传输至结构体 AP_IF_S**ap 中
    OPERATE_RET tuya_adapter_wifi_assign_ap_scan(IN CONST CHAR_T *ssid,OUT AP_IF_S **ap)
    //设置 Wi-Fi 模式。当 SDK 判断 Wi-Fi 输入的空口包数据正确,将 Wi-Fi 状态设置为 station 模式
    OPERATE_RET tuya_adapter_wifi_set_work_mode(IN CONST WF_WK_MD_E mode)
    // sniffer 模式回调函数注册
    int tuya_adapter_wifi_sniffer_set(const bool en, const SNIFFER_CALLBACK cb)
    //根据解析到的数据,进行联网操作
    OPERATE_RET tuya_adapter_wifi_station_connect(IN CONST CHAR_T *ssid,IN CONST CHAR_T *passwd)
    //配网成功后,通知 SDK 已获取到 IP 地址。此接口为高频调用接口。
    OPERATE_RET tuya_adapter_wifi_station_get_status(OUT WF_STATION_STAT_E *stat)
    
  • 传入 Wi-Fi 接收到的空口包信号进行处理,显示信号强度。

    OPERATE_RET tuya_adapter_wifi_station_get_conn_ap_rssi(OUT SCHAR_T *rssi)
    

    如需更改成其他接口,请咨询后再进行修改,避免出现未知错误。

二维码配网

开发流程

  • TUYA_IPC_SDK_START 函数中,选择:WIFI_INIT_AUTO 配网模式,token 参数填 NULL

    CHAR_T token[30] = {0};	//token是二维码配网过程中,由云服务器生成的标识码,10 分钟有效期
    WIFI_INIT_MODE_E mode = WIFI_INIT_AUTO;	//mode 是配网模式选择。
    
  • 二维码配网一般流程:

    1. 抓取二维码图片。

    2. 将二维码图片传入 zbar 函数(开源函数)进行识别。图像分辨率建议:不低于 320*240。

    3. 识别获取到的二维码信息(ssid、password 和 token)。

    4. 根据获取到的信息(ssid、password 和 token),调用 tuya_adapter_wifi_station_connect,建立 MQTT 连接。

      具体实现流程可以参照 demo_src/tuya_ipc_qrcode_demo.c 文件,需要启动文件中的线程进行功能的调用。

  • 以下接口需要您实现。具体实现可以参考demo。

    //Wi-Fi模块状态的获取
    OPERATE_RET tuya_adapter_wifi_get_work_mode(OUT WF_WK_MD_E *mode)
    //数据的获取和解析,需要分析摄像头获取到的二维码信息
    STATIC CHAR_T* __tuya_linux_get_snap_qrcode(VOID)
    //根据解析到的数据,进行联网操作
    OPERATE_RET tuya_adapter_wifi_station_connect(IN CONST CHAR_T *ssid,IN CONST CHAR_T *passwd)
    

    如需更改成其他接口,请咨询后再进行修改,避免出现未知错误。

有线配网(局域网发现)

  • 设备端调用:TUYA_IPC_SDK_START 函数进行 SDK 初始化。

  • 以下接口需要您实现。具体实现可以参考 demo_src/tuya_ipc_wired_demo.c

    //设备获取 IP 地址。设备在检测到已连接路由器时,返回获取到的 IP 地址
    OPERATE_RET tuya_adapter_wifi_get_ip(IN CONST WF_IF_E wf,OUT NW_IP_S *ip)
    //获取设备 MAC 地址
    OPERATE_RET tuya_adapter_wifi_get_mac(IN CONST WF_IF_E wf,INOUT NW_MAC_S *mac)
    
  • SDK 将获取到的设备 IP 地址与 MAC 地址通过路由器发送广播包,手机连上路由器 Wi-Fi 并接收到广播信息后,向服务器获取 token,获取成功后给到设备端进行联网。至此,配网操作完成。

    如需更改成其他接口,请咨询后再进行修改,避免出现未知错误。

直连扫码(手机扫设备二维码)

  • 确认已下载带有直连扫码功能的涂鸦 SDK,涂鸦 UUID 经过后台打标处理。

  • 运行时确保设备已连接到 Internet。

  • 调用:TUYA_IPC_SDK_START 函数进行 SDK 初始化。入参 WIFI_INIT_MODE_E 选择:WIFI_INIT_NULL,token 填写 NULL

  • 手机单击右上角扫描图标,扫描二维码激活设备。

    如需更改成其他接口,请咨询后再进行修改,避免出现未知错误。

音视频开发

实时预览开发

SDK 初始化时,将默认创建 10 秒音视频数据缓存,您将设备采集到的音视频数据送入 ringbuffer 即可。SDK 库文件已实现数据发送,您无需再做对应开发。

开发相关函数

  • 确认 TUYA_IPC_SDK_START 函数中视频参数信息:

    GOP 推荐:FPS 参数的 2-3 倍,码率上限 1.5Mbit/s,分辨率推荐 16:9,支持 H264 和 H265 编码格式。

    //主码流信息配置,可根据需要修改参数
    ipc_sdk_run_var.media_info.media_info.channel_enable[E_IPC_STREAM_VIDEO_MAIN] = TRUE; 	/* 是否启用本地高清视频流 */
    ipc_sdk_run_var.media_info.media_info.video_fps[E_IPC_STREAM_VIDEO_MAIN] = 30; 			/* FPS */
    ipc_sdk_run_var.media_info.media_info.video_gop[E_CHANNEL_VIDEO_MAIN] = 30; 			/* GOP */
    ipc_sdk_run_var.media_info.media_info.video_bitrate[E_CHANNEL_VIDEO_MAIN] = TUYA_VIDEO_BITRATE_1M; /*速率限制*/
    ipc_sdk_run_var.media_info.media_info.video_width[E_CHANNEL_VIDEO_MAIN] = 640; 			/* 单帧宽度分辨率 */
    ipc_sdk_run_var.media_info.media_info.video_height[E_CHANNEL_VIDEO_MAIN] = 360;			/* 单帧高度分辨率 */
    ipc_sdk_run_var.media_info.media_info.video_freq[E_CHANNEL_VIDEO_MAIN] = 90000; 		/* 时钟频率 */
    ipc_sdk_run_var.media_info.media_info.video_codec[E_CHANNEL_VIDEO_MAIN] = TUYA_CODEC_VIDEO_H264; /* 编码格式 */
    
    //子码流信息配置,可根据需要修改参数
    ipc_sdk_run_var.media_info.media_info.channel_enable[E_IPC_STREAM_VIDEO_SUB] = TRUE;   	/* 是否启用本地高清视频流 */
    ipc_sdk_run_var.media_info.media_info.video_fps[E_IPC_STREAM_VIDEO_SUB] = 30;  			/* FPS */
    ipc_sdk_run_var.media_info.media_info.video_gop[E_IPC_STREAM_VIDEO_SUB] = 30;  			/* GOP */
    ipc_sdk_run_var.media_info.media_info.video_bitrate[E_IPC_STREAM_VIDEO_SUB] = TUYA_VIDEO_BITRATE_512K; /*速率限制*/
    ipc_sdk_run_var.media_info.media_info.video_width[E_IPC_STREAM_VIDEO_SUB] = 640; 		/* 单帧宽度分辨率 */
    ipc_sdk_run_var.media_info.media_info.video_height[E_IPC_STREAM_VIDEO_SUB] = 360;		/* 单帧高度分辨率 */
    ipc_sdk_run_var.media_info.media_info.video_freq[E_IPC_STREAM_VIDEO_SUB] = 90000; 		/* 时钟频率 */
    ipc_sdk_run_var.media_info.media_info.video_codec[E_IPC_STREAM_VIDEO_SUB] = TUYA_CODEC_VIDEO_H264; /* 编码格式 */
    
  • 确认 TUYA_IPC_SDK_START 函数中音频参数信息:

    音频支持 PCM、G711U 和 G711A 格式,上传最大长度:1400 字节,支持 8K 和 16K 音频,App 下发音频长度固定为:320 字节。

    ipc_sdk_run_var.media_info.media_info.channel_enable[E_IPC_STREAM_AUDIO_MAIN] = TRUE; /* 是否启用本地声音采集 */
    ipc_sdk_run_var.media_info.media_info.audio_codec[E_IPC_STREAM_AUDIO_MAIN] = TUYA_CODEC_AUDIO_PCM;/* 编码格式 */
    ipc_sdk_run_var.media_info.media_info.audio_sample [E_IPC_STREAM_AUDIO_MAIN]= TUYA_AUDIO_SAMPLE_8K;/* 采样率 */
    ipc_sdk_run_var.media_info.media_info.audio_databits [E_IPC_STREAM_AUDIO_MAIN]= TUYA_AUDIO_DATABITS_16;/* 位宽 */
    ipc_sdk_run_var.media_info.media_info.audio_channel[E_IPC_STREAM_AUDIO_MAIN]= TUYA_AUDIO_CHANNEL_MONO;/* 通道 */
    ipc_sdk_run_var.media_info.media_info.audio_fps[E_IPC_STREAM_AUDIO_MAIN] = 25;	   /* 帧率 */
    
  • 调用 tuya_ipc_ring_buffer_open 开启一个新的 session 用于音视频传输,其函数如下:

    所有关于 ringbuffer 的接口兼容 NVR 和 IPC,IPC 中 device 和 channel 参数一般为 0。

    /* 打开一个新的会话进行读/写
    [in]device :设备编号(用以多设备)
    [in]channel:摄像头编号(一个设备可以有多个摄像头,如只有一个摄像头请填写 0)
    [in]stream:码流类型(主码流,子码流)
    [in]open_type:session 的 ringbuffer 的类型
    */
    Ring_Buffer_User_Handle_S tuya_ipc_ring_buffer_open(INT_T device, INT_T channel, IPC_STREAM_E stream, RBUF_OPEN_TYPE_E open_type);
    
  • 将摄像机采集到的音视频数据通过函数:TUYA_APP_Put_Frame 传入 ringbuffer 中,函数如下所示:

    /* 将音频和视频的原始数据发送到 SDK
    [in] handle:对应 tuya_ipc_ring_buffer_open 返回的 handle
    [in] p_frame:需要传输的音视频帧
    */
    OPERATE_RET TUYA_APP_Put_Frame(Ring_Buffer_User_Handle_S handle,IN CONST MEDIA_FRAME_S *p_frame);
    
  • 您需在 TUYA_APP_Enable_Speaker_CB 函数中自行实现喇叭的开关控制,函数如下所示:

    VOID TUYA_APP_Enable_Speaker_CB(BOOL_T enabled)
    {
     printf("enable speaker %d \r\n", enabled);
     //TODO/* 开发人员需要打开或关闭扬声器硬件操作。
     如果 IPC 硬件功能不需要显式打开,则该功能可以留为空白。*/
    }
    
  • TUYA_APP_Rev_Audio_CB 函数中实现音频数据的播放操作,函数如下所示:

    VOID TUYA_APP_Rev_Audio_CB(IN CONST MEDIA_FRAME_S *p_audio_frame,
                  TUYA_AUDIO_SAMPLE_E audio_sample,
                  TUYA_AUDIO_DATABITS_E audio_databits,
                  TUYA_AUDIO_CHANNEL_E audio_channel)
    {
     printf("rev audio cb len:%u sample:%d db:%d channel:%d\r\n", p_audio_frame->size, audio_sample, audio_databits, audio_channel);
     //PCM-Format 8K 16Bit MONO//TODO/*开发人员需要实现语音播放的操作 */
    }
    
  • App 默认下发 G711U 格式音频,设备端如需以 PCM 格式播放,需要调用函数: tuya_g711_decode 对下发的音频做转码处理,函数如下所示:

    函数参数:

    • 1:TUYA_G711_MU_LAW
    • 2:被解析数据的地址
    • 3:被解析数据的长度
    • 4:数据解析后输出的地址
    • 5:数据解析后的长度
    int tuya_g711_decode(unsigned char type, unsigned short *src, unsigned int srcLen, unsigned char *drc, unsigned int *pOut);
    

示例开发流程

  1. TUYA_IPC_SDK_START 中配置视频主码流、子码流和音频参数信息。

  2. 在主函数中开启音视频线程(两个线程)。

    pthread_t h264_output_thread;
    pthread_create(&h264_output_thread, NULL, thread_live_video, NULL);
    pthread_detach(h264_output_thread);
    
    pthread_t pcm_output_thread;
    pthread_create(&pcm_output_thread, NULL, thread_live_audio, NULL);
    pthread_detach(pcm_output_thread);
    
  3. 分别在音视频线程中,调用 tuya_ipc_ring_buffer_open 函数,开启新的 session。

  4. 在线程函数中采集事先准备的音视频文件的信息,调用 TUYA_APP_Put_Frame 将采集到的视频帧传输至 ringbuffer 中。

    您根据实际需求,将此步骤替换为对摄像头采集的音频信息传入 ringbuffer。

  5. P2P 线程将会自动将 ringbuffer 中的数据传输给 App。

本地录像开发

涂鸦 SDK 库文件已集成本地存储功能,您仅需要将音视频送入 ringbuffer,且完成本地存储的设置。
因此,本节将主要介绍如何开发本地存储录像的开启与设置。

开发相关函数

  • TUYA_IPC_SDK_START 函数中开启本地存储功能,设定相关参数:

    ipc_sdk_run_var.local_storage_info.enable = 1;//1 代表开启,0 代表关闭
    

    连续存储开启后,无需做其他操作,SDK 自动将录像保存至 SD 卡,设备启动后,本地存储初始化仅需要调用一次。本地存储仅支持 SD 卡使用 FAT32 格式。若检测到 SD 卡格式异常,可以通过 DP 上报 SD 卡格式异常信息。用户在 App 单击 SD 卡设置时,会有弹窗提醒 SD 卡异常,引导客户对 SD 卡进行格式化。

  • 若已开启事件存储,当设备需要进行录像时,调用函数:tuya_ipc_start_storage 开启录像,调用:tuya_ipc_stop_storage 关闭录像,模式选择 E_ALARM_SD_STORAGE (本地存储)。

    单个事件录像最长时间 10 分钟,超过 10 分钟若没有调用 tuya_ipc_stop_storage,则 SDK 会自动关闭录像。

    /**
    * \fn OPERATE_RET tuya_ipc_start_storage
    * \brief 开始存储
    * \param[in] storage_type: 存储类型
    * \存储类型分为 E_ALARM_SD_STORAGE(本地存储)和 E_ALARM_CLOUD_STORAGE(云存储)
    * \return 如果开始存储成功返回 OPRT_OK,其他情况则是失败
    */
    OPERATE_RET tuya_ipc_start_storage(INT_T storage_type);
    
    	/**
    	* \fn OPERATE_RET tuya_ipc_stop_storage
    	* \brief 停止存储
    	* \param[in] storage_type: 存储类型
    	* \存储类型分为 E_ALARM_SD_STORAGE(本地存储)和 E_ALARM_CLOUD_STORAGE(云存储)
    	* \return 如果停止存储成功返回 OPRT_OK,其他情况则是失败;
    	*/
    OPERATE_RET tuya_ipc_stop_storage(INT_T storage_type);
    
  • 按照实际情况,在 tuya_ipc_sd_get_status 函数中实现 SD 卡的检测,函数如下所示:

    当检测 SD 卡格式异常,例如:NTFS 格式的 SD 卡时,返回 abnormal 状态,其余都返回 normal 状态。

    E_SD_STATUS tuya_ipc_sd_get_status(VOID)
    {
    //todo 您自行实现接口
    }
    

    为防止丢失录像等数据,请在系统重启或休眠之前调用 sync(); 系统接口,确保可以将缓存中的数据全部写入本地存储。

示例开发流程

  1. 在主函数中开启本地存储,设置本地存储相关参数:

    ipc_sdk_run_var.local_storage_info.enable = 1;//1 代表开启,0 代表关闭
    ipc_sdk_run_var.local_storage_info.max_event_num_per_day = 500;//每天最大事件存储数
    ipc_sdk_run_var.local_storage_info.skills = 0;//0 表示开启全部功能
    ipc_sdk_run_var.local_storage_info.sd_status_cb = tuya_ipc_sd_status_upload ;//SD 卡状态更新
    strcpy(ipc_sdk_run_var.local_storage_info.storage_path, IPC_APP_SD_BASE_PATH);//设置本地存储路径
    
  2. 在主函数中调用 TUYA_IPC_SDK_START 函数开始 SDK 开发。

  3. SDK 中的 tuya_ipc_sdk_mqtt_online_proc 函数若判断本地存储已开启,将会调用 tuya_ipc_ss_init 函数。tuya_ipc_ss_init 函数集成了本地存储初始化、SD 卡状态检测、存储线程开启等一系列功能。

功能点开发

DP 主要用以控制设备的各项功能,您需要根据服务器下发的 DP 与 DP 值,设置设备并将设置后的状态上报服务器。tuya_ipc_dp_utils.h 文件对通用 DP 进行了具体说明。您可以根据已有 demo 代码来进行开发与设置。若有定制化 DP 加入,则仿照已有的相同数据类型的 DP 代码,进行添加与设置即可。

目前,DP 参数类型有 5 种:布尔型(BOOL)、值型(VALUE)、字符串型(STR)、枚举型(ENUM)、BITMAP 型(作为预留暂不使用),分别适应不同 DP 参数的设置。具体参见结构体 TY_OBJ_DP_VALUE_U

DP 数据保存与读取

您可根据实际需求,决定是否保存与读取 DP 数据。

  • 需要在以下两个函数: __tuya_app_write_INT__tuya_app_write_STR 中实现 DP 数据保存的具体操作,函数如下所示:

    /* 设置保存 DP 本地配置数据。
    这里的参考代码直接写到 tmp 中,如需要您可自行替换路径。
    注意:替换的路径请与 DP 数据的读取路径保持一致。*/
    STATIC VOID __tuya_app_write_INT(CHAR_T *key, INT_T value)
    {
     //TODO
      CHAR_T tmp_cmd[128] = {0};
      snprintf(tmp_cmd, 128, "mkdir -p /tmp/tuya.cfgs/;echo %d > /tmp/tuya.cfgs/%s", value, key);
      printf("write int exc: %s \r\n", tmp_cmd);
      system(tmp_cmd);
    }
    
    STATIC VOID __tuya_app_write_STR(CHAR_T *key, CHAR_T *value)
    {
     //TODO
     CHAR_T tmp_cmd[256] = {0};
     snprintf(tmp_cmd, 256, "echo %s > /tmp/tuya.cfgs/%s", value, key);
     printf("write STR exc: %s \r\n", tmp_cmd);
     system(tmp_cmd);
    }
    
  • 您需要在以下两个函数: __tuya_app_read_INT__tuya_app_read_STR 中实现 DP 数据读取的具体操作。具体函数请参考 demo 程序,部分函数如下所示:

    	/* 读取 DP 本地配置数据,如需要您可自行替换路径。
    	注意:替换的路径请与 DP 数据的保存路径保持一致。*/
    	STATIC INT_T __tuya_app_read_INT(CHAR_T *key)
    	{
    	 //TODO
    	 CHAR_T tmp_file[64] = {0};
    	 snprintf(tmp_file, 64, "cat /tmp/tuya.cfgs/%s", key);
     printf("read int exc: %s \r\n", tmp_file);
    	 FILE *p_file = popen(tmp_file, "r");
    
    	STATIC INT_T __tuya_app_read_STR(CHAR_T *key, CHAR_T *value, INT_T value_size)
    	{
    	 //TODOmemset(value, 0, value_size);
    	 CHAR_T tmp_file[64] = {0};
    	 snprintf(tmp_file, 64, "cat /tmp/tuya.cfgs/%s", key);
    	 printf("read str exc: %s \r\n", tmp_file);
    	 FILE *p_file = popen(tmp_file, "r");
    	}
    

OSD 水印功能开发

  • OSD 水印 DP:104

  • 根据服务器下发的 DP 值,给视频加上或取消时间水印,水印位置与内容由设备端控制。

示例开发流程

  1. tuya_ipc_dp_utils.h 中加入:

    #define TUYA_DP_WATERMARK         104
    
  2. tuya_ipc_dp_utils.cs_dp_table[] 中加入:

    #ifdef TUYA_DP_WATERMARK
    	{TUYA_DP_WATERMARK,             handle_DP_WATERMARK},
    #endif
    

    handle_DP_WATERMARK 为此功能 DP 数据处理函数。您可自行开发,其 demo 函数如下:

    #ifdef TUYA_DP_WATERMARK
    STATIC VOID handle_DP_WATERMARK(IN TY_OBJ_DP_S *p_obj_dp)
    {
    BOOL_T watermark_on_off = check_dp_bool_invalid(p_obj_dp);
    IPC_APP_set_watermark_onoff(watermark_on_off);
    watermark_on_off = IPC_APP_get_watermark_onoff();
    respone_dp_bool(TUYA_DP_WATERMARK, watermark_on_off);
    }
    #endif
    
  3. 在主函数中调用 IPC_APP_handle_dp_cmd_objs 函数,对接收到的服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START 函数中已调用此函数)。

移动侦测开发

SDK 提供了控制移动侦测功能开关控制、灵敏度控制、定时控制、间歇控制以及数据上报的函数,您可以调用 SDK API 接口完成功能移动侦测以及移动追踪功能的开发。

开发相关函数

  • 移动侦测开关DP:134
  • 移动侦测灵敏度DP:106

开启步骤参照 OSD 水印功能开发。

  • 调用函数:Tuya_Ipc_Motion_Init,进行移动侦测功能初始化,函数如下所示:

    /*********************************************************************************
    * Init input config.
    **********************************************************************************/
    OPERATE_RET Tuya_Ipc_Motion_Init(TUYA_MOTION_TRACKING_CFG mt_cfg);
    

    初始化参数结构体 TUYA_MOTION_TRACKING_CFG 信息说明如下所示:

    typedef struct _TUYA_MOTION_TRACKING_CFG
    {
      INT_T frame_w;  		  //视频宽度
      INT_T frame_h;  		  //视频长度
      INT_T y_thd;  	  	  //移动侦测评判阈值,默认 30,阈值范围推荐 540:范围 1255,值越小,越灵敏
      INT_T sensitivity;      //移动侦测灵敏度,设置 1-7,值越大,越灵敏
      TUYA_RPERCENT_ECT roi;  //移动侦测区域设置,设置参考 TUYA_RECT 结构体
    	TUYA_MULTI_MD_REGION motion_region;//多区域移动侦测设置,范围为 1*1 到 5*5
      INT_T tracking_enable;  //运动跟踪开关,只开移动侦测设置为:0,开启运动跟踪设置为:1
    }TUYA_MOTION_TRACKING_CFG;
    

    初始化参数结构体 _TUYA_AI_RECT 说明下所示:

    typedef struct _TUYA_AI_RECT
    {
      INT_T left;  		//区域坐标的 x 值占 x 轴的百分比(值需要取整),例:x 坐标点为 128,10%
      INT_T top;  		//区域坐标的 y 值占 y 轴的百分比(值需要取整),例:y 坐标点为 72,10%
      INT_T right;  	//移动侦测区域长度所占 x 轴的百分比(值需要取整),例:x 方向长度为 256,20%
      INT_T bottom;  	//移动侦测区域长度所占 y 轴的百分比(值需要取整),例:y 方向长度为 144,20%
    }TUYA_AI_RECT;
    

    结构体 TUYA_AI_RECT,结合以上说明,图例示意如下所示:

    5.x SDK 开发指南
  • 初始化函数:Tuya_Ipc_Motion_Init 调用后,如需修改移动侦测的参数,调用函数: Tuya_Ipc_Set_Motion 进行修改,函数如下所示:

    /*********************************************************************************
    * Set config dynamically.
    **********************************************************************************/
    OPERATE_RET Tuya_Ipc_Set_Motion(TUYA_MOTION_TRACKING_CFG mt_cfg);
    

    获取移动侦测的参数函数:Tuya_Ipc_Get_Motion,函数如下所示:

    /*********************************************************************************
    * Get config dynamically.
    **********************************************************************************/
    void Tuya_Ipc_Get_Motion(TUYA_MOTION_TRACKING_CFG *mt_cfg);
    
  • 设备每隔一段时间调用接口 Tuya_Ipc_Motion,将采集到的 YUV 数据传入。当 SDK 判断两张图片间存在差异,需要触发移动报警,则返回值 1,若无,则返回值 0。函数如下所示:

    传入的图片信息必须是:YUV420 格式,移动追踪功能调优,建议取 5 次移动追踪判断结果的平均值作为舵机转动的目标角度,减少设备追踪不上事物移动的情况。

    函数:Tuya_Ipc_Motion 调用的频率由设备测试调优的实际需求而定。

    • 参数 1:传入图片信息。
    • 参数 2:返回 1,则移动侦测触发成功。
    • 参数 3:若打开了移动追踪功能,则输出物体移动后以图像中心为中心轴坐标系的物体的(x,y)角度。
    /*********************************************************************************
    * Execute Motion Detect \ Motion Tracking.
    * in_data     输入格式为 YUV420
    * motion_flag  返回运动检测的值。
               0 表示不移动,1 表示存在移动对象。
    * motion_point 返回最大移动物体的中心点角度坐标。
               两个值(x,y)均为 0,表示不移动,否则表示移动。
               当 tracking_enable == 0 时返回(0,0)
    **********************************************************************************/
    OPERATE_RET Tuya_Ipc_Motion(UCHAR_T *in_data, INT_T *motion_flag, TUYA_POINT * motion_point);
    

    若函数:Tuya_Ipc_Motion 判断两张图片间的差异需要触发移动侦测,则输出物体移动后以图像中心为中心轴坐标系的物体的(x,y)角度,图例示意如下所示:

    x 为以 IPC 为中心、物体移动前后水平方向上产生的夹角,y 为以 IPC 为中心、物体移动前后垂直方向上产生的夹角。您可以根据(x,y)角度计算出舵机转向的角度并控制舵机转动,实现移动追踪功能。

  • 当画面有变化时,调用函数:tuya_ipc_notify_alarm 上报图片,函数如下所示:

    /**
    * \fn OPERATE_RET tuya_ipc_notify_alarm
    * \将可编辑的警报发送到云服务和 App
    * \param[in] snap_buffer: 当前快照的地址
    * \param[in] snap_size: 快照的大小,以字节为单位
    * \param[in] name: 可修改的事件类型, NOTIFICATION_NAME_E
    * \param[in] is_notify: 如果为 TRUE,同时上报至消息中心和云存储事件列表。如果为 false,只上报到云存储事件列表,不上报到消息中心。
    * \return OPERATE_RET
    */
    OPERATE_RET tuya_ipc_notify_alarm(CONST CHAR_T *snap_buffer, CONST UINT_T snap_size, CONST NOTIFICATION_NAME_E name, BOOL_T is_notify);
    
  • 若在 OTA 过程中需要优化内存资源,可调用函数:Tuya_Ipc_Motion_Release 退出移动侦测以及移动追踪功能,函数如下所示:

    void Tuya_Ipc_Motion_Release();
    

示例开发流程

具体操作,可参考 demo 代码中 thread_md_proc 函数。

  1. 开启移动侦测功能(定义 DP)。

  2. 开启移动侦测线程,如下所示:

    pthread_create(&motion_detect_thread, NULL, thread_md_proc, NULL);
    
  3. 在移动侦测线程函数中,调用时间同步函数来同步当前时间。

    //获取 UTC 时间
    OPERATE_RET tuya_ipc_get_utc_time(OUT TIME_T *time_utc);
    
  4. 在移动侦测线程函数中,调用移动判断函数 get_motion_status 判断对象是否发生移动。

    您可根据不同的芯片平台在此函数自行实现对移动判断的功能,可调用上述 SDK 提供的 Tuya_Ipc_Motion 等相关函数进行开发。

    int fake_md_status = 0;
    int get_motion_status()
    {
    	//if motion detected, return 1
    	//else return 0 返回值存储在 fake_md_status 参数中
    	return fake_md_status;
    }
    
  5. 如果发生移动,则调用 tuya_ipc_start_storage 函数开启本地存储和云存储功能,之后调用 get_motion_snapshot 函数抓取当前图像,相关函数如下:

    tuya_ipc_start_storage(E_ALARM_SD_STORAGE);		//开启本地存储
    tuya_ipc_start_storage(E_ALARM_CLOUD_STORAGE);	//开启云存储
    
    //您可根据不同的芯片平台自行开发抓图函数
    void get_motion_snapshot(char *snap_addr, int *snap_size)
    {
    	//we use file to simulate
    	char snapfile[128];
    	*snap_size = 0;
    	extern char s_raw_path[];
    	printf("get one motion snapshot\n");
    	snprintf(snapfile,64,"%s/resource/media/demo_snapshot.jpg",s_raw_path);
    	FILE*fp = fopen(snapfile,"r+");
    	//...
    }
    
  6. 调用 tuya_ipc_notify_alarm 函数发送报警信号至云端:

    /**
    * \fn OPERATE_RET tuya_ipc_notify_alarm
    * \将可编辑的警报发送到云服务和 App
    * \param[in] snap_buffer: 当前快照的地址
    * \param[in] snap_size: 快照的大小,以字节为单位
    * \param[in] name: 可修改的事件类型, NOTIFICATION_NAME_E
    * \param[in] is_notify: 如果为 TRUE,同时上报至消息中心和云存储事件列表。如果为 false,只上报到云存储事件列表,不上报到消息中心。
    * \return OPERATE_RET
    */
    OPERATE_RET tuya_ipc_notify_alarm(CONST CHAR_T *snap_buffer, CONST UINT_T snap_size, CONST NOTIFICATION_NAME_E name, BOOL_T is_notify);
    
  7. 若检测对象不发生移动,且距离上次移动侦测事件时间超过 10 秒,则调用 tuya_ipc_stop_storage 停止本地存储和云存储。

    tuya_ipc_stop_storage(E_ALARM_SD_STORAGE);		//停止本地存储
    tuya_ipc_stop_storage(E_ALARM_CLOUD_STORAGE);	//停止云存储
    

图像缩放开发(SDK 集成)

图像缩放功能目的在于调整图像的分辨率,将采集到的图像调整至需要的目标分辨率。

图像缩放开发流程

  • 调用函数: Tuya_Ipc_Img_Resize,传入图片信息、参数信息和图片输出的地址信息,函数如下所示:

    参数 1:传入图片地址,参数 2:传入图片参数信息,参数 3:图片输出地址。传入图片信息必须是:YUV420 格式。

    /*********************************************************************************
    * YUV420 image scale interface
    * in_data		输入图片,格式为 YUV420
    * paras			规模结构
    * out_data		输出图片,格式为 YUV420
    **********************************************************************************/
    OPERATE_RET Tuya_Ipc_Img_Resize(UCHAR_T *in_data, TUYA_IMG_RESIZE_PARA paras, UCHAR_T *out_data);
    
  • 结构体 TUYA_IMG_RESIZE_PARA 说明如下所示:

    typedef struct _TUYA_IMG_RESIZE_PARA
    {
        INT_T srcWidth;  	//传入图像的宽度
        INT_T srcHeight;  	//传入图像的高度
        INT_T dstWidth;  	//输出图像的宽度
        INT_T dstHeight;  	//输出图像的高度
        IMG_TYPE img_type;  //缩放方式
        IMG_RESIZE_TYPE resize_type;
    }TUYA_IMG_RESIZE_PARA;
    
  • 选择加密方式结构体 IMG_RESIZE_TYPE 说明如下所示:

    typedef enum
    {
       LINEAR,  //速度快但质量较低
       CUBIC,  	//速度较慢但质量高
    }IMG_RESIZE_TYPE;
    

PTZ 摇头机功能开发

摇头机开始转动 DP 为 119,停止转动 DP 为 116。

#define TUYA_DP_PTZ_CONTROL   119
/* PTZ 旋转控制,枚举类型,0 向上,1 右上,2 右,3 右下,4 向下,5 左下,6 左,7 左上
* 但在 4.0 SDK 中为 0-右上,1-右,2-下右,3-下,4-左下,5-左,6-左上,7-上
*/
#define TUYA_DP_PTZ_STOP   116         /* PTZ 旋转停止,BOOL 型*/

设备接收到服务端 DP 的控制指令后,控制 PTZ 摇头机转动。119 DP下发的常见格式为:

{"range":["1","2","3","4","5","6","7","0"],"type":"enum"}

PTZ 摇头机通用控制开发

  • 云台 DP 控制说明如下表所示。根据服务器下发的 DP 值控制设备方向。控制逻辑:接收到 119 转动方向 DP 后,开始转动,直到接收到 116 停止转动 DP,停止舵机转动。
摇头机 DP DP 说明
119 云台转动控制。0-右上,1-右,2-右下,3-下,4-左下,5-左,6-左上,7-上。
116 云台转动停止,bool 类型。
161 移动跟踪使能开关,bool 类型。true 开启,false 不开启。
178 收藏点操作,string 型。type:1 添加,type:2 删除。不同类型跟的数据串不同。
  • 您可以在 handle_DP_PTZ_CONTROL 函数中自行开发摇头机开始转动的相关控制,在 handle_DP_PTZ_STOP 函数中开发停止转动相关控制,函数如下:

    STATIC VOID handle_DP_PTZ_CONTROL(IN TY_OBJ_DP_S *p_obj_dp)
    {
    	if( (p_obj_dp == NULL) || (p_obj_dp->type != PROP_ENUM) )
    	{
    		printf("Error!! type invalid %d \r\n", p_obj_dp->type);
    		return;
    	}
    
    	//dp 119 format: {"range":["1","2","3","4","5","6","7","0"],"type":"enum"}
    	UINT_T dp_directions[8] = {1,2,3,4,5,6,7,0};
    	UINT_T direction = dp_directions[p_obj_dp->value.dp_enum];
    }
    
    #ifdef TUYA_DP_PTZ_STOP
    STATIC VOID handle_DP_PTZ_STOP(IN TY_OBJ_DP_S *p_obj_dp)
    {
    	IPC_APP_ptz_stop_move();
    	respone_dp_bool(TUYA_DP_PTZ_STOP, TRUE);
    }
    #endif
    

示例开发流程

  1. 开启 PTZ 控制功能,在 tuya_ipc_dp_utils.h 定义相关 DP。例如:

    #define TUYA_DP_PTZ_CONTROL                119         /* PTZ旋转控制,枚举类型*/
    #define TUYA_DP_PTZ_STOP                   116         /* PTZ旋转停止,BOOL型*/
    
  2. tuya_ipc_dp_utils.cs_dp_table[] 中加入:

    #ifdef TUYA_DP_PTZ_CONTROL
    		{TUYA_DP_PTZ_CONTROL,           handle_DP_PTZ_CONTROL},
    #endif
    #ifdef TUYA_DP_PTZ_STOP
    		{TUYA_DP_PTZ_STOP,              handle_DP_PTZ_STOP},
    #endif
    

    handle_DP_PTZ_CONTROL 函数和 handle_DP_PTZ_STOP 函数为对应 DP 操作函数。您需自行在函数中开发摇头机的运动和停止功能,示例函数如下:

    #ifdef TUYA_DP_PTZ_CONTROL
    STATIC VOID handle_DP_PTZ_CONTROL(IN TY_OBJ_DP_S *p_obj_dp)
    {
    if( (p_obj_dp == NULL) || (p_obj_dp->type != PROP_ENUM) )
    {
    	printf("Error!! type invalid %d \r\n", p_obj_dp->type);
    	return;
    }
    
    //dp 119 format: {"range":["1","2","3","4","5","6","7","0"],"type":"enum"}
    UINT_T dp_directions[8] = {1,2,3,4,5,6,7,0};
    UINT_T direction = dp_directions[p_obj_dp->value.dp_enum];
    CHAR_T tmp_str[2] = {0};
    snprintf(tmp_str,2,"%d",direction);
    IPC_APP_ptz_start_move(tmp_str);
    respone_dp_enum(TUYA_DP_PTZ_CONTROL,tmp_str);
    }
    #endif
    
  3. 在主函数中调用 IPC_APP_handle_dp_cmd_objs 函数对接收到的服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START 函数中已调用此函数)。

PTZ 摇头机预设位控制开发

预设位控制功能 DP:178

#define TUYA_DP_PRESET_SET                   178  /* 收藏点操作,string 型,type:1 添加,type:2 删除,不同类型的数据串不同 */

开发相关函数

  • 预设备添加收藏点前,需要上报 179 DP,eum:2 非巡航模式

  • 设备接收到服务器下发的 DP 数据,增加预设点函数:tuya_ipc_preset_add。具体如下所示:

    /**
    * \fn OPERATE_RET tuya_ipc_preset_add(S_PRESET_POSITION* preset_pos)
    * \brief 添加一个预设点
    * \param[in] 输入预设位置
    * \return OPERATE_RET
    */
    OPERATE_RET tuya_ipc_preset_add(S_PRESET_POSITION* preset_pos);
    
  • S_PRESET_POSITION 结构体

    • id[32]:服务器下发的 ID 值,可以不做处理。
    • name[32]:增加收藏点时,name[32] 下发收藏点名字,可以做对应存储。
    • mpId:设备增加对应收藏点后,需要填入收藏点的序号,从 1 开始。
    • ptz:填入设备收藏点的具体 p 与 t 坐标,若是定焦设备,z 坐标值为 0。
    typedef struct
    {
       CHAR_T id[32]; 		//服务器的 ID
       CHAR_T name[32]; 	//收藏点名称
       INT_T mpId; 			//索引 ID
       S_PRESET_PTZ ptz; 	//PTZ 的预设位置
    } S_PRESET_POSITION;
    
    typedef struct
    {
     	INT_T pan;  	//横向
     	INT_T tilt;  	//竖直
     	INT_T zoom;  	//定焦设备该值为 0
    }S_PRESET_PTZ;
    
  • 调用 tuya_ipc_preset_add_pic 函数,传入图片地址以及图片大小,函数如下所示:

    /*
    \fn OPERATE_RET tuya_ipc_preset_add_pic(CHAR_T *addr, UINT_T size)
    \brief 上传当前预设位置的快照
    \param[in] addr/size: 要上传图片的地址和大小
    \return OPERATE_RET
    */
    OPERATE_RET tuya_ipc_preset_add_pic(CHAR_T *addr, UINT_T size);
    
  • 设备上电后,调用接口:tuya_ipc_preset_get 与服务器同步已存在的预设位收藏点信息。

    /*
    \fn OPERATE_RET tuya_ipc_preset_get(S_PRESET_CFG *preset_cfg)
    \brief 获取存储在图雅云中的所有预设位置
    \param[in out] preset_cfg
    \return OPERATE_RET
    */
    OPERATE_RET tuya_ipc_preset_get(S_PRESET_CFG *preset_cfg);
    
  • 服务器下发删除预设位指令,设备端删除相关信息后,调用函数:tuya_ipc_preset_del,回传服务器下发的 ID 值:

    //删除预设位,error 需要 0 与 1 交替
    if(tmp == 0)
    {
     tmp = 1;
    }
    else if(tmp == 1)
    {
     tmp = 0;
    }
    
    tuya_ipc_preset_del(devId->valuestring);
    
    snprintf(respond_del,128,"{\"type\":%d,\"data\":{\"error\":%d}}",type->valueint,tmp);
    

示例开发流程

  1. 开启 PTZ 预设点功能(在 tuya_ipc_dp_utils.h 定义相关 DP),如:

    #define TUYA_DP_PRESET_SET                 178
    /*收藏点操作,string 型,type:1 添加,type:2 删除,不同类型的数据串不同*/
    
  2. tuya_ipc_dp_utils.cs_dp_table[] 中加入:

    #ifdef TUYA_DP_PRESET_SET
    	{TUYA_DP_PRESET_SET,              handle_DP_SET_PRESET},
    #endif
    
  3. handle_DP_SET_PRESET 函数的 IPC_APP_set_preset 函数中,判断对预设点的操作类型(1:增加预置点,2:删除预置点,3:预置点调用)。根据判断的操作类型,分别调用预设点添加函数 tuya_ipc_preset_add、预设点图像上传函数 tuya_ipc_preset_add_pic、预设点删除函数 tuya_ipc_preset_del、预设点调用函数 tuya_ipc_preset_go(此功能您可自行实现),实现预设点的添加、删除、调用功能。示例函数如下:

    调用预设点添加函数 tuya_ipc_preset_add 之前,需先获取 PTZ 当前位置信息。

    /*1:增加预置点,2:删除预置点,3:预置点调用*/
    if(type->valueint == 1)
    {
    	tuya_ipc_preset_add(data);
    }
    else if(type->valueint == 2)
    {
    	tuya_ipc_preset_del(data);
    }
    else if(type->valueint == 3)
    {
    	tuya_ipc_preset_go(data);
    }
    
  4. 在主函数中调用 IPC_APP_handle_dp_cmd_objs 函数,对接收到服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START 函数中已调用此函数)。

日志上报后台开发

  • 设备端开发涂鸦 SDK,禁止连接第三方或私有服务器,否则存在不符合海外法规风险。
  • 设备上报日志到云端,触发动作需要得到用户许可。
  • 设备端 DNS 必须设定为自动获取,否则存在设备联网异常风险。
  • 调用函数:tuya_ipc_set_log_attr 控制日志输出,SDK 默认日志输出级别:4。设置数值越少,日志输出则越少。trace 级别:5。函数调用位置如下所示:

    OPERATE_RET tuya_ipc_sdk_start(IN CONST TUYA_IPC_SDK_RUN_VAR_S * pRunInfo)
    {
    	//...
    	s_ipc_sdk_run_handler.sdk_run_info = *pRunInfo;
        //TODO loglevel
        tuya_ipc_set_log_attr(pRunInfo->debug_info.log_level,NULL);
            //...
        }
    

    外部日志上报云端的需求可以通过 cloud_operation_set_log_path_cb 进行注册,注册完成后,SDK 内部可以将该数据传到云端,可以自行管理文件。回调需要设定路径和文件大小。

    typedef VOID (*GW_APP_LOG_PATH_CB)(OUT CHAR_T *path, IN CONST INT_T len);
    
    VOID cloud_operation_set_log_path_cb(GW_APP_LOG_PATH_CB cb)
    {
        s_log_cb = cb;
    }
    

门铃功能开发

本章节主要介绍门铃产品特性功能的开发,可以实现当门铃被按下时向APP推送提醒、进行视频通话、留言等功能

开发相关函数

  • 调用 TUYA_APP_Enable_DOORBELL 进行门铃初始化。

  • 调用 ty_timer_create 函数进行定时器创建,函数如下:

    //用户需要根据不同的芯片平台,实现此定时器接口
    static int ty_timer_create(TIMER_CB cb, timer_t *p_timer_id)
    {
    	struct sigevent evp;
    	memset(&evp, 0, sizeof(struct sigevent));
    	evp.sigev_notify = SIGEV_THREAD;
    	evp.sigev_notify_function = cb;
    
    	if(timer_create(CLOCK_REALTIME, &evp, p_timer_id) == -1) {
    		printf(" fail to timer create\n");
    		return -1;
    	}
    	return 0;
    }
    
  • 若门铃被按下,则调用 get_snapshot 抓取当前图片,用于后续消息报警,函数如下:

    //此示例函数中用预设的图片作为抓取的图像,用户需要根据不同的芯片平台,自行实现对当前图像的抓取
    void get_snapshot(char *snap_addr, int *snap_size)
    {
    	//we use file to simulate
    	char snapfile[128];
    	*snap_size = 0;
    	extern char s_raw_path[];
    	printf("get one motion snapshot\n");
    }
    
  • 抓取到图像后,调用 tuya_ipc_door_bell_press 将图像信息推送给涂鸦开发者平台和App,函数如下:

    /**
    * \fn OPERATE_RET tuya_ipc_door_bell_press
    * \brief 发送一个门铃按下消息给涂鸦开发者平台和 APP
    * \param[in] doorbell_type: 门铃类型。DOORBELL_NORMAL 为普通门铃,推送图像  DOORBELL_AC 为直供电门铃,推送 P2P
    * \param[in] snap_buffer: snapshot 抓取的图像地址
    * \param[in] snap_size: 抓取图像的大小
    * \param[in] type: 抓取图像文件类型,jpeg 或者 png
    * \return OPERATE_RET
    */
    OPERATE_RET tuya_ipc_door_bell_press(IN CONST DOORBELL_TYPE_E doorbell_type, IN CONST CHAR_T *snap_buffer, IN CONST UINT_T snap_size, IN CONST NOTIFICATION_CONTENT_TYPE_E type);
    

    若要实现门铃留言功能,则调用 tuya_ipc_leave_video_msg 函数开始留言,函数如下:

    /**
    * \fn OPERATE_RET tuya_ipc_leave_video_msg
    * \brief 从现在开始留言
    * \param[in] extra_data 图像的 buffer 地址
    * \param[in] data_len buffer 的大小
    * \return
    */
    OPERATE_RET tuya_ipc_leave_video_msg(CHAR_T *extra_data, INT_T data_len);
    

示例开发流程

  1. 开启门铃功能,设置相关参数:

    ipc_sdk_run_var.video_msg_info.enable = 1;		 //1 为开启,0 为关闭
    ipc_sdk_run_var.video_msg_info.type = MSG_BOTH;	 //支持功能类型,具体参考结构体 MESSAGE_E
    ipc_sdk_run_var.video_msg_info.msg_duration = 10;//最大持续时间,单位为秒
    
  2. 调用 TUYA_IPC_SDK_START 开始 SDK 开发,其内部会调用 tuya_ipc_video_msg_init 函数进行门铃功能初始化。

  3. 如果门铃被按下,调用 doorbell_handler 函数。

  4. doorbell_handler 函数中判断门铃的状态,如果为 DOORBELL_LISTEN 状态(监听状态),则调用 get_snapshot 抓取当前图像,并调用 tuya_ipc_door_bell_press 向涂鸦开发者平台和 App 推送消息。

  5. 如果为 DOORBELL_RECORD(留言),则调用 tuya_ipc_leave_video_msg 开始留言。

低功耗休眠和唤醒功能开发

开发相关函数介绍

  • 休眠状态 DP:149

    149 DP 数值:0 代表设备进入休眠状态,1 代表设备进入唤醒状态。

    #define TUYA_DP_DOOR_SLEEP        "149"    /* 设备休眠开关,BOOL 类型。true 未休眠,false 休眠。 */
    
  • 调用 tuya_ipc_low_power_server_get 获取服务器的 IP 和 port,函数如下:

    /**
    * \fn OPERATE_RET tuya_ipc_low_power_server_addr_get
    * \brief 获取低功耗服务器的信息
    * \return return 如果获取到 IP 和 port,返回 OPRT_OK,其他则失败
    */
    OPERATE_RET tuya_ipc_low_power_server_get(OUT UINT_T *ip,OUT UINT_T *port);
    
  • 调用 tuya_ipc_device_id_get 获取设备 ID,函数如下:

    /**
    * \fn OPERATE_RET tuya_ipc_device_id_get
    * \brief 获取设备 ID
    * \return return 如果获取到 ID,返回 OPRT_OK,其他则失败
    */
    OPERATE_RET tuya_ipc_device_id_get(IN OUT CHAR_T *devid, IN OUT INT_T * id_len);
    
  • 调用 tuya_ipc_local_key_get 获取 local key,函数如下:

    /**
    * \fn OPERATE_RET tuya_ipc_local_key_get
    * \brief 从 IPC 的 SDK 获取 signatrue key
    * \ make destKeyBuf len >=17;
    * \return return 获取成功则返回 OPRT_OK
    */
    OPERATE_RET tuya_ipc_local_key_get(OUT CHAR_T * destKeybuf,OUT UINT_T * len);
    
  • 调用函数 tuya_ipc_low_power_server_connect 连接服务器,函数如下:

    /**
    * \fn OPERATE_RET tuya_ipc_lowpower_server_connect
    * \brief 连接涂鸦低功耗服务器
    * \[in] serverIp:服务器 IP 地址
    * \[in] port:服务器 port
    * \[in] pdevId:设备 ID
    * \[in] idLen:设备 ID 长度
    * \[in] pkey:local key
    * \[in] keyLen:local key 长度
    * \return OPERATE_RET 0:success,others:failed
    */
    OPERATE_RET tuya_ipc_low_power_server_connect(UNW_IP_ADDR_T serverIp,INT_T port,CHAR_T* pdevId, INT_T idLen, CHAR_T* pkey, INT_T keyLen);
    
  • 通过 tuya_ipc_low_power_socket_fd_get 获取对应的 socket fd 信息,函数如下:

    /**
    * \fn OPERATE_RET tuya_ipc_low_power_socket_fd_get
    * \brief 获取涂鸦低功耗保活的 TCP handler
    * \return OPERATE_RET 0:success,others:failed
    */
    OPERATE_RET tuya_ipc_low_power_socket_fd_get();
    
  • 通过 tuya_ipc_low_power_wakeup_data_get 获取 app 唤醒的数据。

    /**
    * \fn OPERATE_RET tuya_ipc_lowpower_server_connect
    * \brief 获取涂鸦低功耗唤醒数据
    * \return OPERATE_RET 0:success,others:failed
    */
    OPERATE_RET tuya_ipc_low_power_wakeup_data_get(OUT CHAR_T* pdata, OUT UINT_T* plen);
    
  • 通过 tuya_ipc_low_power_heart_beat_get 获取心跳包信息:

    /**
    * \fn OPERATE_RET tuya_ipc_lowpower_server_connect
    * \brief 获取心跳包信息
    * \return OPERATE_RET 0:success,others:failed
    */
    OPERATE_RET tuya_ipc_low_power_heart_beat_get(OUT CHAR_T * pdata,OUT UINT_T *plen);
    

示例开发流程

  1. 若需要开启低功耗,则调用 tuya_ipc_low_power_server_get 获取服务器的 IP 地址和 port。

  2. 调用 tuya_ipc_device_id_get 获取设备 ID。

  3. 调用 tuya_ipc_local_key_get 获取 local key。

  4. 根据以上得到的信息,调用 TUYA_APP_LOW_POWER_START 开始低功耗模式。

    OPERATE_RET TUYA_APP_LOW_POWER_START(char * devbuf,char *keybuf,int ip,int port)
    
  5. TUYA_APP_LOW_POWER_START 中调用 tuya_ipc_low_power_server_connect 连接服务器。

  6. TUYA_APP_LOW_POWER_START 中调用 tuya_ipc_low_power_socket_fd_get 获取对应的 socket fd,用于后续心跳包发送。

  7. TUYA_APP_LOW_POWER_START 中调用 tuya_ipc_low_power_wakeup_data_get 获取 App 唤醒的数据。

  8. 创建一个循环,不断发送心跳包通知服务声明此设备的存在,并且不断接收唤醒信号等待唤醒。

    send(low_power_socket, heart_beat, heart_beat_len, 0);//发送心跳包
    
  9. 分析接收到唤醒数据,如果判断需要唤醒设备,则进行启动设备相关操作。

    您需要在 TODO 处自行实现启动设备相关操作。

    if (FD_ISSET(low_power_socket, &rfds))
    {
    	bzero(recBuf, MAXBUF+1);
    	printf("============recv data==============\n");
    	len = recv(low_power_socket, recBuf, MAXBUF, 0);
    	if (len > 0)
    	{
    		printf("Successfully received the message: is {");
    		for(i=0;i<len;i++)
    			printf("0x%02x ",recBuf[i]);
    		printf("}\n");
    		if(strncmp(recBuf,wakeData,wake_data_len)==0)
    		{
    			//TODO  启动 SDK
    			printf("recve data is wake up\n");
    		}
    	}
    }
    

低功耗设备功能启动优化

  • MQTT 上线回调函数:__IPC_APP_Get_Net_Status_cb 可以使用 STAT_MQTT_ONLINE 判定 MQTT 是否上线成功,成功后可往下执行剩余操作。

  • Wi-Fi 连通外网后,可另起线程,进行 P2P 初始化,提高设备接入外网效率。

  • 设备只需检测到 SD 卡挂载成功,即可进行本地录像初始化。SDK 带有时间修复机制,会自动检测并修复时间戳异常的数据。

  • 使用本地存储事件录像功能,调用函数:tuya_ipc_ss_stop_event 后,设备需要稍等 2-3 秒,直至录像数据已经保存至 SD 卡再进入休眠。

  • 本地存储调用了 event stop 接口后,需要调用 tuya_ipc_ss_get_status 获取本地录像的状态,当状态是:E_STORAGE_STOP 时,设备再进入休眠。tuya_ipc_ss_get_status 接口只能用来获取状态,不能设置状态。

  • 使用云存储功能,调用函数 tuya_ipc_cloud_storage_event_delete 后,需要调用:tuya_ipc_cloud_storage_get_event_status_by_id 查询云存储数据上传的状态,查询状态不为:EVENT_ONGOINGEVENT_READY 时,设备才可进入休眠状态,否则容易出现云存储数据丢失的情况,具体说明如下所示:

    typedef enum
    {
      EVENT_NONE,    //没有云存储事件发生或云存储事件传输完成
      EVENT_ONGOING,  //云存储事件正在进行
      EVENT_READY,   //云存储事件进行的临界点,出现在刚调用 add 接口时
      EVENT_INVALID   //云存储功能没有初始化成功
    }EVENT_STATUS_E;
    
  • 云存储可调用接口:tuya_ipc_cloud_storage_set_pre_record_time 设置取预录制的时间,获取更往前的预录制视频数据。

  • 提高 P2P 线程、本地存储线程的优先级。提高线程优先级存在一定风险,设备端需要仔细确认其他线程基本功能不受影响。

OTA 功能开发

OTA 作为设备升级与维护的最重要途径之一,将在本章节进行具体介绍。当有固件需要升级时,SDK 将通过回调的形式告知设备,进行 OTA 操作。

OTA 开发流程

  • OTA 请求回调函数:IPC_APP_Upgrade_Inform_cb(),传入参数包含固件下载的 url(下载链接)以及 size(文件的大小)。

  • 释放系统资源(优化内存):调用 tuya_ipc_ss_set_write_mode() 函数,关闭本地录像功能,然后调用:tuya_ipc_ss_uninit()tuya_ipc_tranfser_close()函数,uninit 与 close 函数主要用于 SDK 本地存储与 P2P 功能的反初始化,尽可能释放内存资源。

    内存资源优化需要在 tuya_ipc_upgrade_sdk 函数前进行。

    VOID IPC_APP_Upgrade_Inform_cb(IN CONST FW_UG_S *fw)
    {
      PR_DEBUG("Rev Upgrade Info");
      PR_DEBUG("fw->fw_url:%s", fw->fw_url);
      PR_DEBUG("fw->sw_ver:%s", fw->sw_ver);
      PR_DEBUG("fw->file_size:%u", fw->file_size);
    
      FILE *p_upgrade_fd = fopen(s_mgr_info.upgrade_file_path, "w+b");
      //此处优化内存资源
      tuya_ipc_upgrade_sdk(fw, __IPC_APP_get_file_data_cb, __IPC_APP_upgrade_notify_cb, p_upgrade_fd);
    }
    

    函数:tuya_ipc_upgrade_sdk 中第一个回调函数 __IPC_APP_get_file_data_cb 是用于获取文件分片数据(下载)。函数中有入参与出参,入参主要提供给您使用,出参由您获取到数据后,需要返回是否已完成数据的写入,写入成功返回 0。

    第二个回调函数 __IPC_APP_upgrade_notify_cb 是用于显示 OTA 升级整体进度。

  • 在函数:__IPC_APP_upgrade_notify_cb 中替换新固件。具体实现参考 tuya_ipc_sdk_upgrade_demo.c 文件。

    固件下载完成后,需要客户在函数中实现固件的具体替换过程。替换成功后,需要在此函数内进行设备的重启操作。

    固件替换前,需要备份 DB 文件。固件替换成功后,对比 DB 文件替换固件前后的 md5 值,若相同,则可以直接重启。若不相同,则将备份的 DB 文件替换现有的 DB 文件,再重启设备。

    VOID __IPC_APP_upgrade_notify_cb(IN CONST FW_UG_S *fw, IN CONST INT_T download_result, IN PVOID_T pri_data)
    
  • 若 OTA 升级失败,需要在函数:__IPC_APP_upgrade_notify_cb 中加入重启操作。

    反初始化的操作不可逆,若 OTA 失败,设备必须进行重启。

OTA 自定义进度上报开发

  • 设备端通过 URL 开始下载固件,调用 tuya_ipc_upgrade_progress_report 函数,上报 OTA 下载的进度,函数如下所示:

    上报的进度值建议小于 99%。

    /*
    \fn OPERATE_RET tuya_ipc_upgrade_progress_report
    \brief 发送升级进度到云服务和 App
    \param[in] percent: 升级进度百分比,有效值 [0,100]
    \return SUCCESS – OPERATE_RET , FAIL – COMM ERROR
    */
    OPERATE_RET tuya_ipc_upgrade_progress_report(IN UINT_T percent);
    
  • 固件升级时,手机 App 进度显示会暂停在 98%。此时,固件已完成下载,App 正在等待设备重启并上报新固件版本号,版本号上报成功后,App 将显示 升级成功

  • App 等待设备替换固件与上报新版本号的时间为 1 分钟。若设备流程超过此时间,需要告知项目经理 PID,在后台进行配置。

云存储功能开发

SDK 库文件已集成云存储功能。在购买云存储服务后,SDK 将 ringbuffer 中的音视频信息发送至云端保存,保存时间根据所购买的云存储服务而定。

  • SDK 默认对数据采用软件加密的方式,加密接口:OpensslAES_CBC128_encrypt 使用 AES CBC 方式。若软件加密给 MCU 带来较大的负载,可改用硬件加密方式,使用 SoC 硬件加密通道,加密方式:PKCS5/PKCS7,如下表所示:
填充类型 说明
不填充 有些块加密模式不需要填充,或者明文都是块长度的整数倍。
ISO10126
  • 例:假设块长度为 64 bit,数据:FF FF FF FF FF FF FF FF FF
  • 填充后:FF FF FF FF FF FF FF FF FF 7D 2A 75 EF F8 EF 07。
PKCS5/PKCS7
  • 例:假设块长度为 64 bit,数据 FF FF FF FF FF FF FF FF FF
  • 填充后:FF FF FF FF FF FF FF FF FF 07 07 07 07 07 07 07。
  • PKCS5 要求块大小为 8 字节,后来扩充到 16 字节,PKCS7 块大小 1-255 字节。
OAEP/PKCS1 为 RSA 算法提供的。

开发流程

  • 连续云存储:

    1. 设定 ipc_sdk_run_var.cloud_storage_info.enable 为 1。

      ipc_sdk_run_var 信息参照结构体 TUYA_IPC_SDK_RUN_VAR_Senable 数值为 1 代表开启云存储功能,数值为 0 代表关闭云存储功能。

    2. 您根据开发需要调用 AES_CBC_initAES_CBC_encryptAES_CBC_destory 等函数初始化加密方式。

      ipc_sdk_run_var.aes_hw_info.aes_fun.init = AES_CBC_init;
      ipc_sdk_run_var.aes_hw_info.aes_fun.encrypt =AES_CBC_encrypt;
      ipc_sdk_run_var.aes_hw_info.aes_fun.destory = AES_CBC_destory;
      
    3. 调用函数 TUYA_IPC_SDK_START,此函数中创建的 tuya_ipc_sdk_mqtt_online_proc 线程调用的 tuya_ipc_cloud_storage_init 函数已集成云存储的相关功能。

      media_setting 参数参考结构体 IPC_MEDIA_INFO_S

      /**
      * \fn OPERATE_RET tuya_ipc_cloud_storage_init
      * \brief 初始化云存储,此操作之后将分配所有所需的内存
      * aes_func 为加密方式
      * \param[in] 用于确定所需的内存和存储
      * \return OPERATE_RET
      */
      OPERATE_RET tuya_ipc_cloud_storage_init(IN IPC_MEDIA_INFO_S *media_setting, IN AES_HW_CBC_FUNC *aes_func);
      
  • 事件云存储录像:

    1. 当 IPC 检测到需要上报的事件时,调用 tuya_ipc_start_storage 函数,开始事件的存储,函数如下:

      /**
      * \fn OPERATE_RET tuya_ipc_start_storage
      * \brief 开始存储
      * \param[in] storage_type: 存储类型。E_ALARM_SD_STORAGE(本地存储), E_ALARM_CLOUD_STORAGE(云存储)
      * \return 如果开始存储成功,返回 OPRT_OK,其他情况则是失败
      */
      OPERATE_RET tuya_ipc_start_storage(INT_T storage_type);
      
    2. SDK 中的云存储线程会自行将填加的事件信息传输至云端。

    3. 当事件结束时,调用:tuya_ipc_stop_storage 终止云存储事件录像。其函数部分解析如下

      /**
      * \fn OPERATE_RET tuya_ipc_stop_storage
      * \brief 停止存储
      * \param[in] storage_type: 存储类型。E_ALARM_SD_STORAGE(本地存储), E_ALARM_CLOUD_STORAGE(云存储)
      * \return 如果停止存储成功,返回 OPRT_OK,其他情况则是失败
      */
      OPERATE_RET tuya_ipc_stop_storage(INT_T storage_type);
      

设备解绑与复位

本章节主要简述设备通过 App 解绑与按复位键这两种复位方式的开发流程与区别。

设备解绑开发流程

  • App 删除设备 按键,SDK 将清除 DB 文件中的配网信息并执行回调函数:IPC_APP_Reset_System_CB 重启设备,函数如下所示:

    需要在函数内,实现设备的重启与对 DP 数据文件进行重置操作,不需要删除 DB 文件。

    VOID IPC_APP_Reset_System_CB(GW_RESET_TYPE_E type)
    {
      printf("reset ipc success. please restart the ipc %d\n", type);
      IPC_APP_Notify_LED_Sound_Status_CB(IPC_RESET_SUCCESS);
      //TODO
      /* Developers need to restart IPC operations */
    }
    

设备复位开发流程

  • 设备检测到复位键被按下,存在复位操作,删除三个 DB 文件:tuya_user.db_baktuya_user.dbtuya_enckey.db

  • 设备对 DP 数据文件进行重置操作并重启。

    更多详情,请参考 硬件交互定义

AP 模式切换功能开发

本章节主要简述通过 App 控制设备开关 AP 模式的功能开发。开发完成后,可以通过 App 开启或关闭设备的 AP 模式。

设备配网完成后,在 STATION 模式下工作。当设备切换到 AP 模式后,App 可以连接到设备发出的热点,此时不需第三方网络就可以进行实时预览和基本功能的配置等。

  • AP 模式查询 DP 点:231

    #define TUYA_DP_AP_MODE             231    /*App 下发 NULL 查询,设备返回 { "is_ap": %d, ap_ssid: "%s",password:"%s" }*/
    

    设备需要实现对 DP 点 231 的处理,根据设备当前所处模式,按照 DP 点格式回复。

    其中 is_ap 为设备是否在 AP 模式下工作。1 为 是,0 为否。app_ssid 为工作在 AP 模式下的 ssid。password 为 AP 模式下的认证密码,password 的值可以为空。

  • AP 模式开关 DP 点:232

    #define TUYA_DP_AP_SWITCH           232    /*App 下发数据格式:{ "ap_enable" : %d, "ap_ssid" : "%s", "ap_pwd" : "%s" },设备返回数据格式:{ "ap_enable" : 1, "errcode" : 0 }*/
    

    DP 点 232 控制设备开启或关闭 AP 模式。当关闭 AP 模式后,设备会在一分钟后自动进入 STATION 模式重连配网热点。

    其中 ap_enable 为开启或关闭 AP 模式。1 为开启,0 为关闭。errorcode 为错误码,0 为成功,其他为失败。

  • AP 模式时间同步 DP 点:233

    #define TUYA_DP_AP_TIME_SYNC        233    /*App 下发数据格式:{ "%s" }*/
    

    由于 AP 模式无法从互联网获取时间,需要根据 App 下发的 DP 点数据,设置 SDK 时间。数据格式为 STR 类型的 Unix 时间戳:{ “1629808340” }。

  • AP 模式时区同步 DP 点:234

    #define TUYA_DP_AP_TIME_ZONE        234    /*App 下发数据格式:{ "%s" }*/
    

    根据 App 下发的 DP 点数据设置时区,数据格式为 STR 类型:{ “+8” }。

开发相关函数

  • tuya_adapter_wifi_ap_start 函数,开启设备 AP 模式,您需要根据设备 Wi-Fi 自行实现这个接口。

    OPERATE_RET tuya_adapter_wifi_ap_start(CONST WF_AP_CFG_IF_S *cfg);
    
  • WF_AP_CFG_IF_S 结构体

    /* tuya sdk definition of ap config info */
    typedef struct {
        unsigned char ssid[WIFI_SSID_LEN+1];       ///< ssid
        unsigned char s_len;                       ///< len of ssid
        unsigned char passwd[WIFI_PASSWD_LEN+1];   ///< passwd
        unsigned char p_len;                       ///< len of passwd
        unsigned char chan;                        ///< channel. default:6
        WF_AP_AUTH_MODE_E md;                      ///< encryption type
        unsigned char ssid_hidden;                 ///< ssid hidden  default:0
        unsigned char max_conn;                    ///< max sta connect nums default:3
        unsigned short ms_interval;                ///< broadcast interval default:100
        NW_IP_S ip;                                ///< ip info for ap mode
    }WF_AP_CFG_IF_S;
    
    /* tuya sdk definition of wifi encryption type */
    typedef enum
    {
        WAAM_OPEN = 0,      ///< open
        WAAM_WEP,           ///< WEP
        WAAM_WPA_PSK,       ///< WPA—PSK
        WAAM_WPA2_PSK,      ///< WPA2—PSK
        WAAM_WPA_WPA2_PSK,  ///< WPA/WPA2
        WAAM_UNKNOWN,       //unknown
    }WF_AP_AUTH_MODE_E;
    
  • tuya_adapter_wifi_ap_stop 函数,关闭设备 AP 模式。您需要根据设备 Wi-Fi 自行实现这个接口。

    OPERATE_RET tuya_adapter_wifi_ap_stop(VOID);
    

示例开发流程

  • AP 模式查询

    1. 开启 AP 模式查询功能。在 tuya_ipc_dp_utils.h 定义相关 DP,如:

      #define TUYA_DP_AP_MODE 231
      
    2. tuya_ipc_dp_utils.cs_dp_table[] 中加入:

      #ifdef TUYA_DP_AP_MODE
          {TUYA_DP_AP_MODE,               handle_DP_AP_MODE},
      #endif
      

      handle_DP_AP_MODE 函数为对应 DP 操作函数,其 demo 函数如下:

      #ifdef TUYA_DP_AP_MODE
      STATIC VOID handle_DP_AP_MODE(IN TY_OBJ_DP_S *p_dp_json)
      {
          if(p_dp_json == NULL )
          {
              printf("Error!! type invalid\r\n");
              return;
          }
          respone_dp_str(TUYA_DP_AP_MODE, IPC_APP_get_ap_mode());
      }
      #endif
      

      IPC_APP_get_ap_mode 函数根据当前设备 Wi-Fi 模式组装 DP 点回复数据:{ “is_ap”: %d, ap_ssid: “%s”,password:“%s” },IPC_APP_get_ap_mode 函数的大致实现:

      通过 tuya_adapter_wifi_get_work_mode 函数获取当前设备 Wi-Fi 工作模式,以是否返回 AP 模式作为 is_ap 的值。

      通过 tuya_adapter_wifi_get_mac 函数获取设备 Wi-Fi mac,并把 ssid 拼接成TUYA_IPC-xxxx,作为 ap_ssid 的值,您可以根据实际需求自定义 ap_ssid 的值。

      通过 __tuya_app_read_STR 函数获取 tuya_ap_passwd 配置项,作为 AP 模式时的认证密码,作为 password 的值,您可以根据实际需求自定义 password 的值。

    3. 在主函数中调用 IPC_APP_handle_dp_cmd_objs 函数,对接收到服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START 函数中已调用此函数)。

  • AP 模式开关

    1. 开启 AP 模式开关功能(在 tuya_ipc_dp_utils.h 定义相关 DP),如:

      #define TUYA_DP_AP_SWITCH 232
      
    2. tuya_ipc_dp_utils.cs_dp_table[] 中加入:

      #ifdef TUYA_DP_AP_SWITCH
          {TUYA_DP_AP_SWITCH,             handle_DP_AP_SWITCH},
      #endif
      

      handle_DP_AP_SWITCH 函数为对应 DP 操作函数,其 demo 函数如下:

      #ifdef TUYA_DP_AP_SWITCH
      STATIC VOID handle_DP_AP_SWITCH(IN TY_OBJ_DP_S *p_dp_json)
      {
          CHAR_T resp[32] = {0};
          INT_T ap_enable = IPC_APP_set_ap_mode((cJSON *)p_dp_json->value.dp_str);
          if(ap_enable < 0)
          {
              snprintf(resp, 32, "{\\\"ap_enable\\\":0,\\\"errcode\\\":0}");
          }
          else
          {
              snprintf(resp, 32, "{\\\"ap_enable\\\":%d,\\\"errcode\\\":0}",ap_enable);
          }
          respone_dp_str(TUYA_DP_AP_SWITCH, resp);
          respone_dp_str(TUYA_DP_AP_MODE, IPC_APP_update_ap_mode(ap_enable)); //switch ap mode, need update ap mode value
          if(ap_enable >= 0)
          {
              change_ap_process();
          }
      }
      #endif
      

      其中 IPC_APP_set_ap_mode 函数主要完成对下发 DP 点数据的解析和状态置位等。

      以下两个 DP 点的回复都是必须的,在回复完 DP 点 232 之后,需要再根据 DP 点 232 的请求回复一次 DP 点 231,以更新 DP 点 231 的最新值。

      respone_dp_str(TUYA_DP_AP_SWITCH, resp);
      respone_dp_str(TUYA_DP_AP_MODE, IPC_APP_update_ap_mode(ap_enable)); //switch ap mode, need update ap mode value
      

      由于在切换 AP 模式后原来的网络连接会丢失,所以需要在回复 DP 点数据之后再进行 AP 模式的切换,实际切换在 change_ap_process 函数中另开线程完成。

      VOID change_ap_process()
      {
          pthread_t ap_change_thread;
          int ret = pthread_create(&ap_change_thread, NULL, __ap_change_thread, NULL);
          if(ret != 0)
          {
              printf("ap_change_thread ,create fail! ret:%d\n",ret);
              return;
          }
      
          pthread_detach(ap_change_thread);
      }
      

      Demo 中实际开启 AP 模式在线程函数 __ap_change_thread 中实现,在调用 tuya_adapter_wifi_ap_start 函数开启 AP 模式前需要调用 tuya_devos_netlink_monitor_disable 函数,关闭对配网 Wi-Fi 连接的监视。

      调用 tuya_adapter_wifi_ap_stop 函数关闭 AP 模式后,需要调用 tuya_devos_netlink_monitor_enable 函数开启对配网 Wi-Fi 连接的监视。

    3. 在主函数中调用 IPC_APP_handle_dp_cmd_objs 函数对接收到服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START函数中已调用此函数)。

  • AP 模式时间同步

    1. 开启 AP 模式时间同步功能(在 tuya_ipc_dp_utils.h 定义相关 DP),如:

      #define TUYA_DP_AP_TIME_SYNC              233
      
    2. tuya_ipc_dp_utils.cs_dp_table[] 中加入:

      #ifdef TUYA_DP_AP_TIME_SYNC
          {TUYA_DP_AP_TIME_SYNC,          handle_DP_AP_TIME_SYNC},
      #endif
      

      handle_DP_AP_TIME_SYNC 函数具体实现请参考 demo。

    3. 在主函数中调用 IPC_APP_handle_dp_cmd_objs 函数,对接收到服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START 函数中已调用此函数)。

  • AP 模式时区同步

    1. 开启 AP 模式时区同步功能(在 tuya_ipc_dp_utils.h 定义相关 DP),如:

      #define TUYA_DP_AP_TIME_ZONE              234
      
    2. tuya_ipc_dp_utils.cs_dp_table[] 中加入:

      #ifdef TUYA_DP_AP_TIME_ZONE
          {TUYA_DP_AP_TIME_ZONE,          handle_DP_AP_TIME_ZONE},
      #endif
      

      handle_DP_AP_TIME_ZONE 函数具体实现请参考 demo。

    3. 在主函数中调用 IPC_APP_handle_dp_cmd_objs 函数,对接收到服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START 函数中已调用此函数)。

  • 设备开机进入 AP 模式

    1. 设备开启 AP 模式后,为了关机后不丢失状态,您需要将设备 AP 模式相关配置保存到设备,待到开机时根据保存的配置调用开启 AP 模式的函数。

    2. Demo 中 tuya_ipc_sdk_mqtt_online_proc 函数所示,本地存储和 P2P 均在联网同步到时间后才进行初始化。但开机若为 AP 模式,这是无法实现的,所以需要单独根据 AP 模式配置进行初始化,如 demo 中 tuya_ipc_ap_mode_start_proc 函数所示。

    3. user_main.c 的 main 函数中,调用 TUYA_IPC_SDK_START 函数之后调用 IPC_APP_get_ap_mode_config 函数获取 AP 模式相关配置,再调用 IPC_APP_switch_ap_mode 函数开启 AP 模式。

      设备在配网之后每次启动 SDK 会默认进入 STATION 模式。如果需要在开机后开启 AP 模式,需要先等 TUYA_IPC_SDK_START 函数调用完成之后,再调用开启 AP 模式的函数,防止再被 STATION 模式覆盖。

API 适配

更多详情,请参考 SDK 接口说明

FAQs