WiFi设备端开发指南
更新时间:2018-05-25 00:02:20
wifi设备端开发指南
1 简介
《wifi设备端开发指南》基于已经适配过alios-things的芯片平台,在alios-things上开发以下几种功能:
配网开发
设备重置
设备控制开发
FOTA开发
2 芯片平台
本开发指南使用庆科MK3060模组(micokit-3060)开发板作为硬件设备,alios-things支持MK3060的模组
3 源码获取
请从github上下载最新的alios-things的release版本:https://github.com/alibaba/AliOS-Things/releases
4 配网开发
4.1 4种配网模式
alios-things支持的配网模式有以下4种:
- 一键配网(Smartconfig)/手机热点配网(phone-config) App直接给设备配网
- 路由器热点配网(router-config)
- 零配(zero-config) 已配网设备为待配网设备配网
- 蓝牙配网(ble-config) 借助BT/BLE为设备配网
alios-things里面的配网代码没有开源,使用库文件的形式提供给上层应用使用。库文件位于
AliOS-Things/framework/ywss4linkkit/lib/
中。
4.2 配网API
配网的api有以下几个:
启动配网服务:
int awss_start()
停止配网服务:
int awss_stop()
确认进行配网:
int awss_config_press()
发送token到云服务器:
int awss_report_cloud()
配网重置:
void awss_report_reset
配网的详细api请参考:智能生活开放平台-配网服务
4.3 添加组件
在mk文件中需要添加如下ywss4linkkit
组件:
$(NAME)_COMPONENTS += ywss4linkkit
4.4 配网流程
配网流程可以分为以下几步:
第一步:启动配网服务
第二步:确认进行配网
第三步:配网过程
第四步:上报token到云端
该流程是配合Demo APP来介绍的。用户在创建产品的时候会生成三元组,Demo APP需要设置对应的三元组才能够正常完成配网流程。</span>具体操作请参考文档//demoapp.html](//demoapp.html)
第一步:启动配网服务
设备上电后,调用函数:int awss_start()
启动配网服务,此时并没有正式开始配网,而是处于扫描模式。
第二步:确认进行配网
当用户操作app后,app提示需要触控设备开始进行配网,此时设备端监听到触控事件,就调用函数int awss_config_press()
确认进行配网,配网过程会有10s以上的等待时间,等配网成功后,app端会显示配网成功。
一般情况就是使用aos_register_event_filter()
注册按键事件:
aos_register_event_filter(EV_KEY, linkkit_key_process, NULL);
然后在按键事件里面调用awss_config_press()
启动配网过程。
void linkkit_key_process(input_event_t *eventinfo, void *priv_data)
{
if (eventinfo->type != EV_KEY) {
return;
}
if (eventinfo->code != CODE_BOOT) {
return;
}
...
if (eventinfo->value == VALUE_KEY_CLICK) {
awss_config_press();
...
}
}
第三步:配网过程
配网过程主要是APP端和设备端的交互,该过程无需开发者关注,app端和设备端会自动完成配网的过程。
第四步:上报token到云端
当设备端配网成功,连接到AP后,需要把信息上报到云端,实现云端和用户账号的绑定,使用函数awss_report_cloud()
实现该过程:
int awss_report_cloud(void);
可以在连接上云服务器的事件中调用该函数上报信息:
/* 注册连接上云服务器的事件回调函数 */
aos_register_event_filter(EV_YUNIO, cloud_service_event, NULL);
static void cloud_service_event(input_event_t *event, void *priv_data) {
static uint8_t awss_reported=0;
if (event->type != EV_YUNIO) {
return;
}
if (event->code == CODE_YUNIO_ON_CONNECTED) {
...
awss_report_cloud();
return;
}
if (event->code == CODE_YUNIO_ON_DISCONNECTED) {
}
}
4.5 配网过程示例
下面以mk3060平台的linkkitapp的示例来展示配网的过程。
4.5.1 app开发并设置三元组
用户在创建产品的时候会生成三元组,Demo APP需要设置对应的三元组才能够正常完成配网流程。</span>具体操作请参考文档//demoapp.html](//demoapp.html)
4.5.2 单板烧写程序
单板需要将原始用例的可执行文件烧录得到mk3060单板中(即master最新的未修改过的代码,通过编译命令aos make linkkitapp@mk3060得到的可执行文件linkkitapp@mk3060.ota.bin)。
烧写的命令是长按boot键的同时短按reboot键,使MK3060进入bootloader,使用如下指令进入烧写模式:
write 0x13200
4.5.3 单板进入配网模式
- 短按reboot键,单板复位,如果设备没有连网,则待设备进入moniter模式后,出现ssid扫描列表:
- 再短按boot键,激活配网模式(短按boot键会调用到函数do_awss_active);
如果设备已配好网,reboot后不会进入moniter模式,会直接连云;
长按boot键5S,会恢复出厂设置,则可以重复1、2步骤,重新配网;
4.5.3 APP配网
待单板进入配网模式后,手机侧进行以下操作,完成一键式配网:
5 设备重置
5.1 设备端配网信息重置
当设备配网成功后,alios-things中的netmgr组件会自动把AP的SSID和密码存储到flash中,如果需要配网重置的话,就需要把SSID和密码从flash中擦除掉,清除配网信息请参考netmgr的API:
void netmgr_clear_ap_config(void);
清除完SSID之后,也建议重启设备(1s后重启):
aos_post_delayed_action(1000,reboot_system,NULL);
5.2 云端的设备信息重置
当设备配网成功后,云端会把设备和用户账户绑定在一块,如果需要设备重置的话,同时也需要重置云端的设备信息,使用如下的API:
void awss_report_reset(void);
配网的详细api请参考:智能生活开放平台-配网服务
该函数是一个阻塞函数,根据当前网络情况,会有不同的阻塞时间,所以建议单独给这个函数创建一个task:
aos_task_new("reset", awss_report_reset, NULL, 2048);
6 设备控制开发
6.1 设备控制开发概要
设备控制开发前先确认以下功能已经具备:
- 设备能够连接到阿里的 智能生活开放平台
- 已经通过智能生活开放平台获取到了设备的激活码,并得到设备联网需要的
ProductKey
、ProductSecret
、DeviceName
、DeviceSecret
。 - 根据设备的功能定义,获取到物的模型(TSL),即一段json格式的描述信息。
关于智能生活开放平台的云端配置以及设备创建等步骤这里不详细描述,详情请参考如何获得设备激活码
设备控制开发需要进行如下的几步:
- 修改设备凭证:
ProductKey
、ProductSecret
、DeviceName
、DeviceSecret
。 - 设置TSL信息。
- 实现设备控制相关函数接口:
- linkkit服务程序初始化:
linkkit_start()
和linkkit_ops_t
结构体中的回调函数 - 事件分发处理:
linkkit_dispatch()
- 创建物的对象:
linkkit_set_tsl()
- 设置对象的TSL属性:
linkkit_set_value()
- 上报设备属性到云端:
linkkit_post_property()
- 上报事件到云端:
linkkit_trigger_event()
- linkkit服务程序初始化:
切换阿里云服务器地址:
linkkit默认的阿里云服务器地址为国内的地址,如果需要更改为国外的阿里云服务器地址的话,需要在example/linkkitapp/linkkitapp.mk
文件中添加宏 SUPPORT_SINGAPORE_DOMAIN
:
6.2 接口实现
6.2.1 修改设备凭证
根据云端产生的设备凭证,修改头文件framework/protocol/linkkit/iotkit/sdk-encap/imports/iot_import_product.h
下的设备凭证信息。
#define PRODUCT_KEY "a1BDCKKSpdu"
#define PRODUCT_SECRET "Rp0aCxyRnd2ecrCX"
#define DEVICE_NAME "MVwo1x7hB2pyEbXlLkzE"
#define DEVICE_SECRET "OKHWuIaMzwtJAjUzPRLG1rcYxFo43akU"
6.2.2 修改TSL信息
在产品功能定义完成后,平台将自动生成JSON格式的TSL(即 Thing Specification Language),用来描述物的模型。该信息经过“压缩并转义”后,转换成C语言下的字符串格式,这样可以添加到设备端的TSL信息中。“压缩并转义”的方式可以参考链接:https://www.sojson.com/yasuo.html
在创建物的对象时,需要转义后的TSL信息,其修改可以参考文件:example/linkkitapp/linkkit_app.c
中的const char TSL_STRING[]
的定义。
6.2.3 linkkit函数接口
linkkit服务例程初始化需要在配网完成之后,建议注册在事件CODE_WIFI_ON_GOT_IP
里。linkkit示例分为以下步骤顺序完成:linkkit服务程序初始化,进入事件分发处理,创建对象,设置物对象的TSL属性、上报云端。后面会介绍该过程,具体请参考(aos\example\linkkitapp
下的用例代码,入口函数为linkkit_main()
)
6.2.3.1 linkkit服务程序初始化
名称 | 描述 |
---|---|
函数 | int linkkit_start(int max_buffered_msg, int get_tsl_from_cloud, linkkit_loglevel_t log_level, linkkit_ops_t *ops, linkkit_cloud_domain_type_t domain_type, void *user_context) |
说明 | 启动 linkkit 服务,与云端建立连接并安装回调函数 |
返回值 | 0: linkkit 服务程序启动成功; -1: linkkit 服务程序启动失败 |
参数 | 类型 | 描述 | 备注 |
---|---|---|---|
max_buffered_msg | int | 消息队列大小 | 此参数影响事件消息最大暂存数,频繁业务可适当增大此参数,建议值 [16~32] |
get_tsl_from_cloud | int | 获取 TSL 路径 | 0: 从本地获取物模型,非0: 从云端获取物模型 |
log_level | linkkit_loglevel_t | 设置打印的日志等级 | [0~5]: 数字越大, 打印越详细 |
ops | linkkit_ops_t* | linkkit 事件回调函数 | linkkit 通过回调函数将事件通知给用户 |
domain_type | linkkit_cloud_domain_type_t | 云服务器域名 | linkkit_cloud_domain_sh: 上海域名linkkit_cloud_domain_usa: 美国域名 |
user_context | void* | 用户上下文环境指针 | 用户自定义结构体的指针, 回调函数中传出 |
事件类型以及对应的事件回调函数如下所示,没当linkkit处理完相应的事件后,都会执行用户注册的回调函数,具体实现可参考linkkit_app.c内的实现:
typedef enum {
dm_callback_type_property_value_set = 0,
dm_callback_type_service_requested,
#ifdef RRPC_ENABLED
dm_callback_type_rrpc_requested,
#endif /* RRPC_ENABLED */
dm_callback_type_cloud_connected,
dm_callback_type_cloud_disconnected,
dm_callback_type_new_thing_created,
dm_callback_type_thing_disabled,
dm_callback_type_thing_enabled,
dm_callback_type_raw_data_arrived,
dm_callback_type_number,
} dm_callback_type_t;
static linkkit_ops_t alinkops = {
.on_connect = on_connect,
.on_disconnect = on_disconnect,
.raw_data_arrived = raw_data_arrived,
.thing_create = thing_create,
.thing_enable = thing_enable,
.thing_disable = thing_disable,
.thing_call_service = thing_call_service,
.thing_prop_changed = thing_prop_changed,
};
6.2.3.2 创建对象
根据设备描述信息(tsl),创建物的对象,并添加到linkkit中。如果创建成功,会返回物的唯一标志符,后续只需要通过该标识符来管理对象。
名称 | 描述 |
---|---|
函数 | void* linkkit_set_tsl(const char* tsl, int tsl_len) |
说明 | 从本地读取 TSL 文件,生成物的对象并添加到 linkkit 中 |
返回值 | 0: 成功; -1: 物的唯一标识符 |
参数 | 类型 | 描述 | 备注 |
---|---|---|---|
tsl | const char* | 设备描述信息( JSON 类型) | tsl 必须和设备三元组所属的产品的 Alink JSON 格式协议相同 |
tsl_len | int | 设备描述信息长度 | - |
代码示例
int linkkit_main()
{
sample_context_t* sample_ctx = &g_sample_context;
int execution_time = 0;
int get_tsl_from_cloud = 0;
execution_time = execution_time < 1 ? 1 : execution_time;
memset(sample_ctx, 0, sizeof(sample_context_t));
sample_ctx->thing_enabled = 1;
linkkit_start(16, get_tsl_from_cloud, linkkit_loglevel_debug, &alinkops, linkkit_cloud_domain_sh, sample_ctx);
if (!get_tsl_from_cloud) {
linkkit_set_tsl(TSL_STRING, strlen(TSL_STRING));
}
aos_post_delayed_action(1000, linkkit_action, sample_ctx);
return 0;
}
6.2.3.3 事件分发处理
linkkit服务程序初始化之后,需要周期性地进行进行事件分发处理,建议放到任务或delay事件中执行。该函数会根据事件回调的类型来执行相应的回调函数,linkkit_app.c的用例代码中已默认调用了该接口。
名称 | 描述 |
---|---|
函数 | int linkkit_dispatch() |
说明 | 事件分发函数,触发linkkit_start安装的回调 |
返回值 | 0: 成功; -1: 失败 |
代码示例
void linkkit_action(void *params)
{
int ret;
sample_context_t* sample_ctx = params;
linkkit_dispatch();
aos_post_delayed_action(200, linkkit_action, sample_ctx);
}
6.2.3.4 设置对象的TSL属性
设置TSL属性,就是给已创建的对象的TSL属性赋值,赋值完成后,可以通过接口linkkit_post_property
将对象属性上报到云端,也可以通过linkkit_trigger_event
将事件上报云端。
名称 | 描述 |
---|---|
函数 | int linkkit_set_value(linkkit_method_set_t method_set, const void* thing_id, const char* identifier, const void* value, const char* value_str) |
说明 | 根据identifier设置物对象的 TSL 属性,如果标识符为struct类型、event output类型或者service output类型,使用点'.'分隔字段;例如"identifier1.identifier2"指向特定的项 |
返回值 | 0: 物对象 TSL 属性设置成功; -1: 物对象 TSL 属性设置失败 |
参数 | 类型 | 描述 | 备注 |
---|---|---|---|
method_set | linkkit_method_set_t | 设置 TSL 属性的类型 | linkkit_method_set_property_value:设置模型property valuelinkkit_method_set_event_output_value:设置物模型event output valuelinkkit_method_set_service_output_value:设置物模型service output value |
thing_id | const void* | 物的唯一标识符 | 物对象的标识符,可从 thing_create 回调获得linkkit创建的 thing id |
identifier | const char* | TSL 属性标示符 | 物对象 TSL 属性的标识符 |
value | const void* | 输入的属性值 | 和 value_str, 二选一必填 |
value_str | const char* | 输入的字符串格式的属性值 | 和 value, 二选一必填 |
6.2.3.5 上报设备属性到云端
名称 | 描述 |
---|---|
函数 | int linkkit_post_property(const void* thing_id, const char* property_identifier) |
说明 | 上报设备属性到云端 |
返回值 | 0: 上报成功; -1: 上报失linkkit_post_property败 |
参数 | 类型 | 描述 | 备注 |
---|---|---|---|
thing_id | const void* | 物的唯一标识符 | 物对象的标识符,可从 thing_create 回调获得 linkkit 创建的 thing id |
property_identifier | const char* | 属性标示符 | 上报的物对象的TSL属性标识符 |
物对象的TSL标识符请参考物的TSL字符串或下图中的“标识符”列
int linkkit_data_publish(const char* identifier, const void* value, const char* value_str)
{
int ret = 0;
ret = linkkit_set_value(linkkit_method_set_property_value,g_thing_id,identifier,value,value_str);
if(0 != ret) {
return ret;
}
ret = linkkit_post_property(g_thing_id,identifier);
return ret;
}
int data_publish_test()
{
int ret = 0;
/*温度值为24.6*/
ret = linkkit_data_publish("CurrentTemperature",NULL,"24.6");
if(0 != ret) {
return ret;
}
/*湿度值为43%*/
ret = linkkit_data_publish("CurrentHumidity",NULL,"43");
if(0 != ret) {
return ret;
}
return 0;
}
6.2.3.6 上报事件到云端
linkkit_set_value
的第一个参数值选择linkkit_method_set_event_output_value
,则可以设置完事件(event)的TSL信息,之后可以通过linkkit_trigger_event
将事件发送到云端。
名称 | 描述 |
---|---|
函数 | int linkkit_trigger_event(const void* thing_id, const char* event_identifier) |
说明 | 上报设备事件到云端 |
返回值 | 0: 上报成功; -1: 上报失败 |
参数 | 类型 | 描述 | 备注 |
---|---|---|---|
thing_id | const void* | 物的唯一标识符 | 物对象的标识符,可从 thing_create 回调获得 linkkit创建的thing id |
event_identifier | const char* | 事件标示符 | 上报的事件标识符 |
设备端详细的API使用请参考文档:https://living.aliyun.com/doc#cusllk.html
6.3 用例编译
编译前确保aos\example\linkkitapp
目录下面为修改过的用例(参考第3章介绍)
编译命令为:
aos make linkkitapp@mk3060
其中 linkkitapp 为用例名称;mk3060为单板类型
编译后生成的文件为aos\out\linkkitapp@mk3060\binary\linkkitapp@mk3060.ota.bin
6.4 用例执行
用原始的linkkitapp可执行文件配网成功后,然后替换新的用例执行,在云端可以看到设备已经是在线状态:
而当设备端上传数据成功后,可以在云端看到设备上传的数据:
7 FOTA开发
7.1 准备工作
FOTA服务支持mqtt、coap、alink等协议连接阿里云,使用fota之前请确保以下功能已经具备:
至少支持了mqtt、coap、alink其中一种
bootloader支持固件升级
支持flash操作
7.2 启动FOTA服务
启动FOTA服务无需其他的初始化代码,只需要在连接上阿里云之后,调用该函数启动FOTA服务即可。
aos_post_event(EV_SYS, CODE_SYS_ON_START_FOTA, 0u);
7.3 实现FOTA回调函数
当启动了FOTA服务之后,系统会自动和云端交互。当有固件需要更新的时候,FOTA服务会自动从云端下载固件,然后通过FOTA的回调函数写入固件到flash中。
所以通过实现平台相关的的FOTA的回调函数就可以对接上FOTA功能。FOTA回调函数的实现代码位于platform/mcu/xxx/hal/ota_port.c
中。
FOTA回调函数的声明:
struct hal_ota_module_s {
hal_module_base_t base;
/* Link to HW */
int (*init)(hal_ota_module_t *m, void *something);
int (*ota_write)(hal_ota_module_t *m, volatile uint32_t *off_set,
uint8_t *in_buf , uint32_t in_buf_len);
int (*ota_read)(hal_ota_module_t *m, volatile uint32_t *off_set,
uint8_t *out_buf , uint32_t out_buf_len);
int (*ota_set_boot)(hal_ota_module_t *m, void *something);
};
这些回调函数一般在platform/mcu/xxx/hw.c
中的hw_start_hal()
函数中注册到ota服务中:
hal_ota_register_module(&esp8266_ota_module);
其中
esp8266_ota_module
类型为struct hal_ota_module_s
.具体的api详细说明请参考OTA服务
7.4 FOTA功能验证
本指南使用linkkit连接阿里IoT平台,以MQTT作为交互通道进行固件新版本查询,下载链接获取及进度上报;以http/https作为下载通道进行固件下载;
设备控制开发前先确认以下功能已经具备:
- 已经注册了阿里云账号并开通了物联网套件
- 设备能够连接到阿里的 智能生活开放平台
7.4.1 修改设备凭证
根据云端产生的设备凭证,修改头文件framework/protocol/linkkit/iotkit/sdk-encap/imports/iot_import_product.h
下的设备凭证信息。
#define PRODUCT_KEY "a1kxXZkfbQZ"
#define PRODUCT_SECRET "nLZxX3i0T9UMFw9K"
#define DEVICE_NAME "aos_air_box_01"
#define DEVICE_SECRET "bFwGW47hp4vmsBtAeWMGDq8JLJClDoRq"
7.4.2 准备更新固件文件
修改文件:framework/common/common.mk
:
先将app版本号改为app-2开头,目的:让服务器端版本高于终端运行版本。
编译 相关的例程,以 mk3060 为例:
aos make clean
aos make linkkitapp@mk3060
生成的linkkitapp bin 文件,路径:out/linkkitapp@mk3060/binary/linkkitapp@mk3060.bin
7.4.3 下载固件到mk3060
7.4.4 查看设备上线状态
在智能生活开放平台中可以看到当前设备的ProductKey
、DeviceName
、DeviceSecret
、固件版本号,也能看到当前设备是否上线:
7.4.5 固件上传
在智能生活开放平台中,点击固件升级栏目,新增固件,填入如下的信息,并上传固件bin文件:
7.4.6 固件升级
点击固件栏目的右边的验证固件按钮,选择固件版本号,开始更新固件,更新完成后,会有更新成功提示: