SoC方案
本章适用的设备:设备的控制逻辑与WiFi联网功能部署在同一个WiFi芯片上。如果您的设备不属于这种情况,请跳转到WiFi设备开发指导-开发环境准备,参看“确定开发模式”。
名词解释
名称 | 含义 |
---|---|
UserApp | User Application,用户部署在WiFi芯片上的应用程序,开发者自行实现的设备控制逻辑和业务逻辑 |
AcAPI | Ablecloud API,Ablecloud提供给开发者的API,供UserApp调用 |
设备应用开发框架
对于使用Ablecloud的WiFi SDK的嵌入式WiFi设备,设备的用户控制逻辑与WiFi联网模块在同一个WiFi芯片上。AbleCloud提供了整个程序的工程框架和SDK,SDK软件逻辑框图如下所示:
在使用SDK开发时,软件逻辑分如下三层:
1. UserApp:用户应用层。该层实现设备的控制过程,此部分由厂商自行实现。
2. AcAPI:Ablecloud为UserApp提供的接口层。该层负责用户控制逻辑与云端/APP之间的数据收发、协议报文的解析/组装等。
3. AC联网模块:Ablecloud提供的WiFi联网模块,包括硬件抽象层,云端协议层以及链路安全层。
开发时,开发者只需要了解Ablecloud提供的API接口,剩下的就是用户自己的控制逻辑的开发,不需要关心硬件相关的东西。
下面首先介绍开发者需要掌握的基本概念,之后以功能为主体,结合场景叙述开发者如何通过调用Ablecloud的API实现各个功能。
基本概念
UserApp通过AC联网模块可以实现与云端/移动端之间的消息交互,开发者不需要关心交互的消息的完整格式,只需要掌握如下概念:
(1)消息构成
对开发者来说,设备与云端/APP之间的消息由以下部分构成:
名称 | 作用 |
---|---|
MsgSN | 消息的序号(Msg Sequence Number),设备主动上报时填0;设备响应云端指令时必须填写对应的云端指令携带的MsgSN |
MsgCode | 标识payload的类型(具体见下一小节) |
Payloadlen | 消息携带的payload的长度,以字节为单位 |
payload | 消息体 |
(2)MsgCode
MsgCode用来标识设备与云端交互的消息类型,云端和设备端根据对应的MsgCode区分消息携带的payload的类型。
消息类型表(MsgCode)如下:
MsgCode | 消息类型 | 消息类型说明 |
---|---|---|
2 | AC_CODE_WIFI_CONNECTED | WiFi链接成功通知 |
3 | AC_CODE_WIFI_DISCONNECTED | WiFi断链通知 |
4 | AC_CODE_CLOUD_CONNECTED | 云端链接成功通知 |
5 | AC_CODE_CLOUD_DISCONNECTED | 云端链接断链通知 |
17 | ZC_CODE_OTA_BEGIN | MCU OTA开始通知 |
18 | ZC_CODE_OTA_FILE_BEGIN | MCU OTA文件传输开始通知 |
19 | ZC_CODE_OTA_FILE_CHUNK | MCU OTA文件块到达通知 |
20 | ZC_CODE_OTA_FILE_END | MCU OTA文件传输结束通知 |
21 | ZC_CODE_OTA_END | MCU OTA结束通知 |
63 | ZC_CODE_SUBSCRIBE_REPORT | 请求设备快速上报通知 |
64 | AC_EVENT_BASE | 设备自定义控制消息基址 |
[64,200) | AC_EVENT_CONTROL_AND_RESPONSE | 设备自定义,由服务或APP发给设备的控制消息以及设备的应答消息 |
[200,255] | AC_EVENT_DEVICE_REPORT | 设备自定义,设备上报信息 |
注意:
小于64的MsgCode是Ablecloud使用的消息码,开发者不能占用。[64,255]是提供给开发者的设备自定义消息码,开发者使用时需要遵守表中的规则,且要与移动端及UDS协商一致
(3)payload
是由MsgCode定义的消息类型的具体内容。payload的格式可以是二进制,也可以是JSON。
软件开发配置
(1)搭建开发环境
对于不同厂家的WiFi芯片,其对应的软件开发环境各不相同,开发者可以参考WiFi芯片的配套开发手册,搭建开发环境并掌握WiFi固件的烧录方法。
(2)修改设备配置信息
Ablecloud为开发者提供了两套环境:用于开发及上线前测试的test环境以及用于生产的生产环境。开发者需要确定使用哪一个环境,修改配置文件ac_cfg.h中的宏CLOUD_ADDR的配置值(参见下面描述)。
打开工程里的ac_cfg.h文件,将在注册开发者账号和注册产品时申请到的主域id更新到MAJOR_DOMAIN_ID,子域id更新到SUB_DOMAIN_ID,设备私钥更新到DEFAULT_IOT_PRIVATE_KEY,设备物理id更新到DEVICE_ID。如果使用WiFi的MAC地址作为设备Id,此处的DEVICE_ID可以不关心。 如下所示:
#ifndef __AC_CFG_H__
#define __AC_CFG_H__
/*
AC_DEV_SERVER :development environment,
AC_CH_SERVER:china production environment,
AC_US_SERVER:us production environment,
AC_EU_SERVER:eu production environment,
AC_EA_SERVER:ea production environment
*/
#define CLOUD_ADDR AC_CH_SERVER
/*可选,开发者需要对接微信时填写*/
#define WEIXIN_DEVICE_TYPE "gh_6bbea48e771e"
/*可选,开发者需要对接京东时需要填写*/
#define JD_DEVICE_UUID "EQSCNN"
//[8,64)字节,不足的前面补0,只能是英文字母和数字(其他的非法字符会被滤除)。如果使用MAC地址作为设备Id,此处可以不用关心
#define DEVICE_ID "66666666"
#define MAJOR_DOMAIN "ablecloud" /*主域*/
#define MAJOR_DOMAIN_ID 3/*主域id*/
#define SUB_DOMAIN "test" /*子域*/
#define SUB_DOMAIN_ID 6 /*子域id*/
#define DEFAULT_IOT_PRIVATE_KEY {\
0xE5,0x49,0x6A,0xCC,\
0x9D,0xE8,0x68,0x76,\
0xCE,0x5D,0xF4,0xB9,\
0xD5,0xE5,0x30,0x44,\
0xB6,0x39,0x9B,0x6C,\
0xB2,0x38,0xC8,0xCC,\
0x59,0x1B,0xD0,0x3C,\
0x9B,0x03,0x00,0x6B,\
0xFD,0xDE,0xB1,0x99,\
0x72,0x35,0xE7,0x9E,\
0xD8,0xD0,0x64,0x73,\
0xF5,0xE0,0x44,0xB9,\
0xE7,0x35,0xEB,0x65,\
0xCE,0xE9,0xF1,0x54,\
0xEB,0x14,0x84,0x9A,\
0x6F,0x5F,0x24,0x43,\
0x34,0xCC,0x61,0xE7,\
0x65,0xE7,0x6C,0x1A,\
0x8F,0x41,0x18,0x03,\
0x3D,0xF9,0xBC,0x91,\
0x02,0x62,0x87,0xFF,\
0x10,0xD7,0x50,0xE9,\
0xF3,0x52,0xCE,0xDB,\
0x58,0xF2,0xBE,0x49,\
0xE4,0x9B,0x1A,0x58,\
0x90,0x53,0x8F,0x7C,\
0xF6,0xDD,0x3B,0x12,\
0x78,0x9C,0x59,0xDA\
}
#endif
(3)配置固件版本号
使用Ablecloud提供的WiFi SDK编译得到的固件的版本号在对WiFi SDK进行编译的Makefile中通过预定义宏进行定义,对于没有Makefile的固件,在编译器的编译选项中的预定义宏ZC_MODULE_VERSION
进行定义:
ZC_MODULE_VERSION=0x161209
该值会与WiFi模块型号拼接后产生一个版本号,如AI6060H__V16.12.09
。该版本号对应于云端控制台中看到的设备的通信模块固件版本号,如下所示:
设备在Ablecloud后台上传并发布新的OTA版本时,需要填写新的OTA固件的版本号,填写的版本号要与新的固件的版本号一致,否则会导致设备不断地重复进行OTA流程。
(4)配置设备ID(仅使用自定义设备ID进行注册时用)
SDK默认使用设备的MAC地址做为设备ID进行注册。如果开发者需要使用自定义的ID做为设备ID,则需要将ac_cfg.h中的宏#define DEVICE_ID
的值改为开发者自定义的设备ID。
注意
开发者自定义的设备ID必须是字符串的形式
开发者自定义的设备ID字符串长度不能超过64个字节,超过的部分会被截断
设备ID只能是英文字母和数字,其他的非法字符会被滤除
获取API接口详细信息
下面开发指导中用到的所有AC提供的SoC API函数,其接口的详细信息请参阅WiFi设备API参考手册。
配置服务器环境
如上所述,开发者可以通过修改ac_cfg.h中的宏CLOUD_ADDR的值来配置WiFi固件连接的服务器环境。
配网
WiFi设备是通过WiFi模块连接上路由器,再与广域网相连接的,所以WiFi设备要连接到云端,第一步就是连接上路由器。配网即指:利用smartLink、AirKiss或AP配网等技术,通过路由器、WiFi模块以及连接上路由器的手机APP的协同工作,是WiFi模块获取到路由器的SSID和密码,并接入路由器的流程。
AC提供提供两种配网方式:Station配网模式(smartlink等)和AP配网模式。大部分WiFi模块这两种配网方式都支持,部分支持其中一种。
不论使用何种方式配网,当WiFi模块在成功连接上路由器后,都会切换到Station模式运行,并保存路由器的SSID和密码,断电重启后会自动连接该路由器。
Station配网模式(smartlink等)
Station配网模式是指WiFi模块工作在站(Station)模式时,使用smartLink、AirKiss等技术,通过路由器、WiFi模块以及连接上路由器的手机APP的协同工作,实现在WiFi模块不知道路由器的SSID和密码的情况下,使WiFi模块获取到路由器的信息、并连接上路由器的过程。配网状态即WiFi模块进入smartLink或AirKiss等状态,准备好开始配网流程的状态。
WiFi模块进入配网状态后,会等待APP在局域网广播的路由器的SSID和密码,获取到密码后,WiFi模块会连接到路由器并通过DHCP获取到IP地址。
开发者要使WiFi进入配网状态,需要调用 AC_SendRestMsg
接口,通过AC联网模块给WiFi模块发送重置WiFi指令。
注意:
调用该接口会清除存储的路由器信息,并使WiFi芯片重启。
开发示例如下:
/*本处开发示例通过按键触发WiFi进入配网状态,*/
void KeyIntHandle(void)
{
unsigned long ulStatus;
ulStatus = GPIOIntStatus(GPIO_PORTF_BASE, true);
GPIOIntClear(GPIO_PORTF_BASE, ulStatus);
if (ulStatus & GPIO_PIN_0) //
{
SysCtlDelay(SysCtlClockGet() / 1000 / 3);//10ms
AC_SendRestMsg(NULL);//调用AbleCloud接口,给WiFi模块发送重置WiFi指令,触发WiFi进入配网状态
while (GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_0) == 0x00);
SysCtlDelay(SysCtlClockGet() / 3); // 延时约10ms,消除松键抖动
}
}
AP配网
AP配网适用于不支持广播的路由器。
AP配网过程如下:
(1)通过命令/API使WiFi模块切换到AP模式
(2)将手机通过WiFi连接到WiFi模块
(3)AP发送命令给WiFi模块,获取周围的AP热点,选择要连接的路由器,将其SSID和密码发送给WiFi模块
(4)WiFi模块保存路由器信息后重启并切换到Station模式,连接路由器
使WiFi切换到AP模式并配置切换到AP后的SSID的API为ZC_ConfigSoftAp
,开发示例如下:
char SsidName[] = "AblecloudAP"; /*must less than 27bytes, the excess bytes will be ignored*/
ZC_ErrorCode ret;
ret = AC_SendSetApMsg(SsidName, strlen(SsidName));
if (ZC_RET_OK != ret)
{
ZC_Printf("set ap's SSID failed\n");
}
云端激活
设备连接到云端,和云端完成通信握手叫做设备云端激活。云端激活需要UserApp处理AC联网模块的相应通知。当WiFi模块连接上路由器后,AC联网模块会在ac_hal.c
函数AC_DealNotifyMessage
中发送事件ZC_CODE_WIFI_CONNECTED
给UserApp,UserApp需要在该事件通知中调用设备注册函数把设备的注册信息(包括设备域信息,设备密钥,设备ID,设备版本)发给AC联网模块,AC联网模块之后会启动连接云端的流程,连接到云端后,会给UserApp发送连接云端成功的通知消息ZC_CODE_CLOUD_CONNECTED
,其处理Demo Code如下:
void AC_DealNotifyMessage(ZC_MessageHead *pstruMsg, AC_OptList *pstruOptList, u8 *pu8Playload)
{
//deal notify message about state of WiFi connection and cloud connection
switch(pstruMsg->MsgCode)
{
case ZC_CODE_WIFI_CONNECTED://WiFi module disconnect with AP
{
AC_SendDeviceRegsiter(g_u8EqVersion,g_u8ModuleKey,(u8*)&g_u64Domain,NULL);
break;
}
case ZC_CODE_CLOUD_CONNECTED://WiFi module connect with cloud
{
#if defined(DEMO_REPORT)
UserCallback_CloudConnect();
#endif
AC_StoreStatus(CLOUDSTATUS,CLOUDCONNECT);
break;
}
case ZC_CODE_CLOUD_DISCONNECTED://WiFi module disconnect with cloud
{
#if defined(DEMO_REPORT)
UserCallback_CloudDisconnect();
#endif
AC_StoreStatus(CLOUDSTATUS,CLOUDDISCONNECT);
break;
}
.
.
.
.
.
.
}
}
注意:
1. 默认使用WiFi模块的MAC地址作为设备id。如果开发者需要使用自定义的设备id进行注册,则需要在调用AC_SendDeviceRegsiter
时,将最后一个参数NULL
改为g_u8DeviceId
。
2. 开发者可以根据需要在不同的通知中添加自己的处理逻辑,AC_StoreStatus
接口仅用作示例说明,开发者可以修改。
接收云端消息
设备云端激活后,UserApp就可以开始和云端进行通信了。
云端消息到达时,AC联网模块会通过文件ac_hal.c
中的接口函数 AC_DealEvent
将数据传递给UserApp,开发者需要在该函数中调用自己的处理逻辑,对通过云端传输的控制指令进行处理,处理结束后,需要回复应答消息给云端。应答消息中的MsgId要与云端下发的控制指令的中的MsgId一致。
开发示例如下:
/*云端下行控制消息通知函数入口*/
void AC_DealEvent(ZC_MessageHead *pstruMsg, AC_OptList *pstruOptList,u8 *pu8Payload, u16 u16PayloadLen)
{
//deal with message defined by device.
switch(pstruMsg->MsgCode)
{
case MSG_SERVER_CLIENT_SET_LED_ONOFF_REQ:
{
AC_DealLed(pstruMsg, pstruOptList, pu8Payload, u16PayloadLen);
break;
}
default:
{
break;
}
}
}
示例中的MsgCode的值由开发者自定义,其中,[64, 200)预留给开发者,用于设备与云端/移动端之间的控制指令及响应,[200, 255]预留给设备上报的消息。
为了更好的体验,我们建议消息处理接口中,开发者在处理完控制逻辑后,将最新的设备状态作为响应信息返回给云端。
注意:
对于云端下发的数据,设备需要在5秒内给出响应,否则云端会认为下发的消息处理失败。
处理云端数据
开发者通过 AC_DealEvent
接口获取到云端下发的数据的payload后,需要解析payload。payload的格式支持二进制和JSON,具体使用哪一种,由开发者决定。两种格式的开发示例分别如下:
二进制格式
以开关灯为例,示例如下:
/*控制数据包MsgCode:68*/
typedef struct tag_STRU_LED_ONOFF
{
u8 u8LedOnOff ; // 0:关,1:开
u8 u8ControlStatus;//控制消息忽略
u8 u8Pad[2];
}STRU_LED_ONOFF;
/*响应数据包MsgCode:102*/
/*byte0:(1:命令执行成功,0:命令执行错误)byte1-3(忽略)*/
void AC_DealLed(ZC_MessageHead *pstruMsg, AC_OptList *pstruOptList, u8 *pu8Payload, u16 u16PayloadLen)
{
u16 u16DataLen;
u8 resp[4] = {0};
ZC_ErrorCode ret;
switch (((STRU_LED_ONOFF *)pu8Payload)->u8LedOnOff)
{
case 0://deal control message of switch
case 1:
resp[0] = AC_BlinkLed(((STRU_LED_ONOFF *)pu8Payload)->u8LedOnOff);
break;
default:
{
ZC_Printf("dealled\n");
break;
}
}
/*构造并发送消息,接口含义详见详见《WiFi设备API参考手册》*/
ret = AC_SendMessage(CLIENT_SERVER_OK,pstruMsg->MsgSN,
(u8*)test, sizeof(test),
pstruOptList);
if (ZC_RET_OK != ret)
{
ZC_Printf("AC_DealLed Err, AC_SendMessage failed\n");
return;
}
return;
}
JSON格式
//请求数据包
{ 70 :[
//关灯
{"switch", 0}
//开灯
{"switch", 1}
]}
//响应数据包
{ 102 :[
//失败
{"result", false},
//成功
{"result", true}
]}
void AC_DealJsonMessage(ZC_MessageHead *pstruMsg, AC_OptList *pstruOptList, u8 *pu8Playload)
{
/*处理设备自定义控制消息*/
u16 u16DataLen;
u32 u32LedOnOff;
bool result = false;
char *out;
cJSON *root;
/*解析收到的JSON数据*/
cJSON *format = cJSON_Parse(pu8Playload);
ZC_ErrorCode ret;
if(format)
{
u32LedOnOff = cJSON_GetObjectItem(format,"switch")->valueint;
switch (u32LedOnOff)
{
case 0://处理开关消息
case 1:
result = AC_BlinkLed(u32LedOnOff);
break;
}
cJSON_Delete(format);
}
/*JSON内存申请/*
root=cJSON_CreateObject();
/*构造JSON消息*/
cJSON_AddBoolToObject(root,"result",result);
out=cJSON_Print(root);
cJSON_Delete(root);
/*发送消息,接口含义详见《WiFi设备API参考手册》*/
ret = AC_SendMessage(CLIENT_SERVER_OK, pstruMsg->MsgSN,
(u8*)out, strlen(out),
pstruOptList);
if (ZC_RET_OK != ret)
{
free(out);
ZC_Printf("AC_DealJsonMessage Err, AC_SendMessage failed\n");
return;
}
/*释放JSON消息*/
free(out);
}
注意:
以上示例中处理的设备自定义控制消息格式及语义,需要开发者自己定义,此处代码仅用于说明。
上报数据给云端
设备在云端激活后,UserApp就可以定时或者根据外界条件触发来将设备的数据和状态主动上报到云端。
注意:
UserApp上报的消息号(MsgCode)必须大于等于200。
上报数据时,需要调用接口 AC_BuildMessage
进行组包,之后调用 AC_SendMessage
接口将数据通过AC联网模块发送给云端。
二进制格式和JSON格式的开发示例分别如下:
二进制格式
/*上报数据包=code:203 + req:{1,0,0,0}*/
typedef struct tag_STRU_LED_ONOFF
{
u8 u8LedOnOff ; // 0:关,1:开
u8 u8ControlStatus;//0为APP控制开关,1为按键控制开关
u8 u8Pad[2];
}STRU_LED_ONOFF;
void AC_SendStatus2Server(u8 u8control)
{
ZC_ErrorCode ret;
/*上报demo灯的状态*/
STRU_LED_ONOFF struReport;
u16 u16DataLen;
/*读取demo灯状态*/
struReport.u8LedOnOff = GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_2);
struReport.u8LedOnOff = struRsp.u8LedOnOff>>2;
struReport.u8ControlStatus = u8control;
/*构造并发送消息*/
ret = AC_SendMessage(203, 0, (u8*)&struReport, sizeof(STRU_LED_ONOFF), NULL);
if (ZC_RET_OK != ret)
{
ZC_Printf("AC_SendStatus2Server Err, AC_SendMessage failed\n");
return;
}
}
JSON格式
用户可调用第三方源码构造JSON格式的消息体。
//请求数据包
{ 203 :[
//关灯
{"switch", 0}
//开灯
{"switch", 1}
]
[
//APP控制开关
{"controltype", 0},
//按键控制开关
{"controltype", 1}
]}
void AC_SendLedStatus2Server(u8 controltype)
{
ZC_ErrorCode ret;
/*上报demo灯的状态*/
cJSON *root;
char *out;
u8 u8LedOnOff;
u16 u16DataLen;
/*JSON协议内存分配*/
root=cJSON_CreateObject();
u8LedOnOff = GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_2);
u8LedOnOff = u8LedOnOff>>2;
/*构造JSON消息体*/
cJSON_AddNumberToObject(root,"action", u8LedOnOff);
cJSON_AddNumberToObject(root,"controltype", controltype);
out=cJSON_Print(root);
cJSON_Delete(root);
/*发送消息*/
ret = AC_SendMessage(203, 0, (u8*)&out, strlen(out), NULL);
if (ZC_RET_OK != ret)
{
/*释放JSON消息*/
free(out);
ZC_Printf("AC_SendLedStatus2Server Err, AC_SendMessage failed\n");
return;
}
/*JSON协议内存释放*/
free(out);
}
注意:
以上示例中上报的设备状态消息格式及语义,需要开发者自己定义,此处代码仅用于说明。
上报数据给云端后等待响应消息
如果开发者在上报给UDS数据后需要等待UDS的响应,则需要:
(1)UDS开发者在接收到(或者处理完成)设备的上报信息后,调用Ablecloud的UDS接口handleDeviceMsg
发送响应消息给设备
(2)开发者自己维护上报的消息的序列号MsgSN,并在通过数据上报接口发送数据时,填写上报的消息的MsgSN
注意:
1.对于设备上报消息的响应消息,其MsgCode由设备端开发者与UDS开发者协议制定,但是必须在[200,255]之间
2.对于UDS的响应消息,开发者不需要且不能再回复应答消息给云端
设备绑定与解绑
设备绑定指的是WiFi设备和移动端APP客户端之间的绑定。
设备云端激活后,如果移动端的APP发起了设备绑定流程,AC联网模块会与其建立绑定关系。该过程对设备端开发者是透明的,开发者不需要关心。
对于已经绑定过的设备,开发者可以调用Ablecloud的API接口 AC_SendUbindMsg
强制解除设备与移动端APP用户之间的绑定关系。
局域网通信
局域网通信是指移动端APP和设备在一个局域网内时,不通过云端直接进行通信。
局域网通信能够保证在外网异常的情况下对设备的控制。同时在局域网环境下还可以提供设备之间的高速访问能力及关联控制功能。
如果APP与设备在同一个局域网,同时还可以通过云端连接,则由APP开发者选择配置APP发给设备的控制指令优先走局域网还是优先走云端:
(1)如果优先走局域网,APP会通过局域网发送控制消息给设备,如果在超时时间内没有收到应答,则APP会再次尝试通过云端重发该消息,如果还是超时内没有收到设备的应答,则APP会返回发送失败
(2)如果优先走云端,APP会先通过云端发送控制消息给设备,如果在超时时间内没有收到应答,则APP会再次尝试通过局域网重发该消息,如果还是超时内没有收到设备的应答,则APP会返回发送失败
(3)app通过局域网发送消息给设备,前提条件是app完成了局域网内的设备发现,如果没有在局域网内发现设备,则无论是否设置了局域网优先,app都不会发送局域网消息给设备
(4)app SDK不会对云端连接状态进行判定,也就是说,如果云端无效而局域网有效,但是设置了云端优先,还是会先走云端,超时后再走局域网
建议在云端和局域网都在线的情况下,移动端与设备的交互以及设备的数据上报仍然走云端。只有在外网异常,只有局域网的时候,才切换到只使用局域网的状态。这样利于多用户间的数据同步,并能完整记录用户的行为数据和设备的历史数据,使云端数据分析的结果更可靠。
AC联网模块实现了设备与移动端的局域网通信的相关流程,设备端开发人员不需要再做额外的开发,只需要添加自己的处理逻辑,对接收到的局域网消息进行处理。
AC联网模块对发送给设备的局域网消息的处理与发送给设备的云端消息的处理流程是一个流程,都在ac_hal.c中的AC_DealEvent
函数中处理。开发者可以根据该函数的入参AC_OptList *pstruOptList
是否为空来区分消息的来源:如果该参数为空,则消息为云端发送来的消息;如果该参数不为空,则消息为局域网发送来的消息。返回应答消息给局域网的流程与返回应答消息给云端的流程是一样的,都是先调用APIAC_BuildMessage
进行组包,之后再调用APIAC_SendMessage
将消息发送给APP。具体开发示例参见上面的章节处理云端数据和上报数据给云端。
注意
开发者在发送局域网消息时,需要在组包时将AC_DealEvent
函数传入的参数AC_OptList *pstruOptList
传给组包函数AC_BuildMessage
。
不支持设备通过局域网主动上报消息给APP
局域网加密
在进行局域网通信时,设备端与APP之间的链路可能会需要加密,为此Ablecloud为开发者提供了三种加密方式,分别是:
(1)动态加密。采用云端动态分配的密钥进行加密。
(2)静态加密。采用程序内置的默认密钥进行加密。
(3)不加密。
加密方式由WiFi指定,默认为动态加密方式,如果开发者需要使用静态加密方式,则需要在ac_cfg.h中添加预定义宏LOCAL_STATIC_TOKEN
设备属性快速上报
Ablecloud提供了设备属性快速上报的功能,主要针对以下两种场景:
1.通过云端快速上报设备属性
当有APP打开时,用户有可能想更实时的查看到设备的状态变化,此时需要设备提高上报频率;而当没有APP打开时,设备的上报频率可以降低
2.局域网内快速上报设备属性
在局域网内,当有APP打开时,用户有可能想更实时的收到设备状态的上报消息。
对于场景1,设备上报的频率及持续时间是由APP开发者配置的,对于场景2,设备上报的频率是默认的(3秒一次)。
当需要设备上报属性时,AC联网模块会在ac_hal.c
的函数AC_DealNotifyMessage
中发送事件ZC_CODE_SUBSCRIBE_REPORT
给UserApp,UserApp需要在该事件通知中构造并上报设备的全量信息。示例如下:
void AC_DealNotifyMessage(ZC_MessageHead *pstruMsg, AC_OptList *pstruOptList, u8 *pu8Playload)
{
//deal notify message about state of WiFi connection and cloud connection
switch(pstruMsg->MsgCode)
{
.
.
.
.
.
.
case ZC_CODE_SUBSCRIBE_REPORT:
{
AC_SendDeviceStatus(pstruOptList);
break;
}
default:
{
ZC_Printf(eLL_Err, eMT_Prot, "AC_DealNotifyMessage error\n");
break;
}
}
}
void AC_SendDeviceStatus(AC_OptList *pstruOptList)
{
u8 DeviceStatus[100] = {0};
AC_SendMessage(MSG_SERVER_CLIENT_GET_LED_STATUS_RSP,0,
(u8*)&DeviceStatus, sizeof(DeviceStatus),
pstruOptList);
}
注意:
1.无论是云端快速上报请求还是局域网快速上报请求,AC联网模块都是通过上述接口通知开发者的,开发者可以通过入参
pstruOptList
是否为空来判断本次上报是发送给云端还是局域网。如果pstruOptList
为空,则本次上报是发送给云端(UDS)的;如果pstruOptList
不为空,则本次上报是发送给局域网内的APP的。
2. 对于局域网快速上报,WiFi是通过局域网UDP广播发送给局域网内的所有APP的。
3.上报消息的MsgCode必须不小于200
OTA
OTA即空中下载技术,对于设备端开发者,即是通过云端连接对WiFi固件进行升级的功能。
对于嵌入式WiFi设备,AC联网模块已经集成了OTA功能,开发者不需要做额外的开发。
如下事项需要设备端开发者注意:
1. WiFi固件的版本号需要由开发者维护,要修改WiFi固件版本号,需要修改工程配置中的预定义宏ZC_MODULE_VERSION的值,如: ZC_MODULE_VERSION=0x161209
,该值会与WiFi模块的型号拼接后生成一个版本号,如AI6060H__V16.12.09
。
2. 设备要进行OTA,需要在Ablecloud的控制台上传用于OTA的固件版本并填写版本号。
3. 设备云端激活时会将当前的设备固件版本号上报给云端,云端会比较该版本号和用于OTA的固件版本号,如果一致,则无动作,如果不一致,则触发OTA升级。
如果开发者希望自行处理OTA的事件通知和数据包,AC联网模块也提供了相关的接口(但是强烈不建议开发者自行修改),在ac_hal.c
文件中的函数AC_DealEvent
中,如下:
void AC_DealEvent(ZC_MessageHead *pstruMsg, AC_OptList *pstruOptList,u8 *pu8Payload, u16 u16PayloadLen)
{
/*deal with message defined by device.*/
switch(pstruMsg->MsgCode)
{
case ZC_CODE_OTA_BEGIN:
{
AC_DealEvent_OtaBegin(pstruMsg, pu8Payload, u16PayloadLen);
break;
}
case ZC_CODE_OTA_FILE_BEGIN:
{
AC_DealEvent_OtaFileBegin(pstruMsg, pu8Payload, u16PayloadLen);
break;
}
case ZC_CODE_OTA_FILE_CHUNK:
{
AC_DealEvent_OtaFileChunk(pstruMsg, pu8Payload, u16PayloadLen);
break;
}
case ZC_CODE_OTA_FILE_END:
{
AC_DealEvent_OtaFileEnd(pstruMsg, pu8Payload, u16PayloadLen);
break;
}
case ZC_CODE_OTA_END:
{
AC_DealEvent_OtaEnd(pstruMsg, pu8Payload, u16PayloadLen);
break;
}
case MSG_SERVER_CLIENT_SET_LED_ONOFF_REQ:
{
AC_DealLed(pstruMsg, pstruOptList, pu8Payload, u16PayloadLen);
break;
}
default:
{
ZC_Printf(eLL_Warn, eMT_Prot, "AC_DealEvent unknown msgcode: %d\n", pstruMsg->MsgCode);
break;
}
}
ZC_Printf(eLL_Info, eMT_Prot, "AC_DealEvent finished\n");
}
具体的每个OTA事件的含义请参见本文的基本概念中关于msgcode的描述,每个OTA事件的处理示例请参见ac_hal.c
中的对应函数实现。
OTA的更多信息请参考功能介绍中的OTA功能说明。
定时任务
定时任务使用云端定时。
云端定时指的是移动端APP或者设备将定时任务信息发送到云端,云端记下来任务触发的时间。在到达触发时间后,云端将控制指令发送给设备。云端定时要求在任务触发时设备一定是在线的,否则执行失败。云端定时只适用于长连接的设备。
由于云端定时从设备看来只是执行了一条云端发来的普通指令,因此设备端不需要进行针对开发。
NTP
AC联网模块提供了从云端服务器通过NTP标准协议获取UTC时间的功能。需要获取NTP时间时,需要调用APIAC_GetNtpTime
通知AC联网模块进行NTP时间的获取,获取到时间后,AC联网模块会通过函数AC_HandleNtp
将NTP得到的数据交给UserApp,开发者可以在此添加自己的处理逻辑。开发示例如下:
void AC_HandleNtp(ZC_NTP_TIME* ntp)
{
ZC_Printf("ntp time is %d-%d-%d %d:%d:%d weekday is %d\n",ZC_HTONS(ntp->year),
ntp->month,
ntp->day,
ntp->hour,
ntp->min,
ntp->sec,
ntp->weekday);
}
ZC_NTP_TIME的定义如下:
typedef struct
{
u16 year;
u8 month;
u8 day;
u8 weekday;
u8 hour;
u8 min;
u8 sec;
u32 ntp_sec;
}ZC_NTP_TIME;
字段 | 类型 | 说明 |
---|---|---|
year | u16 | 年(UTC时间) |
month | u8 | 月(UTC时间) |
day | u8 | 日(UTC时间) |
weekday | u8 | 星期(UTC时间) |
hour | u8 | 时(UTC时间) |
min | u8 | 分(UTC时间) |
sec | u8 | 秒(UTC时间) |
ntp_sec | u32 | 当前时间距1970年1月1日00:00:00的描述(UTC时间) |
获取链路信号质量
AC联网模块提供了获取WiFi和其连接的路由器之间的无线链路的信号质量的功能。需要获取信号质量时,UserApp要调用APIAC_GetLinkQuality
通知AC联网模块进行信号链路质量的获取,获取到结果后,AC联网模块会通过函数AC_HandleLinkQuality
将结果交给UserApp,开发者可以在此添加自己的处理逻辑。开发示例如下:
void AC_HandleLinkQuality(s8 s8LinkQuality)
{
ZC_Printf(eLL_Info, eMT_Cloud, "Link quality is %d\n",s8LinkQuality);
}
产测
Ablecloud针对开发者的产测需求提供了产测服务,具体流程请参见产测服务。
要触发进入产测模式,需要调用APIAC_SetProductTest
。
通过局域网打印log
AC联网固件提供了通过局域网将WiFi固件运行过程中的log打印到PC端的功能,便于开发者在开发阶段调试和观察设备的运行。
要开启/关闭该功能,需要使用诸如NetAssist等网络调试工具给设备发送UDP命令。
(1)首先,网络调试助手所在的PC要与设备处于同一局域网
(2)通过网络助手向设备6689端口发送UDP数据 00 00 10 00开启日志打印功能
(3)通过网络助手向设备6689端口发送UDP数据 00 00 20 00关闭日志打印功能
(4)开启局域网日志打印功能后,通过ZC_Printf函数打印的log都会通过UDP端口6689发送给PC端上的网络调试助手。