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);

参数、返回值描述见设备端应答消息发送接口中的描述。

注意
API AC_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;
        }
    }