4.x SDK 开发指南

更新时间:2022-11-24 09:20:04下载pdf

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

名词解释

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

SDK 目录

涂鸦 IPC 嵌入式 SDK 文件目录如下:

.
└── tuya_ipc_sdk
	├── include
	│   ├── base_hwl.h
	│   ├── cJSON.h
	│   ├── tuya_cloud_base_defs.h
	│   ├── tuya_cloud_com_defs.h
	│   ├── tuya_cloud_error_code.h
	│   ├── tuya_cloud_types.h
	│   ├── tuya_cloud_wifi_defs.h
	│   ├── tuya_g711_utils.h
	│   ├── tuya_iot_config.h
	│   ├── tuya_ipc_ai_detect_storage.h
	│   ├── tuya_ipc_ai_face_db.h
	│   ├── tuya_ipc_ai_face_detect.h
	│   ├── tuya_ipc_api.h
	│   ├── tuya_ipc_chromecast.h
	│   ├── tuya_ipc_cloud_storage.h
	│   ├── tuya_ipc_echo_show.h
	│   ├── tuya_ipc_img_defs.h
	│   ├── tuya_ipc_img_proc.h
	│   ├── tuya_ipc_media.h
	│   ├── tuya_ipc_mqt_proccess.h
	│   ├── tuya_ipc_p2p.h
	│   ├── tuya_ipc_ptz.h
	│   ├── tuya_ipc_qrcode_proc.h
	│   ├── tuya_ipc_skill.h
	│   ├── tuya_ipc_stream_storage.h
	│   ├── tuya_ipc_video_msg.h
	│   ├── tuya_ipc_video_proc.h
	│   ├── tuya_ipc_webrtc.h
	│   ├── tuya_ipc_wifi_migrate.h
	│   ├── tuya_ring_buffer.h
	│   ├── uni_network.h
	│   └── wifi_hwl.h
	└── libs
		├── libmbedtls.a
		└── libtuya_ipc.a

业务流程

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

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

4.x SDK 开发指南

快速体验

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

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

第一步:创建产品

创建产品的详细步骤可参考 选品类创建产品

  1. 登录 涂鸦 IoT 开发平台

  2. 单击 创建产品

    4.x SDK 开发指南

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

    4.x SDK 开发指南

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

    4.x SDK 开发指南

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

    4.x SDK 开发指南

  6. 添加需要的 DP。

    4.x SDK 开发指南

  7. 单击 设备交互

    4.x SDK 开发指南

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

    4.x SDK 开发指南

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

    4.x SDK 开发指南

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

    4.x SDK 开发指南

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

    4.x SDK 开发指南

  12. 单击订单详情。

    4.x SDK 开发指南

  13. 下载授权码清单。

    4.x SDK 开发指南

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

第二步:获取 IPC SDK

请登录 GitHub 下载 SDK。

第三步:运行 Demo

通过运行 Demo,您可以快速体验涂鸦 IPC SDK 功能。

  1. 按照 Demo 使用说明,下载 Demo 代码到本地。

    #git clone https://github.com/tuya/tuya-iotos-embeded-multimedia-demo.git
    
  2. 将下载的 SDK 解压到 demo_for_ipc/sdk 目录下,生成可执行程序。

    #cd demo_for_ipc
    #make APP_NAME=demo
    
  3. 手机安装智能生活 App,选择添加摄像机,生成二维码。通过微信 扫一扫 功能扫描二维码。

    {“s”:“Tuya-Test”,“p”:“88888888”,“t”:“AYm1YVV5jupJcF”}

    该 Token 有效期为 10 分钟。

  4. 执行虚拟机,体验 Demo。

    #cd output
    #./tuya_ipc_demo -m 2 -p [PID] -u [UUID] -a [AUTHKEY] -r "[./]" -t "[TOKEN]"
    
    4.x SDK 开发指南
  5. 通过如下指令体验不同的功能,例如虚拟设备执行过程中,运行以下命令:

    #start
    

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

    4.x SDK 开发指南

    其他模拟指令说明如下表:

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

SDK 配网开发

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

填写基本参数

  • 分别修改 DB 文件、OTA 文件、本地录像文件的保存路径,宏定义如下:

    #define IPC_APP_STORAGE_PATH "/tmp/"
    #define IPC_APP_UPGRADE_FILE "/tmp/upgrade.file"
    #define IPC_APP_SD_BASE_PATH "/tmp/"
    

    说明:DB 文件中保存设备的配网信息,需要保存至掉电不丢失的路径下。DB 文件与本地录像文件保存路径需要检查结尾是否有/

  • 修改 PID、UUID、AUTHKEY 的宏定义:

    说明:PID 需要联系涂鸦项目经理获取,UUID、AUTHKEY 可以在 涂鸦 IoT 开发平台创建产品(PID)后申请获取。

  • 修改嵌入式软件版本号的宏定义:

    #define IPC_APP_VERSION "1.2.3"
    

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

Wi-Fi 快联配网

4.x SDK 开发指南

开发流程

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

  • SDK 跳转至:hwl_wf_wk_mode_get 函数,如下所示:

    说明:需要在函数内根据实际情况,实现 Wi-Fi 模组状态的获取操作。

    OPERATE_RET hwl_wf_wk_mode_get(OUT WF_WK_MD_E *mode)
    {
       if(NULL == mode)
     {
       return OPRT_INVALID_PARM;
      }
    
  • SDK 跳转至:hwl_wf_all_ap_scan 函数扫描信道,需要往 AP_IF_S 结构体传入 Wi-Fi 扫描信道结果,函数如下所示。

    说明:需要在函数内根据实际情况,实现 Wi-Fi 模组信道扫描的操作。

    OPERATE_RET hwl_wf_all_ap_scan(OUT AP_IF_S **ap_ary,OUT UINT_T *num)
    {
     if(NULL == ap_ary || NULL == num)
     {
      return OPRT_INVALID_PARM;
     }
    
     static AP_IF_S s_aps[MAX_AP_SEARCH];
    
     memset(s_aps, 0, sizeof(s_aps));
     *ap_ary = s_aps;
     *num = 0;
    
  • SDK跳转至:hwl_wf_sniffer_set 函数,需要往其中传入 Wi-Fi 接收到的空口包信号,函数如下所示。

    说明:需要在函数内根据实际情况,实现 Wi-Fi 模组 sniffer 状态数据的获取操作。

    #define MAX_REV_BUFFER 512
    BYTE_T rev_buffer[MAX_REV_BUFFER];
    
    int skipLen = 26;/* Radiotap default length is 26 */
    
    while((s_pSnifferCall != NULL) && (TRUE == s_enable_sniffer))
    {
     int rev_num = recvfrom(sock, rev_buffer, MAX_REV_BUFFER, 0, NULL, NULL);
     ieee80211_radiotap_header *pHeader = (ieee80211_radiotap_header *)rev_buffer;
     skipLen = pHeader->it_len;
    
  • 当 SDK 判断 Wi-Fi 输入的空口包数据正确,调用函数:hwl_wf_wk_mode_set,将 Wi-Fi 状态设置为 station 模式。

    说明:需要在函数内根据实际情况,实现开启 Wi-Fi 模组不同模式的操作。

    case WWM_SNIFFER:
    {
    #ifndef WIFI_CHIP_7601
        snprintf(tmpCmd, 100, "ifconfig %s down", WLAN_DEV);
        exec_cmd(tmpCmd);
    #endif
        snprintf(tmpCmd, 100, "iwconfig %s mode Monitor", WLAN_DEV);
        exec_cmd(tmpCmd);
    #ifndef WIFI_CHIP_7601
        snprintf(tmpCmd, 100, "ifconfig %s up", WLAN_DEV);
        exec_cmd(tmpCmd);
    
  • SDK 跳转至:hwl_wf_station_connect 函数进行联网操作。

    说明:需要在函数内根据实际情况,实现 Wi-Fi 模组的联网操作。

    OPERATE_RET hwl_wf_station_connect(IN CONST CHAR_T *ssid,IN CONST CHAR_T passwd)
    {
     if(sniffer_set_done)
     {
      sniffer_set_done = FALSE;
      IPC_APP_Notify_LED_Sound_Status_CB(IPC_REV_WIFI_CFG);
      usleep(10001000);
     }
    
     IPC_APP_Notify_LED_Sound_Status_CB(IPC_CONNECTING_WIFI);
    
      //TODO
      //Add a blocking operation for the Wi-Fi connection here.
    
      sleep(2);
    
       return OPRT_OK;
    }
    
  • 配网成功后,调用:hwl_wf_station_stat_get,通知 SDK 已获取到 IP 地址。

    说明:此接口为高频接口,需要按照实时状态返回。

    OPERATE_RET hwl_wf_station_stat_get(OUT WF_STATION_STAT_E *stat)
    {
     if(NULL == stat)
     {
      return OPRT_INVALID_PARM;
     }
     *stat = WSS_GOT_IP; //Be sure to return in real time//Reservedreturn OPRT_OK;
    }
    

二维码配网

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

  • SDK 跳转至:hwl_wf_wk_mode_get 函数。

    说明:需要在函数内根据实际情况,实现 Wi-Fi 模组状态的获取操作。

    OPERATE_RET hwl_wf_wk_mode_get(OUT WF_WK_MD_E *mode)
    {
       if(NULL == mode)
     {
       return OPRT_INVALID_PARM;
      }
    
  • SDK 跳转至:hwl_wf_sniffer_set 函数,获取二维码的扫描结果,函数如下所示:

    说明:需要在函数内根据实际情况,在 __tuya_linux_get_snap_qrcode 中调用 zbar 库实现二维码的识别操作。

    #define MAX_REV_BUFFER 512
    BYTE_T rev_buffer[MAX_REV_BUFFER];
    
    int skipLen = 26;/* Radiotap default length is 26 */
    
    while((s_pSnifferCall != NULL) && (TRUE == s_enable_sniffer))
    {
     int rev_num = recvfrom(sock, rev_buffer, MAX_REV_BUFFER, 0, NULL, NULL);
     ieee80211_radiotap_header *pHeader = (ieee80211_radiotap_header *)rev_buffer;
     skipLen = pHeader->it_len;
    
  • 当 SDK 获取到二维码的解析结果,调用函数:hwl_wf_wk_mode_set,将 Wi-Fi 状态设置为 station 模式。

    说明:需要在函数内根据实际情况,实现开启 Wi-Fi 模组不同模式的操作。

    case WWM_SNIFFER:
    {
    #ifndef WIFI_CHIP_7601
        snprintf(tmpCmd, 100, "ifconfig %s down", WLAN_DEV);
        exec_cmd(tmpCmd);
    #endif
        snprintf(tmpCmd, 100, "iwconfig %s mode Monitor", WLAN_DEV);
        exec_cmd(tmpCmd);
    #ifndef WIFI_CHIP_7601
        snprintf(tmpCmd, 100, "ifconfig %s up", WLAN_DEV);
        exec_cmd(tmpCmd);
    
  • SDK 跳转至:hwl_wf_station_connect 函数进行联网操作,具体如下所示。

    说明:需要在函数内根据实际情况,实现 Wi-Fi 模组的联网操作。

    OPERATE_RET hwl_wf_station_connect(IN CONST CHAR_T *ssid,IN CONST CHAR_T passwd)
    {
     if(sniffer_set_done)
     {
      sniffer_set_done = FALSE;
      IPC_APP_Notify_LED_Sound_Status_CB(IPC_REV_WIFI_CFG);
      usleep(10001000);
     }
    
     IPC_APP_Notify_LED_Sound_Status_CB(IPC_CONNECTING_WIFI);
    
      //TODO
      //Add a blocking operation for the Wi-Fi connection here.
    
      sleep(2);
    
       return OPRT_OK;
    }
    
  • 配网成功后,调用:hwl_wf_station_stat_get,通知 SDK 已获取到 IP 地址。

    说明:此接口为高频接口,需要按照实时状态返回。

    OPERATE_RET hwl_wf_station_stat_get(OUT WF_STATION_STAT_E *stat)
    {
     if(NULL == stat)
     {
      return OPRT_INVALID_PARM;
     }
     *stat = WSS_GOT_IP; //Be sure to return in real time//Reservedreturn OPRT_OK;
    }
    

AP 配网

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

  • SDK 跳转至:hwl_wf_wk_mode_get 函数,如下所示:

    说明:需要在函数内根据实际情况,实现 Wi-Fi 模组状态的获取操作。

    OPERATE_RET hwl_wf_wk_mode_get(OUT WF_WK_MD_E *mode)
    {
       if(NULL == mode)
     {
       return OPRT_INVALID_PARM;
      }
    
  • SDK 跳转至:hwl_wf_ap_start 函数开启 AP 模式。

    说明:需要在函数内根据 WF_AP_CFG_IF_S 结构体参数信息,打开 AP 模式。

    OPERATE_RET hwl_wf_ap_start(IN CONST WF_AP_CFG_IF_S *cfg)
    {
     if(NULL == cfg)
      {
      return OPRT_INVALID_PARM;
     }
    
     printf("Start AP SSID:%s \r\n", cfg->ssid);
     //Reservedreturn OPRT_OK;
    }
    
  • SDK 接收到 AP 模式的配网信息,调用函数:hwl_wf_wk_mode_set,将 Wi-Fi 状态设置为 station 模式。

    说明:需要在函数内根据实际情况,实现开启 Wi-Fi 模组不同模式的操作。

    case WWM_STATION:
     {
    #ifndef WIFI_CHIP_7601
      snprintf(tmpCmd, 100, "ifconfig %s down", WLAN_DEV);
      exec_cmd(tmpCmd);
    #endif
      snprintf(tmpCmd, 100, "iwconfig %s mode Managed", WLAN_DEV);
      exec_cmd(tmpCmd);
    #ifndef WIFI_CHIP_7601
      snprintf(tmpCmd, 100, "ifconfig %s up", WLAN_DEV);
      exec_cmd(tmpCmd);
    #endif
    
  • SDK 跳转至:hwl_wf_station_connect 函数进行联网操作,具体如下所示:

    说明:需要在函数内根据实际情况,实现 Wi-Fi 模组的联网操作。

    OPERATE_RET hwl_wf_station_connect(IN CONST CHAR_T *ssid,IN CONST CHAR_T passwd)
    {
     if(sniffer_set_done)
     {
      sniffer_set_done = FALSE;
      IPC_APP_Notify_LED_Sound_Status_CB(IPC_REV_WIFI_CFG);
      usleep(10001000);
     }
    
     IPC_APP_Notify_LED_Sound_Status_CB(IPC_CONNECTING_WIFI);
    
      //TODO
      //Add a blocking operation for the Wi-Fi connection here.
    
      sleep(2);
    
       return OPRT_OK;
    }
    
  • 配网成功后,调用:hwl_wf_station_stat_get,通知 SDK 已获取到 IP 地址:

    说明:此接口为高频接口,需要按照实时状态返回。

    OPERATE_RET hwl_wf_station_stat_get(OUT WF_STATION_STAT_E *stat)
    {
     if(NULL == stat)
     {
      return OPRT_INVALID_PARM;
     }
     *stat = WSS_GOT_IP; // Be sure to return in real time// Reservedreturn OPRT_OK;
    }
    

有线配网(局域网发现)

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

  • SDK 调用:hwl_wf_get_ip 函数,获取设备 IP 地址。设备在检测到已连接路由器时,返回获取到的 IP 地址,函数如下所示:

    说明:需要在函数内根据实际情况,开发设备获取 IP 地址的具体操作。

    OPERATE_RET hwl_wf_get_ip(IN CONST WF_IF_E wf,OUT NW_IP_S *ip)
    {
     if(NULL == ip)
     {
      return OPRT_INVALID_PARM;
     }
    
  • SDK 调用:hwl_wf_get_mac 函数,获取设备 MAC 地址,函数如下所示:

    说明:需要在函数内根据实际情况,开发设备获取 MAC 地址的具体操作。

    OPERATE_RET hwl_wf_get_mac(IN CONST WF_IF_E wf,INOUT NW_MAC_S *mac)
    {
     if(NULL == mac)
     {
      return OPRT_INVALID_PARM;
     }
    
  • SDK 将获取到的设备 IP 地址与 MAC 地址通过路由器发送广播包,手机连上路由器 Wi-Fi 并接收到广播信息后,向服务器获取 token,获取成功后给到设备端进行联网。至此,配网操作完成。

有线配网(手机扫设备二维码)

  • 确认已下载带有 wired 属性涂鸦 SDK 且涂鸦 UUID 经过后台打标处理。

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

  • 设备调用:tuya_ipc_set_region 函数,设置设备的国家码,函数如下所示:

    说明:国家码需要按照设备实际出货的区域来设定,否则容易出现设备跟其他国家区域服务器交互的异常问题。

    #if defined(QRCODE_ACTIVE_MODE) && (QRCODE_ACTIVE_MODE==1)
       tuya_ipc_set_region(REGION_CN);
       p_token = NULL;
    #endif
    
  • 设备调用:tuya_ipc_get_qrcode 函数,获取二维码 URL 信息,函数如下所示:

    说明:每个 UUID 的 URL 链接固定不变,带屏幕的设备可将 URL 链接转换成二维码来显示,不带屏幕的设备可以直接将短链接打印贴出。

    #if defined(QRCODE_ACTIVE_MODE) && (QRCODE_ACTIVE_MODE==1)/* demo: how to get qrcode from tuya server for display */
     sleep(2);
     CHAR_T info[32] = {0};
     tuya_ipc_get_qrcode(NULL,info, 32);
     printf("###info:%s\n", info);
    #endif
    
  • 手机单击右上角扫描图标,扫描二维码激活设备。

音视频开发

实时预览开发

SDK 初始化时,将默认创建 10 秒音视频数据缓存,您将设备采集到的音视频数据送入 ringbuffer即可。

SDK 库文件已实现数据的发送,您不需要再做对应开发。

开发流程

  • 确认IPC_APP_Set_Media_Info 函数中视频参数信息。

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

    s_media_info.channel_enable[E_CHANNEL_VIDEO_MAIN] = TRUE; /* Whether to enable local HD video streaming /
    s_media_info.video_fps[E_CHANNEL_VIDEO_MAIN] = 30; / FPS /
    s_media_info.video_gop[E_CHANNEL_VIDEO_MAIN] = 30; / GOP /
    s_media_info.video_bitrate[E_CHANNEL_VIDEO_MAIN] = TUYA_VIDEO_BITRATE_1M; / Rate limit /
    s_media_info.video_width[E_CHANNEL_VIDEO_MAIN] = 640; / Single frame resolution of width*/
    s_media_info.video_height[E_CHANNEL_VIDEO_MAIN] = 360;/* Single frame resolution of height /
    s_media_info.video_freq[E_CHANNEL_VIDEO_MAIN] = 90000; / Clock frequency /
    s_media_info.video_codec[E_CHANNEL_VIDEO_MAIN] = TUYA_CODEC_VIDEO_H264; / Encoding format */
    
  • 确认 IPC_APP_Set_Media_Info 函数中音频参数信息。

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

    s_media_info.channel_enable[E_CHANNEL_VIDEO_SUB] = TRUE; /* Whether to enable local SD video stream /
    s_media_info.video_fps[E_CHANNEL_VIDEO_SUB] = 30; / FPS /
    s_media_info.video_gop[E_CHANNEL_VIDEO_SUB] = 30; / GOP /
    s_media_info.video_bitrate[E_CHANNEL_VIDEO_SUB] = TUYA_VIDEO_BITRATE_512K; / Rate limit /
    s_media_info.video_width[E_CHANNEL_VIDEO_SUB] = 640; / Single frame resolution of width /
    s_media_info.video_height[E_CHANNEL_VIDEO_SUB] = 360;/ Single frame resolution of height /
    s_media_info.video_freq[E_CHANNEL_VIDEO_SUB] = 90000; / Clock frequency /
    s_media_info.video_codec[E_CHANNEL_VIDEO_SUB] = TUYA_CODEC_VIDEO_H264; / Encoding format */
    
  • 将摄像机采集到的音视频数据通过函数:tuya_ipc_ring_buffer_append_data 传入 ringbuffer 中,函数如下所示。

    /* append new frame into a ring buffer*/
    OPERATE_RET tuya_ipc_ring_buffer_append_data(CHANNEL_E channel, UCHAR_T *addr, UINT_T size, MEDIA_FRAME_TYPE_E type, UINT64_T pts);
    
  • TUYA_APP_Enable_Speaker_CB 函数中实现喇叭的开关控制,函数如下所示。

    VOID TUYA_APP_Enable_Speaker_CB(BOOL_T enabled)
    {
     printf("enable speaker %d \r\n", enabled);
     //TODO/* Developers need to turn on or off speaker hardware operations.
     If IPC hardware features do not need to be explicitly turned on, the function can be left blank. */
    }
    
  • 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/*Developers need to implement the operations of voice playback */
    }
    
  • 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);
    

本地录像开发

涂鸦 SDK 库文件已集成本地存储功能,您仅需要将音视频送入 ringbuffer,且完成本地存储的设置即可。

因此,本节将主要介绍如何开发本地存储录像的开启与设置。

同步时间开发

  • 在 MQTT 上线后,调用 IPC_APP_Sync_Utc_Time 函数与服务器同步时间。判断 MQTT 上线回调函数:__IPC_APP_Get_Net_Status_cb,如下所示:

    说明

    • 开启本地录像前必须先同步时间,避免时间不正确,找不到回放视频问题。
    • 同步时间,偶尔会出现失败现象,需要多调用几次,直到成功。
    STATIC VOID __IPC_APP_Get_Net_Status_cb(IN CONST BYTE_T stat)
    {
     PR_DEBUG("Net status change to:%d", stat);
     switch(stat)
     {
    #if defined(WIFI_GW) && (WIFI_GW == 1)
       case STAT_CLOUD_CONN: //for Wi-Fi ipc
       case STAT_MQTT_ONLINE: //for low-power Wi-Fi ipc
    #endif
    #if defined(WIFI_GW) && (WIFI_GW==0)
       case GB_STAT_CLOUD_CONN: //for wired ipc
    #endif
    {
       IPC_APP_Notify_LED_Sound_Status_CB(IPC_MQTT_ONLINE);
       PR_DEBUG("mqtt is online\r\n");
       s_mqtt_status = 1;
       break;
    
  • 设备端调用接口:IPC_APP_Sync_Utc_Time 同步服务器时间,函数如下所示:

    说明

    • 时区设定:仅在设备第一次配网前,更改手机自身时区,引导设备配网,新时区会生效(即:若设备已配网成功,修改手机时区,新时区不会生效)。
    • 以手机自身的时区时间为准,与账号的区域和 App 显示的时区无关。
    OPERATE_RET IPC_APP_Sync_Utc_Time(VOID)
    {
      TIME_T time_utc;
      INT_T time_zone;
      PR_DEBUG("Get Server Time ");
      OPERATE_RET ret = tuya_ipc_get_service_time_force(&time_utc, &time_zone);
    
       if(ret != OPRT_OK)
       {
        return ret;
       }
       //The API returns OK, indicating that UTC time has been successfully obtained.
       //If it return not OK, the time has not been fetched.
    
       PR_DEBUG("Get Server Time Success: %lu %d", time_utc, time_zone);
       return OPRT_OK;
    }
    
  • 调用接口:tuya_ipc_check_in_dls 做夏令时的判断,判断结果为 true,则时区 +1。

    说明:夏令时具有周期性变化的属性,设备端需要周期性判断夏令时是否发生变化。

    /*
    \fn OPERATE_RET tuya_ipc_check_in_dls(IN TIME_T time_utc, OUT BOOL * pIsDls)
    \brief check if specified utc time is in daylight saving time夏令时
    \return OPERATE_RET
    */
    OPERATE_RET tuya_ipc_check_in_dls(IN TIME_T time_utc, OUT BOOL_T * pIsDls);
    
  • 夏令时判断范例代码:

    TIME_T time_utc;
    tuya_ipc_get_utc_time(&time_utc);
    BOOL_T isDls = FALSE;
    tuya_ipc_check_in_dls(time_utc,&isDls);
    if (TRUE == isDls)
    {
     time_utc += 3600;
    }
    

本地存储开发

  • 调用 TUYA_APP_Init_Stream_Storage 函数进行本地存储初始化,App 下发指令,开启事件录像或全时录像,函数如下所示。

    说明

    • 连续存储开启后,无需做其他操作,SDK 自动将录像保存至 SD 卡,设备启动后,本地存储初始化仅需要调用一次。
    • 本地存储仅支持 SD 卡使用 FAT32 格式。若检测到 SD 卡格式异常,可以通过 DP 上报 SD 卡格式异常信息。
    • 用户在 App 单击 SD 卡设置时,会有弹窗提醒 SD 卡异常,引导客户对 SD 卡进行格式化。
    OPERATE_RET TUYA_APP_Init_Stream_Storage(IN CONST CHAR_T *p_sd_base_path)
    {
     STATIC BOOL_T s_stream_storage_inited = FALSE;
     if(s_stream_storage_inited == TRUE)
     {
      PR_DEBUG("The Stream Storage Is Already Inited");
      return OPRT_OK;
     }
    
  • 若已开启事件存储,当设备需要进行录像时,调用函数 tuya_ipc_ss_start_event 开启录像,调用 tuya_ipc_ss_stop_event 关闭录像。单个事件录像最长时间 10 分钟,超过 10 分钟若没有调用 tuya_ipc_ss_stop_event,则 SDK 会自动关闭录像。

  • 按照实际情况,在 tuya_ipc_sd_get_status 函数中实现 SD 卡的检测,函数如下所示。

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

    E_SD_STATUS tuya_ipc_sd_get_status(VOID)
    {
     FILE *fp = fopen(LINUX_SD_DEV_FILE, "rb");
     if(!fp)
     {
      return SD_STATUS_NOT_EXIST;
     }
     fclose(fp);
    

功能点开发

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

DP 数据保存与读取

  • 需要在以下两个函数: __tuya_app_write_INT__tuya_app_write_STR 中实现 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 数据读取的具体操作,函数如下所示。

    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 值,给视频加上或取消时间水印,水印位置与内容由设备端控制。

PIR 功能开发

  • PIR DP:152
  • 根据服务器下发的 DP 值,控制设备 PIR 功能的开关以及灵敏度。当 PIR 被触发时,调用tuya_ipc_notify_door_bell_press 函数,将摄像机采集到的图像上报服务器。

    说明:PIR 上报的频率建议:大于 30 秒做一次检测并上报。

移动侦测开发

SDK 提供了控制移动侦测功能开关控制、灵敏度控制、定时控制、间歇控制以及数据上报的函数,4.4.6 SDK 库文件已实现移动侦测。

以及移动追踪功能的判断,您可以调用 SDK API 接口完成功能移动侦测以及移动追踪功能的开发。

移动侦测开发流程(通用)

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

  • 当摄像机移动侦测功能已被打开,发现画面有变化时,调用函数 tuya_ipc_notify_motion_detect 上报图片,函数如下所示。

    说明:移动侦测消息上报,需要选择对应的图片数据类型。

    /*
    \fn OPERATE_RET tuya_ipc_notify_motion_detect
    \brief send a motion-detecttion alarm to tuya cloud and APP
    \param[in] snap_buffer: address of current snapshot
    \param[in] snap_size: size fo snapshot, in Byte
    \param[in] type: snapshot file type, jpeg or png
    \return OPERATE_RET
    */
    OPERATE_RET tuya_ipc_notify_motion_detect(IN CONST CHAR_T *snap_buffer, IN CONST UINT_T snap_size, IN CONST NOTIFICATION_CONTENT_TYPE_E type);
    

移动侦测开发流程(SDK 集成)

  • 调用函数: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 结构体
      INT_T tracking_enable;  //运动跟踪开关,只开移动侦测设置为:0,开启运动跟踪设置为:1
    }TUYA_MOTION_TRACKING_CFG;
    
  • 初始化参数结构体 TUYA_RECT 说明如下所示:

    typedef struct _TUYA_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_RECT,图例示意如下所示:

    4.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       Input YUV.
    * motion_flag     Return the value of Motion Detect.
               0 for no moving; 1 for moving exist.
    * motion_point   Return the center point coordinate of the largest moving object.
               Both values(x, y) are 0 for no moving; otherwise, moving exist.
               Return values(0, 0) when tracking_enable==0
    **********************************************************************************/
    OPERATE_RET Tuya_Ipc_Motion(UCHAR_T *in_data, INT_T *motion_flag, TUYA_POINT * motion_point);
    
  • 若函数:Tuya_Ipc_Motion 判断两张图片间的差异需要触发移动追踪,则输出物体移动后,以图像中心为中心轴坐标系的物体(x,y)像素坐标点,图例示意如下所示:

    说明:设备需根据(x,y)像素坐标点计算出舵机转向的角度并控制舵机转动。

    4.x SDK 开发指南
  • 若在 OTA 中,需要优化内存资源,可调用函数 Tuya_Ipc_Motion_Release 退出移动侦测以及移动追踪功能,函数如下所示:

    /*********************************************************************************
    * release.
    **********************************************************************************/
    void Tuya_Ipc_Motion_Release();
    

间歇移动侦测

  • 间歇移动侦测 DP:133
  • 根据服务器下发的 DP 值,间歇地开启移动侦测功能,例如:接收到 133 DP 值为 5,则每间隔 5 分钟开启移动侦测,移动侦测每次开启时间为 5 分钟。

图像缩放开发(SDK 集成)

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

图像缩放开发流程

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

    参数说明如下:

    • 1:传入图片地址。
    • 2:传入图片参数信息。
    • 3:图片输出地址。
    • 传入图片信息必须是:YUV420 格式。
    /*********************************************************************************
    * YUV420 image scale interface
    * in_data  input YUV420
    * paras   scale struct
    * out_data  output 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 的控制指令后,控制 PTZ 摇头机转动。

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 删除,不同类型跟的数据串不同。

PTZ 摇头机预设位控制开发

  • 预设位控制功能 DP :

    #define TUYA_DP_PRESET_SET               178            /* 收藏点操作,string 型,type:1 添加,type:2 删除,不同类型跟的数据串不同 */
    
  • 预设备添加收藏点前,需要上报 179 DP ,eum:2 非巡航模式。

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

    error_num = tuya_ipc_preset_add(&node[num]);
    
    snprintf(respond_add,128,"{\"type\":%d,\"data\":{\"seq\":%d,\"pan\":%d,\"tilt\": %d,\"zoom\": 0 }}",
    type-valueint,error_num,node[num].ptz.pan,node[num].ptz.tilt);
    
  • S_PRESET_POSITION 结构体

    说明

    • id[32]:服务器下发的 ID 值,可以不做处理。
    • name[32]:增加收藏点时,name[32] 下发收藏点名字,可以做对应存储。
    • mpId:设备增加对应收藏点后,需要填入收藏点的序号,从 1 开始。
    • ptz:填入设备收藏点的具体 p 与 t 坐标,若是定焦设备,z 坐标值为 0。
    typedef struct
    {
       CHAR_T id[32]; //id in server
        CHAR_T name[32]; //preset point name
       INT_T mpId; //index ID
       S_PRESET_PTZ ptz; //ptz for preset position
    } 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 upload a snapshot for current preset position
    \param[in] addr/size: address and size of the picture to be uploaded
    \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 get all preset positions stored in tuya cloud.
    \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);
    

AP 预览功能开发

开启 AP 预览

  • PID 增加 231、232、233、234 DP。

  • APP 操作页面输入 AP 信息(包含:ssid 与 passwd),通过 232 DP 下发信息给到设备端,即触发 handle_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);
      if(ap_enable >= 0)
      {
        change_ap_process();
      }
    }
    
  • 在函数 IPC_APP_set_ap_mode 中,可以获取到下发的 AP 信息,设备需要调用 tuya_ipc_save_ap_info 接口,将 AP 信息(ssid 与 passwd)保存至涂鸦数据库,设备回复 232 DP 内容:"{"ap_enable":1,"errcode":0}",回复 231 DP 内容:"{"is_ap":1,"ap_ssid":"xxx","password":"xxx"}"IPC_APP_set_ap_mode函数如下所示:

    说明:设备回复 232 DP 后,需要主动回复 231 DP ,刷新设备在服务器状态。

    INT_T IPC_APP_set_ap_mode(IN cJSON *p_ap_info)
    {
      if (NULL == p_ap_info)
     {
      return 0;
     }
    
     INT_T ap_onoff = -1;
    
     printf("%s %d handle_DP_AP_SWITCH:%s rn",FUNCTION,LINE, (char *)p_ap_info);
    
     cJSON * pJson = cJSON_Parse((CHAR_T *)p_ap_info);
    
  • DP 上报完成后,Wi-Fi 切换模式,从 station 状态切换至 AP 状态,手机寻找对应 ssid 连接,重新点击设备列表设备,即可进行 AP 预览。

  • 若设备已开启 AP 热点,拔电重启,SDK 初始化函数:IPC_APP_Init_SDK 入参: WIFI_INIT_MODE_E 需要选择:WIFI_INIT_AP,token 填 NULL,需要调用接口: tuya_ipc_set_ap_info 传入 AP 热点信息,函数如下所示:

    说明:设备拔电重启后,AP 预览,设备无法连接外网,P2P 的初始化,需要脱离 MQTT 上线的前置条件,即 SDK 初始化后,开始初始化 P2P。

    /*
    \fn OPERATE_RET tuya_ipc_set_ap_info(IN CONST CHAR_T *ssid, IN CONST CHAR_T *passwd)
    \brief set ap info when start SDK
    \return OPERATE_RET
    */
    OPERATE_RET tuya_ipc_set_ap_info(IN CONST CHAR_T *ssid, IN CONST CHAR_T *passwd);
    

设备时间获取

设备连接 App 后,App 通过 233 DP 下发 UTC 时间信息,通过 234 下发时区信息,设备需要调用接口 tuya_ipc_set_service_timeuni_set_time_zone 将 UTC 时间信息以及时区信息设置到 SDK 中,函数如下所示。

说明:时区设置传入值,例:-5:00 (纽约非夏令时)。

/*
\fn OPERATE_RET tuya_ipc_set_service_time(IN TIME_T new_time_utc)
\brief set time of tuya SDK
\return OPERATE_RET
*/
OPERATE_RET tuya_ipc_set_service_time(IN TIME_T new_time_utc);

/*
\Function: uni_set_time_zone
\Input: time_zone->"+/-hh:mm"
\Output: none
\ Return: none
*/
OPERATE_RET uni_set_time_zone(IN CONST CHAR_T *time_zone)

关闭 AP 预览

  • 手机单击 结束 AP 模式handle_DP_AP_SWITCH 函数被触发,Wi-Fi AP 模式关闭并触发 tuya_ipc_reconnect_wifi 函数,设备重连 Wi-Fi,函数如下所示:

    if(cur_mode == WWM_SOFTAP)
    {
      hwl_wf_ap_stop();
      tuya_ipc_reconnect_wifi();
    }
    
  • 若设备已关闭 AP 热点,拔电重启,SDK 初始化函数:IPC_APP_Init_SDK 入参: WIFI_INIT_MODE_E 需按照原有的入参填入,token 填 NULL,仍需要调用接口: tuya_ipc_set_ap_info 传入上一次 AP 热点信息。

    说明:设备若没有启动过 AP 预览功能,则不需要传入上一次 AP 热点信息。若有启动过,则之后的每一次启动,都需要调用 tuya_ipc_set_ap_info 传入上一次 AP 热点信息。

日志上报后台开发

  • 设备启动应用前,判断 SD 卡中是否存在标志位信息。若存在,将应用的输出日志重定向到 SD 卡,若不存在,则将应用的输出日志重定向到目录:/tmp/tuya.log中。

    说明:设备端开发涂鸦 SDK,禁止连接第三方或私有服务器,否则存在不符合海外法规风险。设备端 DNS 必须需要设定为自动获取,否则存在设备联网异常风险。

  • 调用函数:tuya_ipc_set_log_attr 控制日志输出,SDK 默认日志输出级别:4,设置数值越少,日志输出则越少。trace 级别:5。函数调用位置如下所示:

    tuya_ipc_init_sdk(&env);
    tuya_ipc_set_log_attr(5,NULL);
    tuya_ipc_start_sdk(init_mode, p_token);
    

门铃功能开发

本章节主要介绍门铃产品特性功能的开发,包含:低功耗休眠功能和门铃触发功能。若所开发产品不是门铃,可忽略本章节。

设备进入休眠状态

  • 增加 DP:149

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

    #define TUYA_DP_DOOR_SLEEP        "149"    /* 设备休眠开关,BOOL 类型,true 未休眠,false 休眠 */
    
  • 进入低功耗状态:

    1. 调用函数:tuya_ipc_book_wakeup_topic 通知服务器,设备将要准备进入睡眠状态。
    2. 通过:tuya_ipc_get_mqtt_socket_fd 获取对应的 socket fd 信息。
    3. 通过:tuya_ipc_get_wakeup_data 获取 App 唤醒的数据,设备低功耗芯片与服务器取得通信后,判断是否有接收到唤醒数据来唤醒设备。
    4. 通过:tuya_ipc_get_heartbeat_data 获取设备休眠时,与服务器建立连接的保活包,设备休眠时,保活包的发送频率建议为 30s-120s。
    5. 上报 DP:149,通知服务器设备进入休眠。
    6. 设备进入休眠状态,只留下低功耗芯片与网络保持心跳包通信。

设备进入唤醒状态

  • 设备判断唤醒数据成功后,启动设备并回复 149 DP,回复值:1。

  • 若是通过手动按门铃方式唤醒设备,则:

    • 门铃呼叫(图片):设备回复 149 DP(低功耗-唤醒状态)点与函数 tuya_ipc_door_bell_press,函数入参如下所示:

      说明

      • 参数1:DOORBELL_NORMALL
      • 2:图片地址,
      • 3:图片大小
      • 4:图片类型
      /*
      \fn OPERATE_RET tuya_ipc_door_bell_press
      \brief send a doorbell pressing message to tuya cloud and APP
      \param[in] doorbell_type: DOORBELL_NORMAL or DOORBELL_AC
      \param[in] snap_buffer: address of current snapshot
      \param[in] snap_size: size fo snapshot, in Byte
      \param[in] type: snapshot file type, jpeg or 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);
      
    • 门铃呼叫(P2P):设备回复 149 DP(低功耗-唤醒状态)点,函数 tuya_ipc_door_bell_press,以及函数:tuya_ipc_notify_with_event,函数入参如下所示:

      说明

      • 1:DOORBELL_AC
      • 2:NULL
      • 3:空
      • 4:NULL
      /*
      \fn OPERATE_RET tuya_ipc_door_bell_press
      \brief send a doorbell pressing message to tuya cloud and APP
      \param[in] doorbell_type: DOORBELL_NORMAL or DOORBELL_AC
      \param[in] snap_buffer: address of current snapshot
      \param[in] snap_size: size fo snapshot, in Byte
      \param[in] type: snapshot file type, jpeg or 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);
      

      说明

      • 1:图片地址
      • 2:图片 size
      • 3:图片 type
      • 4:NOTIFICATION_NAME_DOORBELL
      /*
      \fn OPERATE_RET tuya_ipc_notify_with_event
      \brief send a editable alarm to tuya cloud and App
      \param[in] snap_buffer: address of current snapshot
      \param[in] snap_size: size fo snapshot, in Byte
      \param[in] type: snapshot file type, jpeg or png
      \param[in] name: editable event type, NOTIFICATION_NAME_E
      \return OPERATE_RET
      */
      
      OPERATE_RET tuya_ipc_notify_with_event(IN CONST CHAR_T *snap_buffer, IN CONST UINT_T snap_size, IN CONST NOTIFICATION_CONTENT_TYPE_E type, IN CONST NOTIFICATION_NAME_E name);
      
  • App 操作函数查询接口:

    • 获取音视频开启、关闭信息回调函数:__TUYA_APP_p2p_event_cb
    • 获取设备连接数信息查询函数:tuya_ipc_get_client_conn_info

低功耗设备功能启动优化

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

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

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

  • 带有 Echo 与 Chromecast 增值服务的设备,Echo 或 Chromecast 启动时,会调用对应的回调接口。TUYA_APP_Echoshow_StartTUYA_APP_Chromecast_Start,设备收到start 回调时不能休眠,直到收到停止回调 TUYA_APP_Echoshow_StopTUYA_APP_Chromecast_Stop 时才能休眠。

  • 使用本地存储事件录像功能,调用函数: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(文件的大小),函数如下所示:

    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->fw_md5:%s", fw->fw_md5);
      PR_DEBUG("fw->sw_ver:%s", fw->sw_ver);
      PR_DEBUG("fw->file_size:%u", fw->file_size);
    
  • 释放(优化)系统(内存)资源:调用 tuya_ipc_ss_set_write_mode,关闭本地录像功能,然后调用:tuya_ipc_ss_uninit()tuya_ipc_tranfser_close 函数,uninitquit 函数主要用于 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->fw_md5:%s", fw->fw_md5);
      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);
    
  • 开始下载固件,回调函数:__IPC_APP_get_file_data_cb,函数如下所示:

    说明:函数中有入参与出参。入参主要提供给您使用。出参由您获取到数据后,需要返回是否已完成数据的写入,写入成功返回 0。

    OPERATE_RET __IPC_APP_get_file_data_cb(IN CONST FW_UG_S *fw, IN CONST UINT_T total_len,IN CONST UINT_T offset,
    IN CONST BYTE_T *data,IN CONST UINT_T len,OUT UINT_T *remain_len, IN PVOID_T pri_data)
    {
       PR_DEBUG("Rev File Data");
      PR_DEBUG("total_len:%d fw_url:%s", total_len, fw->fw_url);
      PR_DEBUG("Offset:%d Len:%d", offset, len);
    
  • 函数:tuya_ipc_upgrade_sdk 如下所示:

    说明:第一个回调给到 SDK 与 App,用以显示 OTA 升级整体进度。固件下载完成后,第二个回调会有反馈。

    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->fw_md5:%s", fw->fw_md5);
      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);
    
  • 在函数:__IPC_APP_upgrade_notify_cb 中替换新固件,函数如下所示:

    说明:固件下载完成后,需要客户在函数中实现固件的具体替换过程。替换成功后,需要在此函数内进行设备的重启操作。固件替换前,需要备份 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)
    {
      FILE *p_upgrade_fd = (FILE *)pri_data;
      fclose(p_upgrade_fd);
    
      PR_DEBUG("Upgrade Finish");
      PR_DEBUG("download_result:%d fw_url:%s", download_result, fw->fw_url);
    
      if(download_result == 0)
      {
      /* The developer needs to implement the operation of OTA upgrade,
      when the OTA file has been downloaded successfully to the specified path. [ p_mgr_info->upgrade_file_path ]*/
      }
      //TODO
      //reboot system
    }
    
  • 若 OTA 升级失败,需要在函数:__IPC_APP_upgrade_notify_cb 中加入重启操作。

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

  • 固件升级时,手机 App 进度显示会暂停在92%-98%之间。此时,固件已完成下载,App 正在等待设备重启并上报新固件版本号,版本号上报成功后,App 将显示 升级成功

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

OTA 自定义进度上报开发

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

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

    说明:设备端可根据实际启用的 SDK 接口,做对应的反初始化,优化内存空间。

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

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

    /*
    \fn OPERATE_RET tuya_ipc_upgrade_progress_report
    \brief send a upgrade progress to tuya cloude and app
    \param[in] percent: upgrade progress percent , valid value [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 中的音视频信息发送至涂鸦 IoT 保存,保存时间根据所购买的云存储服务而定。

开发流程

  • 每个 UUID 都有一次体验低价购买云存储服务的权限。

  • 成功购买服务后,调用云存储初始化函数:TUYA_APP_Enable_CloudStorage

  • 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 算法提供的。
  • 事件云存储录像:当摄像机发现画面变化时,必须调用 tuya_ipc_cloud_storage_event_add 函数,SDK 才会将音视频信息上传至涂鸦 IoT。当发现画面静止时,调用:tuya_ipc_cloud_storage_event_delete 终止云存储事件录像。tuya_ipc_cloud_storage_event_add 接口调用成功,返回 event ID 用以调用 tuya_ipc_cloud_storage_event_delete 函数。若 tuya_ipc_cloud_storage_event_add 接口调用失败,返回:INVALID_EVENT_ID。云存储录像最终以列表的形式展现,函数如下所示:

    说明:调用 tuya_ipc_cloud_storage_event_add

    • 1:图片地址
    • 2:图片 size
    • 3:EVENT_TYPE_MOTION_DETECT
    • 4:300(单个云存储事件最大长度 300 秒)
    • 开启标记,最长时间为 5 分钟,5 分钟后若没有调用 tuya_ipc_cloud_storage_event_delete,SDK 将自动结束事件。
    /*
    \fn OPERATE_RET tuya_ipc_cloud_storage_event_add
    \brief add a new event
    \param[in] snapshot_buffer snapshot_size of current evnet
    \param[in] type event type
    \param[in] max_duration max duration of the event, not bigger than MAX_CLOUD_EVENT_DURATION
    \ event will be automaticly stopped when times up, if it’s not stopped by delete API
    \return EVENT_ID unique evnet id
    /
    EVENT_ID tuya_ipc_cloud_storage_event_add(CHAR_T *snapshot_buffer, UINT_T snapshot_size, ClOUD_STORAGE_EVENT_TYPE_E type, UINT_T max_duration);
    
    /*
    \fn OPERATE_RET tuya_ipc_cloud_storage_event_delete
    \brief delete(stop) specified event
    \return OPERATE_RET
    */
    OPERATE_RET tuya_ipc_cloud_storage_event_delete(EVENT_ID event_id);
    

Echo Show 与 Chromecast 功能

Echo Show 与 Chromecast 功能是将摄像机采集到的音视频数据显示在亚马逊或谷歌的智能设备上。本章节将介绍具体开发流程。此类功能的开发需要您自行搭建测试环境。

  • 确认 PID 以及 UUID 是否都完成增值服务打标。

  • 设备端在所有需要启动功能确认初始化成功后,调用接口 tuya_ipc_upload_skills 上报技能值函数如下所示。

    说明:SDK 使用第一路音频发送音频数据(支持 PCM),设备端不需要另起音频通道。

    /*very important! After all module inited, update skill to tuya cloud */
    tuya_ipc_upload_skills();
    
  • 谷歌推流开始时,__TUYA_APP_p2p_event_cb 收到回调: TRANS_STREAMING_VIDEO_START,函数如下所示:

    说明:在低功耗设备应用时,建议收到 start 回调后,设备不进入休眠状态,直到收到 stop 回调。

    case TRANS_STREAMING_VIDEO_START:
    {
      TRANSFER_SOURCE_TYPE_E *pSrcType = (TRANSFER_SOURCE_TYPE_E *)args;
      PR_DEBUG("streaming start type %d",*pSrcType);
      break;
    }
    
  • 谷歌推流结束时,__TUYA_APP_p2p_event_cb 收到回调: TRANS_STREAMING_VIDEO_STOP,函数如下所示:

    case TRANS_STREAMING_VIDEO_STOP:
    {
      TRANSFER_SOURCE_TYPE_E *pSrcType = (TRANSFER_SOURCE_TYPE_E *)args;
      PR_DEBUG("streaming stop type %d",*pSrcType);
      break;
    }
    
  • 亚马逊推流开始时,__TUYA_APP_p2p_event_cb 收到回调: TRANS_LIVE_VIDEO_START,type:3,函数如下所示:

    case TRANS_LIVE_VIDEO_START:
    {
      C2C_TRANS_CTRL_VIDEO_START * parm = (C2C_TRANS_CTRL_VIDEO_START *)args;
      PR_DEBUG("chn[%u] type[%d]video start",parm->channel,parm->type);
      break;
    }
    
  • 亚马逊推流结束时,__TUYA_APP_p2p_event_cb 收到回调:TRANS_LIVE_VIDEO_STOP,type:3,函数如下所示:

    case TRANS_LIVE_VIDEO_STOP:
    {
      C2C_TRANS_CTRL_VIDEO_STOP * parm = (C2C_TRANS_CTRL_VIDEO_STOP *)args;
      PR_DEBUG("chn[%u] type[%d] video stop",parm->channel,parm->type);
      break;
    }
    

设备解绑与复位

本章节主要简述设备如何通过 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 数据文件进行重置操作并重启。

参考文档