1.4.4.2 嵌入式开发指南
名词解释
名词 | 解释 |
---|---|
SDK | 软件开发工具包,HeyThings SDK包含了HeyThings协议的实现,客户在SDK的基础上,可快速完成产品接入 |
MCU | 微控制单元,嵌入式设备的核心控制芯片,这里指客户已有产品中的核心控制芯片 |
schema | 产品功能抽象服务定义,产品抽象为具备多个服务,每个服务包含支持的属性、方法、事件 |
user_helper | 应用层辅助代码,实现基于HeyThings协议的具体产品模型的封装和解析,代理应用层与SDK交互 |
user_code | 应用层产品功能代码,实现产品的实际业务操作和状态改变,通过user_helper与SDK交互 |
1 概述
平台将提供WiFi、BLE、MESH等多种接入的方式,目前主要开放的接入方式为WiFi和BLE两种。WiFi适合较大带宽、低延迟、交流供电的场景,如台灯、插座、空调等大小家电。BLE相对来说适合低功耗、低成本的产品,像电池供电的传感器、穿戴式设备。
本章主要说明如何在IoT WiFi模组中集成HeyThings SDK进行产品开发,实现设备注册、控制、状态上报、OTA升级等业务功能。
2 开发模式
2.1 模组SDK模式
HeyThings平台提供IoT模组的SDK,客户把SDK和产品功能软件一起集成到产品上。
应用场景:使用一块IoT控制芯片实现产品功能,节省芯片成本;产品自身功能不复杂,容易移植到IoT模组上。
2.2 模组SDK + MCU 模式
HeyThings平台提供集成了SDK的IoT模组,客户在模组上添加串口协议,与自己产品的独立MCU通信。
应用场景: 客户有自己的MCU,产品自身的主要业务已在MCU侧实现,客户不修改MCU侧的产品实现。
2.3 移植SDK到其他模组
应用场景:客户有自己的模组,且MCU资源丰富,希望将HeyThings SDK移植到自己的模组上运行。
该模式下的移植将主要由客户自己完成,HeyThings平台提供协助。移植完成后的使用将与上述的模组SDK模式一致。
3 模组SDK模式
该模式指使用单芯片模组完成整个产品的核心控制功能,且HeyThings SDK与用户设备控制功能都运行在同一IoT模组上。
开发步骤:
- 定义设备schema(设备所支持的服务)
- 申请及配置设备证书、产品信息
- 完成应用层需实现的接口(设备控制回调、设备属性状态变化设置等)
- 调用HeyThings SDK循环
HeyThings SDK的主要运行流程如下:
@startuml
participant "app/clouds" as app
participant sdk
participant user_helper
participant user_code
group iot_sdk_start
group sdk 初始化
sdk->sdk : 加载设备基本信息
sdk->sdk : 加载设备证书
sdk->sdk : sdk内部资源初始化
sdk->user_code : 初始化完成回调\nhandle->init_complete_cb()
end group
sdk->app : connect, login ...
sdk->user_code : 状态通知 handle->event_handle()
group 设备控制、状态上报
app->sdk : 控制命令
sdk->user_helper : 属性设置控制类回调:handle->cmd_set_handle()\n 数组类属性操作回调:cmd_array_xxx_handle()
user_helper -> user_code : user_handle_dev_xx() \n业务层填充设备具体响应操作
user_code -> user_helper: serviec_xxx_property_xxx_changed()\n业务层完成操作后回复属性改变
user_helper->sdk : 设备状态变化通知sdk\n iot_dev_property_changed_string()
sdk -> app : 状态上报
end group
group 辅助函数
user_code -> sdk : 获取系统时间 sdk_time_get_date(time_date_t * now)
end group
end group
@enduml
业务应用层需要实现的函数,都封装在oc_handler_t结构中。
//需用户层实现的函数
typedef struct {
char *app_version;
//user handle 'method:set' command.
//return 0 on success, -1 on error
int (*cmd_set_handle)(int siid, int n_iid, unsigned int *iids, int len, unsigned char *changes);
//if user care about some get cmd of service. set the ssid to siid_monitor array.
//siid_monitor_cnt indicate the count of the service user want to monitor.
int siid_monitor[ MAX_MONITOR_SERVICE ];
int siid_monitor_cnt;
//get cmd callback, if user care about some get cmd of service.
int (*cmd_get_cmd_cb)(int siid, int n_iid, unsigned int *iids);
//user handle 'method:arrayAdd' command.
//return the id the of the item added, return < 0 if error occurs.
int (*cmd_array_add_handle)(int siid, int iid, int len, unsigned char *content);
//user handle 'method:arrayDel' command.
//return 0 on success, -1 on error
int (*cmd_array_del_handle)(int siid, int iid, int n_ids, int *ids);
//user handle 'method:arrayUpdate' command.
//return 0 on success, -1 on error
int (*cmd_array_update_handle)(int siid, int iid, int id, int len, unsigned char *changes);
//sdk notify state changes to app layer.
void (*event_handle)(user_event_t evt);
//sdk notify user to allow device provision.
void (*dev_provision_confirm)(void);
//called when init complete.
void (*init_complete_cb)(void);
//user handle 'method:actioncall' command.
int (*cmd_action_call)(int siid, unsigned int iid, int len, unsigned char *data);
} oc_handler_t;
/**
* @brief : start sdk, function not return to the caller
* @param user_handler : callback function provided by user.
* @param odos : whether it is one device one secert device
* @param secert_mode : whether added device by PIN
* @param p_dev_info : device base info.
* @return int : -1 on error.
*/
int iot_sdk_start(oc_handler_t *user_handler, unsigned int odos,
DEV_SECERT_MODE_T secert_mode, iot_dev_info_t *p_dev_info);
/**
* @brief : if device property value changes, it should call this function to notify sdk
* @param siid : service instance id.
* @param iid : property instance id.
* @param value : property value serialized.
* @param len : length the value data.
* @return int : 0 on success, -1 on error.
*/
int iot_dev_property_changed_string(int siid, int iid, unsigned char *value, int len);
/**
* @brief : register property to sdk.
* @param siid : service instance id
* @param iid : property instance id
* @param value : property value in binary format
* @param len : value length
*/
void iot_dev_property_register(int siid, int iid, unsigned char *value, int len);
3.1 设置产品硬件相关信息
对具体某一类设备,业务开发者通过硬编码的方式填充iot_dev_info_t结构,作为初始化函数的参数传递信息到SDK。
typedef struct
{
unsigned char cid[DEV_CID_BYTES + 1]; //产品category id
unsigned char pid[DEV_PID_BYTES + 1]; //产品product id
unsigned char brand[DEV_BRAND_BYTES + 1]; //品牌名
unsigned char manufacture[DEV_MANUF_BYTES + 1]; //厂商名
unsigned char model[DEV_MODEL_BYTES + 1]; //型号
unsigned char connect_type; //接入类型:wifi,zigbee,bt
unsigned char hw_version[DEV_HWVER_LEN + 1]; //硬件版本
} iot_dev_info_t;
3.2 设置设备证书等出厂信息
HeyThings SDK定义了出厂信息的格式和存储区域,并提供了工具生成信息烧录文件,开发者需要按照指定位置烧录文件。
3.3 SDK消息循环
/**
* @brief : start sdk, function not return to the caller
* @param user_handler : callback function provided by user.
* @param odos : whether it is one device one secert device
* @param secert_mode : whether added device by PIN
* @param p_dev_info : device base info.
* @return int : -1 on error.
*/
int iot_sdk_start(oc_handler_t *user_handler, unsigned int odos,
DEV_SECERT_MODE_T secert_mode, iot_dev_info_t *p_dev_info);
该函数完成SDK的初始化、各功能实现(配网、云端和app端消息处理等)。调用该函数后,该函数将循环运行,不返回。
典型调用如下:
int user_sdk_main_thread(void)
{
int ret;
ret = iot_sdk_start(&handler);
if( ret < 0) {
return -1;
}
return 0;
}
3.4 设备控制
云端或APP控制设备时,SDK接收控制命令,然后通过回调函数通知用户层程序完成设备的实际控制。
int (*cmd_set_handle)(int siid, int n_iid, unsigned int *iids, int len, unsigned char *changes);
由于控制端与设备之间的模型交互为protobuf格式,SDK将protobuf序列化之后的数据传递给应用层,应用层需要通过设备模型定义才能理解含义。 为简化开发者的应用层开发,SDK所提供的demo中已实现设备抽象层代码、服务代码自动生成等,开发者只需完成实际设备控制函数。
set命令在sdk中的定义:
struct _Iot__CmdSetPropertiesReq
{
ProtobufCMessage base;
uint64_t did; //子设备时使用
uint32_t siid; //服务id
/*
* property id list.
*/
size_t n_iid; //iid 数量
uint32_t *iid; //iid 列表
/*
* packed service(properties) value
*/
ProtobufCBinaryData changes; // 服务、属性值
};
以灯操作为例:
SDK -> user_helper : cmd_set_user_cb(int siid, int n_iid, \nunsigned int *iids, int len, \nunsigned char *changes) 通用代码
user_helper -> user_code : cmd_set_handle( iid, service_object ) 通用代码
user_code -> user_code : switch(iid) : \ncase 1: user_handle_dev_on_off(x)\n case 2: user_handle_dev_bright(x)\n case xxx :...\n(开发者填充,实际控制设备)
3.5 注册设备属性
注册设备属性到SDK,根据属性类型,调用相应的注册接口。属性以key-value形式表示。
用户定义设备支持的服务后,该部分代码可由工具自动生成。
//示例:
//服务id定义,来源于开放平台
#define SIID_LIGHT 512
//属性定义
struct iid_name ServiceLight[] = {
{.iid = 1, .field_name = "switch_" },
{.iid = 2, .field_name = "brightness" },
{.iid = 3, .field_name = "colortemperature" },
{.iid = 4, .field_name = "colorrgb" },
{.iid = 5, .field_name = "mode" },
};
//注册各属性
int service_light_init()
{
iot_dev_property_register( SIID_LIGHT, iid, buf, buf_len );
}
3.6 属性上报
设备属性变化后,将新的值设置到SDK中,由SDK完成上报。
//灯服务示例:
void serviec_light_property_switch__changed(int switch_);
void serviec_light_property_brightness_changed(uint32_t brightness);
设备状态变化(如用户按键开关灯):
user_code -> user_helper: 用户按照属性调用\nserviec_light_property_xxx_changed(property_value)
user_helper -> sdk: iot_dev_property_changed_string(xx)
sdk -> sdk : 存储、发布状态
3.7 SDK内部状态通知
用户程序可通过sdk的状态通知,获取sdk的状态变化。如:根据wifi模块的状态,指示灯进行对应模式的闪烁。
typedef enum
{
E_LAN_CONNECTED, //已连接到路由器,还未连接到云端
E_CLOUD_CONNECTED, //已连接到云端
E_LAN_DISCONNECTED, //和路由器断开连接
E_CLOUD_DISCONNECTED, //和云端断开连接
E_DEV_ADDING, //正处于配网过程中
E_DEV_ADDING_SUCCESS, //已配网,还未连接到路由器
E_DEV_ADDING_FAIL, //配网超时
E_DEV_DELETED, //device deleted by app or cloud.
}user_event_t;
void demo_user_event_handle(user_event_t evt);
3.8 OTA升级接口
OTA升级是定义为设备的一个抽象服务,与其他应用服务类似,需要由应用层完成其服务模型解析,以获取到固件的URL和文件签名数据,并完成固件烧写和进度上报的动作。
3.9 应用层demo代码
详细代码内容见demo源码文件。
app_src/
├── protobuf-c //protobuf的C语言实现库
│ ├── protobuf-c.c
│ └── protobuf-c.h
├── app_main.c //应用层入口主程序文件
├── app_main.h
├── app_version.h //应用层软件版本号定义
├── debug_cli.c //终端调试命令任务
├── debug_cli.h
├── dev_abstract.c //设备抽模型接收回调,SDK命令处理入口
├── dev_abstract.h
├── devInfo.pb-c.c //设备基础信息服务protobuf定义
├── devInfo.pb-c.h
├── devInfo.proto
├── light.pb-c.c //智能灯控制服务protobuf定义
├── light.pb-c.h
├── light.proto
├── light_service.c //灯控制服务的模型封装和解析,即user helper
├── light_service.h
├── light_service_devinfo.c //灯基础信息服务的模型封装和解析
├── light_service_devinfo.h
├── light_service_upgrade.c //设备OTA升级服务的模型封装和解析
├── light_service_upgrade.h
├── ota_task.c //应用层实现的ota升级处理,平台相关
├── softwareUpdate.pb-c.c //设备OTA升级服务protobuf定义
├── softwareUpdate.pb-c.h
└── softwareUpdate.proto
4 IoT模组 + MCU
客户的设备控制功能运行在客户已有MCU上,通过串口与IoT模组通讯,实现云端接入和app控制。 如果客户已有私有的串口通信协议,需在模块上完成串口协议的开发,完成与MCU的通信。 如果客户还未开发串口通信协议,可使用IoT模组定义的通用串口协议(TODO)
串口协议需支持设备控制、状态获取、OTA升级等功能。