Demo application for using the AT&T IoT Starter Kit Powered by AWS.

Dependencies:   SDFileSystem

Fork of ATT_AWS_IoT_demo by Anthony Phillips

IoT Starter Kit Powered by AWS Demo

This program demonstrates the AT&T IoT Starter Kit sending data directly into AWS IoT. It's explained and used in the Getting Started with the IoT Starter Kit Powered by AWS on starterkit.att.com.

What's required

  • AT&T IoT LTE Add-on (also known as the Cellular Shield)
  • NXP K64F - for programming
  • microSD card - used to store your AWS security credentials
  • AWS account
  • Python, locally installed

If you don't already have an IoT Starter Kit, you can purchase a kit here. The IoT Starter Kit Powered by AWS includes the LTE cellular shield, K64F, and a microSD card.

AWS_openssl/aws_iot_src/shadow/aws_iot_shadow_json.cpp

Committer:
ampembeng
Date:
2016-12-01
Revision:
15:6f2798e45099

File content as of revision 15:6f2798e45099:

/*
 * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

#include "aws_iot_shadow_json.h"

#include <string.h>
#include <stdbool.h>
#include <inttypes.h>
#include "aws_iot_json_utils.h"
#include "aws_iot_log.h"
#include "aws_iot_shadow_key.h"
#include "aws_iot_config.h"

extern char mqttClientID[MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES];

static uint32_t clientTokenNum = 0;

//helper functions
static IoT_Error_t convertDataToString(char *pStringBuffer, size_t maxSizoStringBuffer, JsonPrimitiveType type,
		void *pData);

void resetClientTokenSequenceNum(void) {
	clientTokenNum = 0;
}

static void emptyJsonWithClientToken(char *pJsonDocument) {
	sprintf(pJsonDocument, "{\"clientToken\":\"");
	FillWithClientToken(pJsonDocument + strlen(pJsonDocument));
	sprintf(pJsonDocument + strlen(pJsonDocument), "\"}");
}

void iot_shadow_get_request_json(char *pJsonDocument) {
	emptyJsonWithClientToken(pJsonDocument);
}

void iot_shadow_delete_request_json(char *pJsonDocument) {
	emptyJsonWithClientToken(pJsonDocument);
}

static inline IoT_Error_t checkReturnValueOfSnPrintf(int32_t snPrintfReturn, size_t maxSizeOfJsonDocument) {

	if (snPrintfReturn >= maxSizeOfJsonDocument) {
		return SHADOW_JSON_BUFFER_TRUNCATED;
	} else if (snPrintfReturn < 0) {
		return SHADOW_JSON_ERROR;
	}
	return NONE_ERROR;
}

IoT_Error_t aws_iot_shadow_init_json_document(char *pJsonDocument, size_t maxSizeOfJsonDocument) {

	IoT_Error_t ret_val = NONE_ERROR;
	int32_t snPrintfReturn = 0;

	if (pJsonDocument == NULL) {
		return NULL_VALUE_ERROR;
	}
	snPrintfReturn = snprintf(pJsonDocument, maxSizeOfJsonDocument, "{\"state\":{");

	ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, maxSizeOfJsonDocument);

	return ret_val;

}

IoT_Error_t aws_iot_shadow_add_desired(char *pJsonDocument, size_t maxSizeOfJsonDocument, uint8_t count, ...) {
	IoT_Error_t ret_val = NONE_ERROR;
	int32_t tempSize = 0;
	int8_t i;
	size_t remSizeOfJsonBuffer = maxSizeOfJsonDocument;
	int32_t snPrintfReturn = 0;
	va_list pArgs;
	va_start(pArgs, count);
	jsonStruct_t *pTemporary;

	if (pJsonDocument == NULL) {
		return NULL_VALUE_ERROR;
	}

	tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument);
		if(tempSize <= 1){
			return SHADOW_JSON_ERROR;
		}
		remSizeOfJsonBuffer = tempSize;

	snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"desired\":{");
	ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer);

	if (ret_val != NONE_ERROR) {
		return ret_val;
	}

	for (i = 0; i < count; i++) {
		tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument);
			if(tempSize <= 1){
				return SHADOW_JSON_ERROR;
			}
			remSizeOfJsonBuffer = tempSize;
		pTemporary = va_arg (pArgs, jsonStruct_t *);
		if (pTemporary != NULL) {
			snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"%s\":",
					pTemporary->pKey);
			ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer);
			if (ret_val != NONE_ERROR) {
				return ret_val;
			}
			if (pTemporary->pKey != NULL && pTemporary->pData != NULL) {
				ret_val = convertDataToString(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer,
						pTemporary->type, pTemporary->pData);
			} else {
				return NULL_VALUE_ERROR;
			}
			if (ret_val != NONE_ERROR) {
				return ret_val;
			}
		} else {
			return NULL_VALUE_ERROR;
		}
	}

	va_end(pArgs);
	snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument) - 1, remSizeOfJsonBuffer, "},");
	ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer);
	return ret_val;
}

IoT_Error_t aws_iot_shadow_add_reported(char *pJsonDocument, size_t maxSizeOfJsonDocument, uint8_t count, ...) {
	IoT_Error_t ret_val = NONE_ERROR;

	int8_t i;
	size_t remSizeOfJsonBuffer = maxSizeOfJsonDocument;
	int32_t snPrintfReturn = 0;
	int32_t tempSize = 0;
	va_list pArgs;
	va_start(pArgs, count);
	jsonStruct_t *pTemporary;

	if (pJsonDocument == NULL) {
		return NULL_VALUE_ERROR;
	}


	tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument);
	if(tempSize <= 1){
		return SHADOW_JSON_ERROR;
	}
	remSizeOfJsonBuffer = tempSize;

	snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"reported\":{");
	ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer);

	if (ret_val != NONE_ERROR) {
		return ret_val;
	}

	for (i = 0; i < count; i++) {
		tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument);
		if(tempSize <= 1){
			return SHADOW_JSON_ERROR;
		}
		remSizeOfJsonBuffer = tempSize;

		pTemporary = va_arg (pArgs, jsonStruct_t *);
		if (pTemporary != NULL) {
			snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"%s\":",
					pTemporary->pKey);
			ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer);
			if (ret_val != NONE_ERROR) {
				return ret_val;
			}
			if (pTemporary->pKey != NULL && pTemporary->pData != NULL) {
				ret_val = convertDataToString(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer,
						pTemporary->type, pTemporary->pData);
			} else {
				return NULL_VALUE_ERROR;
			}
			if (ret_val != NONE_ERROR) {
				return ret_val;
			}
		} else {
			return NULL_VALUE_ERROR;
		}
	}

	va_end(pArgs);
	snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument) - 1, remSizeOfJsonBuffer, "},");
	ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer);
	return ret_val;
}


int32_t FillWithClientTokenSize(char *pBufferToBeUpdatedWithClientToken, size_t maxSizeOfJsonDocument) {
	int32_t snPrintfReturn;
	snPrintfReturn = snprintf(pBufferToBeUpdatedWithClientToken, maxSizeOfJsonDocument, "%s-%d", mqttClientID,
			clientTokenNum++);

	return snPrintfReturn;
}

IoT_Error_t aws_iot_fill_with_client_token(char *pBufferToBeUpdatedWithClientToken, size_t maxSizeOfJsonDocument){

	int32_t snPrintfRet = 0;
	snPrintfRet = FillWithClientTokenSize(pBufferToBeUpdatedWithClientToken, maxSizeOfJsonDocument);
	return checkReturnValueOfSnPrintf(snPrintfRet, maxSizeOfJsonDocument);

}

IoT_Error_t aws_iot_finalize_json_document(char *pJsonDocument, size_t maxSizeOfJsonDocument) {
	size_t remSizeOfJsonBuffer = maxSizeOfJsonDocument;
	int32_t snPrintfReturn = 0;
	int32_t tempSize = 0;
	IoT_Error_t ret_val = NONE_ERROR;

	if (pJsonDocument == NULL) {
		return NULL_VALUE_ERROR;
	}

	tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument);
	if(tempSize <= 1){
		return SHADOW_JSON_ERROR;
	}
	remSizeOfJsonBuffer = tempSize;

	// strlen(ShadowTxBuffer) - 1 is to ensure we remove the last ,(comma) that was added
	snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument) - 1, remSizeOfJsonBuffer, "}, \"%s\":\"",
			SHADOW_CLIENT_TOKEN_STRING);
	ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer);

	if (ret_val != NONE_ERROR) {
		return ret_val;
	}
	// refactor this XXX repeated code
	tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument);
	if(tempSize <= 1){
		return SHADOW_JSON_ERROR;
	}
	remSizeOfJsonBuffer = tempSize;


	snPrintfReturn = FillWithClientTokenSize(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer);
	ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer);

	if (ret_val != NONE_ERROR) {
		return ret_val;
	}
	tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument);
	if(tempSize <= 1){
		return SHADOW_JSON_ERROR;
	}
	remSizeOfJsonBuffer = tempSize;


	snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"}");
	ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer);

	return ret_val;
}

void FillWithClientToken(char *pBufferToBeUpdatedWithClientToken) {
	sprintf(pBufferToBeUpdatedWithClientToken, "%s-%d", mqttClientID, clientTokenNum++);
}

static IoT_Error_t convertDataToString(char *pStringBuffer, size_t maxSizoStringBuffer, JsonPrimitiveType type,
		void *pData) {
	int32_t snPrintfReturn = 0;
	IoT_Error_t ret_val = NONE_ERROR;

	if (maxSizoStringBuffer == 0) {
		return SHADOW_JSON_ERROR;
	}

	if (type == SHADOW_JSON_INT32) {
		//snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%"PRIi32",", *(int32_t * )(pData));
		snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%li,", *(int32_t * )(pData));
	} else if (type == SHADOW_JSON_INT16) {
		//snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%"PRIi16",", *(int16_t * )(pData));
		snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%i,", *(int16_t * )(pData));
	} else if (type == SHADOW_JSON_INT8) {
		//snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%"PRIi8",", *(int8_t * )(pData));
		snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%i,", *(int8_t * )(pData));
	} else if (type == SHADOW_JSON_UINT32) {
		//snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%"PRIu32",", *(uint32_t * )(pData));
		snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%lu,", *(uint32_t * )(pData));
	} else if (type == SHADOW_JSON_UINT16) {
		//snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%"PRIu16",", *(uint16_t * )(pData));
		snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%u,", *(uint16_t * )(pData));
	} else if (type == SHADOW_JSON_UINT8) {
		//snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%"PRIu8",", *(uint8_t * )(pData));
		snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%u,", *(uint8_t * )(pData));
	} else if (type == SHADOW_JSON_DOUBLE) {
		snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%f,", *(double * )(pData));
	} else if (type == SHADOW_JSON_FLOAT) {
		snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%f,", *(float * )(pData));
	} else if (type == SHADOW_JSON_BOOL) {
		snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%s,", *(bool *)(pData)?"true":"false");
	} else if (type == SHADOW_JSON_STRING) {
		snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "\"%s\",", (char * )(pData));
	}

	ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, maxSizoStringBuffer);

	return ret_val;
}
static jsmn_parser shadowJsonParser;
static jsmntok_t jsonTokenStruct[MAX_JSON_TOKEN_EXPECTED];

bool isJsonValidAndParse(const char *pJsonDocument, void *pJsonHandler, int32_t *pTokenCount) {
	int32_t tokenCount;

	jsmn_init(&shadowJsonParser);

	tokenCount = jsmn_parse(&shadowJsonParser, pJsonDocument, strlen(pJsonDocument), jsonTokenStruct,
			sizeof(jsonTokenStruct) / sizeof(jsonTokenStruct[0]));

	if (tokenCount < 0) {
		WARN("Failed to parse JSON: %d\n", tokenCount);
		return false;
	}

	/* Assume the top-level element is an object */
	if (tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) {
		WARN("Top Level is not an object\n");
		return false;
	}

	pJsonHandler = (void *) jsonTokenStruct;
	*pTokenCount = tokenCount;

	return true;
}

static IoT_Error_t UpdateValueIfNoObject(const char *pJsonString, jsonStruct_t *pDataStruct, jsmntok_t token) {
	IoT_Error_t ret_val = NONE_ERROR;
	if (pDataStruct->type == SHADOW_JSON_BOOL) {
		ret_val = parseBooleanValue((bool *)pDataStruct->pData, pJsonString, &token);
	} else if (pDataStruct->type == SHADOW_JSON_INT32) {
		ret_val = parseInteger32Value((int32_t *)pDataStruct->pData, pJsonString, &token);
	} else if (pDataStruct->type == SHADOW_JSON_INT16) {
		ret_val = parseInteger16Value((int16_t *)pDataStruct->pData, pJsonString, &token);
	} else if (pDataStruct->type == SHADOW_JSON_INT8) {
		ret_val = parseInteger8Value((int8_t *)pDataStruct->pData, pJsonString, &token);
	} else if (pDataStruct->type == SHADOW_JSON_UINT32) {
		ret_val = parseUnsignedInteger32Value((uint32_t *)pDataStruct->pData, pJsonString, &token);
	} else if (pDataStruct->type == SHADOW_JSON_UINT16) {
		ret_val = parseUnsignedInteger16Value((uint16_t *)pDataStruct->pData, pJsonString, &token);
	} else if (pDataStruct->type == SHADOW_JSON_UINT8) {
		ret_val = parseUnsignedInteger8Value((uint8_t *)pDataStruct->pData, pJsonString, &token);
	} else if (pDataStruct->type == SHADOW_JSON_FLOAT) {
		ret_val = parseFloatValue((float *)pDataStruct->pData, pJsonString, &token);
	} else if (pDataStruct->type == SHADOW_JSON_DOUBLE) {
		ret_val = parseDoubleValue((double *)pDataStruct->pData, pJsonString, &token);
	}

	return ret_val;
}

bool isJsonKeyMatchingAndUpdateValue(const char *pJsonDocument, void *pJsonHandler, int32_t tokenCount,
		jsonStruct_t *pDataStruct, uint32_t *pDataLength, int32_t *pDataPosition) {
	int32_t i;

	jsmntok_t *pJsonTokenStruct;

	pJsonTokenStruct = (jsmntok_t *) pJsonHandler;
	for (i = 1; i < tokenCount; i++) {
		if (jsoneq(pJsonDocument, &(jsonTokenStruct[i]), pDataStruct->pKey) == 0) {
			jsmntok_t dataToken = jsonTokenStruct[i + 1];
			uint32_t dataLength = dataToken.end - dataToken.start;
			UpdateValueIfNoObject(pJsonDocument, pDataStruct, dataToken);
			*pDataPosition = dataToken.start;
			*pDataLength = dataLength;
			return true;
		}
	}
	return false;
}

bool isReceivedJsonValid(const char *pJsonDocument) {
	int32_t tokenCount;

	jsmn_init(&shadowJsonParser);

	tokenCount = jsmn_parse(&shadowJsonParser, pJsonDocument, strlen(pJsonDocument), jsonTokenStruct,
			sizeof(jsonTokenStruct) / sizeof(jsonTokenStruct[0]));

	if (tokenCount < 0) {
		WARN("Failed to parse JSON: %d\n", tokenCount);
		return false;
	}

	/* Assume the top-level element is an object */
	if (tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) {
		return false;
	}

	return true;
}

bool extractClientToken(const char *pJsonDocument, char *pExtractedClientToken) {
	bool ret_val = false;
	jsmn_init(&shadowJsonParser);
	int32_t tokenCount, i;
	jsmntok_t ClientJsonToken;

	tokenCount = jsmn_parse(&shadowJsonParser, pJsonDocument, strlen(pJsonDocument), jsonTokenStruct,
			sizeof(jsonTokenStruct) / sizeof(jsonTokenStruct[0]));

	if (tokenCount < 0) {
		WARN("Failed to parse JSON: %d\n", tokenCount);
		return false;
	}

	/* Assume the top-level element is an object */
	if (tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) {
		return false;
	}

	for (i = 1; i < tokenCount; i++) {
		if (jsoneq(pJsonDocument, &jsonTokenStruct[i], SHADOW_CLIENT_TOKEN_STRING) == 0) {
			ClientJsonToken = jsonTokenStruct[i + 1];
			uint8_t length = ClientJsonToken.end - ClientJsonToken.start;
			strncpy(pExtractedClientToken, pJsonDocument + ClientJsonToken.start, length);
			pExtractedClientToken[length] = '\0';
			return true;
		}
	}

	return false;
}

bool extractVersionNumber(const char *pJsonDocument, void *pJsonHandler, int32_t tokenCount, uint32_t *pVersionNumber) {
	int32_t i;
	jsmntok_t *pJsonTokenStruct;
	IoT_Error_t ret_val = NONE_ERROR;

	pJsonTokenStruct = (jsmntok_t *) pJsonHandler;
	for (i = 1; i < tokenCount; i++) {
		if (jsoneq(pJsonDocument, &(jsonTokenStruct[i]), SHADOW_VERSION_STRING) == 0) {
			jsmntok_t dataToken = jsonTokenStruct[i + 1];
			uint32_t dataLength = dataToken.end - dataToken.start;
			ret_val = parseUnsignedInteger32Value(pVersionNumber, pJsonDocument, &dataToken);
			if (ret_val == NONE_ERROR) {
				return true;
			}
		}
	}
	return false;
}