TuyaOS开发学习笔记(2)——NB-IoT开发SDK架构、运
一、SDK架构
1.1 架构框图
基于 TuyaOS 系统,可以裁剪得到的适用于 NB-IoT 协议产品接入的 SDK。SDK 将设备配网、上下行数据通信、产测授权、固件 OTA 升级等接口进行封装,并提供相关函数。
1.2 目录结构
1.2.1 TuyaOS目录说明
- adapter:TuyaOS kernel 适配层目录。包含了 TuyaOS kernel adapter layer 定义的标准接口头文件。
-
apps:TuyaOS 应用程序目录。包含了开发框架自带的演示程序,开发者应用程序。
- tuyaos_demo_nb_sample:基础的数据上报与下发演示demo。
- tuyaos_demo_nb_3rdcloud_app:接入第三方云应用demo。
- build:编译配置文件目录
- docs:TuyaOS 文档目录。包含了 TuyaOS 的使用说明、接口文档、各个功能模块的介绍和使用说明。
-
include:TuyaOS 外部头文件目录。包含了 TuyaOS 对外提供服务的功能组件的接口文件。
- adapter:平台驱动适配目录
- base:基础头文件。自动生成的通用头文件。
- components:组件头文件,对外提供服务的组件头文件。
- libs:TuyaOS 库文件目录。包含了组件的静态、动态库文件。库文件名一般为 libtuyaos.a,或者 libtuya_iot.so,也可以包含其他的名称的库,可以按需链接。
- vendor:原厂 SDK,
- CHANGELOG.md:修改记录。记录了各个版本的修改记录,包括修改的 bug,新增的特性支持。
- LICENSE:授权声明。介绍使用 TuyaOS 开发框架的方式和范围、知识产权等。
- README.md:介绍文档。包含了介绍、下载、编译、接口文档链接、应用对接文档链接等内容。
- build_app.sh:应用编译入口脚本文件。编译脚本,用户可以根据自己的需求进行修改、适配。可以按照需求对 build_app.sh 的内容进行定制,比如说有较大的、特殊的 TuyaOS 开发框架,可以存放在指定的位置,在 build_app.sh 里进行下载。支持 sh、bat、py 格式。
1.2.2 应用程序及Demo目录说明
此处展示的 Demo 是 TuyaOS NB-IoT 开发包最基本的 Demo,开发者可以通过该 Demo 体验开发包支持的几乎所有功能,当然也可以基于该 Demo 开发任何产品。
- tuyaos_demo_nb_sample:基础的数据上报与下发演示demo。
- tuyaos_demo_nb_3rdcloud_app:接入第三方云应用demo。
1.2.3 组件(含库)目录说明
组件是 TuyaOS NB-IoT 开发包的主体部分,TuyaOS 的目标就是实现代码的组件化,熟练地掌握常用组件的基本原理和实现方法对于产品开发可以达到事半功倍的效果。
1.2.4 原厂SDK目录说明
Vender 是开发环境所在目录,包含芯片原厂 SDK、各类适配层以及通用头文件,由涂鸦和芯片原厂共同维护。
- mt2625:MTK芯片原厂SDK。
- toolchain:编译工具链。
-
tuyaos/tuya_os_adapter:TuyaOS 各类适配层。
- include:通用头文件。为保证 TKL(Tuya Kernel Layer)层以上能够达到一套代码适用于多个芯片平台的目标,Flash 地址、外设引脚、线程优先级等平台相关的因素都通过统一的宏定义设置。
- driver:涉及TKL(Tuya Kernel Layer)层 NB-IoT、外设驱动(ADC、Flash、GPIO、I2C、SPI、UART……)等适配。
- system:涉及TKL(Tuya Kernel Layer)层系统驱动(Memory、Network、OTA、Mutex、Semaphore、Thread……)等适配。
二、运行流程
2.1 涂鸦SDK初始化流程
2.1.1 tuya_app_main
static void app_init_thread(void* param)
{
user_main();
/* Kill init thread after all init tasks done */
tal_thread_delete(app_init_task);
app_init_task = NULL;
}
void tuya_app_main(void)
{
THREAD_CFG_T cfg = {
.priority = TASK_PRIO_NORMAL,
.stackDepth = 4096*2,
.thrdname = "app_init_thread",
};
tal_thread_create_and_start(&app_init_task, NULL, NULL, app_init_thread, NULL, &cfg);
}
2.1.2 user_main()
void user_main(void)
{
OPERATE_RET op_ret = OPRT_OK;
//涂鸦Device OS 日志及kv初始化:不要修改
tuya_nbiot_init_params();
// 应用初始化前置准备工作,用户在此处可以进行一些设置,为后续继续标准化预留
pre_init();
//涂鸦Device OS SDK 初始化前准备:不要修改
#ifdef TUYA_RELEASE
__tuya_nbiot_pre_init(APP_BIN_NAME, USER_SW_VER, true);
#else
__tuya_nbiot_pre_init(APP_BIN_NAME, USER_SW_VER, false);
#endif
// 产测初始化, 注册函数需要应用实现,其中串口驱动不需要应用提供
MF_IMPORT_INTF_S user_intf = {
.user_test = tuya_user_prod_test,
};
mf_test_system_start(&user_intf,APP_BIN_NAME, USER_SW_VER);
//涂鸦Device OS SDK 初始化:不要修改
__tuya_nbiot_init();
//应用初始化
device_init();
//标记涂鸦应用初始化完成:不要修改
tuya_nbiot_set_initialized();
}
2.2 设备应用初始化流程
2.2.1 pre_init()
此阶段对应初始化 NBIOT SDK 之前需要做的一些工作,用户可以根据自己的需求在 tuya_device.c
文件中实现,也可以不实现,不实现便不会执行,该函数主要用于一些外设的基本配置与需要上电快速启动的一些功能,例如:唤醒引脚初始化
、ADC 初始化
、快速点亮 LED 灯
或者 使能外设
等操作。注意:请不要在此函数中使用较长时间延时。
例如在 tuyaos_demo_nb_sample 基础的数据上报与下发演示 Demo 中进行 按键初始化与中断配置
和 LED 灯 GPIO 配置
int pre_init(void)
{
/*
该位置仅可以添加系统启动时硬件相关的初始化操作例如GPIO、ADC、I2C等
注:UART1不可在此处配置,请在device_init()内配置
请不要在此处操作长时间延时
*/
tuya_key_init_test();
tuya_gpio_init_test();
return 0;
}
例如在 tuyaos_demo_nb_3rdcloud_app 接入第三方云应用 Demo 中进行 接入云参数配置
int pre_init(void)
{
TAL_NBIOT_LWM2M_REGISTER_T params;
int ret = 0;
params.bootstrap_en = 0; // ctcc及直连:不开启bs;cmcc:drx专网不开启,psm网络开启
params.srv_ip = "117.60.157.137"; //服务器地址,电信线上
params.srv_port = 5684; //服务器端口号,5684(加密),5683(不加密),移动暂时支持不加密
params.isp_type = NBIOT_ISP_OTHER; //NBIOT_ISP_TUYA:表示直连三方云;否则连运营商云中转
params.lifetime = 7200; //lwm2m协议交互心跳间隔,单位:秒
params.psk = "bFFFcDDDEB7aaBbc"; //16~32个字符lwm2m协议交互秘钥
//params.imei = "862363050000149"; //15个字符的imei,可以由底层获取,可选!/*endpoint_name,pskid*/
//device attribute:
params.attri.obj_id = 19;
params.attri.ins_id_up = 0;
params.attri.ins_id_down = 1;
params.attri.res_id = 0;
ret = tuya_user_api_3rd_cloud_config(¶ms); //返回0:成功
return ret;
}
2.2.2 device_init()
此阶段用于初始化产品功能,用户根据自己的需求在 tuya_device.c
文件中实现。如果是连接涂鸦云,需要配置 PRODUCT_KEY
与一些必要的回调注册,下面以一种经典的场景举例介绍该函数的使用方法:
int device_init(void)
{
int ret = OPRT_OK;
// 配置产品 PID
tuya_user_api_set_product_key(PRODUCT_KEY);
// 设置事件捕获回调函数
tuya_user_api_event_loop_set_cb(tuya_event_process_cb, NULL);
// 启动事件捕获任务
tuya_user_api_event_loop_start();
// 设置云端下发数据点回调函数
tuya_user_api_dp_write_default_cb(tuya_dp_write_cb);
// 设置记录型数据点上报结果回调函数
tuya_user_api_dp_report_record_ack_register_cb(dp_report_notify_callback);
// 设置心跳时间
tuya_user_api_lifetime_set(600);
// 设置记录型数据在弱网条件下的上报时间间隔
tuya_user_api_record_dp_lifetime_set(600);
/*
此处可创建用户任务
*/
return ret;
}
2.3 系统事件捕获流程
-
svc_nb
NB-IoT服务组件
API 位于 TuyaOS/include/components/svc_nb/include/tuya_event_loop.h
首先在 device_init()
函数中进行 tuya_user_api_event_loop_set_cb()
设置事件捕获回调和 tuya_user_api_event_loop_start()
启动事件捕获任务。
int device_init(void)
{
···
···
// 设置事件捕获回调函数
tuya_user_api_event_loop_set_cb(tuya_event_process_cb, NULL);
// 启动事件捕获任务
tuya_user_api_event_loop_start();
···
···
}
2.3.1 系统事件捕获回调
在事件捕获回调 tuya_event_process_cb()
中进行判断和处理 SDK 返回的事件 ID
static OPERATE_RET tuya_event_process_cb(void* ctx, system_event_t* event)
{
USER_API_LOGD("tuya user event:%d",(event->event_id));
switch (event->event_id) {
case SYSTEM_EVENT_NETWORK_DISCONNECT:
if (STR_EQU(event->event_info.param, "TRUE")) {
//网络断开,数据无法发送!
USER_API_LOGD("SYSTEM_EVENT_NETWORK_DISCONNECT");
}
break;
case SYSTEM_EVENT_NETWORK_READY:
USER_API_LOGD("SYSTEM_EVENT_NETWORK_READY");
break;
case EVENT_LWM2M_CONNECTED:
USER_API_LOGD("EVENT_LWM2M_CONNECTED");
break;
case EVENT_LWM2M_READY:
USER_API_LOGD("EVENT_LWM2M_READY");
data_send();
break;
case SYSTEM_EVENT_GOING_REBOOT:
//系统准备重启!
USER_API_LOGD("SYSTEM_EVENT_GOING_REBOOT");
break;
case SYSTEM_EVENT_GOING_SLEEP:
//系统准备进入睡眠!
USER_API_LOGD("SYSTEM_EVENT_GOING_SLEEP");
break;
default:
break;
}
return OPRT_OK;
}
2.3.2 系统事件ID
位于 TuyaOS/include/components/svc_nb/include/tuya_comm.h 中,事件 ID 包含以下各种状态:
typedef enum {
SYSTEM_EVENT_ID_CARD, //设备识别到SIM卡正常
SYSTEM_EVENT_NO_ID_CARD, //设备未识别到SIM卡
SYSTEM_EVENT_NETWORK_READY, //成功附着基站
SYSTEM_EVENT_NETWORK_DISCONNECT,//网络断开
SYSTEM_EVENT_REG_DENIED,
SYSTEM_EVENT_BE_AWAKENED, //设备正准备睡眠时被打断醒来!
SYSTEM_EVENT_DELAY_SLEEP, //进入睡眠倒计时阶段!
SYSTEM_EVENT_GOING_SLEEP, //马上进入睡眠!
SYSTEM_EVENT_DELAY_REBOOT, //进入重启倒计时阶段!
SYSTEM_EVENT_GOING_REBOOT, //设备正在重启
SYSTEM_EVENT_WAKE_FROM_NORMAL_RTC_TIMEOUT, //普通RTC超时唤醒
SYSTEM_EVENT_WAKE_FROM_DISCRETE_RTC_TIMEOUT, //离散RTC超时唤醒
EVENT_LWM2M_CONNECTED, //已连接LWM2M服务器
EVENT_LWM2M_READY, //LWM2M网络服务已可用
EVENT_LWM2M_UPDATE_SUCCESS, //数据上报成功
EVENT_LWM2M_RESPONSE_SUCCESS, //数据响应成功
EVENT_LWM2M_SEND_FAIL, //LWM2M协议层发送失败
EVENT_LWM2M_RESTART, //LWM2M网络重连
EVENT_DEVICE_INFO_RESET, //设备信息重置
EVENT_DEVICE_BIND_ON, //设备已绑定
EVENT_DEVICE_UNBIND_ON, //设备未绑定
EVENT_DEVICE_DEACTIVE, //设备重置
EVENT_POWERKEY_PRESS, //POWER按键被按下
EVENT_SLP_UNLOCK,
EVENT_FOTA_UPDATE_DELAY,
EVENT_HEARTBEAT_SEND, //心跳发送
EVENT_FACTORY_RESETING,
EVENT_COMPOSITE_ACTIVE_SUCCESS, //复合产品NB为X模组激活成功
EVENT_LWDP_PACKET_SEND, //LWDP数据包异步发送
EVENT_SLEEP_TYPE, //睡眠事件类型
EVENT_DISCRETE_ON, //离散开始
EVENT_CFUN_ON,
EVENT_HEARTBEAT_LIFETIME_UPDATE, //心跳周期更新
SYSTEM_EVENT_MAX
} system_event_id_t;
主要事件包括:
- 当设备读到SIM卡后,响应
SYSTEM_EVENT_ID_CARD
此事件。 - 当网络注册上基站后,响应
SYSTEM_EVENT_NETWORK_READY
此事件。 - 当ISP模式(代理服务器模式)时,设备会先与代理服务器通讯,通讯正常,则设备响应
EVENT_LWM2M_READY
事件。 - 当设备登录上代理服务器,则响应
EVENT_LWM2M_CONNECTED
事件。 - 当设备网络断开,响应
SYSTEM_EVENT_NETWORK_DISCONNECT
事件。
• 由 Leung 写于 2023 年 9 月 9 日