AWS IOT Device C SDK 使用
AWS IOT接入及测试
阿里云设备影子:类似MQTT的遗嘱
阿里云设备接入、设备管理方案
阿里云:一机一密、一型一密、CA证书、证书获取、证书烧录
公钥、私钥、数字签名和数字证书的概念及解密(数字证书:CA用自己的私钥,对需要认证的公钥及相关的信息进行加密) (CA也有自己的公钥和私钥,CA的公钥就是默认放在操作系统或浏览器中)SSL数字证书(一)CA、根证书与数字证书:根证书是CA认证中心给自己颁发的证书,是信任链的起始点
公钥、私钥、证书、加密、解密、加签(数字签名)、验签
公钥、私钥、hash、数字签名、CA以及验证过程:证书的格式一般为X.509或者Base64编码
树莓派 + AWS IoT 入门实验
亚马逊云物联网AWS IoT初体验( MQTT.fx的配置)
烟感器设备接入AWS IOT的一种方法
如何把设备安全的接入AWS IoT(一)
如何把设备安全的接入AWS IoT(二)
AWS Client端SDK授权方案(Amazon Cognito 身份池、token及IdentityId)
重点关注main函数里4个重要API函数:
1、jsonStruct_t
2、aws_iot_shadow_register_delta
3、aws_iot_shadow_get
4、aws_iot_shadow_update
#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尝试解析desiredreported = cJSON_GetObjectItemCaseSensitive(state, "reported"); //从stated尝试解析reportedif(!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添加到statecJSON_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) {//收到json文档 动作调用期间传入的void *数据(更新,获取或删除)IOT_INFO("get shadow json:\n%s",pReceivedJsonDocument);parseCMDJSON(pReceivedJsonDocument);//解析出reported desired 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>中含有该属性时 会触发该回调函数//json数据内容 json数据长度
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;
}
//************************************************************************//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);//数字为超时时间 调用aws_iot_shadow_update
}int main(int argc, char **argv) {IoT_Error_t rc = FAILURE;int32_t i = 0;char *pJsonStringToUpdate;jsonStruct_t powerHandler; //1、jsonStruct_t state中某个属性 有4个成员powerHandler.cb = power_Callback; //当接受delta消息有该参数时触发的回调函数地址powerHandler.pKey = "power"; //属性名称powerHandler.pData = &(camera.power); //属性值地址powerHandler.dataLength = sizeof(int);//属性大小powerHandler.type = SHADOW_JSON_INT32;//属性类型jsonStruct_t streamHandler; //1、jsonStruct_t state中某个属性 有4个成员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); //2、aws_iot_shadow_register_delta(收到增量消息触发1中回调函数)rc = aws_iot_shadow_register_delta(&mqttClient, &powerHandler);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); //3、aws_iot_shadow_get(一般启动时获取设备配置)// while(updateFlag==0){ // shadow_get的结果将在callback中传递给程序// sleep(1);// }// iot_reporte(&mqttClient, HandlerArray); //4、aws_iot_shadow_update// 上线自检 更新状态}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); //4、aws_iot_shadow_update(向云端更新设备影子,只更新不跟踪)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;
}