API文档
本章先介绍Linux设备开发者在使用API时需要掌握的基本概念,然后分功能介绍Ablecloud提供给Linux设备的所有的API。
建议开发者先阅读Linux设备开发指南。
基本概念
开发者通过调用Ablecloud的API可以实现与云端/移动端之间的消息交互,开发者不需要关心交互的消息的完整格式,只需要掌握如下概念:
(1)消息构成
对开发者来说,设备与云端之间的消息由以下部分构成:
名称 | 作用 |
---|---|
MsgCode | 标识payload的类型 |
MsgSN | 消息的序号(Msg Sequence Number),对于SDK AC_LI_v1.0.2_20170110之前的版本:设备主动上报时0;设备响应云端指令时必须填写对应的云端指令携带的MsgSN;对于SDK AC_LI_v1.0.2_20170110以及之后的版本,开发者只有在上报且异步接收响应的场景中才需要关心,其他场景不需要关心 |
IsLANMsg | 表示消息的来源:1-局域网,2-云端 |
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结束通知 |
64 | AC_EVENT_BASE | 设备自定义控制消息基址 |
(64,200) | AC_EVENT_CONTROL_AND_RESPONSE | 设备自定义,由服务或APP发给设备的控制消息以及设备的应答消息 |
[200,255] | AC_EVENT_DEVICE_REPORT | 设备自定义,设备上报信息 |
注意:
小于64的MsgCode是AC使用的消息码,开发者不能占用。[64,255]是提供给开发者的设备自定义消息码,开发者使用时需要遵守表中的规则,且要与移动端协商一致
(3)payload
是由MsgCode定义的消息类型的具体内容。payload的格式可以是二进制,也可以是JSON。
服务器环境配置接口
Ablecloud为开发者提供了用于开发测试的test环境,以及用于产品正式生产使用的生产环境。通过在ac_cfg.h
中修改CLOUD_ADDR
的值,可以选择不同的环境。
AC_TEST_SERVER :测试环境,对应test.ablecloud.cn
AC_CH_SERVER:生产环境-中国,对应device.ablecloud.cn
AC_US_SERVER:生产环境-美洲,对应usdevice.ablecloud.cn
AC_EU_SERVER:生产环境-欧洲,对应eudevice.ablecloud.cn
AC_EA_SERVER:生产环境-东南亚,对应eadevice.ablecloud.cn
局域网通信接口
局域网通信加密方式配置接口
接口定义
void AC_SetLocalLevel(PCT_LOCAL_LEVEL Etoken);
参数
字段 | 类型 | 说明 |
---|---|---|
Etoken | PCT_LOCAL_LEVEL | 局域网通信加密方式 |
typedef enum
{
PCT_LOCAL_NONE_TOKEN = 0, /*不加密*/
PCT_LOCAL_STATIC_TOKEN = 1, /*静态加密*/
PCT_LOCAL_DYNAMIC_TOKEN = 2 /*动态加密*/
}PCT_LOCAL_LEVEL;
注意:
该函数在AC_Hal_Init函数中调用。
状态通知类回调函数
当设备的网络连接状态发生变化,如连接上路由器、连接上云端等,Ablecloud联网模块会通过调用回调函数,开发者可以在相应的回调函数中添加自己的处理逻辑,如置某些标志,表明可以开始进行数据上报。
设备与云端连接状态变化通知接口
所在文件
Src/UserSrc/ac_user_eventCallback.c
接口定义
/*设备与云端连接成功回调函数*/
void UserCallback_CloudConnect()
{
gu8UserConnWithACloudStatus = 1;
}
/*设备与云端连接断开回调函数*/
void UserCallback_CloudDisconnect()
{
gu8UserConnWithACloudStatus = 0;
}
强制解绑设备接口
设备与APP用户绑定后,开发者在需要时可以调用API强制解除该绑定关系。
API:
void AC_SendUbindMsg(void);
API行为 通知Ablecloud联网模块发起设备绑定关系强制解除操作
设备控制消息接收回调函数
当设备接收到相关的设备自定义控制消息后,会调用开发者的接收回调函数将消息传递给开发者。自定义消息的MsgCode必须在[64,200)之间。
所在文件
Src/UserSrc/ac_user_eventCallback.c
接口定义
void UserCallback_RecvCtrlMsg(T_AC_SAL_MsgInfo *ptCtrlMsgInfo, u8 *pu8Msg, u8 MsgSN)
参数
字段 | 类型 | 说明 |
---|---|---|
ptCtrlMsgInfo | T_AC_SAL_MsgInfo * | 指向 描述传递给开发者的控制消息的结构体 的指针 |
pu8Msg | u8 * | 传递给开发者的控制消息的buffer地址 |
MsgSN | u8 | 控制消息的消息序列号(开发者可以不关心) |
T_AC_SAL_MsgInfo的定义如下:
/*This struct used to describe a message between device and cloud/app*/
typedef struct
{
u8 MsgCode; /*msg code of msg*/
u8 IsLANMsg; /*1: LAN network msg, 0: WLAN msg to cloud*/
u16 MsgLen; /*the real len of msg*/
int ClientId; /*valid only when IsLANMsg is 1, indicate the tcp client's ID*/
} T_AC_SAL_MsgInfo;
字段 | 类型 | 说明 |
---|---|---|
MsgCode | u8 | 消息的MsgCode |
IsLANMsg | u8 | 消息来源/去向:1-局域网;0-云端 |
MsgLen | u16 | 消息长度,单位为Byte |
ClientId | int | 仅在消息为局域网消息时有效,表征发送该消息的APP与设备之间的Socket表示,开发者在进行应答时,需要回填该值 |
开发示例
void UserCallback_RecvCtrlMsg(T_AC_SAL_MsgInfo *ptCtrlMsgInfo, u8 *pu8Msg)
{
if ((NULL == ptCtrlMsgInfo) || (NULL == pu8Msg))
{
printf("UserRecvCtrlMsgCallBack input parameter is invalid\n");
return;
}
switch(ptCtrlMsgInfo->MsgCode)
{
case MSGCODE_LED_CTRL:
{
AC_Sal_DealLed(ptCtrlMsgInfo, pu8Msg);
break;
}
default:
{
printf("UserRecvCtrlMsgCallBack recv ctrl msg err, invalid msgcode %d\n", ptCtrlMsgInfo->MsgCode);
break;
}
}
}
示例中,的MsgCode的值由开发者自定义,其中,[64, 200)预留给开发者,用于设备与云端/移动端之间的控制指令及响应,[200, 255]预留给设备上报的消息。
为了更好的体验,我们建议消息处理接口中,开发者在处理完控制逻辑后,将最新的设备状态作为响应信息返回给云端。
注意:
对于云端下发的数据,设备需要在5秒内给出响应,否则云端会认为下发的消息处理失败。
如果开发者的处理逻辑较为耗时(超过了5秒),建议开发者先回复响应给云端,之后再进行处理
与云端/移动端通信接口
设备与云端的交互流程及代码开发框架具体流程参见Linux设备开发指导中的章节接收云端/移动端消息、处理云端/移动端数据和发送应答信息中的描述。这里只描述相关API的具体信息及开发示例。
设备端应答消息发送接口
开发者在收到云端/移动端发送的控制消息后,需要在处理后回复应答消息,接口如下:
API:
AC_STATUS AC_Sal_SendMsg_NoRsp(T_AC_SAL_MsgInfo *ptSendMsgInfo, u8 *pu8SendMsgBuf);
参数
字段 | 类型 | 说明 |
---|---|---|
ptSendMsgInfo | T_AC_SAL_MsgInfo * | 指向 描述开发者要发送的消息 的结构体 的指针 |
pu8SendMsgBuf | u8 * | 开发者要发送的消息的buffer地址 |
T_AC_SAL_MsgInfo的定义如下:
/*This struct used to describe a message between device and cloud/app*/
typedef struct
{
u8 MsgCode; /*msg code of msg*/
u8 IsLANMsg; /*1: LAN network msg, 0: WLAN msg to cloud*/
u16 MsgLen; /*the real len of msg*/
int ClientId; /*valid only when IsLANMsg is 1, indicate the tcp client's ID*/
} T_AC_SAL_MsgInfo;
字段 | 类型 | 说明 |
---|---|---|
MsgCode | u8 | 消息的MsgCode |
IsLANMsg | u8 | 消息来源/去向:1-局域网;0-云端 |
MsgLen | u16 | 消息长度,单位为Byte,不能超过32KByte。如果超过此长度,请调用文件上传接口,以文件的方式进行传输 |
ClientId | int | 仅在消息为局域网消息时有效,表征发送该消息的APP与设备之间的Socket表示,开发者在进行应答时,需要回填该值 |
注意:
结构体T_AC_SAL_MsgInfo
用于描述消息的信息,其中ClientId开发者不需要关心,只需要在进行应答时,将描述应答消息的T_AC_SAL_MsgInfo
结构体中的ClientId赋值为发送给设备端开发者的控制消息的描述信息T_AC_SAL_MsgInfo
结构体中的ClientId。
返回值
返回值(AC_STATUS) | 说明 |
---|---|
eAC_OK | API调用成功(只代表发送到TCP底层,不代表发送到云端) |
eAC_InParaInvalid | 发送失败,传入的参数无效 |
其他值 | 发送失败,其他错误 |
API行为:
- Ablelcoude联网模块会将数据发送给云端/移动端
开发示例
以开关灯为例,协议如下:
二进制格式
以开关灯为例,示例如下:
/*控制数据包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_Sal_DealLed(T_AC_SAL_MsgInfo *ptCtrlMsgInfo, u8 *pu8Msg)
{
u8 RspData[4] = {0};
u16 RspDataLen = 0;
AC_STATUS ret;
/*描述回复的应答消息的结构体*/
T_AC_SAL_MsgInfo tResponsedMsgInfo = {0}; /*the information of response to the control message*/
if ((NULL == ptCtrlMsgInfo) || (NULL == pu8Msg))
{
printf("AC_Sal_DealLed input parameter is invalid\n");
return;
}
switch (((STRU_LED_ONOFF *)pu8Msg)->u8LedOnOff)
{
case 0:
case 1:
{
RspData[0] = AC_BlinkLed(((STRU_LED_ONOFF *)pu8Msg)->u8LedOnOff);
printf("dealled payload len is %d\n", ptCtrlMsgInfo->MsgLen);
/*构造响应消息*/
RspData[0] = 1;
RspDataLen = sizeof(RspData);
tResponsedMsgInfo.MsgCode = CLIENT_SERVER_OK;
tResponsedMsgInfo.MsgLen = RspDataLen;
tResponsedMsgInfo.IsLANMsg = ptCtrlMsgInfo->IsLANMsg;
tResponsedMsgInfo.ClientId = ptCtrlMsgInfo->ClientId;
/*发送对控制消息的响应,接口含义详见《WiFi设备API参考手册》*/
ret = AC_Sal_SendMsg_NoRsp(&tResponsedMsgInfo, RspData); /*send */
if (eAC_OK != ret)
{
printf("AC_Sal_DealLed send rsp err! errcode is %d\n", ret);
}
break;
}
default:
{
printf("AC_Sal_DealLed payload is invalid\n");
ret = AC_Sal_SendErrMsg();
if (eAC_OK != ret)
{
printf("AC_Sal_DealLed send err msg err! errcode is %d\n", ret);
}
break;
}
}
}
JSON格式
JSON格式
//请求数据包
{ 70 :[
//关灯
{"switch", 0}
//开灯
{"switch", 1}
]}
//响应数据包
{ 102 :[
//失败
{"result", false},
//成功
{"result", true}
]}
void AC_DealJsonMessage(T_AC_SAL_MsgInfo *ptCtrlMsgInfo, u8 *pu8Msg)
{
/*处理设备自定义控制消息*/
u16 u16DataLen;
u32 u32LedOnOff;
bool result = false;
char *out;
cJSON *root;
/*解析收到的JSON数据*/
cJSON *format = cJSON_Parse(pu8Msg);
AC_STATUS ret;
/*描述回复的应答消息的结构体*/
T_AC_SAL_MsgInfo tResponsedMsgInfo = {0}; /*the information of response to the control message*/
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参考手册》*/
tResponsedMsgInfo.MsgCode = CLIENT_SERVER_OK;
tResponsedMsgInfo.MsgLen = strlen(out);
tResponsedMsgInfo.IsLANMsg = ptCtrlMsgInfo->IsLANMsg;
tResponsedMsgInfo.ClientId = ptCtrlMsgInfo->ClientId;
ret = AC_Sal_SendMsg_NoRsp(&tResponsedMsgInfo, (u8*)out); /*send */
if (eAC_OK != ret)
{
printf("AC_Sal_DealLed send rsp err! errcode is %d\n", ret);
}
if (eAC_OK != ret)
{
/*释放JSON消息*/
free(out);
printf("AC_DealJsonMessage Err, AC_Sal_SendMsg_NoRsp failed\n");
return;
}
/*释放JSON消息*/
free(out);
}
主动上报接口
设备主动上报接口有三种,分别用于:
(1)普通的数据上报,开发者不关心上报是否到达UDS或被处理
(2)需要同步响应的上报,开发者在上报后同步阻塞直到收到UDS回复的响应消息或超时
(3)需要异步响应的上报,开发者在上报后异步等待UDS回复的响应消息或超时
不需要响应的上报接口
API
AC_STATUS AC_Sal_SendMsg_NoRsp(T_AC_SAL_MsgInfo *ptSendMsgInfo, u8 *pu8SendMsgBuf);
参数、返回值描述见设备端应答消息发送接口中的描述。
注意
APIAC_Sal_SendMsg_NoRsp
既可以用于回复应答消息给云端/移动端,也可以用于主动上报,依靠传递给API的入参来进行区分:在应答云端/设备端的控制消息时,回复的MsgCode要在[64,200)之间,IsLANMsg和ClientId要与控制消息的描述信息结构体中的IsLANMsg和ClientId相同;在进行数据上报时,MsgCode要在[200,255]之间,IsLANMsg和ClientId都要为0
API行为 - 发送数据给UDS,返回成功不代表UDS接收到上报的数据
开发示例
二进制格式
/*上报数据包=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)
{
/*描述开发者上报消息的结构体*/
T_AC_SAL_MsgInfo tReportMsgInfo = {0}; /*the information of the report message*/
AC_STATUS ret;
/*上报demo灯的状态*/
STRU_LED_ONOFF struReport;
/*读取demo灯状态*/
struReport.u8LedOnOff = GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_2);
struReport.u8LedOnOff = struRsp.u8LedOnOff>>2;
struReport.u8ControlStatus = u8control;
/*填写要发送的消息的描述细信息并调用发送接口进行发送*/
tResponsedMsgInfo.MsgCode = 203;
tResponsedMsgInfo.MsgLen = sizeof(STRU_LED_ONOFF); /*要发送的消息的长度*/
tResponsedMsgInfo.IsLANMsg = 0;
tResponsedMsgInfo.ClientId = 0;
ret = AC_Sal_SendMsg_NoRsp(&tResponsedMsgInfo, (u8*)&struReport); /*send */
if (eAC_OK != ret)
{
printf("AC_SendStatus2Server Err, AC_Sal_SendMsg_NoRsp failed\n");
return;
}
}
JSON格式
用户可调用第三方源码构造JSON格式的消息体。
//请求数据包
{ 203 :[
//关灯
{"switch", 0}
//开灯
{"switch", 1}
]
[
//APP控制开关
{"controltype", 0},
//按键控制开关
{"controltype", 1}
]}
void AC_SendLedStatus2Server(u8 controltype)
{
/*描述开发者上报消息的结构体*/
T_AC_SAL_MsgInfo tReportMsgInfo = {0}; /*the information of the report message*/
AC_STATUS ret;
/*上报demo灯的状态*/
cJSON *root;
char *out;
u8 u8LedOnOff;
/*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);
/*填写要发送的消息的描述细信息并调用发送接口进行发送*/
tResponsedMsgInfo.MsgCode = 203;
tResponsedMsgInfo.MsgLen = strlen(out); /*要发送的消息的长度*/
tResponsedMsgInfo.IsLANMsg = 0;
tResponsedMsgInfo.ClientId = 0;
ret = AC_Sal_SendMsg_NoRsp(&tResponsedMsgInfo, (u8*)out); /*send */
if (eAC_OK != ret)
{
/*释放JSON消息*/
free(out);
printf("AC_SendLedStatus2Server Err, AC_Sal_SendMsg_NoRsp failed\n");
return;
}
/*JSON协议内存释放*/
free(out);
}
需要同步等待UDS响应的上报接口
如果开发者需要在发送上报消息后,同步阻塞住,直到收到UDS的应答消息或超时,则可以调用同步等待响应的上报接口。
API
AC_STATUS AC_Sal_SendMsg_SynRsp(
T_AC_SAL_MsgInfo *ptSendMsgInfo,
u8 *pu8SendMsgBuf,
u16 timeout_ms,
T_AC_SAL_MsgInfo *ptRspMsgInfo,
u8 *pu8RspMsgBuf,
u16 u16RspMsgLenMax)
参数
字段 | 类型 | 说明 |
---|---|---|
ptSendMsgInfo | T_AC_SAL_MsgInfo * | 指向 描述开发者要发送的消息 的结构体 的指针 |
pu8SendMsgBuf | u8 * | 开发者要发送的消息的buffer地址 |
timeout_ms | u16 | 超时时间(单位为ms,建议设置为0.5~2s之间),如果在超时时间内没有收到响应,则该API调用结束并返回超时 |
ptRspMsgInfo | T_AC_SAL_MsgInfo * | 指向 描述UDS对上报消息的响应消息 的结构体 的指针(开发者提供) |
pu8RspMsgBuf | u8 * | 存储UDS对上报消息的响应消息的buffer地址 (开发者提供) |
u16RspMsgLenMax | u16 | 开发者可以接收的对上报消息的响应消息的长度的最大值,如果实际的响应消息长度超过该值,则多出的部分会被截断 |
T_AC_SAL_MsgInfo
的定义如下:
/*This struct used to describe a message between device and cloud/app*/
typedef struct
{
u8 MsgCode; /*msg code of msg*/
u8 IsLANMsg; /*1: LAN network msg, 0: WLAN msg to cloud*/
u16 MsgLen; /*the real len of msg*/
int ClientId; /*valid only when IsLANMsg is 1, indicate the tcp client's ID*/
} T_AC_SAL_MsgInfo;
字段 | 类型 | 说明 |
---|---|---|
MsgCode | u8 | 消息的MsgCode |
IsLANMsg | u8 | 消息来源/去向:1-局域网;0-云端 |
MsgLen | u16 | 消息长度,单位为Byte |
ClientId | int | 仅在消息为局域网消息时有效,表征发送该消息的APP与设备之间的Socket表示,开发者在进行应答时,需要回填该值 |
返回值
值(AC_STATUS) | 说明 |
---|---|
eAC_OK | 发送成功,且已经收到了UDS发送的响应 |
eAC_InParaInvalid | 发送失败,传入的参数无效 |
eAC_Timeout | 发送成功,但是在规定时间内没有收到UDS的响应信息(不带表UDS收到了上报消息) |
eAC_RspBufNULL | 发送成功,且收到了UDS的响应信息,但是响应信息的消息体为空 |
其他值 | 发送失败,其他错误 |
API行为 - 发送数据给UDS,并阻塞,直到收到UDS回复的响应消息或超时
开发示例
如下demo创建了一个线程,该线程周期性上报,并同步等待UDS的响应。上报的数据为线程ID,UDS端的demo会echo上报的数据:
/*同步上报线程:周期上报,同步等待UDS响应。上报的数据是线程ID,UDS demo会在响应消息中携带上报的数据,设备端接收到响应后,取出响应的数据,与本地存储的线程ID判断是否相等*/
static void User_Thread_ReportAndWaitRspSyn(void* arg)
{
T_AC_SAL_MsgInfo tReportMsgInfo = {0}; /*the information of report message*/
T_AC_SAL_MsgInfo tRspMsgInfo = {0}; /*the information of response message*/
u8 RspMsgBuf[1024] = {0};
AC_STATUS ret = 0;
int timeout = 2000; /*2s*/
int RptCnt = 0;
pthread_t ThreadId_rsp;
int UserThreadId = (int)arg;
u8 ReportData[8] = {0};
printf("%d\n", UserThreadId);
while(1)
{
sleep(10);
/*wait to connect with Ablecloud OK*/
if (1 != User_GetConnectionStatus())
{
continue;
}
memcpy(ReportData, &UserThreadId, sizeof(int));
tReportMsgInfo.MsgCode = MSGCODE_USER_REPORT;
tReportMsgInfo.MsgLen = sizeof(ReportData); /*length of report message*/
tReportMsgInfo.IsLANMsg = 0; /*Report is not be supported in LAN communication*/
tReportMsgInfo.ClientId = 0; /*needn't care this parameter when report*/
ret = AC_Sal_SendMsg_SynRsp(&tReportMsgInfo, (u8 *)ReportData, (u16)timeout, &tRspMsgInfo, RspMsgBuf, sizeof(RspMsgBuf));
if (eAC_OK == ret)
{
memcpy(&ThreadId_rsp, RspMsgBuf, sizeof(pthread_t));
if (ThreadId_rsp != UserThreadId)
{
printf("%s thread %d recv rsp: msgcode is %d, msgLength is %d ThreadId_rsp is %d\n",
UserGetTime(), UserThreadId, tRspMsgInfo.MsgCode, tRspMsgInfo.MsgLen, (int)ThreadId_rsp);
}
printf("OK %d\n", (int)ThreadId_rsp);
}
else if (eAC_Timeout == ret)
{
printf("%s user recv rsp timeout1 %d\n", UserGetTime(), UserThreadId);
}
else
{
printf("%s User_Thread AC_Sal_SendMsg_SynRsp failed1 %d-%d\n",
UserGetTime(), (int)ret, (int)UserThreadId);
}
RptCnt++;
}
}
/*开发者的上报线程初始化*/
pthread_t UserThread_t;
int DemoInit_ReportAndWaitRspSyn()
{
pthread_create(&UserThread_t, NULL, (void *)User_Thread_ReportAndWaitRspSyn, (void *)UserThread_t);
printf("UserApplicationInit ok\n");
return 1;
}
需要异步等待UDS响应的上报接口
API
AC_STATUS AC_Sal_SendMsg_AsynRsp(
T_AC_SAL_MsgInfo *ptSendMsgInfo,
u8 *pu8SendMsgBuf,
u16 timeout_ms,
u8 *SendMsgSN,
AC_RspCallBackFuncPointer UserRspCallBack,
void *arg
)
字段 | 类型 | 说明 |
---|---|---|
ptSendMsgInfo | T_AC_SAL_MsgInfo * | 指向 描述开发者要发送的消息 的结构体 的指针 |
pu8SendMsgBuf | u8 * | 开发者要发送的消息的buffer地址 |
timeout_ms | u16 | 超时时间(单位为ms,建议设置为0.5~2s之间),如果在超时时间内没有收到响应,则Able-Link会调用开发者注册的回调函数UserRspCallBack |
SendMsgSN | u8 * | 存储本次上报的消息的消息序号的地址,该消息序列号由Able-Link SDK生成,开发者只需要在调用该函数接口时存储下来,并且在收到UDS的响应消息时,在回调函数UserRspCallBack中使用,用以匹配UDS响应消息对应的是哪次上报(开发者提供) |
UserRspCallBack | AC_RspCallBackFuncPointer | 当设备收到UDS的响应消息或者超时后,对响应消息或超时进行处理的开发者回调函数(开发者提供) |
arg | void * | 开发者提供的执行回调函数UserRspCallBack时的参数地址,可选。如果使用,则参数的存储空间由开发者提供,且必须是全局地址或者从对分配的地址,Able-Link SDK不会对其执行free操作 |
T_AC_SAL_MsgInfo
的定义如下:
/*This struct used to describe a message between device and cloud/app*/
typedef struct
{
u8 MsgCode; /*msg code of msg*/
u8 IsLANMsg; /*1: LAN network msg, 0: WLAN msg to cloud*/
u16 MsgLen; /*the real len of msg*/
int ClientId; /*valid only when IsLANMsg is 1, indicate the tcp client's ID*/
} T_AC_SAL_MsgInfo;
字段 | 类型 | 说明 |
---|---|---|
MsgCode | u8 | 消息的MsgCode |
IsLANMsg | u8 | 消息来源/去向:1-局域网;0-云端 |
MsgLen | u16 | 消息长度,单位为Byte |
ClientId | int | 仅在消息为局域网消息时有效,表征发送该消息的APP与设备之间的Socket表示,开发者在进行应答时,需要回填该值 |
AC_RspCallBackFuncPointer
的定义如下:
typedef void (* AC_RspCallBackFuncPointer) (T_AC_SAL_MsgInfo *ptRspMsgInfo, u8 *pu8Msg, u8 RspMsgSN, u8 IsTimeout, void *arg);
字段 | 类型 | 说明 |
---|---|---|
ptRspMsgInfo | T_AC_SAL_MsgInfo * | 描述 UDS回复的响应消息的结构体 的指针 |
pu8Msg | u8 * | 仅在IsTimeout=0时有效。UDS回复的响应消息的buffer地址(Able-Link提供,开发者不能执行free操作) |
RspMsgSN | u8 | UDS回复的响应消息的消息序列号N,用于与上报消息进行匹配 |
IsTimeout | int | 1:上报后在超时时间内没有接收到UDS回复的响应消息;0:在超时时间内收到了UDS回复的响应消息 |
arg | void * | 开发者在调用AC_Sal_SendMsg_SynRsp 时传递给回调函数AC_RspCallBackFuncPointer 的参数地址,由开发者提供地址并进行释放(如果需要) |
返回值
值(AC_STATUS) | 说明 |
---|---|
eAC_OK | API调用成功(只代表发送到TCP底层,不代表发送到云端) |
eAC_InParaInvalid | 发送失败,传入的参数无效 |
其他值 | 发送失败,其他错误 |
API行为 - 发送数据给UDS,之后返回,返回成功不代表UDS接收到了数据。在接收到UDS回复的响应消息或超时后,调用开发者提供的(入参UserRspCallBack)响应消息回调函数。
开发示例
如下demo创建了一个线程,该线程周期性上报,并异步等待UDS的响应。上报的数据为线程ID,UDS端的demo会echo上报的数据:
/*处理超时或UDS响应的回调函数*/
void UserCallback_RecvResponse(T_AC_SAL_MsgInfo *ptRspMsgInfo, u8 *pu8RspMsg, u8 RspMsgSN, u8 IsTimeout, void *arg)
{
int ThreadId = (int)arg;
int ThreadIdRsp = 0;
if (NULL == ptRspMsgInfo)
{
printf("UserRecvDownCtrlMsgCallBack input parameter is invalid\n");
return;
}
if (TRUE == IsTimeout)
{
printf("timeout\n");
return;
}
memcpy(&ThreadIdRsp, pu8RspMsg, 4);
if (ThreadId != ThreadIdRsp)
{
printf("%s thread %d recv rsp: msgcode is %d, msgLength is %d ThreadId_rsp is %d\n",
UserGetTime(), ThreadId, ptRspMsgInfo->MsgCode, ptRspMsgInfo->MsgLen, (int)ThreadIdRsp);
}
else
{
printf("OK %d\n", ThreadId);
}
return;
}
/*同步上报线程:周期上报,异步等待UDS响应。上报的数据是线程ID,UDS demo会在响应消息中携带上报的数据,设备端接收到响应后,取出响应的数据,与本地存储的线程ID判断是否相等*/
static void User_Thread_ReportAndWaitResponseAsyn(void* arg)
{
u8 SendMsgSN = 0;
T_AC_SAL_MsgInfo tReportMsgInfo = {0};
AC_STATUS ret = 0;
int UserThreadId = (int)arg;
u8 ReportData[8] = {0};
printf("%d\n", UserThreadId);
while(1)
{
sleep(10);
/*wait to connect with Ablecloud OK*/
if (1 != User_GetConnectionStatus())
{
continue;
}
memcpy(ReportData, &UserThreadId, sizeof(UserThreadId));
tReportMsgInfo.MsgCode = 205;
tReportMsgInfo.MsgLen = sizeof(UserThreadId);
tReportMsgInfo.IsLANMsg = 0;
tReportMsgInfo.ClientId = 0;
printf("ASYN report-----begin\n");
ret = AC_Sal_SendMsg_AsynRsp(&tReportMsgInfo, ReportData, 2000, &SendMsgSN, UserCallback_RecvResponse, (void *)UserThreadId);
if (eAC_OK == ret)
{
printf("ASYN report----- send ok\n");
}
else
{
printf("ASYN report------------------------------------- send failed %d\n", ret);
}
}
}
/*上报线程初始化*/
static pthread_t UserThread_t;
int DemoInit_ReportAndWaitRspAsyn()
{
pthread_create(&UserThread_t, NULL, (void *)User_Thread_ReportAndWaitResponseAsyn, (void *)UserThread_t);
printf("UserApplicationInit ok\n");
return 1;
}
设备属性快速上报接口
Ablecloud提供了设备属性快速上报的功能,主要针对以下两种场景:
1.通过云端快速上报设备属性
当有APP打开时,用户有可能想更实时的查看到设备的状态变化,此时需要设备提高上报频率;而当没有APP打开时,设备的上报频率可以降低
2.局域网内快速上报设备属性
在局域网内,当有APP打开时,用户有可能想更实时的收到设备状态的上报消息。
对于场景1,设备上报的频率及持续时间是由APP开发者配置的,对于场景2,设备上报的频率是默认的(3秒一次)。
当需要设备上报属性时,ablink会调用回调函数UserCallback_HandleSubscribeReq
,请求开发者构造并发送设备的全量信息。
API:
void UserCallback_HandleSubscribeReq(u8 IsLANMsg, u32 ClientId)
参数
字段 | 类型 | 说明 |
---|---|---|
IsLANMsg | u8 | 1:局域网快速上报请求。0:云端快速上报请求 |
ClientId | u32 | 仅在IsLANMsg为1时有效,表征APP的clientID |
注意:
1.无论是云端快速上报请求还是局域网快速上报请求,AC联网模块都是通过上述接口通知开发者的,开发者可以通过入参
IsLANMsg
来判断本次上报是发送给云端还是局域网。
2.对于局域网快速上报,WiFi是通过局域网UDP广播发送给局域网内的所有APP的。
3.上报消息的MsgCode必须不小于200
TCP环境初始化
如果Linux设备要使用Ablecloude的长连接联网功能,则需要在程序开始处调用初始化Ablecloud联网模块的API,触发AC联网模块开始运行。
API:
AC_Sal_Init();//初始化Ablecloud联网协议
开发示例
int main(int argc, char *argv[])
{
/*------------User init begin-----------*/
/*todo @developer. User init. Here developers can not call Ablecloud's API*/
/*------------User init end-----------*/
AC_Sal_Init(); /*init Able-Link module*/
sleep(1);
while (1)
{
sleep(5);
}
return 1;
}
HTTP环境初始化
Linux设备要使用Ablecloude的短连接联网功能,需要在程序开始处调用初始化Ablecloud HTTP环境的API。
API:
int AC_HttpEnvironmentInit(T_AC_HttpInitInfoB *ptACHttpInitInfoB);
参数
字段 | 类型 | 说明 |
---|---|---|
ptACHttpInitInfoB | T_AC_HttpInitInfoB | AC_HttpEnvironmentInit函数入参结构体 |
T_AC_HttpInitInfoB的定义如下:
/*This struct define all info needed by http environment initial*/
typedef struct
{
int cloudAddrType;
int domainID; /*ID or Name, second, choose one*/
int subdomainID;
char DomainName[32]; /*ID or Name, second, choose one*/
char subDomainName[32];
char deviceID[64];
char equipmentVersion[20];
char *privateKey;
} T_AC_HttpInitInfoB;
字段 | 类型 | 说明 |
---|---|---|
cloudAddrType | int | 设备连接的云端服务器环境类型开发准备-配置设备连接的云端服务器环境 |
domainID | int | 产品的主域ID |
subdomainID | int | 产品的子域ID |
DomainName[] | char | 产品的主域名称(超过32Byte会被截断) |
subDomainName[] | char | 产品的子域名称(超过32Byte会被截断) |
deviceID[] | char | 设备ID(超过64Byte会被截断) |
equipmentVersion[] | char | 设备的固件版本号(超过20Byte会被截断) |
privateKey | char * | 设备的私钥 |
注意:
domainID,subdomainID或者DomainName、subDomainName二者可以二选一填写
返回值
值 | 意义 |
---|---|
0 | 初始化成功 |
非0值 | 初始化失败 |
如果http环境初始化失败,可以选择尝试多次。初始化成功前,不能使用OTA功能和文件上传下载的功能。 初始化失败的原因有多种,包括:
(1)主子域ID和主子域名称均未填写
(2)设备私钥```privatekey```字段为空
(3)cloudAddrType、主子域信息和设备私钥不匹配
(4)系统时间未设置
(5)网络连接异常
开发示例
void main()
{
int ret =0;
T_AC_HttpInitInfoB tACHttpInitInfoB = {0};
/*init http environment*/
tACHttpInitInfoB.cloudAddrType = CLOUD_ADDR;
tACHttpInitInfoB.domainID = MAJOR_DOMAIN_ID;
tACHttpInitInfoB.subdomainID = SUB_DOMAIN_ID;
memcpy(tACHttpInitInfoB.deviceID, g_u8DeviceId, sizeof(g_u8DeviceId));
memcpy(tACHttpInitInfoB.equipmentVersion, g_u8EqVersion, sizeof(g_u8EqVersion));
tACHttpInitInfoB.privateKey = (char *)g_u8ModuleKey;
while (1)
{
ret = AC_HttpEnvironmentInit(&tACHttpInitInfoB);
if (0 != ret)
{
ZC_Printf("AC_HttpEnvironmentInit failed, try again 30s later\n");
AC_GetNtpTime(); //get ntp time and Calibration hw clock each 10 minutes
continue;
}
ZC_Printf("AC_HttpEnvironmentInit success\n");
break;
}
}
API行为:
- 完成设备与云端的http认证激活
HTTP方式下载OTA文件
API:
int AC_OtaUpdate(T_AC_OtaUpadateInParameter *pstruAc_OtaInParameter);
参数
字段 | 类型 | 说明 |
---|---|---|
pstruAc_OtaInParameter | T_AC_OtaUpadateInParameter | AC_OtaUpdate函数入参结构体 |
T_AC_OtaUpadateInParameter
的定义如下:
typedef struct
{
int OTAMode;
int OTAFileNum;
char *DownloadFilePath;
char *OTADescription;
char *OTATargetVersion;
unsigned short OTAFileLocalCheckSum[MAX_OTAFILENUM];
AC_OtaFileInfo *DownloadOtaFileInfo;
} T_AC_OtaUpadateInParameter;
字段 | 类型 | 说明 |
---|---|---|
OTAMode | int | ota升级模式,mode:1代表系统升级,mode:2代表通信模组升级 |
OTAFileNum | int | ota文件数目 |
DownloadFilePath | char * | 下载的ota文件在本地的存储路径 |
OTADescription | char * | ota描述 |
OTATargetVersion | char * | 目标版本号 |
OTAFileLocalCheckSum[] | unsigned short | OTA下载的每个文件的校验值 |
DonwloadOtaFileInfo | AC_OtaFileInfo * | 存储ota文件信息数组 |
AC_OtaFileInfo定义如下:
typedef struct
{
char chName[64];
char chDownloadUrl[512];
int IntFileType;
int IntChecksum;
}AC_OtaFileInfo;
字段 | 类型 | 说明 |
---|---|---|
chName | const char * | ota升级文件名 |
chDownloadUrl | char * | 下载的ota文件url |
IntFileType | int * | ota文件类型 |
IntChecksum | char * | ota文件校验值 |
返回值
值 | 意义 |
---|---|
0 | OTA文件下载成功(仅是下载成功,不是校验成功) |
非0值 | OTA文件下载失败,得到的下载成功的文件数为0 |
开发示例
下面给出一个完整的Linux设备的HTTP OTA开发示例,示例中由执行脚本运行demo程序,在AC_Init中初始化设备ID和版本号,之后创建http线程WRTnode_HttpThread
。该线程每一小时查询一次,看是否有新的OTA版本需要下载,如果有,则下载固件后返回并结束进程。期间如果有云端触发的OTA,该线程也会直接进行OTA。执行脚本在检测到demo程序结束后,会将新的OTA固件从存储路径/tmp/AbcloudFirmware拷贝到执行路径/usr/sbin/AbcloudFirmware,并运行新的demo程序。
注意
demo中的OTA固件是打包为.tar.gz格式的压缩包后上传到OTA后台并进行下载的,所以OTA后需要在本地进行解压后执行
int g_OTAFinished = 0;
static void WRTnode_HttpThread(void* arg)
{
int ret =0;
u32 i = 0;
AC_OtaFileInfo fileInfo[MAX_OTAFILENUM];
char otadescription[64];
char otatargetverion[32];
int OTAFailedCnt = 0;
int TryOTAAgainImmediately = 0;
int OTADownloadFileNum = 0;
int HaveNewFirmwareFlag = 0;
T_AC_HttpInitInfoB tACHttpInitInfoB = {0};
T_AC_OtaUpadateInParameter struAc_OtaInParameter = {0};
sleep(20); /*20 seconds*/
/*init http environment*/
tACHttpInitInfoB.cloudAddrType = CLOUD_ADDR;
tACHttpInitInfoB.domainID = MAJOR_DOMAIN_ID;
tACHttpInitInfoB.subdomainID = SUB_DOMAIN_ID;
memcpy(tACHttpInitInfoB.deviceID, g_u8DeviceId, sizeof(g_u8DeviceId));
memcpy(tACHttpInitInfoB.equipmentVersion, g_u8EqVersion, sizeof(g_u8EqVersion));
printf("tACHttpInitInfoB.equipmentVersion is %s\n",tACHttpInitInfoB.equipmentVersion);
tACHttpInitInfoB.privateKey = (char *)g_u8ModuleKey;
while (1)
{
ret = AC_HttpEnvironmentInit(&tACHttpInitInfoB);
if (0 != ret)
{
ZC_Printf("AC_HttpEnvironmentInit failed, try again 30s later\n");
AC_GetNtpTime(); //get ntp time and Calibration hw clock each 10 minutes
sleep(30);
continue;
}
ZC_Printf("AC_HttpEnvironmentInit success\n");
break;
}
/*the Inparameter must be setted value*/
struAc_OtaInParameter.OTAMode = eOTAMode_SystemUpdate; /*OTA mode, do not modify*/
struAc_OtaInParameter.DownloadFilePath = "/tmp"; /*path to store OTA file*/
struAc_OtaInParameter.OTADescription = otadescription;
struAc_OtaInParameter.OTATargetVersion = otatargetverion;
struAc_OtaInParameter.DownloadOtaFileInfo = fileInfo;
while(1)
{
TryOTAAgainImmediately = 0;
ret = AC_OtaUpdate(&struAc_OtaInParameter);
if (0 == ret)
{
OTADownloadFileNum = struAc_OtaInParameter.OTAFileNum;
for (i = 0; i < OTADownloadFileNum; i++)
{
printf("ota filename =%s\n", fileInfo[i].chName);
printf("ota IntFileType =%d\n", fileInfo[i].IntFileType);
printf("ota IntChecksum =%d\n", fileInfo[i].IntChecksum);
if (fileInfo[i].IntChecksum != struAc_OtaInParameter.OTAFileLocalCheckSum[i])
{
printf("OTA get file CRC error!\n");
OTAFailedCnt++;
if (OTAFailedCnt <= 3)
{
TryOTAAgainImmediately = 1;
}
continue;
}
else
{
OTAFailedCnt = 0;
printf("OTA get file CRC OK!\n");
}
if (0 == i)
{
HaveNewFirmwareFlag = 1;
}
}
printf("ota Description = %s,ota target version = %s\n", otadescription, otatargetverion);
if (1 == HaveNewFirmwareFlag) /*application's logic. Those code are just an example.*/
{
HaveNewFirmwareFlag = 0;
char cpCmd[200] = {0};
sprintf(cpCmd, "mv /tmp/%s /tmp/AbcloudFirmware.tar.gz", fileInfo[0].chName);
if (-1 == system(cpCmd))
{
printf("system cmd mv failed\n");
continue;
}
printf("to exit AbcloudFirmware\n");
/*resource free begin*/
ZC_Sleep();
/*resource free end*/
g_OTAFinished = 1;
exit(0);
}
}
else
{
printf("AC_OtaUpdate Failed\n");
}
if (1 == TryOTAAgainImmediately)
{
sleep(10); /*sleep and try OTA again 10s later*/
printf("Try OTA again\n");
continue;
}
int sleepCnt = 360;
while (sleepCnt--) /*sleep 3600s and check for OTA version again*/
{
if (1 == g_LinuxToHttpOTAFlag)
{
g_LinuxToHttpOTAFlag = 0;
printf("to OTA, trigged by cloud\n");
/*todo @developer
@developer
If thread run here, means that someone upload a new OTA version and
the cloud has inform the device, developer should determine whether and when to
execute the OTA.*/
break;
}
sleep(10); /*10 seconds*/
}
AC_GetNtpTime(); //get ntp time and Calibration hw clock each 10 minutes
printf("OTA thread\n");
}
}
/*************************************************
* Function: AC_Init
* Description:
* Author: cxy
* Returns:
* Parameter:
* History:
*************************************************/
pthread_t HttpOTA_t;
void AC_Init()
{
#if defined(REGISTER_WITH_MAC)
char u8Mac[20] = {0};
/*user eth0's MAC addr to regist*/
g_struProtocolController.pstruMoudleFun->pfunGetMac((u8 *)u8Mac);
memcpy(g_u8DeviceId, u8Mac, 12);
#else /*use user-defined-deviceID to regist*/
/*todo @developer
get user-defined deviceID and copy to g_u8DeviceId*/
#endif
AC_Sal_SetDeviceID(g_u8DeviceId, strlen((char *)g_u8DeviceId));
AC_ConfigWifi(CLOUD_ADDR);
#ifdef LOCAL_STATIC_TOKEN
AC_SetLocalLevel(PCT_LOCAL_DYNAMIC_TOKEN);
#endif
//pthread_create(&HttpOTA_t, NULL, (void *)WRTnode_HttpThread, NULL);
return;
}
固件切换脚本参考
#!/bin/sh -x
# Copyright Abcloud Team
#@/etc/init.d/
while $1
do
if [ -f "/tmp/AbcloudFirmware.tar.gz" ]; then
tar -xzvf /tmp/AbcloudFirmware.tar.gz -C /tmp/
echo "tar AbcloudFirmware.tar.gz"
mv /tmp/ablink /tmp/AbcloudFirmware
fi
if [ -f "/tmp/AbcloudFirmware" ]; then
cp /tmp/AbcloudFirmware /usr/sbin/
echo "1"
fi
#export LD_PRELOAD=/usr/local/ssl/lib/libcrypto.so.1.0.0:/usr/local/ssl/lib/libssl.so.1.0.0:/usr/local/lib/libcurl.so
chmod 777 /usr/sbin/AbcloudFirmware
/usr/sbin/AbcloudFirmware
echo "ablecloudFirmware finished!"
done
文件存储
文件存储是AbleCloud提供的非结构化存储功能。可以用来存储头像、文件等大的数据块。单个文件最大支持4G。
下载文件
API:
int AC_DownloadFile(const char *bucketName,char *remotefilename,char *localfilepath);
参数
字段 | 类型 | 说明 |
---|---|---|
bucketName | const char * | 云端bucket名 |
remotefilename | char * | 下载的云端文件名 |
localfilename | char * | 存储本地文件完整路径名 |
返回值
值 | 意义 |
---|---|
0 | 初始化成功 |
非0值 | 初始化失败 |
上传文件
API:
int AC_UploadFile(const char *bucketName, char *remotefilename, char *localfilepath);
参数
字段 | 类型 | 说明 |
---|---|---|
bucketName | const char * | 云端bucket名 |
remotefilename | char * | 上传的云端文件名 |
localfilename | char * | 存储本地文件完整路径名 |
返回值
值 | 意义 |
---|---|
0 | 初始化成功 |
非0值 | 初始化失败 |
开发示例
void main()
{
int ret =0;
AC_OtaFileInfo fileInfo[MAX_OTAFILENUM];
char otadescription[64];
int otamode = 1;//system update
int otadowloadfilenum = 0;
int i= 0;
char deviceid[]="6666666666666666";//建议开发者采用mac地址,前面补0000,一共16个字节
char privatekey[] = DEFAULT_IOT_PRIVATE_KEY;
ret = AC_DeviceServiceInit(MAJOR_DOMAIN,SUB_DOMAIN,deviceid,privatekey,deviceversion); VICE_VERSION); //初始化环境变量
if(0 != ret)
{
printf("AC_DeviceServiceInit errror=%d\n",ret);
}
else
{
ret = AC_UploadFile("test","1","cJSON.c");
if(0 != ret)
{
printf("AC_UploadFile errror=%d\n",ret);
return;
}
ret = AC_DownloadFile("test","1","test.c");
if(0 != ret)
{
printf("AC_DownloadFile errror=%d\n",ret);
return;
}
}
}
NTP时间获取接口
触发获取NTP时间接口
API:
void AC_GetNtpTime(void);
API行为: - 触发AC联网模块到云端服务器获取NTP时间
注意:
获取NTP时间的前提是WiFi模块已经连接上外网
处理NTP结果的回调函数
开发者调用APIAC_GetNtpTime
触发AC联网模块获取NTP时间后,得到的结果会通过调用Src/ac_user_eventCallback.c
中的NTP回调函数UserCallback_HandleNTP
将NTP得到的数据交给UserApp,开发者可以在此添加自己的处理逻辑。
所在文件
Src/ac_user_eventCallback.c
回调函数:
void UserCallback_HandleNTP(ZC_NTP_TIME* ntp);
参数
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时间) |
注意:
通过NTP获取到的时间是UTC时间,开发者如果需要本地时间,需要根据所在时区自行进行转换。
开发示例
void UserCallback_HandleNTP(ZC_NTP_TIME* ntp)
{
int ret = 0;
time_t tmNtp = 0;
char DateCmd[64] = {0};
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);
/*将NTP获取到的UTC时间配置到系统时钟,再通过 hwclock -w 写入硬件时钟*/
tmNtp = ZC_HTONL(ntp->ntp_sec);
struct timeval tv;
tv.tv_sec = tmNtp;
tv.tv_usec = 0;
ret = settimeofday(&tv, NULL);
if (0 != ret)
{
printf("settimeofday failed\n");
}
memset(DateCmd, 0, sizeof(DateCmd));
sprintf(DateCmd, "hwclock -w");
if (-1 == system(DateCmd))
{
printf("system cmd hwclock failed\n");
return;
}
}