/*
* AWS IoT EduKit - Core2 for AWS IoT EduKit
* Smart Thermostat v1.2.0
* main.c
*
* Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
* Additions Copyright 2016 Espressif Systems (Shanghai) PTE LTD
*
* 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.
*/
/**
* @file main.c
* @brief simple MQTT publish, subscribe, and device shadows for use with AWS IoT EduKit reference hardware.
*
* This example takes the parameters from the build configuration and establishes a connection to AWS IoT Core over MQTT.
*
* Some configuration is required. Visit https://edukit.workshop.aws
*
*/
#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"
#include "mhz19b.h"
#include <ultrasonic.h>
#define MAX_DISTANCE_CM 50 // 0.5m max
#define TRIGGER_GPIO 26 // PORT B
#define MUSHROOM_PIN 36 // PORT B - yellow (when mushroom connected)
#define ECHO_GPIO 36 // PORT B - yellow (when ultrasonic sensor is connected)
float distance;
uint8_t peopleCount = 0;
static const char *TAG = "MAIN";
// Number of slices to split the microphone sample into
#define AUDIO_TIME_SLICES 60
#define MAX_LENGTH_OF_UPDATE_JSON_BUFFER 220
/* 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;
/* Semaphore for people count */
SemaphoreHandle_t xPeopleCountSemaphore;
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");
ESP_LOGI(TAG, "Disconnected from AWS IoT Core...");
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");
}
}
float temperature = 0;
uint8_t soundBuffer = 0;
uint8_t reportedSound = 0;
uint8_t masksCount = 0;
uint8_t noMasksCount = 0;
uint8_t emergency = 0;
int16_t co2 = 0;
bool mustEscape = 0;
// 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 ultrasonic_task(void *pvParameters)
{
ultrasonic_sensor_t sensor = {
.trigger_pin = TRIGGER_GPIO,
.echo_pin = ECHO_GPIO
};
ultrasonic_init(&sensor);
for (;;) {
//float distance;
esp_err_t res = ultrasonic_measure(&sensor, MAX_DISTANCE_CM, &distance);
if (res != ESP_OK)
{
printf("Error %d: ", res);
switch (res)
{
case ESP_ERR_ULTRASONIC_PING:
printf("Cannot ping (device is in invalid state)\n");
break;
case ESP_ERR_ULTRASONIC_PING_TIMEOUT:
printf("Ping timeout (no device found)\n");
break;
case ESP_ERR_ULTRASONIC_ECHO_TIMEOUT:
printf("Echo timeout (i.e. distance too big)\n");
break;
default:
printf("%s\n", esp_err_to_name(res));
}
}
else {
// printf("Distance: %0.04f m\n", distance);
if (distance > 0.05 && distance < 0.50) {
ESP_LOGI(TAG,"Beam crossed");
ui_display_scanned();
xSemaphoreTake(xPeopleCountSemaphore, portMAX_DELAY);
peopleCount++;
xSemaphoreGive(xPeopleCountSemaphore);
vTaskDelay(pdMS_TO_TICKS(500));
ui_display_danbo();
continue;
}
}
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void alarm_task(void *pvParameters) {
if (mustEscape) {
ui_display_alarm();
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();
Core2ForAWS_Motor_SetStrength(80);
}
else
{
ui_init();
ESP_LOGI(TAG, "clearing side LEDs");
Core2ForAWS_Sk6812_Clear();
Core2ForAWS_Sk6812_Show();
Core2ForAWS_Motor_SetStrength(0);
}
vTaskDelete(NULL);
}
void escape_Callback(const char *pJsonString, uint32_t JsonStringDataLen, jsonStruct_t *pContext) {
IOT_UNUSED(pJsonString);
IOT_UNUSED(JsonStringDataLen);
if(pContext != NULL) {
ESP_LOGI(TAG, "Delta - alarm state changed to %d", *(bool *) (pContext->pData));
xTaskCreatePinnedToCore(&alarm_task, "alarm_task", 4096, NULL, 1, NULL, 1);
}
}
// TODO this code does not work
void mushroom_read_task(){
for(;;){
Core2ForAWS_Port_PinMode(MUSHROOM_PIN, INPUT);
bool isHigh = Core2ForAWS_Port_Read(MUSHROOM_PIN);
if (isHigh){
emergency = 1;
ESP_LOGI(TAG, "Mushroom pressed ");
} else {
emergency = 0;
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
#define MHZ19B_TX 12
#define MHZ19B_RX 13
static void CO2_sensor_task(void *arg){
//ESP_LOGI(TAG, "UART - Tx task start");
mhz19b_dev_t dev;
char version[6];
uint16_t range;
bool autocal;
ESP_ERROR_CHECK(mhz19b_init(&dev, UART_NUM_1, PORT_C_UART_TX_PIN, PORT_C_UART_RX_PIN));
while (!mhz19b_detect(&dev))
{
ESP_LOGI(TAG, "MHZ-19B not detected, waiting...");
vTaskDelay(1000 / portTICK_RATE_MS);
}
mhz19b_get_version(&dev, version);
ESP_LOGI(TAG, "MHZ-19B firmware version: %s", version);
ESP_LOGI(TAG, "MHZ-19B set range and autocal");
mhz19b_set_range(&dev, MHZ19B_RANGE_5000);
mhz19b_set_auto_calibration(&dev, false);
mhz19b_get_range(&dev, &range);
ESP_LOGI(TAG, " range: %d", range);
mhz19b_get_auto_calibration(&dev, &autocal);
ESP_LOGI(TAG, " autocal: %s", autocal ? "ON" : "OFF");
while (mhz19b_is_warming_up(&dev, true)) // use smart warming up detection
{
//ESP_LOGI(TAG, "MHZ-19B is warming up");
vTaskDelay(1000 / portTICK_RATE_MS);
}
while (1) {
mhz19b_read_co2(&dev, &co2);
//ESP_LOGI(TAG, "CO2: %d", co2);
vTaskDelay(5000 / portTICK_RATE_MS);
}
}
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 temperatureHandler;
temperatureHandler.cb = NULL;
temperatureHandler.pKey = "temperature";
temperatureHandler.pData = &temperature;
temperatureHandler.type = SHADOW_JSON_FLOAT;
temperatureHandler.dataLength = sizeof(float);
jsonStruct_t soundHandler;
soundHandler.cb = NULL;
soundHandler.pKey = "sound";
soundHandler.pData = &reportedSound;
soundHandler.type = SHADOW_JSON_UINT8;
soundHandler.dataLength = sizeof(uint8_t);
jsonStruct_t maskCountHandler;
maskCountHandler.cb = NULL;
maskCountHandler.pKey = "masksCount";
maskCountHandler.pData = &masksCount;
maskCountHandler.type = SHADOW_JSON_UINT8;
maskCountHandler.dataLength = sizeof(uint8_t);
jsonStruct_t noMaskCountHandler;
noMaskCountHandler.cb = NULL;
noMaskCountHandler.pKey = "noMasksCount";
noMaskCountHandler.pData = &noMasksCount;
noMaskCountHandler.type = SHADOW_JSON_UINT8;
noMaskCountHandler.dataLength = sizeof(uint8_t);
jsonStruct_t peopleCountHandler;
peopleCountHandler.cb = NULL;
peopleCountHandler.pKey = "peopleCount";
peopleCountHandler.pData = &peopleCount;
peopleCountHandler.type = SHADOW_JSON_UINT8;
peopleCountHandler.dataLength = sizeof(uint8_t);
jsonStruct_t mushroomHandler;
mushroomHandler.cb = NULL;
mushroomHandler.pKey = "emergency";
mushroomHandler.pData = &emergency;
mushroomHandler.type = SHADOW_JSON_UINT8;
mushroomHandler.dataLength = sizeof(uint8_t);
jsonStruct_t co2Handler;
co2Handler.cb = NULL;
co2Handler.pKey = "CO2";
co2Handler.pData = &co2;
co2Handler.type = SHADOW_JSON_UINT8;
co2Handler.dataLength = sizeof(uint8_t);
jsonStruct_t mustEscapeActuator;
mustEscapeActuator.cb = escape_Callback;
mustEscapeActuator.pKey = "mustEscape";
mustEscapeActuator.pData = &mustEscape;
mustEscapeActuator.type = SHADOW_JSON_BOOL;
mustEscapeActuator.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();
}
ESP_LOGI(TAG, "Device client Id: %s", client_id);
/* 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();
}
ESP_LOGI(TAG, "Connected to AWS IoT Device Shadow service");
// this task will perform sound capture
xTaskCreatePinnedToCore(µphone_task, "microphone_task", 4096, NULL, 1, NULL, 1);
// this task will perform people detection using ultrasound
xTaskCreatePinnedToCore(&ultrasonic_task, "ultrasonic_task", 4096, NULL, 1, NULL, 1);
// this task will monitor the mushroom state
// xTaskCreatePinnedToCore(&mushroom_read_task, "mushroom_read", 1024*4, NULL, 1, NULL, 1);
// this task will manage the comm with the CO2 sensor
esp_err_t err = Core2ForAWS_Port_PinMode(PORT_C_UART_TX_PIN, UART);
if (err == ESP_OK){
Core2ForAWS_Port_C_UART_Begin(115200);
xTaskCreatePinnedToCore(&CO2_sensor_task, "CO2_sensor_task", 4096*2, NULL, 5, 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 alarm
rc = aws_iot_shadow_register_delta(&iotCoreClient, &mustEscapeActuator);
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);
// get masks counts
masksCount = ui_countMasks();
noMasksCount = ui_countNoMasks();
// END get sensor readings
ESP_LOGI(TAG, "*****************************************************************************************");
ESP_LOGI(TAG, "On Device: temperature %f", temperature);
ESP_LOGI(TAG, "On Device: sound %d", reportedSound);
ESP_LOGI(TAG, "On Device: mask count %d", masksCount);
ESP_LOGI(TAG, "On Device: no mask count %d", noMasksCount);
ESP_LOGI(TAG, "On Device: people count %d", peopleCount);
ESP_LOGI(TAG, "On Device: emergency %d", emergency);
ESP_LOGI(TAG, "On Device: CO2 %d", co2);
rc = aws_iot_shadow_init_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer);
if(SUCCESS == rc) {
rc = aws_iot_shadow_add_reported(JsonDocumentBuffer, sizeOfJsonDocumentBuffer, 8,
&temperatureHandler, &soundHandler, &maskCountHandler, &noMaskCountHandler,
&peopleCountHandler, &mushroomHandler, &co2Handler, &mustEscapeActuator
);
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(5000));
}
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();
xPeopleCountSemaphore = xSemaphoreCreateMutex();
ui_init();
initialise_wifi();
xTaskCreatePinnedToCore(&aws_iot_task, "aws_iot_task", 4096*2, NULL, 5, NULL, 1);
}
Comments