#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "esp_log.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"
#include "core2forAWS.h"
#include "wifi.h"
#include "fft.h"
#include "ui.h"
static const char *TAG = "MAIN";
#define HEATING "HEATING"
#define COOLING "COOLING"
#define STANDBY "STANDBY"
#define STARTING_SOUNDLEVEL 0x00
#define STARTING_UVSTATUS LOW
#define STARTING_ROOMOCCUPANCY false
// Number of slices to split the microphone sample into
#define AUDIO_TIME_SLICES 60
#define MAX_LENGTH_OF_UPDATE_JSON_BUFFER 200
/* CA Root certificate */
extern const uint8_t aws_root_ca_pem_start[] asm("_binary_aws_root_ca_pem_start");
extern const uint8_t aws_root_ca_pem_end[] asm("_binary_aws_root_ca_pem_end");
/* Default MQTT HOST URL is pulled from the aws_iot_config.h */
char HostAddress[255] = AWS_IOT_MQTT_HOST;
/* Default MQTT port is pulled from the aws_iot_config.h */
uint32_t port = AWS_IOT_MQTT_PORT;
/*
Semaphore for sound levels
*/
SemaphoreHandle_t xMaxNoiseSemaphore;
void iot_subscribe_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen,
IoT_Publish_Message_Params *params, void *pData)
{
ESP_LOGI(TAG, "Subscribe callback");
ESP_LOGI(TAG, "%.*s\t%.*s", topicNameLen, topicName, (int)params->payloadLen, (char *)params->payload);
}
void disconnect_callback_handler(AWS_IoT_Client *pClient, void *data)
{
ESP_LOGW(TAG, "MQTT Disconnect");
ui_textarea_add("Disconnected from AWS IoT Core...", NULL, 0);
IoT_Error_t rc = FAILURE;
if (NULL == pClient)
{
return;
}
if (aws_iot_is_autoreconnect_enabled(pClient))
{
ESP_LOGI(TAG, "Auto Reconnect is enabled, Reconnecting attempt will start now");
}
else
{
ESP_LOGW(TAG, "Auto Reconnect not enabled. Starting manual reconnect...");
rc = aws_iot_mqtt_attempt_reconnect(pClient);
if (NETWORK_RECONNECTED == rc)
{
ESP_LOGW(TAG, "Manual Reconnect Successful");
}
else
{
ESP_LOGW(TAG, "Manual Reconnect Failed - %d", rc);
}
}
}
static bool shadowUpdateInProgress;
void ShadowUpdateStatusCallback(const char *pThingName, ShadowActions_t action, Shadow_Ack_Status_t status,
const char *pReceivedJsonDocument, void *pContextData)
{
IOT_UNUSED(pThingName);
IOT_UNUSED(action);
IOT_UNUSED(pReceivedJsonDocument);
IOT_UNUSED(pContextData);
shadowUpdateInProgress = false;
if (SHADOW_ACK_TIMEOUT == status)
{
ESP_LOGE(TAG, "Update timed out");
}
else if (SHADOW_ACK_REJECTED == status)
{
ESP_LOGE(TAG, "Update rejected");
}
else if (SHADOW_ACK_ACCEPTED == status)
{
ESP_LOGI(TAG, "Update accepted");
}
}
void uv_Callback(const char *pJsonString, uint32_t JsonStringDataLen, jsonStruct_t *pContext)
{
IOT_UNUSED(pJsonString);
IOT_UNUSED(JsonStringDataLen);
char *status = (char *)(pContext->pData);
if (pContext != NULL)
{
ESP_LOGI(TAG, "Delta - uvStatus state changed to %s", status);
}
if (strcmp(status, HIGH) == 0)
{
ESP_LOGI(TAG, "setting side LEDs to red");
Core2ForAWS_Sk6812_SetSideColor(SK6812_SIDE_LEFT, 0xFF0000);
Core2ForAWS_Sk6812_SetSideColor(SK6812_SIDE_RIGHT, 0xFF0000);
Core2ForAWS_Sk6812_Show();
}
else if (strcmp(status, MEDIUM) == 0)
{
ESP_LOGI(TAG, "setting side LEDs to blue");
Core2ForAWS_Sk6812_SetSideColor(SK6812_SIDE_LEFT, 0x0000FF);
Core2ForAWS_Sk6812_SetSideColor(SK6812_SIDE_RIGHT, 0x0000FF);
Core2ForAWS_Sk6812_Show();
}
else
{
ESP_LOGI(TAG, "clearing side LEDs");
Core2ForAWS_Sk6812_Clear();
Core2ForAWS_Sk6812_Show();
}
}
void occupancy_Callback(const char *pJsonString, uint32_t JsonStringDataLen, jsonStruct_t *pContext)
{
IOT_UNUSED(pJsonString);
IOT_UNUSED(JsonStringDataLen);
if (pContext != NULL)
{
ESP_LOGI(TAG, "Delta - roomOccupancy state changed to %d", *(bool *)(pContext->pData));
}
}
uint8_t soundBuffer = STARTING_SOUNDLEVEL;
uint8_t reportedSound = STARTING_SOUNDLEVEL;
char uvStatus[7] = STARTING_UVSTATUS;
bool roomOccupancy = STARTING_ROOMOCCUPANCY;
// helper function for working with audio data
long map(long x, long in_min, long in_max, long out_min, long out_max)
{
long divisor = (in_max - in_min);
if (divisor == 0)
{
return -1; //AVR returns -1, SAM returns 0
}
return (x - in_min) * (out_max - out_min) / divisor + out_min;
}
void microphone_task(void *arg)
{
static int8_t i2s_readraw_buff[1024];
size_t bytesread;
int16_t *buffptr;
double data = 0;
Microphone_Init();
uint8_t maxSound = 0x00;
uint8_t currentSound = 0x00;
for (;;)
{
maxSound = 0x00;
fft_config_t *real_fft_plan = fft_init(512, FFT_REAL, FFT_FORWARD, NULL, NULL);
i2s_read(I2S_NUM_0, (char *)i2s_readraw_buff, 1024, &bytesread, pdMS_TO_TICKS(100));
buffptr = (int16_t *)i2s_readraw_buff;
for (uint16_t count_n = 0; count_n < real_fft_plan->size; count_n++)
{
real_fft_plan->input[count_n] = (float)map(buffptr[count_n], INT16_MIN, INT16_MAX, -1000, 1000);
}
fft_execute(real_fft_plan);
for (uint16_t count_n = 1; count_n < AUDIO_TIME_SLICES; count_n++)
{
data = sqrt(real_fft_plan->output[2 * count_n] * real_fft_plan->output[2 * count_n] + real_fft_plan->output[2 * count_n + 1] * real_fft_plan->output[2 * count_n + 1]);
currentSound = map(data, 0, 2000, 0, 256);
if (currentSound > maxSound)
{
maxSound = currentSound;
}
}
fft_destroy(real_fft_plan);
// store max of sample in semaphore
xSemaphoreTake(xMaxNoiseSemaphore, portMAX_DELAY);
soundBuffer = maxSound;
xSemaphoreGive(xMaxNoiseSemaphore);
}
}
void aws_iot_task(void *param)
{
IoT_Error_t rc = FAILURE;
char JsonDocumentBuffer[MAX_LENGTH_OF_UPDATE_JSON_BUFFER];
size_t sizeOfJsonDocumentBuffer = sizeof(JsonDocumentBuffer) / sizeof(JsonDocumentBuffer[0]);
jsonStruct_t soundHandler;
soundHandler.cb = NULL;
soundHandler.pKey = "sound";
soundHandler.pData = &reportedSound;
soundHandler.type = SHADOW_JSON_UINT8;
soundHandler.dataLength = sizeof(uint8_t);
jsonStruct_t uvStatusActuator;
uvStatusActuator.cb = uv_Callback;
uvStatusActuator.pKey = "uvStatus";
uvStatusActuator.pData = &uvStatus;
uvStatusActuator.type = SHADOW_JSON_STRING;
uvStatusActuator.dataLength = strlen(uvStatus) + 1;
jsonStruct_t roomOccupancyActuator;
roomOccupancyActuator.cb = occupancy_Callback;
roomOccupancyActuator.pKey = "roomOccupancy";
roomOccupancyActuator.pData = &roomOccupancy;
roomOccupancyActuator.type = SHADOW_JSON_BOOL;
roomOccupancyActuator.dataLength = sizeof(bool);
ESP_LOGI(TAG, "AWS IoT SDK Version %d.%d.%d-%s", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_TAG);
// initialize the mqtt client
AWS_IoT_Client iotCoreClient;
ShadowInitParameters_t sp = ShadowInitParametersDefault;
sp.pHost = HostAddress;
sp.port = port;
sp.enableAutoReconnect = false;
sp.disconnectHandler = disconnect_callback_handler;
sp.pRootCA = (const char *)aws_root_ca_pem_start;
sp.pClientCRT = "#";
sp.pClientKey = "#0";
#define CLIENT_ID_LEN (ATCA_SERIAL_NUM_SIZE * 2)
char *client_id = malloc(CLIENT_ID_LEN + 1);
ATCA_STATUS ret = Atecc608_GetSerialString(client_id);
if (ret != ATCA_SUCCESS)
{
ESP_LOGE(TAG, "Failed to get device serial from secure element. Error: %i", ret);
abort();
}
ui_textarea_add("\nDevice client Id:\n>> %s <<\n", client_id, CLIENT_ID_LEN);
/* Wait for WiFI to show as connected */
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
false, true, portMAX_DELAY);
ESP_LOGI(TAG, "Shadow Init");
rc = aws_iot_shadow_init(&iotCoreClient, &sp);
if (SUCCESS != rc)
{
ESP_LOGE(TAG, "aws_iot_shadow_init returned error %d, aborting...", rc);
abort();
}
ShadowConnectParameters_t scp = ShadowConnectParametersDefault;
scp.pMyThingName = client_id;
scp.pMqttClientId = client_id;
scp.mqttClientIdLen = CLIENT_ID_LEN;
ESP_LOGI(TAG, "Shadow Connect");
rc = aws_iot_shadow_connect(&iotCoreClient, &scp);
if (SUCCESS != rc)
{
ESP_LOGE(TAG, "aws_iot_shadow_connect returned error %d, aborting...", rc);
abort();
}
ui_textarea_add("\nConnected to AWS IoT Core and pub/sub to the device shadow state\n", NULL, 0);
xTaskCreatePinnedToCore(µphone_task, "microphone_task", 4096, NULL, 1, NULL, 1);
/*
* Enable Auto Reconnect functionality. Minimum and Maximum time of Exponential backoff are set in aws_iot_config.h
* #AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL
* #AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL
*/
rc = aws_iot_shadow_set_autoreconnect_status(&iotCoreClient, true);
if (SUCCESS != rc)
{
ESP_LOGE(TAG, "Unable to set Auto Reconnect to true - %d, aborting...", rc);
abort();
}
// register delta callback for roomOccupancy
rc = aws_iot_shadow_register_delta(&iotCoreClient, &roomOccupancyActuator);
if (SUCCESS != rc)
{
ESP_LOGE(TAG, "Shadow Register Delta Error");
}
// register delta callback for uvStatus
rc = aws_iot_shadow_register_delta(&iotCoreClient, &uvStatusActuator);
if (SUCCESS != rc)
{
ESP_LOGE(TAG, "Shadow Register Delta Error");
}
// loop and publish changes
while (NETWORK_ATTEMPTING_RECONNECT == rc || NETWORK_RECONNECTED == rc || SUCCESS == rc)
{
rc = aws_iot_shadow_yield(&iotCoreClient, 200);
if (NETWORK_ATTEMPTING_RECONNECT == rc || shadowUpdateInProgress)
{
rc = aws_iot_shadow_yield(&iotCoreClient, 1000);
// If the client is attempting to reconnect, or already waiting on a shadow update,
// we will skip the rest of the loop.
continue;
}
// START get sensor readings
// sample temperature, convert to fahrenheit
MPU6886_GetTempData(&temperature);
temperature = (temperature * 1.8) + 32 - 50;
// sample from soundBuffer (latest reading from microphone)
xSemaphoreTake(xMaxNoiseSemaphore, portMAX_DELAY);
reportedSound = soundBuffer;
xSemaphoreGive(xMaxNoiseSemaphore);
// END get sensor readings
ESP_LOGI(TAG, "*****************************************************************************************");
ESP_LOGI(TAG, "On Device: roomOccupancy %s", roomOccupancy ? "true" : "false");
ESP_LOGI(TAG, "On Device: uvStatus %s", uvStatus);
ESP_LOGI(TAG, "On Device: sound %d", reportedSound);
rc = aws_iot_shadow_init_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer);
if (SUCCESS == rc)
{
rc = aws_iot_shadow_add_reported(JsonDocumentBuffer, sizeOfJsonDocumentBuffer, 4,
&soundHandler, &roomOccupancyActuator, &uvStatusActuator);
if (SUCCESS == rc)
{
rc = aws_iot_finalize_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer);
if (SUCCESS == rc)
{
ESP_LOGI(TAG, "Update Shadow: %s", JsonDocumentBuffer);
rc = aws_iot_shadow_update(&iotCoreClient, client_id, JsonDocumentBuffer,
ShadowUpdateStatusCallback, NULL, 4, true);
shadowUpdateInProgress = true;
}
}
}
ESP_LOGI(TAG, "*****************************************************************************************");
ESP_LOGI(TAG, "Stack remaining for task '%s' is %d bytes", pcTaskGetTaskName(NULL), uxTaskGetStackHighWaterMark(NULL));
vTaskDelay(pdMS_TO_TICKS(10000));
}
if (SUCCESS != rc)
{
ESP_LOGE(TAG, "An error occurred in the loop %d", rc);
}
ESP_LOGI(TAG, "Disconnecting");
rc = aws_iot_shadow_disconnect(&iotCoreClient);
if (SUCCESS != rc)
{
ESP_LOGE(TAG, "Disconnect error %d", rc);
}
vTaskDelete(NULL);
}
void app_main()
{
Core2ForAWS_Init();
Core2ForAWS_Display_SetBrightness(80);
Core2ForAWS_LED_Enable(1);
xMaxNoiseSemaphore = xSemaphoreCreateMutex();
ui_init();
initialise_wifi();
xTaskCreatePinnedToCore(&aws_iot_task, "aws_iot_task", 4096 * 2, NULL, 5, NULL, 1);
}
Comments