当前位置:   article > 正文

AWS IOT Device C SDK 使用_c语言 aws_iot_sdk

c语言 aws_iot_sdk

AWS shadow使用可参考:
https://blog.csdn.net/m0_37263637/article/details/81103440
关于C SDK测试及交叉编译可参考:
https://blog.csdn.net/m0_37263637/article/details/81103497
前排C SDK API文 档镇楼:
http://aws-iot-device-sdk-embedded-c-docs.s3-website-us-east-1.amazonaws.com/files.html

Sample code基于C SDK中的sample shadow_sample.c进行修改。
文档有点难写和描述具体看code中注释吧

1 CSDK中关键类型及API

1.1 jsonStruct_t

C SDK 中用于定义shadow state中某个属性(比如stream),它有下列四个成员

const char *    pKey   属性名称
void * pData 属性值地址
 JsonPrimitiveType  type 该属性类型 
 uint32_t   dataLength 该属性大小
 jsonStructCallback_t cb 当接受delta消息有该参数时触发的回调函数地址
  • 1
  • 2
  • 3
  • 4
  • 5

http://aws-iot-device-sdk-embedded-c-docs.s3-website-us-east-1.amazonaws.com/structjsonStruct.html#details

1.2 aws_iot_shadow_register_delta

注册delta触发的回调jsonStruct结构,每次发布增量时,Json文档都将被发送到pStruct-> cb。

rc = aws_iot_shadow_register_delta(&mqttClient, &streamHandler);
  • 1

收到delta消息时会触发1.1中的回调函数

1.3 aws_iot_shadow_get

此功能的一个用途通常是在启动时获取设备的配置。它类似于内部的Update函数,除了它不将JSON文档作为输入。整个JSON文档将通过接受的主题接收

aws_iot_shadow_get(&mqttClient, AWS_IOT_MY_THING_NAME, ShadowGetStatusCallback, NULL, 2, true);
pClient MQTT客户端用作协议层
pThingName  Thing所需的JSON文档的名称
callback    这是将用于通知来自AWS IoT Shadow服务的响应的回调。如果响应不重要,则可以将回调设置为NULL
pContextData    这是一个可以与回调一起传递的额外参数。如果不使用它应该设置为NULL
timeout_seconds 在声明操作超时之前,SDK将等待接受/拒绝的响应
isPersistentSubscribe   如上所述,每次设备获得相同的Sahdow(JSON文档)时,应将其设置为true以避免重复订阅和取消订阅。如果Thing Name是一个off get,那么这应该设置为false
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

比较主要的是callback 参数,我们需要按照格式编写callback函数,get shadow的结果将在callback中传递给程序
下面是callback函数结构

typedef void(* fpActionCallback_t)(const char * pThingName,ShadowActions_t action,Shadow_Ack_Status_t status,const char * pReceivedJsonDocument,void * pContextData)

pThingName  收到的回复的名称
action  操作的反应
status  通知操作是接受/拒绝还是超时
pReceivedJsonDocument   收到JSON文档
pContextData    动作调用期间传入的void *数据(更新,获取或删除)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

具体如何使用可参考2 中code

1.4 aws_iot_shadow_update

update是设备最常用的功能之一。在大多数情况下,如果没有回调或JSON文档没有客户端令牌,则设备可能只是报告几个参数来更新云更新操作中的事物阴影,然后只发布更新而不跟踪它。

aws_iot_shadow_update(mqttClient, AWS_IOT_MY_THING_NAME, test,ShadowUpdateStatusCallback, NULL, 10, true);
  • 1
pClient MQTT客户端用作协议层
pThingName  事物需要更新的阴影的名称
pJsonString 更新操作需要发送JSON文档。JSON字符串应该是以空字符结尾的字符串。此JSON文档应遵循AWS IoT Thing Shadow规范。为了帮助创建此文档,SDK提供了apiaws_iot_shadow_json_data.h
callback    这是将用于通知来自AWS IoT Shadow服务的响应的回调。如果响应不重要,则可以将回调设置为NULL
pContextData    这是一个可以与回调一起传递的额外参数。如果不使用它应该设置为NULL
timeout_seconds 在声明操作超时之前,SDK将等待接受/拒绝的响应
isPersistentSubscribe   如上所述,每次设备更新相同的阴影时,应将其设置为true以避免重复订阅和取消订阅。如果Thing Name是一次性更新,则应将其设置为false
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

比较重要的是callback和pJsonString。callback同1.3中一致,需要我们按照特定格式编写,pJsonString为我们要更新的状态值(JSON)格式如下:

{"state":{"reported":{"power":1,"stream":1},"desired":{}},"clientToken":"testdevicetokyo-1"}
  • 1

2 sample

运行前提:完成C SDK测试并配置好了相关certs,这部分请参考:http://ichscfn01.icatchtek.com:8080/pages/viewpage.action?pageId=12255659

sample code定义了两个状态 power 和stream 即 state{“reported”:{power:1, stream:1}}
程序运行时,程序会主动连接上AWS IOT 并绑定相应delta消息回调函数处理来自shadow的控制信息。
然后获取shadow并同步自身状态,等待来自shadow控制信息。具体内容可以看code注释。
PS:code没有使用sdk自带json库,而是使用的CJSON进行格式处理

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <cJSON.h>
#include "aws_iot_config.h"
#include "aws_iot_log.h"
#include "aws_iot_version.h"
#include "aws_iot_mqtt_client_interface.h"
#include "aws_iot_shadow_interface.h"
#define MAX_LENGTH_OF_UPDATE_JSON_BUFFER 400
static char certDirectory[PATH_MAX + 1] = "./certs";
#define HOST_ADDRESS_SIZE 255
static char HostAddress[HOST_ADDRESS_SIZE] = AWS_IOT_MQTT_HOST;
static uint32_t port = AWS_IOT_MQTT_PORT;
static uint8_t numPubs = 5;
typedef struct cameras{
    int power;
    int stream;
}cameraState; 
cameraState camera;
int reporetedFlag = 0;

void ShadowUpdateStatusCallback(const char *pThingName, ShadowActions_t action, Shadow_Ack_Status_t status,
                                const char *pReceivedJsonDocument, void *pContextData) {
    //device 发起update请求,收到server accpected消息时会触发这个回调,用于判断update状态是否成功
    if(SHADOW_ACK_TIMEOUT == status) {
        IOT_INFO("Update Timeout--");
    } else if(SHADOW_ACK_REJECTED == status) {
        IOT_INFO("Update RejectedXX");
    } else if(SHADOW_ACK_ACCEPTED == status) {
        IOT_INFO("Update Accepted !!");
    }
}
//***************CJSON*********************// parseCMDJSON create_monitor_with_helpers用于解析或构造state内容
int parseCMDJSON(const char * const monitor){//使用CJSON解析get shadow返回的消息
    const cJSON *state = NULL;
    const cJSON *desired = NULL;
    const cJSON *reported = NULL;
    const cJSON *power = NULL;
    const cJSON *stream = NULL;
    int status = 0;
    cJSON *monitor_json = cJSON_Parse(monitor);//它将解析JSON并分配cJSON表示它的项目树。
    if (monitor_json == NULL)
    {
        const char *error_ptr = cJSON_GetErrorPtr();
        if (error_ptr != NULL)
        {
            fprintf(stderr, "Error before: %s\n", error_ptr);
        }
        status = 0;
        goto end;
    }
    state = cJSON_GetObjectItemCaseSensitive(monitor_json, "state");//得到state内容
    if (cJSON_IsString(state) && (state->valuestring != NULL))
    {
        printf("Checking monitor \"%s\"\n", state->valuestring);
    }
    desired = cJSON_GetObjectItemCaseSensitive(state, "desired");//从stated尝试解析desired
    reported = cJSON_GetObjectItemCaseSensitive(state, "reported");//从stated尝试解析reported
    if(!cJSON_IsNull(reported))//如果reported不为空
    {
        power = cJSON_GetObjectItemCaseSensitive(reported, "power");
        stream = cJSON_GetObjectItemCaseSensitive(reported, "stream");
        if (cJSON_IsNumber(power)){//使用云上状态同步本地状态
            camera.power = power->valueint;
            IOT_INFO("reported power:%d",   camera.power );
        }
        if (cJSON_IsNumber(stream)){//使用云上状态同步本地状态
            camera.stream = stream->valueint;
            IOT_INFO("reported stream:%d",  camera.stream);
        }
        status = 1;
    }
    if (!cJSON_IsNull(desired))//如果desired不为空,所以应该用desired状态同步本状态。上面reported同步的状态将被覆盖
    {
        power = cJSON_GetObjectItemCaseSensitive(desired, "power");
        stream = cJSON_GetObjectItemCaseSensitive(desired, "stream");
        if (cJSON_IsNumber(power)){ 
            camera.power = power->valueint;
            IOT_INFO("desired power:%d",    camera.power );
        }
        if (cJSON_IsNumber(stream)){ 
            camera.stream = stream->valueint;
            IOT_INFO("desired stream:%d",   camera.stream);
        }
        reporetedFlag = 1;//更新状态
        status = 1;
    }
end:
    cJSON_Delete(monitor_json);
    return status;
}
char *create_monitor_with_helpers(void)//构造reported update 的state内容 
{
    char *string = NULL;
    cJSON *monitor = cJSON_CreateObject();
    cJSON *state = cJSON_CreateObject();
    cJSON *reporteds = cJSON_CreateObject();
    cJSON *desired = cJSON_CreateObject();
    cJSON *reported = cJSON_CreateObject();
    if (cJSON_AddNumberToObject(reported, "power", camera.power) == NULL)//构造power属性到reported中
        goto end;
    if (cJSON_AddNumberToObject(reported, "stream", camera.stream) == NULL)//构造stream属性到reported中
        goto end;
    cJSON_AddItemToObject(state, "reported", reported);
    cJSON_AddItemToObject(state, "desired", desired);//将reported和desired添加到state
    cJSON_AddItemToObject(monitor, "state", state);//将state添加到返回的主json中

    char tempClientTokenBuffer[MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE];
    if(aws_iot_fill_with_client_token(tempClientTokenBuffer, MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE) != SUCCESS){
        return false;
    }
    cJSON_AddStringToObject(monitor, "clientToken", tempClientTokenBuffer);//将clientToken标识添加到主JSON中
    string = cJSON_PrintUnformatted(monitor);//生成JSON PrintUnformatted函数为不格式化生成,生成一个紧凑的json字串 
    if (string == NULL) {
        fprintf(stderr, "Failed to print monitor.\n");
    }
end:
    cJSON_Delete(monitor);
    return string;
}
// get shadow API的回调 当得到server返回时会触发这个函数
static void ShadowGetStatusCallback(const char *pThingName, ShadowActions_t action, Shadow_Ack_Status_t status,
                                const char *pReceivedJsonDocument, void *pContextData) {
    IOT_INFO("get shadow json:\n%s",pReceivedJsonDocument);
    parseCMDJSON(pReceivedJsonDocument);
    if(SHADOW_ACK_TIMEOUT == status) {
        IOT_INFO("get Timeout--");
    } else if(SHADOW_ACK_REJECTED == status) {
        IOT_INFO("get RejectedXX");
    } else if(SHADOW_ACK_ACCEPTED == status) {
        IOT_INFO("get Accepted !!");
    }
}
//****************************************************//
//当收到delta消息中disired中含有该属性时 会触发该回调函数
void power_Callback(const char *pJsonString, uint32_t JsonStringDataLen, jsonStruct_t *pContext) {
    char res[10];
    strncpy(res, pJsonString, JsonStringDataLen);
    camera.power = atoi(res);//得到delta消息中power的值
    if(camera.power == 1){
        IOT_INFO("power on");
    }
    else{
        IOT_INFO("power off");
    }
    reporetedFlag = 1;
}
//当收到delta消息中disired中含有该属性时 会触发该回到函数
void stream_Callback(const char *pJsonString, uint32_t JsonStringDataLen, jsonStruct_t *pContext) {
    char res[10];
    strncpy(res, pJsonString, JsonStringDataLen);
    camera.stream = atoi(res);//得到delta消息中stream的值
    if(camera.stream == 0){
        IOT_INFO("stream off");
    }
    else{
        IOT_INFO("stream open");
    }
    reporetedFlag = 1;
}
//reported函数 内部封装了sdk update接口 用于update reported state
void iot_reporte(AWS_IoT_Client *mqttClient, jsonStruct_t JsonHander[2]){
    IoT_Error_t rc = FAILURE;
    char JsonDocumentBuffer[MAX_LENGTH_OF_UPDATE_JSON_BUFFER];
    size_t sizeOfJsonDocumentBuffer = sizeof(JsonDocumentBuffer) / sizeof(JsonDocumentBuffer[0]);
    char test[300];
    strcpy(test, create_monitor_with_helpers());
    IOT_INFO("Update Shadow: %s", test);
    rc = aws_iot_shadow_update(mqttClient, AWS_IOT_MY_THING_NAME, test,ShadowUpdateStatusCallback, NULL, 10, true);//数字为超时时间
}

int main(int argc, char **argv) {
    IoT_Error_t rc = FAILURE;
    int32_t i = 0;
    char *pJsonStringToUpdate;
    jsonStruct_t powerHandler;//aws c sdk里state中某个属性的结构 这里定义power
    powerHandler.cb = power_Callback;
    powerHandler.pKey = "power";
    powerHandler.pData = &(camera.power);
    powerHandler.dataLength = sizeof(int);
    powerHandler.type = SHADOW_JSON_INT32;
    jsonStruct_t streamHandler;//aws c sdk里state中某个属性的结构 这里定义stream
    streamHandler.cb = stream_Callback;
    streamHandler.pKey = "stream";
    streamHandler.pData = &(camera.stream);
    streamHandler.dataLength = sizeof(int);
    streamHandler.type = SHADOW_JSON_INT32;

    jsonStruct_t HandlerArray[2] = {powerHandler, streamHandler};
    //******************完成client为接入 iot server 不用更改*************//
    char rootCA[PATH_MAX + 1];
    char clientCRT[PATH_MAX + 1];
    char clientKey[PATH_MAX + 1];
    char CurrentWD[PATH_MAX + 1];
    IOT_INFO("\nAWS IoT SDK Version %d.%d.%d-%s\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_TAG);
    getcwd(CurrentWD, sizeof(CurrentWD));
    snprintf(rootCA, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_ROOT_CA_FILENAME);
    snprintf(clientCRT, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_CERTIFICATE_FILENAME);
    snprintf(clientKey, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_PRIVATE_KEY_FILENAME);
    AWS_IoT_Client mqttClient;
    ShadowInitParameters_t sp = ShadowInitParametersDefault;
    sp.pHost = AWS_IOT_MQTT_HOST;
    sp.port = AWS_IOT_MQTT_PORT;
    sp.pClientCRT = clientCRT;
    sp.pClientKey = clientKey;
    sp.pRootCA = rootCA;
    sp.enableAutoReconnect = false;
    sp.disconnectHandler = NULL;
    IOT_INFO("Shadow Init");
    rc = aws_iot_shadow_init(&mqttClient, &sp);
    if(SUCCESS != rc) {
        IOT_ERROR("Shadow Connection Error");
        return rc;
    }
    ShadowConnectParameters_t scp = ShadowConnectParametersDefault;
    scp.pMyThingName = AWS_IOT_MY_THING_NAME;
    scp.pMqttClientId = AWS_IOT_MQTT_CLIENT_ID;
    scp.mqttClientIdLen = (uint16_t) strlen(AWS_IOT_MQTT_CLIENT_ID);
    IOT_INFO("Shadow Connect");
    rc = aws_iot_shadow_connect(&mqttClient, &scp);
    if(SUCCESS != rc) {
        IOT_ERROR("Shadow Connection Error");
        return rc;
    }
    rc = aws_iot_shadow_set_autoreconnect_status(&mqttClient, true);
    if(SUCCESS != rc) {
        IOT_ERROR("Unable to set Auto Reconnect to true - %d", rc);
        return rc;
    }
    //*******************************//
    rc = aws_iot_shadow_register_delta(&mqttClient, &streamHandler);//注册收到delta消息 stream内容的结构 
    rc = aws_iot_shadow_register_delta(&mqttClient, &powerHandler); //注册收到delta消息 power内容的结构 
    if(SUCCESS != rc) {
        IOT_ERROR("Shadow Register Delta Error");
    }
    else{
        IOT_INFO("Shadow Register Success");
        camera.power = 1;
        camera.stream = 0;
        aws_iot_shadow_get(&mqttClient, AWS_IOT_MY_THING_NAME, ShadowGetStatusCallback, NULL, 2, true);//数字为超时时间
        // while(updateFlag==0){
        //  sleep(1);
        // }
        // iot_reporte(&mqttClient, HandlerArray);
        // 上线自检 更新状态
    }

    while(NETWORK_ATTEMPTING_RECONNECT == rc || NETWORK_RECONNECTED == rc || SUCCESS == rc) {
        rc = aws_iot_shadow_yield(&mqttClient, 200);//此函数可以在单独的线程中使用,等待传入消息,确保使用AWS服务保持连接。它还确保清除Shadow操作的过期请求并执行Timeout回调。
        if(NETWORK_ATTEMPTING_RECONNECT == rc) {
            continue;
        }
        if(reporetedFlag == 1){//检测状态改变标志位 进行reported当前状态
            IOT_INFO("stream and power handle function");
            iot_reporte(&mqttClient, HandlerArray);
            reporetedFlag = 0;
        }
        sleep(1);
    }
    if(SUCCESS != rc) {
        IOT_ERROR("An error occurred in the loop %d", rc);
    }
    IOT_INFO("Disconnecting");
    rc = aws_iot_shadow_disconnect(&mqttClient);
    if(SUCCESS != rc) {
        IOT_ERROR("Disconnect error %d", rc);
    }
    return rc;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273

3 测试结果

device端打印

//获取shadow内容
get shadow json:
{"state":{"desired":{"stream":1,"power":1},"reported":{"power":1,"stream":1}},"metadata":{"desired":{"stream":{"timestamp":1531966777},"power":{"timestamp":1531966777}},"reported":{"power":{"timestamp":1531966806},"stream":{"timestamp":1531966806}}},"version":858,"timestamp":1531968326,"clientToken":"testdevicetokyo-0"}
reported power:1
reported stream:1
desired power:1
desired stream:1
get Accepted !!
//发现desired 尝试更新
stream and power handle function
Update Shadow: {"state":{"reported":{"power":1,"stream":1},"desired":{}},"clientToken":"testdevicetokyo-1"}
Update Accepted !!
//接受到delta消息 进行处理和reported
stream off
power on
stream and power handle function
Update Shadow: {"state":{"reported":{"power":1,"stream":0},"desired":{}},"clientToken":"testdevicetokyo-3"}
Update Accepted !!
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/499883
推荐阅读
相关标签
  

闽ICP备14008679号