TuyaOS
TuyaOS开发者指导手册

版本记录

版本 编写/修订说明 修订日期 备注
1.0.1 修改格式 20220321
1.0.0 创建文档 20210219

概述

TuyaOS 是涂鸦智能自主开发的,全球唯一的覆盖物联网感知、中断、网络、平台、应用各个层次的物联网操作系统。受益于涂鸦智能在物联网行业的积累,TuyaOS 提供了全品类的从产品设计、研发、接入到后期运营解决方案。

TuyaOS 是涂鸦的重要组成部分,通过良好的设计,为客户提供了统一的开发接口,丰富的开发套件以及自助化定制功能,屏蔽了物联网行业的碎片化特征,可以应用于工业物联网、车联网、安防监控、户外出行、智能家居等行业的物联网应用开发。

TuyaOS 具有以下特点:

  • 弹性裁剪:提供了丰富的开发套件,涵盖了物联网操作系统内核、通用物联网功能、不同领域的物联网技术栈,开发者可以根据自己的产品的需求进行选择对应的解决方案,有能力的开发者,可以在线按需进行深度的定制。
  • 跨平台:设计层面支持了跨硬件平台、操作系统的特性,可以在任意的硬件芯片和系统上进行移植,极大的提高了开发者的开发效率。
  • 安全隐私:提供了安全数据存储、安全网络通信、安全启动、安全升级等特性,并严格遵守全球各地的安全、隐私政策,有效的保障产品的安全、隐私特性。
  • 丰富生态:涂鸦智能和各大芯片原厂进行了密切的合作,导入了100+款供开发者选择,同时所有使用涂鸦 IoTOS 开发的产品都能够进行互联互通,场景联动,Powered by Tuya 已经形成了完整的物联网产业生态。

名词解释

详细的名词解释请参照涂鸦智能开发者名词解释

目录结构

<TuyaOS-x.x.x-tuyaos-iot-xxxxxxx>
|---adapter #适配层定义
|---apps #demo程序
|---build #编译配置
|---docs #文档
|---include #头文件,包括公共头文件和组件头文件
|---libs #libtuyaos-iot/ipc/gw.a,独立链接的.a文件
|---scripts #工具、脚本
|---vendor #原厂库,空,第一次编译按需下载
|---build.sh/py #tuyaos编译入口
|---README.md
|---LICENSE
|---CHANGELOG
|---RELEASELOG

架构说明

TuyaOS 开发框架 整体框架

设备状态机

# mermaid source code
graph TD
inited
unregistered
registing
actived
upgrading
inited--read flash not actived-->unregistered
unregistered--bind token-->registing
registing--active success-->actived
inited--read flash actived-->actived
actived--start OTA-->upgrading
actived--unactive/unregister-->unregistered
upgrading--OTA success-->actived

功能说明

初始化

初始化是指设备上电、启动之后,调用一系列接口,完成设备硬件环境的初始化、TuyaOS 软件的初始化、应用功能初始化等工作。

一般来说,软件启动会执行main函数,对于 TuyaOS 来说,不同场景,入口不一样,比如说在 Linux 操作系统环境下,我们需要自己实现main函数;在 RTOS 系统下,我们需要实现 user_main 函数。并在 user main 函数里对 TuyaOS 软件和设备应用能力进行初始化。

接口描述

IoTOS初始化

OPERATE_RET tuya_iot_init_params(IN CONST CHAR_T *fs_storge_path, IN CONST TY_INIT_PARAMS_S *p_param);
Definition of TUYA DevOS init param
Definition: tuya_cloud_com_defs.h:163
OPERATE_RET tuya_iot_init_params(IN CONST CHAR_T *fs_storge_path, IN CONST TY_INIT_PARAMS_S *p_param)
tuya_iot_init_params @desc init tuya_iot_sdk

此接口是 TuyaOS 的初始化接口,不区分模式,有线、Wi-FiWi-Fi&BLE、蜂窝网络都会使用。接口内部会对 IoTOS 的内部基础功能进行资源分配、初始化。一般来说,这个接口会第一个调用,但是在一些场景下,可以在此接口之前进行一些功能配置、事件关注等工作,可以在 IoTOS 初始化过程中进行一些特殊的业务逻辑处理。例如在开发照明产品的时候,需要基础功能初始化完成之后立即将灯点亮,则可以在调用此接口之前关注一下 early_init 事件,并在此事件回调里执行点灯逻辑,就可以实现此功能。

Wi-Fi 设备初始化

OPERATE_RET tuya_iot_wf_dev_init(IN CONST GW_WF_CFG_MTHD_SEL cfg, IN CONST GW_WF_START_MODE start_mode,
IN CONST TY_IOT_CBS_S *cbs, IN CONST CHAR_T *firmware_key,
IN CONST CHAR_T *product_key, IN CONST CHAR_T *wf_sw_ver,
IN CONST GW_ATTACH_ATTR_T *attr, IN CONST UINT_T attr_num);
Definition of attach moudule attribute
Definition: tuya_cloud_com_defs.h:175
Definition of gateway callback funtions
Definition: tuya_cloud_com_defs.h:726
OPERATE_RET tuya_iot_wf_dev_init(IN CONST GW_WF_CFG_MTHD_SEL cfg, IN CONST GW_WF_START_MODE start_mode, IN CONST TY_IOT_CBS_S *cbs, IN CONST CHAR_T *firmware_key, IN CONST CHAR_T *product_key, IN CONST CHAR_T *wf_sw_ver, IN CONST DEV_TYPE_T tp, IN CONST GW_ATTACH_ATTR_T *attr, IN CONST UINT_T attr_num)
tuya_iot_wf_dev_init

此接口用来初始化设备能力。在涂鸦物联网开发五步走步骤,会对产品进行定义,得到产品的 PID 等信息;PID 信息在涂鸦云端对应了产品的功能信息,以及设备上其他的附属固件、文件等配置信息。在这个接口里,会对这些信息进行校验,并向尝试配网、网络连接、连接云端,进行设备的激活绑定,在设备激活之后,启动各种 TuyaOS 服务,并连接涂鸦云,进行正常的业务交互。

Wi-Fi 设备初始化接口,可以配置初始化的配网模式和 Wi-Fi 工作模式。

Wi-Fi 通用对接初始化

OPERATE_RET tuya_iot_wf_mcu_dev_init(IN CONST GW_WF_CFG_MTHD_SEL cfg, IN CONST GW_WF_START_MODE start_mode,
IN CONST TY_IOT_CBS_S *cbs, IN CONST CHAR_T *p_firmware_key,
IN CONST CHAR_T *product_key,IN CONST CHAR_T *wf_sw_ver,IN CONST CHAR_T *mcu_sw_ver);
OPERATE_RET tuya_iot_wf_mcu_dev_init(IN CONST GW_WF_CFG_MTHD_SEL cfg, IN CONST GW_WF_START_MODE start_mode, IN CONST TY_IOT_CBS_S *cbs, IN CONST CHAR_T *p_firmware_key, IN CONST CHAR_T *product_key, IN CONST CHAR_T *wf_sw_ver, IN CONST CHAR_T *mcu_sw_ver)
tuya_iot_wf_mcu_dev_init

此接口对 tuya_iot_wf_dev_init 进行了封装,实现了专门用于 mcu 通用对接的初始化接口。在 mcu 通用对接场景下,可以使用该接口,在收到 mcu 通过串口发送过来的 product keymcu 版本信息,对设备进行初始化。

Wi-Fi 设备初始化接口,可以配置初始化的配网模式和 Wi-Fi 工作模式。

Wi-Fi SOC初始化

OPERATE_RET tuya_iot_wf_soc_dev_init_param(IN CONST GW_WF_CFG_MTHD_SEL cfg, IN CONST GW_WF_START_MODE start_mode,
IN CONST TY_IOT_CBS_S *cbs,IN CONST CHAR_T *firmware_key,
IN CONST CHAR_T *product_key,IN CONST CHAR_T *wf_sw_ver);
OPERATE_RET tuya_iot_wf_soc_dev_init_param(IN CONST GW_WF_CFG_MTHD_SEL cfg, IN CONST GW_WF_START_MODE start_mode, IN CONST TY_IOT_CBS_S *cbs, IN CONST CHAR_T *firmware_key, IN CONST CHAR_T *product_key, IN CONST CHAR_T *wf_sw_ver)
tuya_iot_wf_soc_dev_init_param

此接口对 tuya_iot_wf_dev_init 进行了封装,实现了专门用于 soc 方案的初始化接口。在 soc 场景下,可以使用该接口,用本地保存的 product key 和固件版本信息,对设备进行初始化。

Wi-Fi 设备初始化接口,可以配置初始化的配网模式和 Wi-Fi 工作模式。

Wi-Fi网络状态回调设置

OPERATE_RET tuya_iot_reg_get_wf_nw_stat_cb_params(IN CONST GET_WF_NW_STAT_CB wf_nw_stat_cb, IN CONST INT_T min_interval_s);
VOID(* GET_WF_NW_STAT_CB)(IN CONST GW_WIFI_NW_STAT_E stat)
Handler when network connection error happens
Definition: tuya_cloud_wifi_defs.h:137
OPERATE_RET tuya_iot_reg_get_wf_nw_stat_cb_params(IN CONST GET_WF_NW_STAT_CB wf_nw_stat_cb, IN CONST INT_T min_interval_s)
tuya_iot_reg_get_wf_nw_stat_cb_params

此接口注册网络状态变化回调函数,并启轮询有线网络状态的任务。轮询网络连接的状态,并在状态变化的时候,调用此接口注册进来的 nw_stat_cb,通知应用进行相应的处理。

示例代码

Wi-Fi配网主要是在初始化的时候指定了配网模式,其主要的工作是初始化的时候进行的,以下是一端示例代码,在初始化的时候设置了 ap_ssidap_passwd,并以 GWCM_SPCL_AUTOCFG 工作模式和 WF_START_AP_FIRST 配网模式启动设备。

#define UUID "f2*b0"
#define AUTHKEY "6PMX"
#define PRODUCT_KEY "U0Zy" //DP控制开关
#define USER_SW_VER "1.0.0"
#define AP_SSID "SmartLife"
#define AP_PASSWD "tuya_wf_sample"
#define CFG_STORAGE_PATH "./tuya_db_files/"
#define WIFI_NETCFG_TIMEOUT 3
STATIC VOID __soc_dev_rev_upgrade_info_cb(IN CONST FW_UG_S *fw);// SOC设备升级入口
STATIC VOID __soc_dev_status_changed_cb(IN CONST GW_STATUS_E status);// SOC设备云端状态变更回调
STATIC VOID __soc_dev_dp_query_cb(IN CONST TY_DP_QUERY_S *dp_qry);// SOC设备特定数据查询入口
STATIC VOID __soc_dev_obj_dp_cmd_cb(IN CONST TY_RECV_OBJ_DP_S *dp);// SOC设备格式化指令数据下发入口
STATIC VOID __soc_dev_raw_dp_cmd_cb(IN CONST TY_RECV_RAW_DP_S *dp);// SOC设备透传指令数据下发入口
STATIC VOID __soc_dev_restart_req_cb(GW_RESET_TYPE_E type);// SOC设备进程重启请求入口
STATIC VOID __soc_dev_net_status_cb(IN CONST GW_BASE_NW_STAT_T stat);// SOC外网状态变动回调
STATIC VOID __soc_dev_reset_cb(event_raw_data_t *data);
int main(int argc, char *argv[])
{
int rt = OPRT_OK;
// 此处为TuyaOS初始化之前的一些特殊工作,代码每一个应用场景可能不同,按需实现
// TBD
// 此处为TuyaOS基础功能初始化,为IoTOS分配资源,并启动一些最基础的能力
TUYA_CALL_ERR_RETURN(tuya_iot_init_param(CFG_STORAGE_PATH, NULL));
// 此处为TuyaOS授权信息写入,仅在Linux Demo代码使用,因为正是产品是在生产测试的时候写入授权信息
// 此处代码是为了避免使用产测授权
WF_GW_PROD_INFO_S prod_info = {UUID, AUTHKEY, AP_SSID, AP_PASSWD};
TUYA_CALL_ERR_RETURN(tuya_iot_set_wf_gw_prod_info(&prod_info));
// 此处为设备初始化,实现了一些基础的回调功能,满足一个设备的基本能力需求,并注册获取连接状态回调函数
TY_IOT_CBS_S iot_cbs = {0};
iot_cbs.gw_status_cb = __soc_dev_status_changed_cb;
iot_cbs.gw_ug_cb = __soc_dev_rev_upgrade_info_cb;
iot_cbs.gw_reset_cb = __soc_dev_restart_req_cb;
iot_cbs.dev_obj_dp_cb = __soc_dev_obj_dp_cmd_cb;
iot_cbs.dev_raw_dp_cb = __soc_dev_raw_dp_cmd_cb;
iot_cbs.dev_dp_query_cb = __soc_dev_dp_query_cb; TUYA_CALL_ERR_RETURN(tuya_iot_wf_soc_dev_init_param(GWCM_SPCL_AUTOCFG,WF_START_AP_FIRST,&iot_cbs,NULL,PRODUCT_KEY,DEV_SW_VERSION));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_get_nw_stat_cb_params(__soc_dev_net_status_cb, 1));
// 此处为设备初始化后的一些设备应用功能初始化,,代码每一个应用场景可能不同,按需实现
// TBD
// demo状态监控
while (1)
{
sleep(10);
// 监控设备状态,打印状态提示
ty_devos_monitor();
}
return 0;
}
tuya sdk ota firmware info
Definition: tuya_cloud_com_defs.h:584
Definition of DP query
Definition: tuya_cloud_com_defs.h:497
GW_RESET_IFM_CB gw_reset_cb
Definition: tuya_cloud_com_defs.h:732
DEV_RAW_DP_CMD_CB dev_raw_dp_cb
Definition: tuya_cloud_com_defs.h:736
DEV_DP_QUERY_CB dev_dp_query_cb
Definition: tuya_cloud_com_defs.h:738
DEV_OBJ_DP_CMD_CB dev_obj_dp_cb
Definition: tuya_cloud_com_defs.h:734
GW_STATUS_CHANGED_CB gw_status_cb
Definition: tuya_cloud_com_defs.h:728
GW_UG_INFORM_CB gw_ug_cb
Definition: tuya_cloud_com_defs.h:730
Definition of recved structured dp
Definition: tuya_cloud_com_defs.h:459
Definition of recved raw dp
Definition: tuya_cloud_com_defs.h:477
Definition: tuya_cloud_wifi_defs.h:116
the event dispatch raw data
Definition: base_event.h:50
OPERATE_RET tuya_iot_reg_get_nw_stat_cb_params(IN CONST GET_NW_STAT_CB nw_stat_cb, IN CONST INT_T min_interval_s)
tuya_iot_reg_get_nw_stat_cb_params @desc enable tuya-sdk network check. sdk will check the network ev...
OPERATE_RET tuya_iot_set_wf_gw_prod_info(IN CONST WF_GW_PROD_INFO_S *wf_prod_info)
tuya_iot_set_wf_gw_prod_info

配网

配网是 Wi-Fi 产品特有的一个能力,他的主要目的,是通过具有配网能力的设备,对未配网的 Wi-Fi 设备进行网络连接配置。在配网过程中,具有配网能力的设备,会通过交互,把指定的Wi-Fi路由器的 SSIDPassword 以及在涂鸦 IoT 平台上申请创建出来的 Token 信息,发送给未配网设备;未配网设备拿到这些信息之后,连接Wi-Fi路由器,并通过涂鸦 IoT 平台提供的 IoT-DNS 服务获取对应区域的服务器地址,然后通过 Atop 服务进行激活,并连接到涂鸦 IoT 平台进行正常的业务交互。

目前按照交互方式,TuyaOS 主要支持以下配网能力。

  • smartcfg 配网

    根据涂鸦 smartcfg 规则,将 SSIDPasswordToken 进行编码到 udp 广播、组播报文中,并在Wi-Fi路由器局域网内部发送,未配网设备在接收到报文,识别特征之后,进行解码,提取出 SSIDPasswordToken,并进行后续的连接 Wi-Fi 路由器,并连接涂鸦 IoT 平台,进行激活绑定。

  • AP 配网

    未配网设备根据用户配置或者是默认的涂鸦 AP 配网规则,创建一个 Wi-Fi AP 热点,并启动涂鸦 AP 配网协议服务。具备配网能力的设备会连接到这个热点,并连接到该服务端口,根据涂鸦 AP 配网协议进行交互,将 SSIDPasswordToken 发送给待配网设备。待配网设备拿到 SSIDPasswordToken 之后,连接 Wi-Fi 路由器,并连接涂鸦 IoT 平台,进行激活绑定。

  • 蓝牙配网

    未配网设备发送蓝牙广播,具备配网能力的设备在接收到蓝牙广播数据之后,会对未配网设备发起配对,配对完成之后,根据涂鸦蓝牙配网协议进行交互,将 SSIDPasswordToken 发送给待配网设备。待配网设备拿到 SSIDPasswordToken 之后,连接 Wi-Fi 路由器,并连接涂鸦 IoT 平台,进行激活绑定。

  • 闪电配网

    闪电配网是一种伙伴免密配网技术,即一台支持闪电配网的已联网设备,对支持闪电配网的的设备进行配网。支持闪电配网的已联网设备通过Wi-Fi管理帧,按照涂鸦闪电配网协议的定义,发现在设备周边新出现的支持闪电配网的未配网设备,并跟涂鸦 APP 进行交互,当客户进入涂鸦 APP 主界面的时候,进行弹窗显示,只需要用户在APP上进行简单的发现、添加操作,即可将设备配网,减少了人为的输入密码操作,优化了配网体验,使得新设备可以做到上电添加之后即可使用。

  • FFS 配网

    FFS配网是 Alexa Frustration-Free Setup的缩写,也是一种伙伴免密配网技术,在amazon Alexa智能音箱的支持下,按照amazon定义的 Frustration-Free Setup流程,对支持FFS配网的设备进行配网,过程不需要人工介入,设备只要插电即可配网,优化了配网的流程和客户的使用体验。

  • 用户配网

    为了支持其他类型的配网,如屏幕输入、声波、扫二维码、局域网等任意开发者自行开发出来的获取 SSIDPasswordToken 的方式,通过这些机制拿到了 SSIDPasswordToken 并调用API设置到 TuyaOS,待配网设备拿到 SSIDPasswordToken 之后,连接 Wi-Fi 路由器,并连接涂鸦 IoT 平台,进行激活绑定。

  • 涂鸦万能配网

    涂鸦万能配网是指涂鸦 IoTOS 支持了上述配网方式共存的配网模式,这些配网模式同时启动,只要有一个配网模式激活即可进行配网。

配网模式

配网模式是指 TuyaOS 根据客户需求,抽象出来的配网方案,包括以下几种。

配网模式 描述
WF_START_AP_ONLY 仅支持 AP 配网模式。
WF_START_SMART_ONLY 仅支持 smartcfg 配网模式。
WF_START_AP_FIRST 支持 AP 配网或者 smartcfg 配网模式,默认 AP 配网模式,但是经过重置,可以切换成 smartcfg 配网模式。
WF_START_SMART_FIRST 支持 AP 配网或者 smartcfg 配网模式,默认 smartcfg 配网模式,但是经过重置,可以切换成 AP 配网模式。
WF_START_SMART_AP_CONCURRENT 涂鸦万能配网模式,支持 APsmartcfg 配网共存,同时根据产品的形态和客户的需求,自定义支持蓝牙配网、闪电配网和 FFS 配网。

注意,在以上几个配网模式下,用户配网方式是默认支持的,即在任意模式之下,都是可以通过接口直接设置 SSIDPasswordToken 进行配网操作的。

Wi-Fi工作模式

Wi-Fi工作模式是Wi-Fi产品特有的模式,其目的是为了

Wi-Fi工作模式 描述
GWCM_OLD 主要使用在通用对接类固件上。
设备只有在 mcu 发起产测的时候,才会扫描产测路由器,任意时刻都可以进入产测。
GWCM_LOW_POWER 主要使用在 soc 插座上。
配网前上电低功耗常亮,需要手工切换才能进入配网状态;
配网状态下,10秒内没配网成功,手工重启保持上次配网状态,10s后未配网,手工重启进入低功耗常亮状态;15分钟未配网,自动进入低功耗常亮状态,其中15分钟可以设置;
配网成功后,app移除设备自动重启,进入配网状态,默认 smartcfg 配网模式,擦除保存的 ssidpassword ;手工移除,设备自动进入配网状态,擦除保存的 ssidpassword
设备只有在低功耗状态下,重启设备才会扫描产测路由,进入产测模式。
GWCM_SPCL_MODE 主要使用在 soc 照明上。
配网前上电低功耗常亮,需要手工切换才能进入配网状态;
配网状态下,10秒内没配网成功,手工重启保持上次配网状态,10s后未配网,手工重启进入低功耗常亮状态,15分钟未配网,自动进入低功耗常亮状态,其中15分钟可以设置;
配网成功后,app移除设备自动重启,进入配网状态,默认 smartcfg 配网模式,擦除保存的 ssidpassword;手工移除,设备自动进入配网状态,进入防误触模式,不擦除保存的 ssidpassword
设备只有在低功耗状态下,重启设备才会扫描产测路由,进入产测模式。
GWCM_OLD_PROD 主要使用在 soc 照明上。
上电即可进入配网状态,并且一直处于配网状态;
设备在配网状态下,重启设备都会主动扫描产测路由,进入产测模式。
GWCM_LOW_POWER_AUTOCFG 配网前上电进入 smartcfg 配网模式,smartcfgap 模式来回切换;
配网状态下,15分钟内重启,保持,保持上次配网状态,15分钟未配网自动进入低功耗常亮状态,其中15分钟可以设置;
配网成功后,app移除设备自动重启,进入配网状态,默认 smartcfg 配网模式;手工移除,设备自动进入配网状态,默认 smartcfg 配网模式;
设备在配网状态下,重启设备都会主动扫描产测路由,进入产测模式。
GWCM_SPCL_AUTOCFG 配网前上电进入 smartcfg 配网模式,smartcfgap 模式来回切换;
配网状态下,15分钟内重启,保持上次配网状态,15分钟未配网自动进入低功耗常亮状态,其中15分钟可以设置;
配网成功后,app移除设备自动重启,进入配网状态,默认 smartcfg 配网模式,擦除保存的 ssidpassword,15分钟内重启,保持上次配网状态,15分钟未配网自动进入低功耗常亮状态,其中15分钟可以设置;手工移除,设备自动进入配网状态,进入防误触模式,不擦除保存的 ssidpassword,10秒内重启,保持上次配网状态,10秒后重启,立即连接已经配网路由器,15分钟内重启,保持,保持上次配网状态,15分钟未配网自动进入低功耗常亮状态,其中15分钟可以设置;;
设备在配网状态下,重启设备都会主动扫描产测路由,进入产测模式。

接口描述

Wi-Fi 设置用户自定义AP

OPERATE_RET tuya_iot_set_user_def_ap_if(IN CONST CHAR_T *ssid,IN CONST CHAR_T *passwd);
OPERATE_RET tuya_iot_set_user_def_ap_if(IN CONST CHAR_T *ssid, IN CONST CHAR_T *passwd)
tuya_iot_set_user_def_ap_if

此接口用于设置用户自定义的AP配网的 SSIDPassword。如果用户设置自动以的AP的 SSIDPassword 之后,AP 配网将显示该 SSID,并在连接的时候使用该 Password 进行验证。因此,此接口必须在设备初始化之前调用。

Wi-Fi 获取用户自定义AP

OPERATE_RET tuya_iot_get_dev_ap_if(OUT CHAR_T *ssid,OUT CHAR_T *passwd);
OPERATE_RET tuya_iot_get_dev_ap_if(OUT CHAR_T *ssid, OUT CHAR_T *passwd)
tuya_iot_get_dev_ap_if

此接口用于查询用户设置的AP配网的 SSIDPassword。如果用户没有设置返回设备烧录授权时写入的默认的 SSIDPassword

Wi-Fi 用户配网

OPERATE_RET tuya_iot_gw_wf_user_cfg(IN CONST CHAR_T *ssid,IN CONST CHAR_T *passwd,IN CONST CHAR_T *token);
OPERATE_RET tuya_iot_gw_wf_user_cfg(IN CONST CHAR_T *ssid, IN CONST CHAR_T *passwd, IN CONST CHAR_T *token)
tuya_iot_gw_wf_user_cfg

此接口用于配置设备 Wi-Fi 网络配置,除了通过涂鸦提供的配网能力去获取 Wi-Fi 网络配置之外,还可以通过用户自己的方式去获取 SSIDPasswordToken,然后通过此接口设置,进行连接路由器、绑定激活等工作。

Wi-Fi 配网超时时间设置

VOID tuya_iot_wf_timeout_set(IN CONST UINT_T timeout);
VOID tuya_iot_wf_timeout_set(IN CONST UINT_T timeout)
tuya_iot_wf_timeout_set @desc set wifi timeout

Wi-Fi 配网超时是指设备进入配网状态之后,一段时间没有完成配网,则退出配网模式的行为。设备配网的过程是一个设备和涂鸦智能APP、路由器频繁交互的一个阶段,功耗很高,长时间处于配网状态会造成设备消耗较多的电力,提高设备的使用成本,也可能会导致设备不稳定。因此TuyaOS默认进入配网状态15分钟之后,配网超时退出配网,这个时间是可设置的,通过 tuya_iot_wf_timeout_set 可以修改超时时间。

示例代码

Wi-Fi配网主要是在初始化的时候指定了配网模式,其主要的工作是初始化的时候进行的,以下是一端示例代码,在初始化的时候设置了 ap_ssidap_passwd,并以 GWCM_SPCL_AUTOCFG 工作模式和 WF_START_AP_FIRST 配网模式启动设备。

#define UUID "f2*b0"
#define AUTHKEY "6PMX"
#define PRODUCT_KEY "U0*Zy" //DP控制开关
#define USER_SW_VER "1.0.0"
#define AP_SSID "SmartLife"
#define AP_PASSWD "tuya_wf_sample"
#define CFG_STORAGE_PATH "./tuya_db_files/"
#define WIFI_NETCFG_TIMEOUT 3
STATIC VOID __soc_dev_rev_upgrade_info_cb(IN CONST FW_UG_S *fw);// SOC设备升级入口
STATIC VOID __soc_dev_status_changed_cb(IN CONST GW_STATUS_E status);// SOC设备云端状态变更回调
STATIC VOID __soc_dev_dp_query_cb(IN CONST TY_DP_QUERY_S *dp_qry);// SOC设备特定数据查询入口
STATIC VOID __soc_dev_obj_dp_cmd_cb(IN CONST TY_RECV_OBJ_DP_S *dp);// SOC设备格式化指令数据下发入口
STATIC VOID __soc_dev_raw_dp_cmd_cb(IN CONST TY_RECV_RAW_DP_S *dp);// SOC设备透传指令数据下发入口
STATIC VOID __soc_dev_restart_req_cb(GW_RESET_TYPE_E type);// SOC设备进程重启请求入口
STATIC VOID __soc_dev_net_status_cb(IN CONST GW_BASE_NW_STAT_T stat);// SOC外网状态变动回调
STATIC VOID __soc_dev_reset_cb(event_raw_data_t *data);
int main(int argc, char *argv[])
{
int rt = OPRT_OK;
// 此处为TuyaOS初始化之前的一些特殊工作,代码每一个应用场景可能不同,按需实现
TUYA_CALL_ERR_RETURN(tuya_iot_wf_timeout_set(WIFI_NETCFG_TIMEOUT));
TUYA_CALL_ERR_RETURN(tuya_iot_set_user_def_ap_if("test_ap", "test_passwd"));
// 此处为TuyaOS基础功能初始化,为IoTOS分配资源,并启动一些最基础的能力
TUYA_CALL_ERR_RETURN(tuya_iot_init_param(CFG_STORAGE_PATH, NULL));
// 此处为TuyaOS授权信息写入,仅在Linux Demo代码使用,因为正是产品是在生产测试的时候写入授权信息
WF_GW_PROD_INFO_S prod_info = {UUID, AUTHKEY, AP_SSID, AP_PASSWD};
TUYA_CALL_ERR_RETURN(tuya_iot_set_wf_gw_prod_info(&prod_info));
// 此处为设备初始化,实现了一些基础的回调功能,满足一个设备的基本能力需求,并注册获取连接状态回调函数
TY_IOT_CBS_S iot_cbs = {0};
iot_cbs.gw_status_cb = __soc_dev_status_changed_cb;
iot_cbs.gw_ug_cb = __soc_dev_rev_upgrade_info_cb;
iot_cbs.gw_reset_cb = __soc_dev_restart_req_cb;
iot_cbs.dev_obj_dp_cb = __soc_dev_obj_dp_cmd_cb;
iot_cbs.dev_raw_dp_cb = __soc_dev_raw_dp_cmd_cb;
iot_cbs.dev_dp_query_cb = __soc_dev_dp_query_cb; TUYA_CALL_ERR_RETURN(tuya_iot_wf_soc_dev_init_param(GWCM_SPCL_AUTOCFG,WF_START_AP_FIRST,&iot_cbs,NULL,PRODUCT_KEY,DEV_SW_VERSION));
TUYA_CALL_ERR_RETURN(tuya_iot_reg_get_nw_stat_cb_params(__soc_dev_net_status_cb, 1));
// 此处为设备初始化后的一些设备应用功能初始化,,代码每一个应用场景可能不同,按需实现
// TBD
// demo状态监控
while (1)
{
sleep(10);
// 监控设备状态,打印状态提示
ty_devos_monitor();
}
return 0;
}

激活绑定

激活绑定,是指涂鸦设备在建立网络连接之后,获取 token 并连接到涂鸦云的一个过程,一般用于自连网场景。例如有线设备、GPRS 设备、LTE 设备或者带屏幕的 Wi-Fi 设备(可以用户输入 Wi-Fissidpassword),不需要通过配网来联网。这些设备建立网络连接之后,需要到涂鸦 IoT 平台上执行绑定、激活操作,然后才能正常的使用。

激活绑定的功能是不要客户进行编码,TuyaOS直接提供了相关的能力。激活绑定目前包含以下几种方式。

方式 描述
局域网绑定 设备启动联网成功之后,建立局域网服务,并向周边广播设备信息,当APP自动发现发现到设备之后,通过APP想云端申请 Token,拿到 Token 之后,将其通过涂鸦局域网协议发送给设备,设备拿到 Token 之后,使用它去涂鸦 IoT 平台进行绑定、激活。
屏幕扫码绑定 屏幕扫码绑定是指,带屏幕设备,在启动之后,根据自身信息,生成二维码,并显示在设备屏幕上,通过APP扫描设备屏幕二维码,可以向涂鸦 IoT 平台发起激活绑定请求,涂鸦 IoT 平台则会根据二维码信息,向队员设备推送MQTT消息,消息里包含了 Token。设备拿到 Token 之后,使用它去涂鸦 IoT 平台进行绑定、激活。
机身扫码绑定 机身扫码绑定是指,设备在生产过程中,生成二维码,并张贴与设备机身,通过APP扫描机身二维码,可以向涂鸦 IoT 平台发起激活绑定请求,涂鸦 IoT 平台则会根据二维码信息,向队员设备推送MQTT消息,消息里包含了 Token。设备拿到 Token 之后,使用它去涂鸦 IoT 平台进行绑定、激活。
蓝牙绑定 设备通过发送蓝牙广播,通过APP发现之后,向云端申请 Token,然后通过蓝牙传输给设备,设备拿到 Token 之后,通过调用 Atop 接口去云端进行绑定、激活。

OTA

TuyaOS支持对 Powered by Tuya 设备的固件进行升级。在涂鸦IoT平台上创建的产品,可以上传固件、绑定升级通道 OTA channel,每一个通道对应着设备上的一个可以进行版本升级的固件或者是文件、配置,开发者通过对该固件、文件或配置进行配置升级,然后推送到设备上。设备在接收到升级通知的时候,会去涂鸦IoT平台上获取对应通道所绑定的升级版本固件、文件或者配置,在本地进行校验、写入更新,然后上报新的版本号,完成升级过程。

TuyaOS的升级操作主要分为以下几种类型:

升级类型 描述
静默升级 设备在启动之时以及每运行6小时,都会自动向IoT平台请求检测是否有静默升级任务。
如果存在静默升级任务,则进入升级流程进行升级;
如果不存在静默升级任务,则再等待6个小时重新检测。
静默升级中打开设备面板,会有进度框显示,并且无法操作设备。
提醒升级 用户手机APP首次打开设备面板时,会收到升级提醒弹框,可以选择升级或者不升级。
如果用户选择升级,则会推送升级任务到设备,设备并获取升级任务,进入升级流程,并上报升级进度。设备面板显示升级进度,并且无法操作设备。
检测升级 用户手机APP打开设备面板,点击点击右上角进入设备信息界面,检测设备固件版本,主动更新。
如果检测到有新的固件版本,则会推送升级任务到设备,设备并获取升级任务,进入升级流程,并上报升级进度。设备面板显示升级进度,并且无法操作设备。
强制升级 用户手机APP首次打开设备面板时,会收到升级提醒弹框,只有确定可以选择,无法取消。
用户选择升级,则会推送升级任务到设备,设备并获取升级任务,进入升级流程,并上报升级进度。设备面板显示升级进度,并且无法操作设备。

OTA Channel

OTA Channel 是 TuyaOS 对设备内部可以进行升级的固件、文件或者配置进行升级通道绑定的一种机制,主要分为两种,分别为系统 OTA channel 和扩展 OTA Channel

  • 系统 OTA channel:TuyaOS 使用的,固化的一些通道,用于指定类型的固件升级使用。
  • 扩展 OTA Channel:TuyaOS 定义的,开发者可以根据自己的实际需求进行规划使用。
名称 通道号 描述
DEV_NM_ATH_SNGL 0 系统 OTA Channel,主联网固件升级通道
DEV_BLE_SNGL 1 系统 OTA Channel,蓝牙固件升级通道
DEV_ZB_SNGL 3 系统 OTA Channel,ZigBee固件升级通道
DEV_NM_NOT_ATH_SNGL 9 系统 OTA Channel,MCU固件升级通道
DEV_ATTACH_MOD_x >=10 扩展 OTA Channel,开发者自定义的升级通道

在升级过程中,TuyaOS在获取到升级信息之后,会对升级通道进行一个检查。如果升级通道是主联网固件升级通道,即 DEV_NM_ATH_SNGL,TuyaOS会自行处理;如果升级通道是其他的系统 OTA Channel 或者是扩展的 OTA Channel,则会调用开发者在初始化时候提供的 TY_IOT_CBS_S 里的回调函数 gw_ug_cb。在这个回调函数里,开发者可以根据里面的 tp 来判断是什么样的 OTA Channel,然后进行区分处理。

一些客户对主联网固件的升级也有特殊的定制需求,可以通过一个接口 tuya_svc_upgrade_register_pre_cb 进行定制,只要注册一个 NULL 指针即可。

除了回调函数之外,OTA的其他功能不需要用于进行编码开发,TuyaOS直接提供相关的能力。

接口描述

启动升级

int tuya_svc_upgrade_start(CONST CHAR_T *dev_id,
CONST FW_UG_S *fw,
CONST GET_FILE_DATA_CB get_file_cb,
VOID *pri_data,
CONST UPGRADE_NOTIFY_CB upgrd_nofity_cb,
CONST BOOL_T upload_upgrade_percent,
CONST UINT_T download_buf_size);
OPERATE_RET tuya_svc_upgrade_start(CONST CHAR_T *dev_id, CONST FW_UG_S *fw, CONST GET_FILE_DATA_CB get_file_cb, VOID *pri_data, CONST UPGRADE_NOTIFY_CB upgrd_nofity_cb, CONST BOOL_T upload_upgrade_percent, CONST UINT_T download_buf_size)
Start to download the specific firmware

此接口用于在开发者提供的 gw_ug_cb 里启动OTA流程。接口里需要提供 get_file_cb,用于获取文件内容写入flash,upgrd_notify_cb 用于通知升级结束。

注册升级回调pre_cb

void tuya_svc_upgrade_register_pre_cb(dev_upgrade_pre_inform_cb pre_ug_cb);
VOID tuya_svc_upgrade_register_pre_cb(DEV_UPGRADE_PRE_INFORM_CB pre_ug_cb)
Register pre-precess handler to replace the default one

此接口用于注册升级回调之前的特殊操作,TuyaOS 内部默认有一个 pre_cb,在内部的pre_cb 里,TuyaOS 处理了主联网固件的升级逻辑。如果需要关闭此逻辑,把主联网固件的升级也放到开发者在初始化时候提供的回调函数 gw_ug_cb 里处理,可以通过 tuya_svc_upgrade_register_pre_cb(NULL) 来替换TuyaOS内部的默认 pre_cb

使用示例

// 通知升级完成
STATIC VOID __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);
if(download_result == 0) {
PR_DEBUG("SOC Upgrade File Download Success");
// UserTODO
exit(1);
}else {
PR_ERR("SOC Upgrade File Download Fail.ret = %d", download_result);
}
}
// 通知升级文件更新
STATIC OPERATE_RET __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)
{
FILE *p_upgrade_fd = (FILE *)pri_data;
fwrite(data, 1, len, p_upgrade_fd);
*remain_len = 0;
return OPRT_OK;
}
// cb回调,设备升级入口
VOID __soc_gw_ug_cb(IN CONST FW_UG_S *fw)
{
PR_DEBUG("SOC Rev Upgrade Info");
PR_DEBUG("fw->tp:%d", fw->tp);
PR_DEBUG("fw->fw_url:%s", fw->fw_url);
PR_DEBUG("fw->fw_hmac:%s", fw->fw_hmac);
PR_DEBUG("fw->sw_ver:%s", fw->sw_ver);
PR_DEBUG("fw->file_size:%u", fw->file_size);
FILE *p_upgrade_fd = fopen(g_devos_virual.ota_path, "w+b");
if(NULL == p_upgrade_fd){
PR_ERR("open upgrade file fail. upgrade fail %s", g_devos_virual.ota_path);
return;
}
OPERATE_RET op_ret = tuya_iot_upgrade_gw(fw, __get_file_data_cb, __upgrade_notify_cb, p_upgrade_fd);
if(OPRT_OK != op_ret) {
PR_ERR("tuya_iot_upgrade_gw err:%d",op_ret);
}
}
// 设备启动,注册__soc_gw_ug_cb之后,调用test_ota替换TuyaOS默认的pre_cb,然后发起OTA
VOID test_ota(VOID)
{
// 重置pre_cb
// 发起主联网固件ota,查看主联网固件升级打印
}

蓝牙

TuyaOS 支持蓝牙作为一个近场通信的功能,和 Powered by Tuya 设备进行配对、连接,建立蓝牙通道,并对设备进行蓝牙配网、蓝牙绑定激活、蓝牙控制、蓝牙校时、蓝牙遥控等功能。

蓝牙配网

未配网设备发送蓝牙广播,涂鸦智能APP在接收到蓝牙广播数据之后,会对未配网设备发起配对,配对完成之后,根据涂鸦蓝牙协议进行配网交互,涂鸦智能APP向云端申请 Token,并将 SSIDPasswordToken 发送给待配网设备。待配网设备拿到 SSIDPasswordToken 之后,连接 Wi-Fi 路由器,并连接涂鸦 IoT 平台,进行激活绑定。

蓝牙配网功能不需要客户进行编码,TuyaOS直接提供了相关的能力。

蓝牙绑定激活

未绑定单以及连接网络的设备发送蓝牙广播,涂鸦智能APP在接收到蓝牙广播数据之后,会对未绑定设备发起配对,配对完成之后,根据涂鸦蓝牙协议进行绑定交互,涂鸦智能APP向云端申请 Token,然后通过蓝牙将 Token 传输给设备,设备拿到 Token 之后,连接涂鸦IoT平台,进行激活绑定。

蓝牙绑定激活功能不需要客户进行编码,TuyaOS直接提供了相关的能力。

蓝牙控制

参考DP功能描述中的蓝牙相关部分

蓝牙校时

当蓝牙连接建立的时候,通过涂鸦蓝牙协议,对设备进行时间校准。

蓝牙校时功能不需要客户进行编码,TuyaOS直接提供了相关的能力。

蓝牙遥控

蓝牙遥控需要 Powered by Tuya 设备开启蓝牙扫描能力。设备在绑定激活的时候,如果支持蓝牙遥控器功能,设备从涂鸦IoT平台获取 app_keynetwork_key,用于蓝牙遥控协议数据的解密。

设备在接收到遥控器发送的蓝牙广播数据的时候,会使用 app_key 对数据进行解密、过滤,并将解密之后的数据发送给开发者,开发者根据蓝牙遥控器协议的定义,解析数据并执行相关的遥控操作。

接口描述

注册蓝牙遥控回调

//callback function for advertisement scanning data processing
typedef VOID (*TUYA_BLE_APP_SCAN_HANDLE)(UCHAR_T *data, UCHAR_T len, UCHAR_T type, UCHAR_T* mac);
/
* @brief Register callback function for advertisement scanning data processing
*
* @param[in] cb: callback function
*
* @note This API is used for registering callback function for advertisement scanning data processing
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tuya_ble_reg_app_scan_adv_cb(TUYA_BLE_APP_SCAN_HANDLE cb);

此接口用于注册蓝牙遥控功能的数据接收回调,其中 TUYA_BLE_APP_SCAN_HANDLE 为回调函数原型。注册该回调之后,设备会的确蓝牙扫描功能,并将接收到的蓝牙广播进行过滤,通过回调通知应用。

示例代码

STATIC VOID __bel_scan_handle(UCHAR_T *data, UCHAR_T len, UCHAR_T type, UCHAR_T* mac)
{
PR_DEBUG("recv ble remote cntl from %s", mac);
return;
}
// 在完成系统初始化之后调用此接口测试蓝牙遥控
VOID test_ble_remote_cntl()
{
tuya_ble_reg_app_scan_adv_cb(__bel_scan_handle);
}

局域网

局域网广播

有线、Wi-FiWi-Fi&BLE 产品,在连接上网络并检测到局域网连接可用的时候,会在局域网内进行设备的设备信息广播。广播是UDP报文,在局域网内部发送,目的IP为255.255.255.255,端口为6667。域网的广播包含以下信息:

内容 描述
ip 设备IP地址
gwId 如果设备已经激活,gwId=虚拟id;如果设备没有激活,gwId=uuid
active 2=已经激活,其他=没有激活
ability 默认为0,尚未使用
encrypt 默认为true,加密
version 局域网协议版本号
token 是否需要token,true=需要,false=不需要
wf_cfg 是否需要配置Wi-Fi,true=需要,false=不需要

此时如果APP和设备处于同一个局域网网络,就可以通过APP的自动发现,接收局域网内的设备广播包,解析广播包内容,识别设备类型,发现该设备。然后可以通过密钥交互,建立安全的局域网连接,然后基于涂鸦局域网协议就可以进行相关的协议交互。

默认情况下,TuyaOS 支持3个局域网连接,即可以通过三个不同的APP或者中控对设备进行控制。

局域网协议

涂鸦局域网定协议定义了 Powered by Tuya 设备和涂鸦智能APP之间进行局域网交互的报文格式和协议细节,这些业务逻辑不需要开发者关心,逻辑都是实现在 TuyaOS 内部。局域网协议主要包括局域网绑定激活、局域网控制等功能。

局域网绑定是指设备和涂鸦智能APP之间建立局域网连接之后,如果设备尚未激活,则可以通过局域网进行绑定激活。涂鸦智能APP基于涂鸦局域网协议,发送局域网协议报文,设备在收到报文之后进行协议解析、识别之后为用户绑定命令,对设备进行绑定激活,参照激活绑定局域网绑定。

局域网控制是指设备和涂鸦智能APP之间建立局域网连接之后,涂鸦智能APP基于涂鸦局域网协议,发送局域网协议报文,设备在收到报文之后进行协议解析、识别之后为局域网控制命令,对设备进行控制操作。局域网控制拥有最高的控制优先级,即当设备和涂鸦智能APP建立局域网连接,会优先走局域网通道进行控制,以保证设备的使用体验,并降低用户的流量成本。

局域网日志

参照Online Log "涂鸦Online Log 服务"服务。

局域网关闭

局域网可以按照开发者的需求打开和关闭。关闭局域网之后,不会再有局域网广播,也不会启动局域网服务。提高了设备的安全性。

局域网关闭只能在设备激活之后进行,在激活之前设置可能会无效。

接口描述

关闭局域网

OPERATE_RET lan_pro_cntl_disable(VOID);
#define lan_pro_cntl_disable()
lan protocol diable
Definition: app_agent.h:46

关闭局域网,停止发送局域网广播,关闭局域网服务。

打开局域网

OPERATE_RET lan_pro_cntl_enable(VOID);
#define lan_pro_cntl_enable()
lan protocol enable
Definition: app_agent.h:53

打开局域网,发送局域网广播,启动局域网服务,能够接受局域网连接请求,并提供涂鸦局域网协议定义的各项功能服务。

使用示例

void test_lan()
{
// 关闭局域网
TUYA_CALL_ERR_LOG(lan_pro_cntl_disable());
// 打开局域网
TUYA_CALL_ERR_LOG(lan_pro_cntl_enable());
}

DP功能

DP 点是涂鸦 IoT 平台对功能点的定义。开发者可以在涂鸦 IoT 平台创建产品,并根据开发者的需求,定义产品功能和功能点的特征,每一个功能点,就是一个 DP 点,其特征就是 DP 点相应的属性。产品的 DP 点的集合,是产品的 Schema。当设备在涂鸦 IoT 平台激活的时候,通过激活时候所携带的产品创建所得的 PID 信息,可以获得产品的 Schema 信息。TuyaOS 会根据 Schema 信息创建设备的能力列表,并在后续的日常运行过程中根据设备的能力,处理 DP 的上报和下发。

DP 点支持布尔型(bool)、数值型(value)、字符串型(string)、枚举型(enum)、故障型(bitmap),RAW 型数据,像定义 C 变量一样简单。其中布尔型(bool)、数值型(value)、字符串型(string)、枚举型(enum)、故障型(bitmap)又被称为 obj 型。使用 DP 点的时候应注意:

  • 目前建议每个产品最多创建 35 个 dp,复杂功能请用 RAW 型数据实现;
  • objDP 上报的时候,会对上报的数值进行过滤,相同则不予上报;
  • 如果想上报相同的 objDP,可以通过带 force 的接口上报,或者将 DP 点定义为统计类型(stat)

DP下发

TuyaOS 在初始化的时候,会需要开发者提供 TY_IOT_CBS_S,其中包括三个回调函数,用于处理 TuyaOS 里接受到的DP相关的命令,具体用途如下。开发者根据自己的实际功能需要,实现相应的回调,并在设备初始化的时候注册到 TuyaOS 。

回调名称 功能描述
dev_obj_dp_cb 结构化DP接受处理回调,结构化DP即DP有明确的类型定义
dev_raw_dp_cb raw型DP接受处理回调,raw型DP即二进制数据流
dev_dp_query_cb DP查询回调

通过向设备下发 DP 点可以对设备进行控制, TuyaOS 可以支持从蓝牙、局域网、涂鸦云 MQTT 三条通道来的 DP 信息,在 TuyaOS 里对收到的 DP 校验处理,然后调用相应的回调接口,告知应用层收到相应的DP命令,交于应用层对DP命令进行处理。

mermaid source code
graph LR
BLE
LAN
MQTT
subgraph device
Tuya_IoTOS
dev_obj_dp_cb
dev_raw_dp_cb
dev_dp_query_cb
user_dp_implement
end
BLE-->Tuya_IoTOS
LAN-->Tuya_IoTOS
MQTT-->Tuya_IoTOS
Tuya_IoTOS-->dev_obj_dp_cb
Tuya_IoTOS-->dev_raw_dp_cb
Tuya_IoTOS-->dev_dp_query_cb
dev_obj_dp_cb-->user_dp_implement
dev_raw_dp_cb-->user_dp_implement
dev_dp_query_cb-->user_dp_implement

DP上报

设备产生的状态、数据,以及 DP 下发操作执行结果可以上报,通过 TuyaOS 提供的 DP 上报接口,并在 TuyaOS 内部对上报的 DP 进行校验和流控,然后通过对应通道进行上报。

#mermaid source code
graph LR
BLE
LAN
MQTT
subgraph device
Tuya_IoTOS
dev_report_dp_json_async
dev_query_dp_json_async
dev_report_dp_raw_sync
dev_report_dp_stat_sync
user_dp_report
end
user_dp_report-->dev_report_dp_json_async
user_dp_report-->dev_query_dp_json_async
user_dp_report-->dev_report_dp_raw_sync
user_dp_report-->dev_report_dp_stat_sync
dev_report_dp_json_async-->Tuya_IoTOS
dev_query_dp_json_async-->Tuya_IoTOS
dev_report_dp_raw_sync-->Tuya_IoTOS
dev_report_dp_stat_sync-->Tuya_IoTOS
Tuya_IoTOS-->BLE
Tuya_IoTOS-->LAN
Tuya_IoTOS-->MQTT

DP通道

DP下发和上报支持蓝牙、局域网和涂鸦MQTT三个通道,三个通道同时处理会造成DP处理逻辑复杂化,并造成流量、功耗的增加,因此DP处理的通道存在一定的逻辑策略。

通道 策略
MQTT 上报:当MQTT通道在线的时候,永远会通过MQTT上报
下发:当MQTT通道在线,且LAN不在线,会通过MQTT通道下发;LAN在线,不会通过MQTT通道下发
LAN 上报:当LAN通道在线的时候,永远会通过LAN上报
下发:当LAN通道在线,只会通过LAN通道下发
BLE 上报:仅当MQTT或者LAN不在线的情况,会通过BLE上报
下发:仅当MQTT或者LAN不在线的情况,会通过BLE下发

接口描述

obj DP接收回调

typedef VOID (*DEV_OBJ_DP_CMD_CB)(IN CONST TY_RECV_OBJ_DP_S *dp);

RAW DP接收回调

typedef VOID (*DEV_RAW_DP_CMD_CB)(IN CONST TY_RECV_RAW_DP_S *dp);

DP查询请求回调

typedef VOID (*DEV_DP_QUERY_CB)(IN CONST TY_DP_QUERY_S *dp_qry);

上报DP

OPERATE_RET dev_report_dp_json_async(IN CONST CHAR_T *dev_id,IN CONST TY_OBJ_DP_S *dp_data,IN CONST UINT_T cnt);
Definition of structured dp
Definition: tuya_cloud_com_defs.h:445
OPERATE_RET dev_report_dp_json_async(IN CONST CHAR_T *dev_id, IN CONST TY_OBJ_DP_S *dp_data, IN CONST UINT_T cnt)
dev_report_dp_json_async @desc report dp info a-synced.

此接口用于上报在涂鸦 IoT 平台上定义为 obj 型类型的 DP 点,该接口会对需要上报的 DP 进行重复性校验,即如果 DP 点的值和上次上报的值一样,不会进行上报。该接口会对上报的数据进行缓存。此接口上报是异步上报,即上报发起即返回,不关心上报的结果。

强制上报DP

OPERATE_RET dev_report_dp_json_async_force(IN CONST CHAR_T *dev_id,IN CONST TY_OBJ_DP_S *dp_data,IN CONST UINT_T cnt);
OPERATE_RET dev_report_dp_json_async_force(IN CONST CHAR_T *dev_id, IN CONST TY_OBJ_DP_S *dp_data, IN CONST UINT_T cnt)
dev_report_dp_json_async_force @desc report dp info a-synced.

此接口用于上报在涂鸦 IoT 平台上定义为 obj 型类型的 DP 点,该接口不会对需要上报的 DP 进行重复性校验,即如果 DP 点的值和上次上报的值一样,也依然会进行上报。该接口会对上报的数据进行缓存。此接口上报是异步上报,即上报发起即返回,不关心上报的结果。

上报DP查询结果

OPERATE_RET dev_query_dp_json_async(IN CONST CHAR_T *dev_id,IN CONST TY_OBJ_DP_S *dp_data,IN CONST UINT_T cnt);
OPERATE_RET dev_query_dp_json_async(IN CONST CHAR_T *dev_id, IN CONST TY_OBJ_DP_S *dp_data, IN CONST UINT_T cnt)
dev_query_dp_json_async @desc report dp info a-synced.

此接口用于上报用户发起的 DP 查询命令的结果。该接口会对需要上报的 DP 进行重复性校验,即如果 DP 点的值和上次上报的值一样,不会进行上报。此接口上报是同步上报,即上报发起即返回,不关心上报的结果。上报 DP 查询结果的时候,会在 DP 上报的 Json 信息里新增一个字段 “type”:“query”,用于协助云端进行数据的过滤。

强制上报DP查询结果

OPERATE_RET dev_query_dp_json_async_force(IN CONST CHAR_T *dev_id,IN CONST TY_OBJ_DP_S *dp_data,IN CONST UINT_T cnt);
OPERATE_RET dev_query_dp_json_async_force(IN CONST CHAR_T *dev_id, IN CONST TY_OBJ_DP_S *dp_data, IN CONST UINT_T cnt)
dev_query_dp_json_async_force @desc report dp info a-synced.

此接口用于上报用户发起的 DP 查询命令的结果。该接口不会对需要上报的 DP 进行重复性校验,即如果 DP 点的值和上次上报的值一样,也依然会进行上报。此接口上报是同步上报,即上报发起即返回,不关心上报的结果。上报 DP 查询结果的时候,会在 DP 上报的 Json 信息里新增一个字段 “type”:“query”,用于协助云端进行数据的过滤。

上报RAW型DP信息

OPERATE_RET dev_report_dp_raw_sync_extend(IN CONST CHAR_T *dev_id,IN CONST BYTE_T dpid,\
IN CONST BYTE_T *data,IN CONST UINT_T len,\
IN CONST UINT_T timeout, IN CONST BOOL_T enable_auto_retrans);
#define dev_report_dp_raw_sync(dev_id, dpid, data, len, timeout) \
dev_report_dp_raw_sync_extend(dev_id, dpid, data, len, timeout, TRUE)
OPERATE_RET dev_report_dp_raw_sync_extend(IN CONST CHAR_T *dev_id, IN CONST BYTE_T dpid, IN CONST BYTE_T *data, IN CONST UINT_T len, IN CONST UINT_T timeout, IN CONST BOOL_T enable_auto_retrans)
dev_report_dp_raw_sync_extend @desc report dp raw info synced.

此接口用于上报在涂鸦 IoT 平台上定义为 RAW 型类型的 DP 点,该接口不会对需要上报的 DP 进行重复性校验,即如果 DP 点的值和上次上报的值一样,也依然会进行上报。该接口不会对上报的数据进行缓存,因为数据量一般会比较大。此接口上报是同步上报,即上报成功或者超时时间到了之后才会进行返回。

上报RAW型DP信息带时间字符串

OPERATE_RET dev_report_dp_raw_sync_extend_with_time(IN CONST CHAR_T *dev_id,IN CONST BYTE_T dpid,\
IN CONST BYTE_T *data,IN CONST UINT_T len,\
IN CONST UINT_T timeout, IN CONST BOOL_T enable_auto_retrans,\
IN CONST CHAR_T *time_str);
#define dev_report_dp_raw_sync_with_time(dev_id, dpid, data, len, timeout, time_str) \
dev_report_dp_raw_sync_extend_with_time(dev_id, dpid, data, len, timeout, TRUE, time_str)
OPERATE_RET dev_report_dp_raw_sync_extend_with_time(IN CONST CHAR_T *dev_id, IN CONST BYTE_T dpid, IN CONST BYTE_T *data, IN CONST UINT_T len, IN CONST UINT_T timeout, IN CONST BOOL_T enable_auto_retrans, IN CONST CHAR_T *time_str)
dev_report_dp_raw_sync_extend_with_time @desc report dp raw info synced.

此接口用于上报在涂鸦 IoT 平台上定义为 RAW 型类型的 DP 点,并带上DP发生的时间,该接口不会对需要上报的 DP 进行重复性校验,即如果 DP 点的值和上次上报的值一样,也依然会进行上报。该接口不会对上报的数据进行缓存,因为数据量一般会比较大。此接口上报是同步上报,即上报成功或者超时时间到了之后才会进行返回。

上报统计型DP
OPERATE_RET dev_report_dp_stat_sync_extend(IN CONST CHAR_T *dev_id,IN CONST TY_OBJ_DP_S *dp_data,\
IN CONST UINT_T cnt,IN CONST UINT_T timeout, IN CONST BOOL_T enable_auto_retrans);
#define dev_report_dp_stat_sync(dev_id, dp_data, cnt, timeout) \
dev_report_dp_stat_sync_extend(dev_id, dp_data, cnt, timeout, TRUE)
OPERATE_RET dev_report_dp_stat_sync_extend(IN CONST CHAR_T *dev_id, IN CONST TY_OBJ_DP_S *dp_data, IN CONST UINT_T cnt, IN CONST UINT_T timeout, IN CONST BOOL_T enable_auto_retrans)
dev_report_dp_stat_sync_extend @desc: report dp status info synced. if time_stamp==0,...

此接口用于上报在涂鸦 IoT 平台上定义为统计型类型的 DP 点,该接口不会对需要上报的 DP 进行重复性校验,即如果 DP 点的值和上次上报的值一样,也依然会进行上报。此接口上报是同步上报,即上报成功或者超时时间到了之后才会进行返回。

使用示例

// rcv obj dp
VOID test_DEV_OBJ_DP_CMD_CB(IN CONST TY_RECV_OBJ_DP_S *dp)
{
PR_DEBUG("SOC Rev DP Obj Cmd t1:%d t2:%d CNT:%u", dp->cmd_tp, dp->dtt_tp, dp->dps_cnt);
// 简单的把收到的DP上报上去,认为处理全部成功
OPERATE_RET rt = OPRT_OK;
TUYA_CALL_ERR_LOG(dev_report_dp_json_async(dp->cid,dp->dps,dp->dps_cnt));
return;
}
// rcv raw dp
VOID test_DEV_RAW_DP_CMD_CB(IN CONST TY_RECV_RAW_DP_S *dp)
{
PR_DEBUG("SOC Rev DP Raw Cmd t1:%d t2:%d dpid:%d len:%u", dp->cmd_tp, dp->dtt_tp, dp->dpid, dp->len);
if(dp->cid != NULL) PR_ERR("soc not have cid.%s", dp->cid);
// 简单的把收到的DP上报上去,认为处理全部成功
OPERATE_RET rt = OPRT_OK;
TUYA_CALL_ERR_LOG(dev_report_dp_raw_sync(dp->cid,dp->dpid,dp->data,dp->len,0));
return;
}
// report obj/stat DP
void test_objdp_report(void)
{
OPERATE_RET rt = OPRT_OK;
INT_T dp_idx = 0;
TY_OBJ_DP_S *dp_arr = (TY_OBJ_DP_S *)Malloc(2*SIZEOF(TY_OBJ_DP_S));
if(NULL == dp_arr) {
PR_ERR("malloc failed");
return OPRT_MALLOC_FAILED;
}
memset(dp_arr, 0, dp_cnt*SIZEOF(TY_OBJ_DP_S));
dp_arr[dp_idx].dpid = 1;
dp_arr[dp_idx].type = PROP_BOOL;
dp_arr[dp_idx].time_stamp = 0;
dp_arr[dp_idx].value.dp_bool = TRUE;
dp_idx ++;
dp_arr[dp_idx].dpid = get_channel_cddpid(ch_idx);
dp_arr[dp_idx].type = PROP_VALUE;
dp_arr[dp_idx].time_stamp = 0;
dp_arr[dp_idx].value.dp_value = 0;
dp_idx ++;
// 一次上报两个DP
TUYA_CALL_ERR_LOG(dev_report_dp_json_async(get_gw_cntl()->gw_if.id, dp_arr, 2));
TUYA_CALL_ERR_LOG(dev_report_dp_stat_sync(get_gw_cntl()->gw_if.id, dp_arr, 2, 3, NULL));
Free(dp_arr);
dp_arr = NULL;
return;
}
// report raw dp
#define BUFFER_LEN 10
void test_rawdp_report(void)
{
OPERATE_RET rt = OPRT_OK;
CHAR_T tmp_buf[BUFFER_LEN] = {0x0};
TUYA_CALL_ERR_LOG(dev_report_dp_raw_sync(NULL, 1, tmp_buf, BUFFER_LEN, 3));
return;
}
GW_CNTL_S * get_gw_cntl(VOID)
Get gateway cntl
DP_PROP_TP_E type
Definition: tuya_cloud_com_defs.h:449
BYTE_T dpid
Definition: tuya_cloud_com_defs.h:447
UINT_T time_stamp
Definition: tuya_cloud_com_defs.h:453
TY_OBJ_DP_VALUE_U value
Definition: tuya_cloud_com_defs.h:451
#define Free(ptr)
free memory
Definition: tal_memory.h:42
#define Malloc(req_size)
malloc memory
Definition: tal_memory.h:25

日志服务

TuyaOS提供按照模块、分级别的日志记录能力,开发者可以根据自己的需求,选择合适的日志级别进行日志记录输出,同时也可以设置默认输出的日志级别,控制只输出默认输出日志级别以上的日志。TuyaOS的日志模块,支持灵活的添加输出的出口,开发者可以根据自己的需求,将日志输出到串口、文件或者是网络上。

TuyaOS同时也支持在线日志的模式,开发者可以在涂鸦IoT平台上,通过设备命令,设置日志的级别和输出出口,如果输出出口是MQTT,并且网络正常,客户即可在涂鸦IoT平台上看到设备日志信息。

日志级别信息

#define TY_LOG_LEVEL_ERR 0 // error information
#define TY_LOG_LEVEL_WARN 1 // warnning information
#define TY_LOG_LEVEL_NOTICE 2 // notice information
#define TY_LOG_LEVEL_INFO 3 // information
#define TY_LOG_LEVEL_DEBUG 4 // debug information , should delete in release version
#define TY_LOG_LEVEL_TRACE 5 // trace information , should delete in release version

接口描述

日志输出宏

PR_ERR(fmt, ...) // error information
PR_WARN(fmt, ...) // warnning information
PR_NOTICE(fmt, ...) // notice information
PR_INFO(fmt, ...) // information
PR_DEBUG(fmt, ...) // debug information , should delete in release version
R_TRACE(fmt, ...) // trace information , should delete in release version

日志输出接口

// error information
int PrintErrLog(const char* pFile, const int line, const char* pFmt,...);
// warnning information
int PrintWarnLog(const char* pFile, const int line, const char* pFmt,...);
// notice information
int PrintNoticeLog(const char* pFile, const int line, const char* pFmt,...);
// information
int PrintInfoLog(const char* pFile, const int line, const char* pFmt,...);
// debug information , should delete in release version
int PrintDebugLog(const char* pFile, const int line, const char* pFmt,...);
// trace information , should delete in release version
int PrintTraceLog(const char* pFile, const int line, const char* pFmt,...);

此类接口开发者不需要关心,直接使用日志输出宏即可。

日志output函数原型

// prototype of log output function
typedef VOID (*LOG_OUTPUT)(IN CONST CHAR_T *str);

按照此原型定义新的日志输出,例如写入文件、输出到网络等。

添加日志output

OPERATE_RET AddOutputTerm(IN CONST CHAR_T *name,IN CONST LOG_OUTPUT term);

删除日志output

VOID DelOutputTerm(IN CONST CHAR_T *name);

设置日志输出级别

OPERATE_RET SetLogManageAttr(IN CONST LOG_LEVEL curLogLevel);

获取日志输出级别

OPERATE_RET GetLogManageAttr(OUT LOG_LEVEL *pCurLogLevel);

使用示例

VOID log_output_to_file(IN CONST CHAR_T *str)
{
uFILE * fp = NULL;
// open file
fp = ufopen("log", "w+");
if(NULL == fp) {
PR_ERR("log uf file can't open and write data!");
return ;
}
// write
uiWriteCnt = ufwrite(fp, str, strlen(str));
if(uiWriteCnt != strlen(str)) {
PR_ERR("log uf file write data error!");
return ;
}
// close
ufclose(fp);
return;
}
// 在完成系统初始化之后调用此接口测试内存分配
VOID test_logoutput()
{
SetLogManageAttr(TY_LOG_LEVEL_DEBUG);
PR_DEBUG("a debug log");
SetLogManageAttr(TY_LOG_LEVEL_NOTICE);
PR_DEBUG("another debug log");
PR_DEBUG("an error log");
// 新增一个新的output
AddOutputTerm("filelog", log_output_to_file);
PR_DEBUG("another debug log");
PR_DEBUG("another error log");
DelOutputTerm("filelog");
return;
}
VOID uFILE
unique file handle
Definition: tuya_uf_db.h:22
UINT_T ufwrite(IN uFILE *fd, IN CONST UCHAR_T *buf, IN CONST UINT_T len)
write to unique file
OPERATE_RET ufclose(IN uFILE *fd)
close a unique file
uFILE * ufopen(IN CONST CHAR_T *filepath, IN CONST CHAR_T *mode)
open a unique file

定时任务

TuyaOS提供定预约操作的能力,即定时任务能力。通过APP配置操作,并预约该操作的时间,操作的时间精度是分钟级别,可以选择当日,或者是每周、每天级别的循环。该预约操作,会作为一条定时任务,发送云端,并按照定时任务的类型决定是否会通知设备将定时任务信息下载到本地,作为本地定时任务,以确保设备在无网络的情况下都能够正常执行该预约任务。定时任务分为以下几种:

定时分类 描述
云端定时 云端定时数据会存储在云端,不会被下载到设备本地。云端会维护定时器,并在定时器老化之后,通过DP控制命令的方式,下发给设备,执行预约的操作。
本地定时 云端通过MQTT的形式通知设备,或者设备每20s进行查询是否有定时更新,如果存在定时更新,会将这些定时数据下载到本地,通过本地定时器进行定时检测,并执行预约操作。
在设备没有和云端、局域网、蓝牙进行时间同步之前,是不会执行本地定时任务的,因为时间可能不准确。
日出日落定时 日出日落定时是指通过当地的日出、日落的天文现象发生的时间,来预约一些操作行为。日出日落定时的逻辑和本地定时一样。

定时任务是不需要开发者进行编码的,TuyaOS和涂鸦IoT平台协同完成了这些功能。

在线日志

TuyaOS提供了日志重定向功能,通过通过涂鸦IoT平台,发送MQTT命令给 Powered by Tuya 设备,并告知重定向的位置和截止时间,然后就可以在对应的位置,获取到对应的设备实时的日志输出了。

日志重定向分为以下几种:

重定向位置 描述
MQTT 将设备日志重定向到涂鸦IoT平台的日志平台。
LAN 将设备日志重定向到局域网,在局域网 255.255.255.255 指定的端口进行 udp 广播
LOCAL 设置本地的日志级别,一般配合 MQTTLAN 一起操作。

在线日志不需要开发者进行编码工作,TuyaOS提供了相关的功能。

产测

TuyaOS提供了产测的协议和连接能力。产测就是生成测试、产品测试,可以对模组、PCBA进行测试,也可以对已经安装好了的成品进行测试,经过测试可以筛选出问题产品,同时也可以进行一些产品的烧录、授权、基本配置等工作。产测的目的是进行批量、规模化、自动化的生成和测试,是产品量产的关键步骤。

TuyaOS支持统涂鸦产测协议,并提供了进入产测的通道机制,主要分为三者途径:

进入产测方式 描述
串口 TuyaOS在启动的时候,通过 mf_init 接口启动产测任务,并等待串口进入产测命令,如果在500ms之后没有进入产测,则退出产测任务。 mf_init 接口需要开发者提供一些产测回调函数,用于处理一些产测命令,具体如下图所示。<br>gpio_test:涂鸦内部使用,开发者不需要关心,提供空的实现即可。<br>user_enter_mf_callback:进入产测回调,通知开发者已经进入产测,需要停止一些操作,防止影响产测。<br>user_callback:涂鸦内部使用,开发者不需要关心,提供空的实现即可。<br>mf_user_product_test:成品产测命令,特殊的需要开发者实现的产测命令会通过此回调函数告知开发者进行一些的测试操作。<br>user_pre_gpio_testgpio 测试前回调,通知开发者进行一些准备动作,比如对 gpio 进行一些设置。
扫描信标 TuyaOS在按照Wi-Fi模式,扫描指定产测信标,如果发现对应的信标,则进入产测模式,调用开发者通过 app_cfg_set 接口设置的回调函数。

接口描述

GPIO测试回调

extern VOID_T mf_user_pre_gpio_test_cb(VOID_T);

进入产测回调

extern VOID_T mf_user_enter_callback(VOID_T);

配置写入回调

extern VOID_T mf_user_callback(VOID_T);

成品产测回调

extern OPERATE_RET mf_user_product_test_cb(USHORT_T cmd,UCHAR_T *data, UINT_T len, OUT UCHAR_T ret_data,OUT USHORT_T *ret_len);

设置信标产测回调

VOID app_cfg_set(IN CONST GW_WF_CFG_MTHD_SEL mthd, APP_PROD_CB callback)
VOID_T(* APP_PROD_CB)(BOOL_T flag, SCHAR_T rssi)
callback for app product test
Definition: mf_test.h:145

设置信标产测信标

void set_prod_ssid(CHAR_T *ssid)

使用示例

// 进入信标产测回调
VOID test_mf_test_cb(BOOL_T flag, SCHAR_T rssi)
{
PR_DEBUG("enter mf test");
// TBD...
return;
}
// 在初始化tuya_iot_init_param调用此接口
VOID test_mf_test()
{
app_cfg_set(GWCM_OLD, test_mf_test_cb);
set_prod_ssid("tuya_mdev_test2");
return;
}

KV DB功能

物联网平台碎片化严重,各种各样的芯片和操作系统,资源、能力差异非常大。但是物联网设备的存储需求又有非常高的安全性,因为涉及到用户的隐私问题;要有非常高的可靠性,因为客户的场景不可控,必须在各种极端的情况下保证用户设备的可用性;还要有性能的要求,保证用户的使用体验。TuyaOS提供了KV DB支持在文件系统、Flash上实现一套基于Key-Value存储的加密数据库,屏蔽了底层介质、操作系统的差异,为开发者提供了安全、可靠、高效的数据存储服务。

接口描述

KV DB写入
OPERATE_RET wd_common_write(IN CONST CHAR_T *key, IN CONST BYTE_T *value, IN CONST UINT_T len);
OPERATE_RET wd_common_write(IN CONST CHAR_T *key, IN CONST BYTE_T *value, IN CONST UINT_T len)
tuya key-value database write entry

KV DB读取

OPERATE_RET wd_common_read(IN CONST CHAR_T *key, OUT BYTE_T value, OUT UINT_T *p_len);
OPERATE_RET wd_common_read(IN CONST CHAR_T *key, OUT BYTE_T **value, OUT UINT_T *p_len)
tuya key-value database read entry

KV DB模糊读取

OPERATE_RET wd_common_fuzzy_read(IN CONST CHAR_T *fuzzy_name, INOUT UINT_T *index, OUT BYTE_T data, OUT UINT_T *len);
OPERATE_RET wd_common_fuzzy_read(IN CONST CHAR_T *fuzzy_name, INOUT UINT_T *index, OUT BYTE_T **data, OUT UINT_T *len)
tuya key-value database fuzzy read entry

KV DB模糊读取数据释放

OPERATE_RET wd_common_free_data(IN BYTE_T *data);
OPERATE_RET wd_common_free_data(IN BYTE_T *data)
free the buffer which allocated by wd_common_read or wd_common_fuzzy_read

KV DB删除数据

OPERATE_RET wd_common_delete(IN CONST CHAR_T *key);
OPERATE_RET wd_common_delete(IN CONST CHAR_T *key)
delete the entry from key-value database

KV DB模糊删除数据

OPERATE_RET wd_common_fuzzy_delete(IN CONST CHAR_T *key);
OPERATE_RET wd_common_fuzzy_delete(IN CONST CHAR_T *key)
fuzzy delete the entry from key-value database

KV DB写入用户自定义数据

OPERATE_RET wd_user_param_write(IN CONST BYTE_T *data, IN CONST UINT_T len);
OPERATE_RET wd_user_param_write(IN CONST BYTE_T *data, IN CONST UINT_T len)
write the user parameter to tuya key-value database

KV DB读取用户自定义数据

OPERATE_RET wd_user_param_read(OUT BYTE_T buf, OUT UINT_T *len);
OPERATE_RET wd_user_param_read(OUT BYTE_T **buf, OUT UINT_T *len)
read the user parameter from tuya key-value database

示例代码

sample_kv()
{
int rt = OPRT_OK;
rt = wd_common_write("ddi-xxx", "hello-world-x", 13);
EXPECT_EQ(rt, OPRT_OK);
BYTE_T *value = NULL;
UINT_T len = 0;
rt = wd_common_read("ddi-xxx", &value, &len);
EXPECT_EQ(rt, OPRT_OK);
EXPECT_EQ(memcmp(value, "hello-world-x", 13), 0);
value = NULL;
len = 0;
rt = wd_common_write("ddi-yyy", "hello-world-y", 13);
EXPECT_EQ(rt, OPRT_OK);
rt = wd_common_write("ddi-xxx", "hello-world-z", 13);
EXPECT_EQ(rt, OPRT_OK);
UINT index = 0;
while(rt == OPRT_OK) {
rt = wd_common_fuzzy_read("ddi-", &index, &value, &len);
if(value) {
value = NULL;
}
}
rt = wd_common_delete("ddi-xxx");
EXPECT_EQ(rt, OPRT_OK);
rt = wd_common_fuzzy_delete("ddi-");
EXPECT_EQ(rt, OPRT_OK);
return OPRT_OK;
}
VOID ws_db_uninit(VOID)
tuya key-value database de-initialization

文件系统

TuyaOS提供了一套文件系统接口,基本上是按照POSIX文件系统标准接口来对外提供文件系统服务。这一套文件系统不仅能够基于Flash来进行文件操作,也可以适配Linux平台的POSIX文件系统接口,同时也可以支持各个芯片平台自己实现的文件系统。

接口描述

打开文件

uFILE* ufopen(IN CONST CHAR_T *filepath, IN CONST CHAR_T *mode);

关闭文件

OPERATE_RET ufclose(IN uFILE *fd);

读文件

UINT_T ufread(IN uFILE *fd, IN UCHAR_T *buf, IN CONST UINT_T len);
UINT_T ufread(IN uFILE *fd, IN UCHAR_T *buf, IN CONST UINT_T len)
read from unique file

写文件

UINT_T ufwrite(IN uFILE *fd, IN CONST UCHAR_T *buf, IN CONST UINT_T len);

删除文件

OPERATE_RET ufdelete(IN CONST CHAR_T *filepath);
OPERATE_RET ufdelete(IN CONST CHAR_T *filepath)
delete the unique file

使用示例

// test file write&read
VOID test_fs_write()
{
uFILE * fp = NULL;
// open file
fp = ufopen("oem_cfg", "w+");
if(NULL == fp) {
PR_ERR("oem cfg uf file can't open and write data!");
return ;
}
// write
uiWriteCnt = ufwrite(fp, pData, usLen);
if(uiWriteCnt != usLen) {
PR_ERR("oem cfg uf file write data error!");
return ;
}
// read
uiReadCnt = ufread(fp, pData, 1024);
PR_DEBUG("oem cfg uf file read data %d!", uiReadCnt);
PR_DEBUG("oem cfg uf file %s", pData);
*pLen = uiReadCnt;
// close
opRet = ufclose(fp);
if(opRet != OPRT_OK) {
PR_ERR("oem cfg uf file close error!");
return ;
}
return;
}

定时器

TuyaOS提供定时器,支持毫秒级的定时功能,开发者可以创建、启动、定制、删除定时器。定时器有两种类型,一种是 TIMER_ONCE,即仅执行一次,执行完成之后不再执行,等待开发者删除;另一种是 TIMER_CYCLE,会循环执行,一直到开发者停止或者删除这个定时器。

通过定时器和消息队列的结合,可以实现提定时消息处理功能。通过创建一个定时消息,并注册定时消息的回调函数,实现异步的定时消息触发、传递、串行处理能力。

接口描述

创建定时器

OPERATE_RET sys_add_timer(IN CONST P_TIMER_FUNC pTimerFunc,\
IN CONST PVOID_T pTimerArg,\
OUT TIMER_ID *p_timerID);

删除定时器

OPERATE_RET sys_delete_timer(IN CONST TIMER_ID timerID);

启动定时器

OPERATE_RET sys_start_timer(IN CONST TIMER_ID timerID,\
IN CONST TIME_MS timeCycle,\
IN CONST TIMER_TYPE timer_type);
```c
### 停止定时器
```c
OPERATE_RET sys_stop_timer(IN CONST TIMER_ID timerID);
TIMER_TYPE
the type of timer
Definition: tal_sw_timer.h:25

使用示例

STATIC VOID_T dev_monitor_cb(VOID_T)
{
PR_DEBUG("sys timer timeout");
return;
}
// 在完成系统初始化之后调用此接口测试timer
VOID test_sys_timer(VOID)
{
OPERATE_RET rt = OPRT_OK;
TIMER_ID m_dev_monitor_timer;
//添加设备检测定时器
TUYA_CALL_ERR_LOG(sys_add_timer(dev_monitor_cb, NULL, &m_dev_monitor_timer));
//启动10s周期定时器
TUYA_CALL_ERR_LOG(sys_start_timer(m_dev_monitor_timer, 10000, TIMER_CYCLE));
tuya_hal_sleep(20*1000);
//启动10s周期定时器
TUYA_CALL_ERR_LOG(sys_stop_timer(m_dev_monitor_timer));
//启动10s周期定时器
TUYA_CALL_ERR_LOG(sys_start_timer(m_dev_monitor_timer, 10000, TIMER_CYCLE));
tuya_hal_sleep(20*1000);
//启动10s周期定时器
TUYA_CALL_ERR_LOG(sys_stop_timer(m_dev_monitor_timer));
TUYA_CALL_ERR_LOG(sys_delete_timer(m_dev_monitor_timer));
return;
}

消息队列

TuyaOS 消息队列提供通用的消息处理功能,通过创建一个消息,并注册消息的回调函数,然后发送对应的消息ID的形式,实现异步的消息传递、串行处理能力。TuyaOS内部维护了一个叫cmmod的任务,所有的消息都是在此任务里处理,因此避免在消息回调里处理复杂的逻辑,否则会阻塞、影响其他消息的效率;避免在回调里采用过于深、复杂的逻辑,否则可能会导致堆栈溢出。

定时消息

定时消息是基于消息队列和定时器,提供了通用的定时消息处理功能。通过创建一个定时消息,并注册定时消息的回调函数,实现异步的定时消息触发、传递、串行处理能力。定时消息的逻辑是创建一个定时器,这个定时器的回调函数是一个消息发送函数;同时创建一个消息,消息的回调函数是定时消息的回调函数。当定时器老化的时候,通过触发定时器的回调,发送消息,并在消息队列列处理对应的消息,从而实现定时的消息处理机制。

接口描述

消息注册

OPERATE_RET cmmod_reg_msgcb(IN CONST MSG_CALLBACK msg_cb, OUT MSG_ID *msg_id);
OPERATE_RET cmmod_reg_msgcb(IN CONST MSG_CALLBACK msg_cb, OUT MSG_ID *msg_id)
Regist callback function for message.

此接口用于创建一个消息并注册回调。接口里会在消息池里选择一个没有被使用的消息ID(判断消息是否被使用的标准是回调函数是否为NULL),并注册回调到此消息ID。

此接口在消息数量满了的时候,会重新分配内存,保证消息可以被正常注册。

移除消息注册

OPERATE_RET cmmod_unreg_msgcb(IN CONST MSG_ID msgID);
OPERATE_RET cmmod_unreg_msgcb(IN CONST MSG_ID msgID)
Unregist callback function for message.

此接口用于创建一个消息并注册回调。接口里会将改消息ID对应的回调设置为 NULL,下次 cmmod_reg_msgcb 接口调用即可以使用该消息。

发送消息

OPERATE_RET cmmod_post_msg(IN CONST MSG_ID msgID,IN CONST P_MSG_DATA pMsgData, IN CONST MSG_DATA_LEN msgDataLen);
OPERATE_RET cmmod_post_msg(IN CONST MSG_ID msgID, IN CONST P_MSG_DATA pMsgData, IN CONST MSG_DATA_LEN msgDataLen)
Post a message.

发送紧急消息

OPERATE_RET cmmod_post_instancy_msg(IN CONST MSG_ID msgID,IN CONST P_MSG_DATA pMsgData, IN CONST MSG_DATA_LEN msgDataLen);
OPERATE_RET cmmod_post_instancy_msg(IN CONST MSG_ID msgID, IN CONST P_MSG_DATA pMsgData, IN CONST MSG_DATA_LEN msgDataLen)
Post a message instantly

创建定时消息

OPERATE_RET cmmod_cr_tm_msg_hand(IN CONST TM_MSG_CB cb,IN CONST VOID *data,OUT TM_MSG_S tm_msg);
OPERATE_RET cmmod_cr_tm_msg_hand(IN CONST TM_MSG_CB cb, IN CONST VOID *data, OUT TM_MSG_S **tm_msg)
Create timer message handle.
timer message definition
Definition: uni_msg_queue.h:54

启动定时消息

OPERATE_RET cmmod_start_tm_msg(IN CONST TM_MSG_S *tm_msg,IN CONST TIME_MS timeCycle, IN CONST TIMER_TYPE timer_type);
OPERATE_RET cmmod_start_tm_msg(IN CONST TM_MSG_S *tm_msg, IN CONST TIME_MS timeCycle, IN CONST TIMER_TYPE timer_type)
Start the timer message.

停止定时消息

OPERATE_RET cmmod_stop_tm_msg(IN CONST TM_MSG_S *tm_msg);
OPERATE_RET cmmod_stop_tm_msg(IN CONST TM_MSG_S *tm_msg)
Stop the timer message.

释放定时消息节点

OPERATE_RET cmmod_release_tm_msg(IN CONST TM_MSG_S *tm_msg);
OPERATE_RET cmmod_release_tm_msg(IN CONST TM_MSG_S *tm_msg)
Release the timer message.

示例代码

// 定义消息处理函数
STATIC VOID __msg_cb(MESSAGE *msg){
...// 用户自行定义
}
// 设备初始化
STATIC OPERATE_RET test_msg_queue(VOID)
{
// 注册一个消息
MSG_ID msg_queue_id;;
op_ret = cmmod_reg_msgcb(__msg_cb, &msg_queue_id);
// post一个消息
cmmod_post_msg(msg_queue_id, NULL, 0);
return OPRT_OK;
}
示例2(定时器消息):
STATIC VOID __tm_msg_cb(MESSAGE *msg){
...// 用户自行定义
}
STATIC OPERATE_RET test_tm_msg_queue()
{
// 创建定时器消息处理
TM_MSG_S* tmm_gw_if_sync = NULL;
op_ret = cmmod_cr_tm_msg_hand(__tm_msg_cb, NULL, &tmm_gw_if_sync);
//启动定时器,定时器超时,即向消息队列添加消息节点,1秒执行一次。
op_ret = cmmod_start_tm_msg(gw_cntl.tmm_gw_if_sync, 1000, TIMER_ONCE);
return OPRT_OK;
}
Init param of message
Definition: uni_msg_queue.h:39

工作队列

TuyaOS 提供通用的异步任务调度机制,将工作任务发送到指定的工作队列,工作队列会串行的执行队列里的工作任务。资源丰富的平台可以根据自身的需求,创建自己的工作队列,并通过工作队列调度一些工作任务。

接口描述

创建工作队列

OPERATE_RET work_queue_create(OUT WORK_QUEUE_HANDLE *phand,\
IN CONST STACK_SIZE stack_size,\
IN CONST TRD_PRI pri,\
IN CONST UINT_T work_num);

添加工作到工作队列

OPERATE_RET work_queue_add(IN CONST WORK_QUEUE_HANDLE hand,IN CONST WK_CALLBACK cb,\
IN CONST VOID *data);

示例代码

#define TY_WQ_STACK_SIZE 2048
#define TY_WQ_TRD_PRIO 3
#define TY_WQ_WORK_NUM 1
VOID sample_work_cb(VOID *data)
{
PR_DEBUG("work is here %p", data);
}
OPERATE_RET test_work_queue()
{
int rt = OPRT_OK;
// 初始化工作队列
WORK_QUEUE_HANDLE ty_wq_hand = NULL;
rt = work_queue_create(&ty_wq_hand, TY_WQ_STACK_SIZE, TY_WQ_TRD_PRIO, TY_WQ_WORK_NUM);
EXPECT_EQ(rt, OPRT_OK);
// 添加待执行任务
rt = work_queue_add(ty_wq_hand, sample_work_cb, NULL);
EXPECT_EQ(rt, OPRT_OK);
return OPRT_OK;
}

事件

TuyaOS Event是一个轻量级的事件通知库。其特点是:

  • 支持订阅-发布模式的事件机制,使用非常简单方便,可以在任意位置、任意时间进行时间的订阅;
  • 轻量级,专注于进程内部的事件通知,代码小巧,仅400行。
  • 跨平台,基于TuyaOS的跨平台特性,可以在任意平台上运行。
  • 有弹性,默认情况下,事件是同步调用,基于TuyaOS的跨平台特性,可以在订阅函数里使用Work Queue或者Timer Queue实现多线程的异步机制。

接口描述

发布事件

int ty_publish_event(const char* name, void *data);
int ty_publish_event(const char *name, void *data)
: publish event

发布一个事件通知,包含了事件的数据,告知所有订阅者进行处理。事件发布首先会去搜索是否当前事件是否已经创建。

  • 没有被创建:说明之前并没有发布过相同的事件,但是可能会存在订阅者,由于他们没有找到事件,会被暂存到 free_subscribe_root。因此需要从 free_subscribe_root 里查找是否有该事件的订阅者,如果有则把这些订阅者从 free_subscribe_root 里拿出来,挂载到事件的 subscribe_root,然后再进行事件的发布。
  • 已经被创建:说之前已经有发布过相同的事件,所有的订阅者都已经被正常处理,不需要再关心是否有订阅者在 free_subscribe_root 中,可以直接进行事件发布。
  • 事件发布,遍历事件 subscribe_root,对于每个订阅者,发布事件的数据,调用订阅者的回调函数,判断并记录回调函数的返回值。
    • 如果不存在订阅者,意味着不需要发布,也不需要创建事件的资源

订阅事件

int ty_subscribe_event(const char *name, const char *desc, const event_subscribe_cb cb, SUBSCRIBE_TYPE_E type);
int(* event_subscribe_cb)(void *data)
event subscribe callback function type
Definition: base_event.h:60
BYTE_T SUBSCRIBE_TYPE_E
subscriber type
Definition: base_event.h:41
int ty_subscribe_event(const char *name, const char *desc, const event_subscribe_cb cb, SUBSCRIBE_TYPE_E type)
: subscribe event

关注一个事件,包含了事件的名称、关注的用途以及处理数据的回调函数。事件订阅首先会去搜索是否当前事件是否已经创建。

  • 没有被创建:说明此事件没有产生过,需要把订阅者暂存到 free_subscribe_root
  • 已经被创建:说明此时间已经产生过,可以直接把订阅者挂载到事件的 subscribe_root

订阅事件不会拿到事件的上一次状态,一个原因是如果暂存数据会消耗较大的资源,另外一个原因是没有必要。

取消订阅

int ty_unsubscribe_event(const char *name, const char *desc, event_subscribe_cb cb);
int ty_unsubscribe_event(const char *name, const char *desc, event_subscribe_cb cb)
: unsubscribe event

取消关注一个事件,包含了事件的名称、关注的用途以及处理数据的回调函数。如果当前订阅者没有绑定事件,则从 free_subscribe_root 移除;

如果订阅者绑定了事件,且为最后一个订阅者,则需要从事件的 subscribe_root 移除,并销毁该事件。否则,仅从事件的 subscribe_root 中移除订阅者。

示例代码

#define EVENT_SAMPLE "publish.sample"
OPERATE_RET sample_subcribe_cb(event_data_t *raw_data)
{
event_data_t *data = (event_data_t*)raw_data;
PR_DEBUG("recv event");
return OPRT_OK;
}
OPERATE_RET sample_subcribe_emergence_cb(event_data_t *raw_data)
{
event_data_t *data = (event_data_t*)raw_data;
PR_DEBUG("recv event emergence");
return OPRT_OK;
}
OPERATE_RET sample_subcribe_onetime_cb(event_data_t *raw_data)
{
event_data_t *data = (event_data_t*)raw_data;
PR_DEBUG("recv event emergence");
return OPRT_OK;
}
OPERATE_RET sample_event()
{
OPERATE_RET rt = OPRT_OK;
// 发布事件,事件没有订阅者,不会创建
rt = ty_publish_event(EVENT_SAMPLE, NULL);
EXPECT_EQ(rt, OPRT_OK);
// 订阅事件
char desc[] = "subscribe.sample";
rt = ty_subscribe_event(EVENT_SAMPLE, desc, sample_subcribe_cb, EVENT_TYPE_NORMAL);
EXPECT_EQ(rt, OPRT_OK);
// 发布事件
rt = ty_publish_event(EVENT_SAMPLE, NULL);
EXPECT_EQ(rt, OPRT_OK);
// 紧急订阅事件
rt = ty_subscribe_event(EVENT_SAMPLE, desc, sample_subcribe_emergence_cb, SUBSCRIBE_TYPE_EMERGENCY);
EXPECT_EQ(rt, OPRT_OK);
// 取消紧急订阅
rt = ty_unsubscribe_event(EVENT_SAMPLE, desc, sample_subcribe_emergence_cb);
EXPECT_EQ(rt, OPRT_OK);
// 订阅事件一次性事件,一次性事件不需要手动去掉订阅
rt = ty_subscribe_event(EVENT_SAMPLE, desc, sample_subcribe_onetime_cb, EVENT_TYPE_ONETIME);
EXPECT_EQ(rt, OPRT_OK);
// 发布事件
rt = ty_publish_event(EVENT_SAMPLE, NULL);
EXPECT_EQ(rt, OPRT_OK);
// 取消订阅,当事件没有订阅者,会自动销毁
rt = ty_unsubscribe_event(EVENT_SAMPLE, desc, sample_subcribe_cb);
EXPECT_EQ(rt, OPRT_OK);
return OPRT_OK;
}

内存管理

TuyaOS提供了内存管理接口,开发者可以分配、释放内存。

接口描述

申请内存

VOID *Malloc(IN SIZE_T reqSize);

释放内存

VOID Free(IN PVOID_T pReqMem);

使用示例

// 在完成系统初始化之后调用此接口测试内存分配
VOID test_memory()
{
CHAR_T* p = (CHAR_T*)Malloc(100);
if (!p) {
PR_ERR("null point");
return;
}
Free(p);
p = NULL;
}

驱动框架

DRIVER

typedef enum {
TUYA_DRV_UART,
TUYA_DRV_PWM,
TUYA_DRV_TIMER,
TUYA_DRV_ADC,
TUYA_DRV_I2C,
} tuya_drv_type_t;

驱动查找

void *tuya_driver_find(uint8_t type, uint8_t port);
参数 描述
type tuya_drv_type_t 枚举中的值
port 端口号,例如TUYA_UART0,TUYA_UART1
返回 ——
!= NULL 查找成功,返回其对应的句柄
== NULL 查找失败

PIN

PIN接口列表

函数名称 功能描述
int tuya_pin_init(tuya_pin_name_t pin, tuya_pin_mode_t mode); 初始化引脚模式
int tuya_pin_write(tuya_pin_name_t pin, tuya_pin_level_t level); 设置引脚的电平
int tuya_pin_read(tuya_pin_name_t pin); 读取引脚的电平
int tuya_pin_irq_init(tuya_pin_name_t pin, tuya_pin_mode_t irq_mode, tuya_pin_irq_cb cb, void *arg); 初始化引脚中断
int tuya_pin_irq_enable(tuya_pin_name_t pin); 使能引脚中断
int tuya_pin_irq_disable(tuya_pin_name_t pin); 禁能引脚中断
int tuya_pin_control(tuya_pin_name_t pin, uint8_t cmd, void *arg); 引脚相关控制

PIN 访问名字

typedef enum {
TUYA_PINS_NAME(TUYA_PA),
TUYA_PINS_NAME(TUYA_PB),
TUYA_PINS_NAME(TUYA_PC),
TUYA_PINS_NAME(TUYA_PD),
TUYA_PINS_NAME(TUYA_PE),
} tuya_pin_name_t;

PIN 工作模式

typedef enum {
TUYA_PIN_MODE_IN_PU,
TUYA_PIN_MODE_IN_PD,
TUYA_PIN_MODE_IN_FL,
TUYA_PIN_MODE_IN_IRQ_RISE,
TUYA_PIN_MODE_IN_IRQ_FALL,
TUYA_PIN_MODE_IN_IRQ_RISE_FALL,
TUYA_PIN_MODE_IN_IRQ_LOW,
TUYA_PIN_MODE_IN_IRQ_HIGH,
TUYA_PIN_MODE_OUT_PP_LOW,
TUYA_PIN_MODE_OUT_PP_HIGH,
TUYA_PIN_MODE_OUT_PP_PU_LOW,
TUYA_PIN_MODE_OUT_PP_PU_HIGH,
TUYA_PIN_MODE_OUT_PP_PD_LOW,
TUYA_PIN_MODE_OUT_PP_PD_HIGH,
TUYA_PIN_MODE_OUT_OD_LOW,
TUYA_PIN_MODE_OUT_OD_HIZ,
TUYA_PIN_MODE_OUT_OD_PU_LOW,
TUYA_PIN_MODE_OUT_OD_PU_HIGH,
} tuya_pin_mode_t;

PIN 电平状态

typedef enum {
TUYA_PIN_LOW = 0,
TUYA_PIN_HIGH
} tuya_pin_level_t;

PIN接口描述

初始化引脚模式

int tuya_pin_init(tuya_pin_name_t pin, tuya_pin_mode_t mode);
参数 描述
pin tuya_pin_name_t枚举中的值
mode tuya_pin_mode_t枚举中的值
返回 ——
OPRT_OK 初始化成功
其他错误码 执行失败

设置引脚电平

int tuya_pin_write(tuya_pin_name_t pin, tuya_pin_level_t level);
参数 描述
pin tuya_pin_name_t枚举中的值
level tuya_pin_level_t枚举中的值
返回 ——
OPRT_OK 初始化成功
其他错误码 执行失败

读取引脚电平

int tuya_pin_read(tuya_pin_name_t pin)
参数 描述
pin tuya_pin_name_t枚举中的值
返回 ——
TUYA_PIN_LOW 低电平
TUYA_PIN_HIGH 高电平

初始化引脚中断模式

int tuya_pin_irq_init(tuya_pin_name_t pin, tuya_pin_mode_t irq_mode, tuya_pin_irq_cb cb, void *arg);
参数 描述
pin tuya_pin_name_t枚举中的值
irq_mode tuya_pin_mode_t枚举中的值
cb 注册的回调函数,参见如下函数原形
arg 注册的参数
返回 ——
OPRT_OK 初始化成功
其他错误码 执行失败

注册函数tuya_pin_irq_cb原形

typedef void (*tuya_pin_irq_cb)(void *args);

使能引脚中断

int tuya_pin_irq_enable(tuya_pin_name_t pin);
参数 描述
pin tuya_pin_name_t枚举中的值
返回 ——
OPRT_OK 初始化成功
其他错误码 执行失败

禁能引脚中断

int tuya_pin_irq_disable(tuya_pin_name_t pin);
参数 描述
pin tuya_pin_name_t枚举中的值
返回 ——
OPRT_OK 初始化成功
其他错误码 执行失败

PIN使用示例

void tuya_pa5_irq_cb(void *arg)
{
PR_NOTICE("TUYA_PA5 irq trigger");
tuya_pin_irq_disable(TUYA_PA5);
}
void tuya_pa12_irq_cb(void *arg)
{
PR_NOTICE("TUYA_PA12 irq trigger");
PR_NOTICE("TUYA_PA5 irq enable");
tuya_pin_irq_enable(TUYA_PA5);
}
void tuya_pin_test(void)
{
STATIC UINT8_T tick = 0;
STATIC UINT8_T stat = TUYA_PIN_HIGH;
tuya_pin_init(TUYA_PA22, TUYA_PIN_MODE_OUT_PP_HIGH);
tuya_pin_init(TUYA_PA19, TUYA_PIN_MODE_OUT_PP_LOW);
tuya_pin_init(TUYA_PA14, TUYA_PIN_MODE_IN_PU);
tuya_pin_irq_init(TUYA_PA5, TUYA_PIN_MODE_IN_IRQ_FALL, tuya_pa5_irq_cb, NULL);
tuya_pin_irq_disable(TUYA_PA5);
tuya_pin_irq_init(TUYA_PA12, TUYA_PIN_MODE_IN_IRQ_FALL, tuya_pa12_irq_cb, TUYA_PA5);
while (1) {
if (++tick >= 3) {
tick = 0;
stat = stat ? TUYA_PIN_LOW : TUYA_PIN_HIGH;
tuya_pin_write(TUYA_PA22, stat);
}
if (tuya_pin_read(TUYA_PA14)) {
tuya_pin_write(TUYA_PA19, TUYA_PIN_HIGH);
} else {
tuya_pin_write(TUYA_PA19, TUYA_PIN_LOW);
}
}
}
#define tuya_hal_system_sleep(msTime)
System sleep
Definition: tuya_hal_system.h:55

UART

uart接口列表

函数名称 功能描述
int tuya_uart_init(tuya_uart_t *uart); 串口初始化
int tuya_uart_read(tuya_uart_t *uart, void *data, uint16_t len); 串口发送数据
int tuya_uart_write(tuya_uart_t *uart, void *data, uint16_t len); 串口读数据
int tuya_uart_control(tuya_uart_t *uart, uint8_t cmd, void *arg); 串口控制
int tuya_uart_deinit(tuya_uart_t *uart); 串口反初始化

串口访问端口

typedef enum {
TUYA_UART0 = 0x00,
TUYA_UART1,
TUYA_UART2,
TUYA_UART3,
} tuya_uart_port_t;

串口配置参数

typedef enum {
TUYA_UART_BAUDRATE_300 = 300,
TUYA_UART_BAUDRATE_600 = 600,
TUYA_UART_BAUDRATE_1200 = 1200,
TUYA_UART_BAUDRATE_2400 = 2400,
TUYA_UART_BAUDRATE_4800 = 4800,
TUYA_UART_BAUDRATE_9600 = 9600,
TUYA_UART_BAUDRATE_19200 = 19200,
TUYA_UART_BAUDRATE_38400 = 38400,
TUYA_UART_BAUDRATE_57600 = 57600,
TUYA_UART_BAUDRATE_74880 = 74880,
TUYA_UART_BAUDRATE_115200 = 115200,
TUYA_UART_BAUDRATE_230400 = 230400,
TUYA_UART_BAUDRATE_460800 = 460800,
TUYA_UART_BAUDRATE_921600 = 921600,
TUYA_UART_BAUDRATE_1500000 = 1500000,
TUYA_UART_BAUDRATE_1843200 = 1843200,
TUYA_UART_BAUDRATE_3686400 = 3686400,
} tuya_uart_baudrate_t;
typedef enum {
TUYA_UART_DATA_BIT5 = 0x05,
TUYA_UART_DATA_BIT6 = 0x06,
TUYA_UART_DATA_BIT7 = 0x07,
TUYA_UART_DATA_BIT8 = 0x08,
} tuya_uart_databits_t;
typedef enum {
TUYA_UART_STOP_BIT1 = 0x01,
TUYA_UART_STOP_BIT1_5 = 0x02,
TUYA_UART_STOP_BIT2 = 0x03,
} tuya_uart_stopbits_t;
typedef enum {
TUYA_UART_PARITY_NONE = 0,
TUYA_UART_PARITY_ODD = 1,
TUYA_UART_PARITY_EVEN = 2,
} tuya_uart_parity_t;

串口简化式配置宏

#define TUYA_UART_8N1_CFG(__UART, __BAUDRATE, __BUFSZ, __FLAG) \
(__UART)->cfg.baudrate = __BAUDRATE; \
(__UART)->cfg.flag = TUYA_DRV_INT_RX_FLAG | __FLAG; \
(__UART)->cfg.bufsz = __BUFSZ; \
(__UART)->cfg.databits = TUYA_UART_DATA_BIT8; \
(__UART)->cfg.stopbits = TUYA_UART_STOP_BIT1; \
(__UART)->cfg.parity = TUYA_UART_PARITY_NONE

串口结构体

struct tuya_uart {
tuya_drv_node_t node;
tuya_uart_cfg_t cfg;
tuya_uart_cb_t cb;
tuya_uart_ops_t *ops;
void *rxfifo;
};
typedef struct tuya_uart tuya_uart_t;

串口配置结构体

typedef struct {
uint16_t flag;
uint16_t bufsz;
tuya_uart_baudrate_t baudrate;
tuya_uart_databits_t databits;
tuya_uart_stopbits_t stopbits;
tuya_uart_parity_t parity;
} tuya_uart_cfg_t;

串口回调结构体

typedef struct {
int (*tx_finish) (tuya_uart_t *uart, void *buffer);
int (*rx_notify) (tuya_uart_t *uart, uint16_t size);
} tuya_uart_cb_t;

串口接口描述

串口初始化函数

int tuya_uart_init(tuya_uart_t *uart);
参数 描述
uart 串口句柄,需要初始化相关参数
返回 ——
OPRT_OK 初始化成功
其他错误码 执行失败

串口数据发送函数

int tuya_uart_write(tuya_uart_t *uart, void *data, uint16_t len);
参数 描述
uart 串口句柄
data 要发送数据的指针
len 要发送的数据字节长度
返回 ——
实际发送的数据长度

串口数据接收函数

int tuya_uart_read(tuya_uart_t *uart, void *data, uint16_t len);
参数 描述
uart 串口句柄
data 要接收数据的指针
len 要接收的数据字节长度
返回 ——
实际读取数据的长度

串口控制函数

int tuya_uart_control(tuya_uart_t *uart, uint8_t cmd, void *arg);
参数 描述
uart 串口句柄
cmd 命令控制字,可取值:TUYA_DRV_CONFIG_CMD
arg 控制的参数,类型: tuya_uart_cfg_t
返回 ——
OPRT_OK 控制成功
其他错误码 控制失败

串口反初始化函数

int tuya_uart_deinit(tuya_uart_t *uart);
参数 描述
uart 串口句柄
返回 ——
OPRT_OK 关闭成功
其他错误码 关闭失败

串口使用示例

#include "tuya_uart.h"
#define UART_BUFSZ 5
void tuya_uart_test(void)
{
int result;
uint8_t byte, size;
uint8_t buffer[UART_BUFSZ + 1];
tuya_uart_t *uart = (tuya_uart_t *) tuya_driver_find(TUYA_DRV_UART, TUYA_UART1);
if (NULL == uart) {
PR_DEBUG("tuya uart find failed");
}
TUYA_UART_8N1_CFG(uart, 115200, 256, 0);
tuya_uart_init(uart);
uint8_t data[] = "uart change baudrate start\r\n";
tuya_uart_write(uart, data, strlen(data));
for (;;) {
int len = tuya_uart_read(uart, &byte, 1);
PR_DEBUG_RAW("%c", byte);
tuya_uart_write(uart, &byte, 1);
if ('q' == byte) {
break;
}
}
TUYA_UART_8N1_CFG_INIT(&uart_cfg, 1500000, 256, 0);
result = tuya_uart_control(uart, TUYA_DRV_CONFIG_CMD, &uart_cfg);
if (OPRT_OK != result) {
PR_ERR("uart cfg failed", result);
}
uint8_t str[] = "uart change baudrate complete\r\n";
tuya_uart_write(uart, str, strlen(str));
for (;;) {
int len = tuya_uart_read(uart, &byte, 1);
PR_DEBUG_RAW("%c", byte);
tuya_uart_write(uart, &byte, 1);
if ('q' == byte) {
break;
}
}
}
Common process - driver uart

硬件TIMER

定时器接口列表

函数名称 功能描述
int tuya_timer_init(tuya_timer_t *timer); 定时器初始化
int tuya_timer_start(tuya_timer_t *timer, uint32_t us); 启动定时器
int tuya_timer_stop(tuya_timer_t *timer); 停止定时器
int tuya_timer_deinit(tuya_timer_t *timer); 定时器关闭

定时器访问端口

typedef enum {
TUYA_TIMER0 = 0,
TUYA_TIMER1,
TUYA_TIMER2,
TUYA_TIMER3,
} tuya_timer_port_t;

定时器配置参数

typedef enum {
TUYA_TIMER_MODE_ONCE = 0,
TUYA_TIMER_MODE_PERIOD
} tuya_timer_mode_t;
typedef struct {
tuya_timer_mode_t mode;
tuya_timer_isr_cb cb;
void *arg;
} tuya_timer_cfg_t;

定时器回调

typedef void (*tuya_timer_isr_cb)(void *args);

定时器简化配置宏

#define TUYA_TIMER_CFG(__TIMER, __MODE, __CB, __ARG) \
(__TIMER)->cfg.mode = __MODE; \
(__TIMER)->cfg.cb = __CB; \
(__TIMER)->cfg.arg = __ARG

定时器结构体

struct tuya_timer {
tuya_drv_node_t node;
tuya_timer_cfg_t cfg;
tuya_timer_ops_t *ops;
};
typedef struct tuya_timer tuya_timer_t;

定时器接口描述

定时器初始化函数

int tuya_timer_init(tuya_timer_t *timer);
参数 描述
timer 定时器句柄,需要初始化定时器参数
返回 ——
OPRT_OK 初始化成功
其他错误码 执行失败

定时器启动函数

int tuya_timer_start(tuya_timer_t *timer, uint32_t us);
参数 描述
timer 定时器句柄
us 超时时间,单位微秒
返回 ——
OPRT_OK 启动成功
其他错误码 启动失败

定时器停止函数

int tuya_timer_stop(tuya_timer_t *timer);
参数 描述
timer 定时器句柄
返回 ——
OPRT_OK 停止成功
其他错误码 停止失败

定时器关闭函数

int tuya_timer_deinit(tuya_timer_t *timer);
参数 描述
timer 定时器句柄
返回 ——
OPRT_OK 关闭成功
其他错误码

定时器使用示例

#include "tuya_pin.h"
#include "tuya_timer.h"
static tuya_timer_t *timer;
static uint8_t led_blink = 1;
void tuya_timer0_cb(void *arg)
{
static uint32_t tick;
uint8_t *led = (uint8_t *)arg;
if (tick++ >= 200) {
tick = 0;
// *led = !*led;
led_blink = !led_blink;
tuya_pin_write(TUYA_PA22, led_blink);
}
}
void tuya_timer_test(void)
{
led_blink = TUYA_PIN_HIGH;
tuya_pin_init(TUYA_PA22, TUYA_PIN_MODE_OUT_PP_HIGH);
timer = (tuya_timer_t *)tuya_driver_find(TUYA_DRV_TIMER, TUYA_TIMER0);
TUYA_TIMER_CFG(timer, TUYA_TIMER_MODE_PERIOD, tuya_timer0_cb, &led_blink);
tuya_timer_init(timer);
tuya_timer_start(timer, 1000);
uint8_t duration = 0;
while (1) {
duration++;
if (duration == 3) {
tuya_timer_stop(timer);
tuya_timer_start(timer, 3000);
} else if (duration == 6) {
tuya_timer_stop(timer);
tuya_timer_start(timer, 1000);
duration = 0;
}
}
}
Common process - Initialization

PWM

pwm接口列表

函数名称 功能描述
int tuya_pwm_init(tuya_pwm_t *pwm); 初始化PWM
int tuya_pwm_start(tuya_pwm_t *pwm); 启动PWM
int tuya_pwm_stop(tuya_pwm_t *pwm); 停止PWM
int tuya_pwm_set(tuya_pwm_t *pwm, float frequency, float percent); 设置PWM频率和占空比
int tuya_pwm_frequency_set (tuya_pwm_t *pwm, float frequency); 设置PWM频率
int tuya_pwm_duty_set(tuya_pwm_t *pwm, float percent); 设置PWM占空比
int tuya_pwm_polarity_set (tuya_pwm_t *pwm, tuya_pwm_polarity_t polarity); 设置PWM初始极性
int tuya_pwm_deinit(tuya_pwm_t *pwm); 关闭PWM

PWM访问端口

typedef enum {
TUYA_PWM0 = 0x00,
TUYA_PWM1,
TUYA_PWM2,
TUYA_PWM3,
TUYA_PWM4,
TUYA_PWM5,
TUYA_PWM6,
TUYA_PWM7,
TUYA_PWM8,
} tuya_pwm_port_t;

PWM配置结构体

typedef struct {
uint8_t pin;
uint8_t polarity;
uint32_t period_ns;
uint32_t pulse_ns;
float percent;
} tuya_pwm_cfg_t;
typedef enum {
TUYA_PWM_POSITIVE = 0,
TUYA_PWM_NEGATIVE,
} tuya_pwm_polarity_t;

PWM简化配置宏

#define TUYA_PWM_CFG(__PWM, __PIN, __FREQUENCY, __PERCENT) \
(__PWM)->cfg.pin = __PIN; \
(__PWM)->cfg.period_ns = (uint32_t)1000000000 / (__FREQUENCY); \
(__PWM)->cfg.percent = __PERCENT; \
(__PWM)->cfg.pulse_ns = (uint32_t)((__PWM)->cfg.period_ns * (__PERCENT)); \
(__PWM)->cfg.polarity = TUYA_PWM_POSITIVE

PWM结构体

struct tuya_pwm {
tuya_drv_node_t node;
tuya_pwm_cfg_t cfg;
tuya_pwm_ops_t *ops;
};
typedef struct tuya_pwm tuya_pwm_t;

PWM接口描述

PWM初始化函数

int tuya_pwm_init(tuya_pwm_t *pwm);
参数 描述
pwm PWM句柄,需要初始化PWM参数
返回 ——
OPRT_OK 初始化成功
其他错误码 初始化失败

PWM启动函数

int tuya_pwm_start(tuya_pwm_t *pwm);
参数 描述
pwm PWM句柄
返回 ——
OPRT_OK 启动成功
其他错误码 启动失败

PWM停止函数

int tuya_pwm_stop(tuya_pwm_t *pwm);
参数 描述
pwm PWM句柄
返回 ——
OPRT_OK 停止成功
其他错误码 停止失败

PWM频率和占空比设置函数

int tuya_pwm_set(tuya_pwm_t *pwm, float frequency, float percent);
参数 描述
pwm PWM句柄
frequency PWM频率
percent PWM占空比,0-1
返回 ——
OPRT_OK 执行成功
其他错误码 执行失败

PWM周期设置函数

int tuya_pwm_frequency_set(tuya_pwm_t *pwm, float frequency);
参数 描述
pwm PWM句柄
frequency PWM频率
返回 ——
OPRT_OK 执行成功
其他错误码 执行失败

PWM脉宽设置函数

int tuya_pwm_duty_set(tuya_pwm_t *pwm, float percent);
参数 描述
pwm PWM句柄
percent PWM占空比,0-1
返回 ——
OPRT_OK 执行成功
其他错误码 执行失败

PWM极性设置函数

int tuya_pwm_polarity_set(tuya_pwm_t *pwm, tuya_pwm_polarity_t polarity);
参数 描述
pwm PWM句柄
polarity PWM初始极性,@tuya_pwm_polarity_t中的枚举值
返回 ——
OPRT_OK 执行成功
其他错误码 执行失败

PWM关闭函数

INT_T ty_pwm_deinit(TY_PWM_DEV_S *pwm);
参数 描述
pwm PWM句柄
返回 ——
OPRT_OK 执行成功
其他错误码 执行失败

PWM使用示例

#include "tuya_pin.h"
#include "tuya_pwm.h"
static tuya_pwm_t *pwm[5];
void tuya_pwm_test(void)
{
UINT8_T pwm_pin[] = {
TUYA_PA14, TUYA_PA15, TUYA_PA0, TUYA_PA5, TUYA_PA12
};
#if 1
int i;
for (i = 0; i < 5; i++) {
pwm[i] = (tuya_pwm_t *)tuya_driver_find(TUYA_DRV_PWM, (tuya_pwm_port_t)i);
TUYA_PWM_CFG(pwm[i], pwm_pin[i], 10 * 1000, 0.1);
tuya_pwm_init(pwm[i]);
tuya_pwm_start(pwm[i]);
}
#else
pwm[0] = tuya_driver_find(TUYA_DRV_PWM, TUYA_PWM0);
TUYA_PWM_CFG(pwm[0], TUYA_PA14, 10 * 1000, 0.1);
tuya_pwm_init(pwm[0]);
tuya_pwm_start(pwm[0]);
pwm[1] = tuya_driver_find(TUYA_DRV_PWM, TUYA_PWM1);
TUYA_PWM_CFG(pwm[1], TUYA_PA15, 10 * 1000, 0.1);
tuya_pwm_init(pwm[1]);
tuya_pwm_start(pwm[1]);
pwm[2] = tuya_driver_find(TUYA_DRV_PWM, TUYA_PWM2);
TUYA_PWM_CFG(pwm[2], TUYA_PA0, 10 * 1000, 0.1);
tuya_pwm_init(pwm[2]);
tuya_pwm_start(pwm[2]);
pwm[3] = tuya_driver_find(TUYA_DRV_PWM, TUYA_PWM3);
TUYA_PWM_CFG(pwm[3], TUYA_PA5, 10 * 1000, 0.1);
tuya_pwm_init(pwm[3]);
tuya_pwm_start(pwm[3]);
pwm[4] = tuya_driver_find(TUYA_DRV_PWM, TUYA_PWM4);
TUYA_PWM_CFG(pwm[4], TUYA_PA12, 10 * 1000, 0.1);
tuya_pwm_init(pwm[4]);
tuya_pwm_start(pwm[4]);
#endif
PR_DEBUG("ns %d us %d", pwm[0]->cfg.period_ns, pwm[0]->cfg.period_ns / 1000);
uint8_t flag = 1;
float percent = TUYA_PWM_PERCENT(pwm[0]);
while (1) {
if (flag) {
if (percent < 1.0) {
percent += 0.005;
} else {
flag = 0;
continue;
}
} else {
if (percent > 0.005) {
percent -= 0.005;
} else {
flag = 1;
continue;
}
}
PR_NOTICE("percent = %d", (int)(percent * 1000));
for (UINT8_T i = 0; i < 5; i ++) {
tuya_pwm_duty_set(pwm[i], percent);
}
}
}

I2C

I2C接口列表

函数名称 功能描述
int tuya_i2c_init(tuya_i2c_t *i2c); I2C初始化
int tuya_sw_i2c_register(tuya_i2c_port_t port, tuya_i2c_sw_cfg_t *cfg); 注册软件I2C初始化
int tuya_i2c_control(tuya_i2c_t *i2c, uint8_t cmd, void *arg); I2C自定义数据发送
int tuya_i2c_deinit(tuya_i2c_t *i2c); I2C关闭
int tuya_i2c_master_send(tuya_i2c_t *i2c, uint16_t addr, uint16_t flags, void *buf, uint16_t count); I2C主机发送函数
int tuya_i2c_master_recv(tuya_i2c_t *i2c, uint16_t addr, uint16_t flags, void *buf, uint16_t count); I2C主机接收函数

I2C访问端口

typedef enum {
TUYA_I2C0 = 0,
TUYA_I2C1,
TUYA_I2C2,
TUYA_I2C3,
} tuya_i2c_port_t;

I2C配置参数

#define TUYA_I2C_WR 0x0000
#define TUYA_I2C_RD (1 << 0)
#define TUYA_I2C_ADDR_10BIT (1 << 2)
#define TUYA_I2C_NO_START (1 << 4)
#define TUYA_I2C_IGNORE_NACK (1 << 5)
#define TUYA_I2C_NO_READ_ACK (1 << 6)
#define TUYA_I2C_NO_WRITE_ACK (1 << 7)
typedef enum {
TUYA_I2C_MODE_MASTER = 0,
TUYA_I2C_MODE_SLAVE,
} tuya_i2c_mode_t;

I2C配置结构体

typedef struct {
uint8_t sda_pin;
uint8_t scl_pin;
uint32_t delay_count;
uint32_t timeout;
} tuya_i2c_sw_cfg_t;
typedef struct {
uint8_t mode;
uint16_t dev_addr;
} tuya_i2c_cfg_t;
typedef struct {
uint16_t addr;
uint16_t flags;
uint16_t len;
uint8_t *buf;
} tuya_i2c_msg_t;

I2C简化配置宏

#define TUYA_I2C_MASTER_CFG(__I2C, __ADDR) \
(__I2C)->cfg.dev_addr = __ADDR; \
(__I2C)->cfg.mode = TUYA_I2C_MODE_MASTER \
#define TUYA_I2C_SW_CFG_INIT(__CFG, __SCL, __SDA, __DELAY) \
(__CFG)->scl_pin = __SCL; \
(__CFG)->sda_pin = __SDA; \
(__CFG)->delay_count = __DELAY; \
(__CFG)->timeout = 100

I2C结构体

struct tuya_i2c {
tuya_drv_node_t node;
tuya_i2c_cfg_t cfg;
tuya_i2c_ops_t *ops;
};
typedef struct tuya_i2c tuya_i2c_t;

I2C接口描述

软件I2C注册函数

int tuya_sw_i2c_register(tuya_i2c_port_t port, tuya_i2c_sw_cfg_t *cfg);
参数 描述
port 需要注册的端口号,@tuya_i2c_port_t
cfg 需要设置的配置, @tuya_i2c_sw_cfg_t
返回 ——
OPRT_OK 执行成功
其他错误码 执行失败

I2C初始化函数

int tuya_i2c_init(tuya_i2c_t *i2c);
参数 描述
i2c I2C句柄(需要查找)
返回 ——
OPRT_OK 执行成功
其他错误码 执行失败

I2C主机数据发送函数

int tuya_i2c_master_send(tuya_i2c_t *i2c, uint16_t addr, uint16_t flags, void *buf, uint16_t count);
参数 描述
i2c I2C句柄
addr 设备地址
flags I2C标志
buf 数据发送缓冲区指针
count 数据长度
返回 ——
OPRT_OK 执行成功
其他错误码 执行失败

I2C主机数据接收函数

int tuya_i2c_master_recv(tuya_i2c_t *i2c, uint16_t addr, uint16_t flags, void *buf, uint16_t count);
参数 描述
i2c I2C句柄
addr 设备地址
flags I2C标志
buf 数据接收缓冲区指针
count 数据长度
返回 ——
OPRT_OK 执行成功
其他错误码 执行失败

I2C关闭函数

int tuya_i2c_deinit(tuya_i2c_t *i2c);
参数 描述
i2c I2C句柄
返回 ——
OPRT_OK 执行成功
其他错误码 执行失败

软件I2C使用示例

/*============================ INCLUDES ======================================*/
#include "tuya_pin.h"
#include "tuya_i2c.h"
/*============================ TYPES =========================================*/
typedef struct {
tuya_i2c_t *i2c;
} sm2135e_dev_t;
typedef struct {
tuya_i2c_t *i2c;
} sm16726b_dev_t;
/*============================ PROTOTYPES ====================================*/
void sm2135_init(sm2135e_dev_t *dev);
void sm2135_rgb_set(sm2135e_dev_t *dev, uint8_t r, uint8_t g, uint8_t b);
void sm2135_cw_set(sm2135e_dev_t *dev, uint8_t c, uint8_t w);
void sm16726b_init(sm16726b_dev_t *dev);
void sm2135_test(void);
void sm16726b_test(void);
/*============================ IMPLEMENTATION ================================*/
static sm2135e_dev_t sm2135e_dev;
static sm2135e_dev_t sm16726b_dev;
void tuya_sw_i2c_test(void)
{
tuya_i2c_sw_cfg_t sw_cfg;
TUYA_I2C_SW_CFG_INIT(&sw_cfg, TUYA_PA14, TUYA_PA15, 1);
tuya_sw_i2c_register(TUYA_I2C0, &sw_cfg);
TUYA_I2C_SW_CFG_INIT(&sw_cfg, TUYA_PA19, TUYA_PA22, 1);
tuya_sw_i2c_register(TUYA_I2C1, &sw_cfg);
sm2135_init(&sm2135e_dev);
sm16726b_init(&sm16726b_dev);
while(1) {
sm16726b_test();
sm2135_test();
}
}
void sm16726b_init(sm16726b_dev_t *dev)
{
uint8_t buffer[2];
dev->i2c = tuya_driver_find(TUYA_DRV_I2C, TUYA_I2C1);
TUYA_I2C_MASTER_CFG(dev->i2c, 0x00);
tuya_i2c_init(dev->i2c);
}
void sm16726b_rgb_set(sm16726b_dev_t *dev, uint8_t r, uint8_t g, uint8_t b)
{
uint8_t buffer[7 + 3 + 1];
memset(buffer, 0, 6);
buffer[6] = 0x01;
buffer[7] = r;
buffer[8] = g;
buffer[9] = b;
buffer[10] = 0xFF;
tuya_i2c_master_send(dev->i2c, 0x00, TUYA_I2C_NO_START | TUYA_I2C_NO_WRITE_ACK, buffer, 11);
}
void sm16726b_test(void)
{
sm16726b_rgb_set(&sm16726b_dev, 255, 0, 0);
sm16726b_rgb_set(&sm16726b_dev, 0, 255, 0);
sm16726b_rgb_set(&sm16726b_dev, 0, 0, 255);
sm16726b_rgb_set(&sm16726b_dev, 0, 0, 0);
// tuya_hal_system_sleep(500);
}
void sm2135_init(sm2135e_dev_t *dev)
{
uint8_t buffer[2];
dev->i2c = tuya_driver_find(TUYA_DRV_I2C, TUYA_I2C0);
TUYA_I2C_MASTER_CFG(dev->i2c, 0x00);
tuya_i2c_init(dev->i2c);
buffer[0] = 0xC0;
buffer[1] = 0x20 | 0x04;
tuya_i2c_master_send(dev->i2c, 0x00, TUYA_I2C_NO_START | TUYA_I2C_NO_WRITE_ACK, buffer, 2);
}
void sm2135_rgb_set(sm2135e_dev_t *dev, uint8_t r, uint8_t g, uint8_t b)
{
uint8_t buffer[5];
buffer[0] = 0xC1;
buffer[1] = 0;
buffer[2] = r;
buffer[3] = g;
buffer[4] = b;
tuya_i2c_master_send(dev->i2c, 0x00, TUYA_I2C_NO_START | TUYA_I2C_IGNORE_NACK, buffer, 5);
}
void sm2135_cw_set(sm2135e_dev_t *dev, uint8_t c, uint8_t w)
{
uint8_t buffer[3];
buffer[0] = 0xC1;
buffer[1] = 0x80;
tuya_i2c_master_send(dev->i2c, 0x00, TUYA_I2C_NO_START | TUYA_I2C_IGNORE_NACK, buffer, 2);
buffer[0] = 0xC5;
buffer[1] = c;
buffer[2] = w;
tuya_i2c_master_send(dev->i2c, 0x00, TUYA_I2C_NO_START | TUYA_I2C_IGNORE_NACK, buffer, 3);
}
void sm2135_test(void)
{
sm2135_rgb_set(&sm2135e_dev, 255, 0, 0);
sm2135_rgb_set(&sm2135e_dev, 0, 255, 0);
sm2135_rgb_set(&sm2135e_dev, 0, 0, 255);
// sm2135_rgb_set(&sm2135e_dev, 0, 0, 0);
sm2135_cw_set(&sm2135e_dev, 255, 0);
sm2135_cw_set(&sm2135e_dev, 0, 255);
sm2135_cw_set(&sm2135e_dev, 0, 0);
// tuya_hal_system_sleep(500);
}

HAL

由于不同产品对应开发平台不同,有Linux系统的,RTOS系统的,等等,由于底层操作系统的接口不尽相同,当某个开发平台的底层接口变化时,必将牵其他开发平台的接口变动。为了解耦,TuyaOS HAL设计了一套注册机制,提供统一对外的操作系统和驱动接口,供TuyaOS 各组件和开发者应用代码使用。

不同开发平台通过注册机制向TuyaOS HAL各自的接口实现。TuyaOS HAL以此屏蔽各系统差异,彻底成为独立、统一的OS平台,给上层提供稳定的接口定义,同时也为下层开发环境提高可扩展性和灵活性。

接口描述

线程

线程接口主要有:

//创建线程
int tuya_hal_thread_create(THREAD_HANDLE* thread,
const char* name,
uint32_t stack_size,
uint32_t priority,
THREAD_FUNC_T func,
void* const arg);
//释放线程
int tuya_hal_thread_release(THREAD_HANDLE thread);
//判断线程是否是自己
int tuya_hal_thread_is_self(THREAD_HANDLE thread, BOOL_T* is_self);
//获取线程水线(获取任务剩余栈空间)
int tuya_hal_thread_get_watermark(THREAD_HANDLE thread, UINT_T* watermark);
//设置线程名字
int tuya_hal_thread_set_self_name(const char* name);

信号量

信号量接口主要有:

//创建并初始化信号量
INT_T tuya_hal_semaphore_create_init(SEM_HANDLE *pHandle, CONST UINT_T semCnt,、
CONST UINT_T sem_max);
//信号量等待
INT_T tuya_hal_semaphore_wait(CONST SEM_HANDLE semHandle);
//信号量超时等待
INT_T tuya_hal_semaphore_waittimeout(CONST SEM_HANDLE semHandle, unsigned int timeout);
//信号量递送
INT_T tuya_hal_semaphore_post(CONST SEM_HANDLE semHandle);
//释放信号量
INT_T tuya_hal_semaphore_release(CONST SEM_HANDLE semHandle);

互斥量

互斥量接口主要有:

//用于创建并初始化tuya mutex
OPERATE_RET tuya_hal_mutex_create_init(MUTEX_HANDLE *pMutexHandle);
//用于lock tuya mutex
OPERATE_RET tuya_hal_mutex_lock(const MUTEX_HANDLE mutexHandle);
//用于unlock tuya mutex
OPERATE_RET tuya_hal_mutex_unlock(const MUTEX_HANDLE mutexHandle);
//e用于释放tuya mutex
OPERATE_RET tuya_hal_mutex_release(const MUTEX_HANDLE mutexHandle);

Wi-Fi管理

wifi接口主要有:

//wifi ap扫描
OPERATE_RET tuya_hal_wifi_all_ap_scan(AP_IF_S ap_ary, UINT_T *num);
//wifi ap扫描获,取指定ap信息
OPERATE_RET tuya_hal_wifi_assign_ap_scan(CONST SCHAR_T *ssid, AP_IF_S ap);
//释放ap
OPERATE_RET tuya_hal_wifi_release_ap(AP_IF_S *ap);
//设置wifi信道
OPERATE_RET tuya_hal_wifi_set_cur_channel(CONST UCHAR_T chan);
//获取wifi当前信道
OPERATE_RET tuya_hal_wifi_get_cur_channel(UCHAR_T *chan);
//设置wifi sniffer模式回调函数
OPERATE_RET tuya_hal_wifi_sniffer_set(CONST BOOL_T en, CONST SNIFFER_CALLBACK cb);
//获取wifi的ip地址
OPERATE_RET tuya_hal_wifi_get_ip(CONST WF_IF_E wf, NW_IP_S *ip);
//设置wifi的Mac地址
OPERATE_RET tuya_hal_wifi_set_mac(CONST WF_IF_E wf, CONST NW_MAC_S *mac);
//获取wifi的Mac地址
OPERATE_RET tuya_hal_wifi_get_mac(CONST WF_IF_E wf, NW_MAC_S *mac);
//设置wifi的工作模式
OPERATE_RET tuya_hal_wifi_set_work_mode(CONST WF_WK_MD_E mode);
//获取wifi的工作模式
OPERATE_RET tuya_hal_wifi_get_work_mode(WF_WK_MD_E *mode);
//启动wifi ap热点
OPERATE_RET tuya_hal_wifi_ap_start(CONST WF_AP_CFG_IF_S *cfg);
//关闭wifi ap热点
OPERATE_RET tuya_hal_wifi_ap_stop(VOID_T);
//获取wifi快连信息
OPERATE_RET tuya_hal_wifi_get_connected_ap_info_v2(FAST_WF_CONNECTED_AP_INFO_V2_S fast_ap_info);
//wifi快连
OPERATE_RET tuya_hal_fast_station_connect_v2(CONST FAST_WF_CONNECTED_AP_INFO_V2_S *fast_ap_info);
//wifi连接
OPERATE_RET tuya_hal_wifi_station_connect(CONST SCHAR_T *ssid, CONST SCHAR_T *passwd);
//wifi断连
OPERATE_RET tuya_hal_wifi_station_disconnect(VOID_T);
//获取wifi信标
OPERATE_RET tuya_hal_wifi_station_get_conn_ap_rssi(SCHAR_T *rssi);
//获取wifi mac
OPERATE_RET tuya_hal_wifi_get_bssid(UCHAR_T *mac);
//获取wifi联网状态
OPERATE_RET tuya_hal_wifi_station_get_status(WF_STATION_STAT_E *stat);
//获取wifi国家码
OPERATE_RET tuya_hal_wifi_set_country_code(CONST COUNTRY_CODE_E ccode);
//发送wifi管理帧
OPERATE_RET tuya_hal_wifi_send_mgnt(CONST UCHAR_T *buf, CONST UINT_T len);
//注册wifi接收管理帧回调函数
OPERATE_RET tuya_hal_wifi_register_recv_mgnt_callback(CONST bool enable, CONST WIFI_REV_MGNT_CB recv_cb);
//设置wifi低功耗模式
OPERATE_RET tuya_hal_set_wifi_lp_mode(CONST BOOL_T en, CONST UINT_T dtim);
//wifi射频校准
BOOL_T tuya_hal_wifi_rf_calibrated(VOID_T);

有线管理

有线接口主要有:

//获取有线IP地址
OPERATE_RET tuya_hal_wired_get_ip(NW_IP_S *ip);
//判断设备是否已连网
BOOL_T tuya_hal_wired_station_conn(VOID);
//获取mac地址
OPERATE_RET tuya_hal_wired_get_mac(NW_MAC_S *mac);
//设置Mac地址
OPERATE_RET tuya_hal_wired_set_mac(const NW_MAC_S *mac);
//设置设备wifi的ssid和密码
OPERATE_RET tuya_hal_wired_wifi_set_station_connect(const char *ssid, const char *passwd);
//判断设备wifi是否配置
BOOL_T tuya_hal_wired_wifi_need_cfg(VOID);
//获取设备wifi ap信标
OPERATE_RET tuya_hal_wired_wifi_station_get_conn_ap_rssi(int8_t *rssi);
//获取连网状态
OPERATE_RET tuya_hal_wired_get_nw_stat(GW_BASE_NW_STAT_T *stat);
//获取有线连接状态
OPERATE_RET tuya_hal_wired_if_connect_internet(BOOL_T *stat);
#define tuya_hal_wired_get_mac(mac)
Check whether the hardware is connected to internet
Definition: tuya_hal_wired.h:55
#define tuya_hal_wired_get_ip(ip)
Get wired ethernet ip info
Definition: tuya_hal_wired.h:31
#define tuya_hal_wired_set_mac(mac)
Set wired ethernet mac information
Definition: tuya_hal_wired.h:67
#define tuya_hal_wired_get_nw_stat(stat)
If the hardware has a wifi interface, and user want to connect wifi, tuya-app will notify current wif...
Definition: tuya_hal_wired.h:120

蓝牙管理

蓝牙接口主要有:

//蓝牙初始化
OPERATE_RET tuya_hal_bt_port_init(ty_bt_param_t *p);
//蓝牙关闭
OPERATE_RET tuya_hal_bt_port_deinit(void);
//蓝牙主动断连
OPERATE_RET tuya_hal_bt_gap_disconnect(void);
//蓝牙发送
OPERATE_RET tuya_hal_bt_send(BYTE_T *data, UINT8_T len);
//更新蓝牙广播参数
OPERATE_RET tuya_hal_bt_reset_adv(tuya_ble_data_buf_t *adv,\
tuya_ble_data_buf_t *scan_resp);
//获取蓝牙rssi
OPERATE_RET tuya_hal_bt_get_rssi(SCHAR_T *rssi);
//打开蓝牙广播
OPERATE_RET tuya_hal_bt_start_adv(void);
//关闭蓝牙广播
OPERATE_RET tuya_hal_bt_stop_adv(void);
//蓝牙产测
OPERATE_RET tuya_hal_bt_assign_scan(INOUT ty_bt_scan_info_t *info);
//获取蓝牙能力
OPERATE_RET tuya_hal_bt_get_ability(VOID_T);
//蓝牙广播扫描初始化
OPERATE_RET tuya_hal_bt_scan_init(IN TY_BT_SCAN_ADV_CB scan_adv_cb);
//启动蓝牙广播扫描功能
OPERATE_RET tuya_hal_bt_start_scan(VOID_T);
//关闭蓝牙广播扫描功能
OPERATE_RET tuya_hal_bt_stop_scan(VOID_T);

OS Queue

OS Queue相关接口有:

OPERATE_RET tuya_hal_queue_create_init(QUEUE_HANDLE *queue, int msgsize,int msgcount);
OPERATE_RET tuya_hal_queue_post(CONST QUEUE_HANDLE pQueue_hanle,void *data, unsigned int timeout);
OPERATE_RET tuya_hal_queue_fetch(QUEUE_HANDLE pQueue_hanle, void *msg, unsigned int timeout);
OPERATE_RET tuya_hal_queue_free(CONST QUEUE_HANDLE pQueue_hanle);
#define tuya_hal_queue_post
tuya_hal_queue_post用于发送一个消息到指定的队列中
Definition: tuya_hal_queue.h:37
#define tuya_hal_queue_fetch
tuya_hal_queue_fetch用于等待一个消息队列
Definition: tuya_hal_queue.h:44
#define tuya_hal_queue_create_init
tuya_hal_queue_create_init用于创建并初始化tuya 消息队列
Definition: tuya_hal_queue.h:29
#define tuya_hal_queue_free
tuya_hal_queue_free 释放消息队列
Definition: tuya_hal_queue.h:52

FAQ