启动ablink

Ablecloud联网模块ablink是作为SDK集成在开发者的应用进程中执行的,一般在守护进程中由开发者直接调用或者通过脚本进行调用。由于ablink具有OTA功能,当获取到一个新的固件后,ablink会调用exit(0)函数结束当前进程,返回调用ablink的进程或脚本 。所以建议开发者在守护进程或者启动脚本中循环调用ablink,如果ablink返回0,表明有新的固件,开发者需要在守护进程或脚本中重新执行新的ablink固件。

以下是一个示例的执行ablink的脚本,在该脚本中,OTA固件是先打成.tar.gz格式的压缩包(这样可以一次下载多个文件)后进行上传和下载的,下载后先解压压缩包,然后将ablink(名字由开发者取)rename为AblecloudFirmware,之后拷贝到/usr/sbin/路径下并执行。


    #!/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

初始化

SDK初始化

ablink联网模块的运行需要进行SDK的环境初始化,开发者可以在执行完自己的初始化流程后,调用 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 ablink module*/
        sleep(1);


        while (1)
        {
            sleep(5); 
        }

        return 1;
    }

当设备连接上外网并获取到IP地址后,ablink会自动检测网络状态并执行联网程序,开发者不需要关心。

设备云端激活

设备连接到云端,和云端完成通信握手叫做设备云端激活。对于云端激活的流程,开发者不需要关心,当云端激活成功后,ablink会通过回调函数通知给开发者,如果设备与云端之间的连接断开,ablink也会通过回调的方式通知开发者。如果开发者关心设备与云端连接的通断,则只需要在事件回调函数中添加自己的逻辑即可。

ablink提供给开发者的事件回调函数定义在Src/UserSrc/ac_user_eventCallback.c中,示例如下:


    /****************************global variable define begin************/
    static u8 gu8UserConnWithACloudStatus = 0;  /*1: connect with ablelcoud 0: disconnect with ablecloud*/
    /****************************global variable define end**************/

    /*the user callback when device connect to cloud successfully*/
    void UserCallback_CloudConnect()
    {
        gu8UserConnWithACloudStatus = 1;
    }

    /*the user callback when device disconnect with cloud*/
    void UserCallback_CloudDisconnect()
    {
        gu8UserConnWithACloudStatus = 0;
    }

注意
如果开发者需要在自己的线程中主动上报数据,则需要检查连接状态,在设备云端激活成功后才能开始数据上报。

接收云端/移动端消息

设备云端激活后,UserApp就可以开始和云端/移动端进行通信了。

云端消息到达时,ablink会通过文件Src/UserSrc/ac_user_eventCallback.c中的回调函数 UserCallback_RecvCtrlMsg 将数据传递给UserApp,开发者需要在该函数中调用自己的处理逻辑,对通过云端传输的控制指令进行处理,处理结束后,需要回复应答消息给云端。

接口说明: 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秒),建议开发者先回复响应给云端,之后再进行处理

处理云端/移动端数据

开发者通过 UserCallback_RecvCtrlMsg接口获取到云端/移动端发送的数据的Msg后,需要解析Msg。Msg的格式支持二进制和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_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);   
     }

发送应答信息

在上述demo中,开发者在处理完云端/移动端数据后,还需要发送应答消息给云端/移动端,这是通过调用消息发送接口AC_Sal_SendMsg_NoRsp实现的。该接口的描述如下:

接口说明: 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表示,开发者在进行应答时,需要回填该值

返回值(AC_STATUS) 说明
eAC_OK API调用成功(只代表发送到TCP底层,不代表发送到云端)
eAC_InParaInvalid 发送失败,传入的参数无效
其他值 发送失败,其他错误

开发示例见“处理云端/移动端数据”章节。

注意:
结构体T_AC_SAL_MsgInfo用于描述消息的信息,其中ClientId开发者不需要关心,只需要在进行应答时,将描述应答消息的T_AC_SAL_MsgInfo结构体中的ClientId赋值为发送给设备端开发者的控制消息的描述信息T_AC_SAL_MsgInfo结构体中的ClientId。

上报数据给云端

设备在云端激活后,UserApp就可以定时或者根据外界条件触发来将设备数据和状态主动上报到云端。

注意:
UserApp上报的消息号(MsgCode)必须大于等于200。 上报消息只能发送给UDS,不能通过局域网进行数据上报。

设备上报的接口与设备发送数据的接口是同一个接口,都是“发送应答信息”章节描述的接口AC_Sal_SendMsg_NoRsp。只是开发者在发送应答消息时,MsgCode是[64,200)之间的值,而上报时MsgCode是[200,255]之间的值。另外,由于上报都是上报给云端UDS,所以调用接口AC_Sal_SendMsg_NoRsp时,描述信息结构体T_AC_SAL_MsgInfo中的ClientId和IsLANMsg都要赋值为0。

二进制格式和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)
    {
        /*描述开发者上报消息的结构体*/
        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的应答,以此确认上报的数据是否被UDS正确接收,以及获取UDS的处理结果等信息。为此,除普通的消息发送接口AC_Sal_SendMsg_NoRsp之外,Ablecloud的Linux SDK还提供了另外两种消息上报接口,分别用于需要同步接收响应消息的数据上报场景,以及需要异步接收响应消息的数据上报场景。

注意
1.开发者如果要使用等待响应的数据上报特性,还需要UDS开发者在接收到(或者处理完成)设备的上报信息后,调用Ablecloud的UDS接口handleDeviceMsg发送响应消息给设备
2.对于设备上报消息的响应消息,开发者不需要且也不能再回复应答消息
3.对于设备上报消息的响应消息,其MsgCode由设备端开发者与UDS开发者协议制定,但是必须在[200,255]之间

上报,并且同步等待UDS响应(阻塞)

如果开发者需要在发送上报消息后,同步阻塞住,直到收到UDS的应答消息或超时,则可以调用同步等待响应的上报接口AC_Sal_SendMsg_SynRsp

接口说明:


    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的响应信息,但是响应信息的消息体为空
其他值 发送失败,其他错误

开发示例(如下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响应(非阻塞)

如果开发者需要在发送上报消息后继续进行其他处理,异步等待UDS的应答消息或超时,则可以调用异步等待响应的上报接口AC_Sal_SendMsg_AsynRsp。异步等待响应时,开发者需要提供响应消息到达或超时时的处理回调函数。

接口说明:


    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之间),如果在超时时间内没有收到响应,则ablink会调用开发者注册的回调函数UserRspCallBack
SendMsgSN u8 * 存储本次上报的消息的消息序号的地址,该消息序列号由ablink SDK生成,开发者只需要在调用该函数接口时存储下来,并且在收到UDS的响应消息时,在回调函数UserRspCallBack中使用,用以匹配UDS响应消息对应的是哪次上报(开发者提供)
UserRspCallBack AC_RspCallBackFuncPointer 当设备收到UDS的响应消息或者超时后,对响应消息或超时进行处理的开发者回调函数(开发者提供)
arg void * 开发者提供的执行回调函数UserRspCallBack时的参数地址,可选。如果使用,则参数的存储空间由开发者提供,且必须是全局地址或者从对分配的地址,ablink 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地址(ablink提供,开发者不能执行free操作)
RspMsgSN u8 UDS回复的响应消息的消息序列号,用于与上报消息进行匹配
IsTimeout int 1:上报后在超时时间内没有接收到UDS回复的响应消息;0:在超时时间内收到了UDS回复的响应消息
arg void * 开发者在调用AC_Sal_SendMsg_SynRsp时传递给回调函数AC_RspCallBackFuncPointer的参数地址,由开发者提供地址并进行释放(如果需要)

返回值

值(AC_STATUS) 说明
eAC_OK API调用成功(只代表发送到TCP底层,不代表发送到云端)
eAC_InParaInvalid 发送失败,传入的参数无效
其他值 发送失败,其他错误

开发示例(如下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,请求开发者构造并发送设备的全量信息。

注意

1.无论是云端快速上报请求还是局域网快速上报请求,AC联网模块都是通过上述接口通知开发者的,开发者可以通过入参来判断本次上报是发送给云端还是局域网。
2.对于局域网快速上报,WiFi是通过局域网UDP广播发送给局域网内的所有APP的。
3.上报消息的MsgCode必须不小于200