一、SDK架構
1.1 架構框圖
基于 TuyaOS 系統(tǒng),可以裁剪得到的適用于 NB-IoT 協(xié)議產(chǎn)品接入的 SDK。SDK 將設備配網(wǎng)、上下行數(shù)據(jù)通信、產(chǎn)測授權、固件 OTA 升級等接口進行封裝,并提供相關函數(shù)。
1.2 目錄結構
1.2.1 TuyaOS目錄說明
- adapter:TuyaOS kernel 適配層目錄。包含了 TuyaOS kernel adapter layer 定義的標準接口頭文件。
-
apps:TuyaOS 應用程序目錄。包含了開發(fā)框架自帶的演示程序,開發(fā)者應用程序。
- tuyaos_demo_nb_sample:基礎的數(shù)據(jù)上報與下發(fā)演示demo。
- tuyaos_demo_nb_3rdcloud_app:接入第三方云應用demo。
- build:編譯配置文件目錄
- docs:TuyaOS 文檔目錄。包含了 TuyaOS 的使用說明、接口文檔、各個功能模塊的介紹和使用說明。
-
include:TuyaOS 外部頭文件目錄。包含了 TuyaOS 對外提供服務的功能組件的接口文件。
- adapter:平臺驅動適配目錄
- base:基礎頭文件。自動生成的通用頭文件。
- components:組件頭文件,對外提供服務的組件頭文件。
- libs:TuyaOS 庫文件目錄。包含了組件的靜態(tài)、動態(tài)庫文件。庫文件名一般為 libtuyaos.a,或者 libtuya_iot.so,也可以包含其他的名稱的庫,可以按需鏈接。
- vendor:原廠 SDK,
- CHANGELOG.md:修改記錄。記錄了各個版本的修改記錄,包括修改的 bug,新增的特性支持。
- LICENSE:授權聲明。介紹使用 TuyaOS 開發(fā)框架的方式和范圍、知識產(chǎn)權等。
- README.md:介紹文檔。包含了介紹、下載、編譯、接口文檔鏈接、應用對接文檔鏈接等內容。
- build_app.sh:應用編譯入口腳本文件。編譯腳本,用戶可以根據(jù)自己的需求進行修改、適配。可以按照需求對 build_app.sh 的內容進行定制,比如說有較大的、特殊的 TuyaOS 開發(fā)框架,可以存放在指定的位置,在 build_app.sh 里進行下載。支持 sh、bat、py 格式。
1.2.2 應用程序及Demo目錄說明
此處展示的 Demo 是 TuyaOS NB-IoT 開發(fā)包最基本的 Demo,開發(fā)者可以通過該 Demo 體驗開發(fā)包支持的幾乎所有功能,當然也可以基于該 Demo 開發(fā)任何產(chǎn)品。
- tuyaos_demo_nb_sample:基礎的數(shù)據(jù)上報與下發(fā)演示demo。
- tuyaos_demo_nb_3rdcloud_app:接入第三方云應用demo。
1.2.3 組件(含庫)目錄說明
組件是 TuyaOS NB-IoT 開發(fā)包的主體部分,TuyaOS 的目標就是實現(xiàn)代碼的組件化,熟練地掌握常用組件的基本原理和實現(xiàn)方法對于產(chǎn)品開發(fā)可以達到事半功倍的效果。
1.2.4 原廠SDK目錄說明
Vender 是開發(fā)環(huán)境所在目錄,包含芯片原廠 SDK、各類適配層以及通用頭文件,由涂鴉和芯片原廠共同維護。
- mt2625:MTK芯片原廠SDK。
- toolchain:編譯工具鏈。
-
tuyaos/tuya_os_adapter:TuyaOS 各類適配層。
- include:通用頭文件。為保證 TKL(Tuya Kernel Layer)層以上能夠達到一套代碼適用于多個芯片平臺的目標,F(xiàn)lash 地址、外設引腳、線程優(yōu)先級等平臺相關的因素都通過統(tǒng)一的宏定義設置。
- driver:涉及TKL(Tuya Kernel Layer)層 NB-IoT、外設驅動(ADC、Flash、GPIO、I2C、SPI、UART……)等適配。
- system:涉及TKL(Tuya Kernel Layer)層系統(tǒng)驅動(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();
// 應用初始化前置準備工作,用戶在此處可以進行一些設置,為后續(xù)繼續(xù)標準化預留
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
// 產(chǎn)測初始化, 注冊函數(shù)需要應用實現(xiàn),其中串口驅動不需要應用提供
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 之前需要做的一些工作,用戶可以根據(jù)自己的需求在 tuya_device.c
文件中實現(xiàn),也可以不實現(xiàn),不實現(xiàn)便不會執(zhí)行,該函數(shù)主要用于一些外設的基本配置與需要上電快速啟動的一些功能,例如:喚醒引腳初始化
、ADC 初始化
、快速點亮 LED 燈
或者 使能外設
等操作。注意:請不要在此函數(shù)中使用較長時間延時。
例如在 tuyaos_demo_nb_sample 基礎的數(shù)據(jù)上報與下發(fā)演示 Demo 中進行 按鍵初始化與中斷配置
和 LED 燈 GPIO 配置
int pre_init(void)
{
/*
該位置僅可以添加系統(tǒng)啟動時硬件相關的初始化操作例如GPIO、ADC、I2C等
注:UART1不可在此處配置,UART1在產(chǎn)測接口中會用到,請在device_init()內配置
請不要在此處操作長時間延時
*/
tuya_key_init_test();
tuya_gpio_init_test();
return 0;
}
例如在 tuyaos_demo_nb_3rdcloud_app 接入第三方云應用 Demo 中進行 接入云參數(shù)配置
int pre_init(void)
{
TAL_NBIOT_LWM2M_REGISTER_T params;
int ret = 0;
params.bootstrap_en = 0; // ctcc及直連:不開啟bs;cmcc:drx專網(wǎng)不開啟,psm網(wǎng)絡開啟
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協(xié)議交互心跳間隔,單位:秒
params.psk = "bFFFcDDDEB7aaBbc"; //16~32個字符lwm2m協(xié)議交互秘鑰
//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()
此階段用于初始化產(chǎn)品功能,用戶根據(jù)自己的需求在 tuya_device.c
文件中實現(xiàn)。如果是連接涂鴉云,需要配置 PRODUCT_KEY
與一些必要的回調注冊,下面以一種經(jīng)典的場景舉例介紹該函數(shù)的使用方法:
int device_init(void)
{
int ret = OPRT_OK;
// 配置產(chǎn)品 PID
tuya_user_api_set_product_key(PRODUCT_KEY);
// 設置事件捕獲回調函數(shù)
tuya_user_api_event_loop_set_cb(tuya_event_process_cb, NULL);
// 啟動事件捕獲任務
tuya_user_api_event_loop_start();
// 設置云端下發(fā)數(shù)據(jù)點回調函數(shù)
tuya_user_api_dp_write_default_cb(tuya_dp_write_cb);
// 設置記錄型數(shù)據(jù)點上報結果回調函數(shù)
tuya_user_api_dp_report_record_ack_register_cb(dp_report_notify_callback);
// 設置心跳時間
tuya_user_api_lifetime_set(600);
// 設置記錄型數(shù)據(jù)在弱網(wǎng)條件下的上報時間間隔
tuya_user_api_record_dp_lifetime_set(600);
/*
此處可創(chuàng)建用戶任務
*/
return ret;
}
2.3 系統(tǒng)事件捕獲流程
-
svc_nb
NB-IoT服務組件
API 位于 TuyaOS/include/components/svc_nb/include/tuya_event_loop.h
首先在 device_init()
函數(shù)中進行 tuya_user_api_event_loop_set_cb()
設置事件捕獲回調和 tuya_user_api_event_loop_start()
啟動事件捕獲任務。
int device_init(void)
{
···
···
// 設置事件捕獲回調函數(shù)
tuya_user_api_event_loop_set_cb(tuya_event_process_cb, NULL);
// 啟動事件捕獲任務
tuya_user_api_event_loop_start();
···
···
}
2.3.1 系統(tǒng)事件捕獲回調
在事件捕獲回調 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")) {
//網(wǎng)絡斷開,數(shù)據(jù)無法發(fā)送!
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:
//系統(tǒng)準備重啟!
USER_API_LOGD("SYSTEM_EVENT_GOING_REBOOT");
break;
case SYSTEM_EVENT_GOING_SLEEP:
//系統(tǒng)準備進入睡眠!
USER_API_LOGD("SYSTEM_EVENT_GOING_SLEEP");
break;
default:
break;
}
return OPRT_OK;
}
2.3.2 系統(tǒng)事件ID
位于 TuyaOS/include/components/svc_nb/include/tuya_comm.h 中,事件 ID 包含以下各種狀態(tài):
typedef enum {
SYSTEM_EVENT_ID_CARD, //設備識別到SIM卡正常
SYSTEM_EVENT_NO_ID_CARD, //設備未識別到SIM卡
SYSTEM_EVENT_NETWORK_READY, //成功附著基站
SYSTEM_EVENT_NETWORK_DISCONNECT,//網(wǎng)絡斷開
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網(wǎng)絡服務已可用
EVENT_LWM2M_UPDATE_SUCCESS, //數(shù)據(jù)上報成功
EVENT_LWM2M_RESPONSE_SUCCESS, //數(shù)據(jù)響應成功
EVENT_LWM2M_SEND_FAIL, //LWM2M協(xié)議層發(fā)送失敗
EVENT_LWM2M_RESTART, //LWM2M網(wǎng)絡重連
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, //心跳發(fā)送
EVENT_FACTORY_RESETING,
EVENT_COMPOSITE_ACTIVE_SUCCESS, //復合產(chǎn)品NB為X模組激活成功
EVENT_LWDP_PACKET_SEND, //LWDP數(shù)據(jù)包異步發(fā)送
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
此事件。 - 當網(wǎng)絡注冊上基站后,響應
SYSTEM_EVENT_NETWORK_READY
此事件。 - 當ISP模式(代理服務器模式)時,設備會先與代理服務器通訊,通訊正常,則設備響應
EVENT_LWM2M_READY
事件。 - 當設備登錄上代理服務器,則響應
EVENT_LWM2M_CONNECTED
事件。 - 當設備網(wǎng)絡斷開,響應
SYSTEM_EVENT_NETWORK_DISCONNECT
事件。
? 由 Leung 寫于 2023 年 9 月 9 日
? 參考:SDK 架構-TuyaOS-涂鴉開發(fā)者
TuyaOS>NB-IoT開發(fā)框架>能力地圖>設備初始化