更新时间: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 文件导航目录如下:
.
└── 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 接入功能。
您可以根据以下流程,快速体验涂鸦 IPC SDK 功能。
tuya_ipc_demo
。登录 涂鸦开发者平台。
单击 创建产品。
选择 摄像机 > 云台摄像机。
选择智能化方式并勾选自定义方案。
输入产品名称和型号,选择设备类型,并单击 创建产品。
添加需要的 DP。
单击 设备交互。
下拉页面,选择对应模板。
单击 硬件开发,选择芯片平台和模组名称。
确定后弹出如下界面,单击 免费领取。
选择交付方式,提交订单。
单击订单详情。
下载授权码清单。
至此,产品创建成功,并获得 2 组试用的授权信息。
获取 SDK。如果未能找到您需要的 SDK,请联系您的涂鸦技术支持工程师。
下载 demo 代码。
解压 SDK,调整视频文件路径。
解压SDK
将解压后的 SDK 中的 SDK 文件夹(包含 include
和 lib
文件夹),复制到 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
后调整路径。
编译生成可执行文件。
在终端,进入 demo_for_ipc_5.x
文件夹,执行指令:
#make APP_NAME=demo
当编译完成并显示 [Build APP Finish]
,即为编译完成。
可执行文件生成位置:demo_for_ipc_5.x/output/
文件下,[tuya_ipc_demo]
为输出的可执行文件。
使用手机下载安装智能生活 App,打开 App,单击添加设备。
选择 安防监控 品类 > 智能摄像机(Wi-Fi)。
勾选 确认指示灯在快闪或听到提示音,然后单击 下一步。
输入 Wi-Fi 名称和密码。
此处连接的 Wi-Fi 需是 2.4 GHz。
生成和识别二维码。
将二维码进行截屏,打开微信,使用 扫一扫 功能来扫描二维码图片。
此处不要单击 听到提示音 按钮,在第 11 步时再单击。
在获取到的数据中,提取 token。
获取到的数据:{"p":"test","s":"test","t":"AYYBJt2OGvY***"}
t
后的参数即为 Token,有效期:10 分钟。10分钟后 token 失效,需要按照上诉流程重新获取。
在 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 是通过扫码二维码解析获取到的。
在执行可执行程序的同时,单击手机 App 上的 听到提示音 按钮。
等待一段时间即可添加成功。
单击添加成功的设备,即可成功获取视频流。如果没有视频显示,则表示之前的 demo_resource
文件没有设置正确,请至第 2 步检查是否操作正确。
在终端中输入下列指令,可以模拟体验 IPC 功能:
指令 | 指令说明 |
---|---|
start | 模拟移动侦测事件触发 |
stop | 模拟停止移动侦测事件 |
status | 获取设备激活状态 |
bell | 模拟门铃事件上报 |
例如虚拟设备执行过程中,执行:
#start
可以在消息中心看到一条移动侦测的上报。
通过上述步骤可以快速体验 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 搭建了一套完整的、用以引导设备配网的函数框架,本章节阐述配网的流程以及各框架函数的用途。建议您按照文档介绍,在对应的框架函数下,实现各功能点的具体操作。
开发流程
在 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 是配网模式选择。
二维码配网一般流程:
抓取二维码图片。
将二维码图片传入 zbar 函数(开源函数)进行识别。图像分辨率建议:不低于 320*240。
识别获取到的二维码信息(ssid、password 和 token)。
根据获取到的信息(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
对下发的音频做转码处理,函数如下所示:
函数参数:
TUYA_G711_MU_LAW
int tuya_g711_decode(unsigned char type, unsigned short *src, unsigned int srcLen, unsigned char *drc, unsigned int *pOut);
在 TUYA_IPC_SDK_START
中配置视频主码流、子码流和音频参数信息。
在主函数中开启音视频线程(两个线程)。
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);
分别在音视频线程中,调用 tuya_ipc_ring_buffer_open
函数,开启新的 session。
在线程函数中采集事先准备的音视频文件的信息,调用 TUYA_APP_Put_Frame
将采集到的视频帧传输至 ringbuffer 中。
您根据实际需求,将此步骤替换为对摄像头采集的音频信息传入 ringbuffer。
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();
系统接口,确保可以将缓存中的数据全部写入本地存储。
在主函数中开启本地存储,设置本地存储相关参数:
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);//设置本地存储路径
在主函数中调用 TUYA_IPC_SDK_START
函数开始 SDK 开发。
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 数据。
需要在以下两个函数: __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)
{
//TODO
memset(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 水印 DP:104
根据服务器下发的 DP 值,给视频加上或取消时间水印,水印位置与内容由设备端控制。
在 tuya_ipc_dp_utils.h
中加入:
#define TUYA_DP_WATERMARK 104
在 tuya_ipc_dp_utils.c
的 s_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
在主函数中调用 IPC_APP_handle_dp_cmd_objs
函数,对接收到的服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START
函数中已调用此函数)。
SDK 提供了控制移动侦测功能开关控制、灵敏度控制、定时控制、间歇控制以及数据上报的函数,您可以调用 SDK API 接口完成功能移动侦测以及移动追踪功能的开发。
开启步骤参照 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
,结合以上说明,图例示意如下所示:
初始化函数: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
调用的频率由设备测试调优的实际需求而定。
/*********************************************************************************
* 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
函数。
开启移动侦测功能(定义 DP)。
开启移动侦测线程,如下所示:
pthread_create(&motion_detect_thread, NULL, thread_md_proc, NULL);
在移动侦测线程函数中,调用时间同步函数来同步当前时间。
//获取 UTC 时间
OPERATE_RET tuya_ipc_get_utc_time(OUT TIME_T *time_utc);
在移动侦测线程函数中,调用移动判断函数 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;
}
如果发生移动,则调用 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+");
//...
}
调用 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);
若检测对象不发生移动,且距离上次移动侦测事件时间超过 10 秒,则调用 tuya_ipc_stop_storage
停止本地存储和云存储。
tuya_ipc_stop_storage(E_ALARM_SD_STORAGE); //停止本地存储
tuya_ipc_stop_storage(E_ALARM_CLOUD_STORAGE); //停止云存储
图像缩放功能目的在于调整图像的分辨率,将采集到的图像调整至需要的目标分辨率。
调用函数: 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;
摇头机开始转动 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"}
摇头机 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
示例开发流程:
开启 PTZ 控制功能,在 tuya_ipc_dp_utils.h
定义相关 DP。例如:
#define TUYA_DP_PTZ_CONTROL 119 /* PTZ旋转控制,枚举类型*/
#define TUYA_DP_PTZ_STOP 116 /* PTZ旋转停止,BOOL型*/
在 tuya_ipc_dp_utils.c
的 s_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
在主函数中调用 IPC_APP_handle_dp_cmd_objs
函数对接收到的服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START
函数中已调用此函数)。
预设位控制功能 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);
开启 PTZ 预设点功能(在 tuya_ipc_dp_utils.h
定义相关 DP),如:
#define TUYA_DP_PRESET_SET 178
/*收藏点操作,string 型,type:1 添加,type:2 删除,不同类型的数据串不同*/
在 tuya_ipc_dp_utils.c
的 s_dp_table[]
中加入:
#ifdef TUYA_DP_PRESET_SET
{TUYA_DP_PRESET_SET, handle_DP_SET_PRESET},
#endif
在 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);
}
在主函数中调用 IPC_APP_handle_dp_cmd_objs
函数,对接收到服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START
函数中已调用此函数)。
调用函数: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);
开启门铃功能,设置相关参数:
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;//最大持续时间,单位为秒
调用 TUYA_IPC_SDK_START
开始 SDK 开发,其内部会调用 tuya_ipc_video_msg_init
函数进行门铃功能初始化。
如果门铃被按下,调用 doorbell_handler
函数。
在 doorbell_handler
函数中判断门铃的状态,如果为 DOORBELL_LISTEN
状态(监听状态),则调用 get_snapshot
抓取当前图像,并调用 tuya_ipc_door_bell_press
向涂鸦开发者平台和 App 推送消息。
如果为 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);
若需要开启低功耗,则调用 tuya_ipc_low_power_server_get
获取服务器的 IP 地址和 port。
调用 tuya_ipc_device_id_get
获取设备 ID。
调用 tuya_ipc_local_key_get
获取 local key。
根据以上得到的信息,调用 TUYA_APP_LOW_POWER_START
开始低功耗模式。
OPERATE_RET TUYA_APP_LOW_POWER_START(char * devbuf,char *keybuf,int ip,int port)
TUYA_APP_LOW_POWER_START
中调用 tuya_ipc_low_power_server_connect
连接服务器。
TUYA_APP_LOW_POWER_START
中调用 tuya_ipc_low_power_socket_fd_get
获取对应的 socket fd,用于后续心跳包发送。
TUYA_APP_LOW_POWER_START
中调用 tuya_ipc_low_power_wakeup_data_get
获取 App 唤醒的数据。
创建一个循环,不断发送心跳包通知服务声明此设备的存在,并且不断接收唤醒信号等待唤醒。
send(low_power_socket, heart_beat, heart_beat_len, 0);//发送心跳包
分析接收到唤醒数据,如果判断需要唤醒设备,则进行启动设备相关操作。
您需要在 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_ONGOING
或 EVENT_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 作为设备升级与维护的最重要途径之一,将在本章节进行具体介绍。当有固件需要升级时,SDK 将通过回调的形式告知设备,进行 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 失败,设备必须进行重启。
设备端通过 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 中的音视频信息发送至云端保存,保存时间根据所购买的云存储服务而定。
OpensslAES_CBC128_encrypt
使用 AES CBC 方式。若软件加密给 MCU 带来较大的负载,可改用硬件加密方式,使用 SoC 硬件加密通道,加密方式:PKCS5/PKCS7,如下表所示:填充类型 | 说明 |
---|---|
不填充 | 有些块加密模式不需要填充,或者明文都是块长度的整数倍。 |
ISO10126 |
|
PKCS5/PKCS7 |
|
OAEP/PKCS1 | 为 RSA 算法提供的。 |
连续云存储:
设定 ipc_sdk_run_var.cloud_storage_info.enable
为 1。
ipc_sdk_run_var
信息参照结构体 TUYA_IPC_SDK_RUN_VAR_S
,enable
数值为 1 代表开启云存储功能,数值为 0 代表关闭云存储功能。
您根据开发需要调用 AES_CBC_init
、AES_CBC_encrypt
、AES_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;
调用函数 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);
事件云存储录像:
当 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);
SDK 中的云存储线程会自行将填加的事件信息传输至云端。
当事件结束时,调用: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_bak
、tuya_user.db
与 tuya_enckey.db
。
设备对 DP 数据文件进行重置操作并重启。
更多详情,请参考 硬件交互定义。
本章节主要简述通过 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 模式查询
开启 AP 模式查询功能。在 tuya_ipc_dp_utils.h
定义相关 DP,如:
#define TUYA_DP_AP_MODE 231
在 tuya_ipc_dp_utils.c
的 s_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 的值。
在主函数中调用 IPC_APP_handle_dp_cmd_objs
函数,对接收到服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START
函数中已调用此函数)。
AP 模式开关
开启 AP 模式开关功能(在 tuya_ipc_dp_utils.h
定义相关 DP),如:
#define TUYA_DP_AP_SWITCH 232
在 tuya_ipc_dp_utils.c
的 s_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 连接的监视。
在主函数中调用 IPC_APP_handle_dp_cmd_objs
函数对接收到服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START
函数中已调用此函数)。
AP 模式时间同步
开启 AP 模式时间同步功能(在 tuya_ipc_dp_utils.h
定义相关 DP),如:
#define TUYA_DP_AP_TIME_SYNC 233
在 tuya_ipc_dp_utils.c
的 s_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。
在主函数中调用 IPC_APP_handle_dp_cmd_objs
函数,对接收到服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START
函数中已调用此函数)。
AP 模式时区同步
开启 AP 模式时区同步功能(在 tuya_ipc_dp_utils.h
定义相关 DP),如:
#define TUYA_DP_AP_TIME_ZONE 234
在 tuya_ipc_dp_utils.c
的 s_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。
在主函数中调用 IPC_APP_handle_dp_cmd_objs
函数,对接收到服务器下发的 DP 数据进行处理,判断是否触发此 DP 对应的功能函数(TUYA_IPC_SDK_START
函数中已调用此函数)。
设备开机进入 AP 模式
设备开启 AP 模式后,为了关机后不丢失状态,您需要将设备 AP 模式相关配置保存到设备,待到开机时根据保存的配置调用开启 AP 模式的函数。
Demo 中 tuya_ipc_sdk_mqtt_online_proc
函数所示,本地存储和 P2P 均在联网同步到时间后才进行初始化。但开机若为 AP 模式,这是无法实现的,所以需要单独根据 AP 模式配置进行初始化,如 demo 中 tuya_ipc_ap_mode_start_proc
函数所示。
在 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 模式覆盖。
更多详情,请参考 SDK 接口说明。
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈