]> granicus.if.org Git - esp-idf/blob - examples/protocols/aws_iot/thing_shadow/main/thing_shadow_sample.c
aws_iot: fix aws_iot examples without NVS initialization
[esp-idf] / examples / protocols / aws_iot / thing_shadow / main / thing_shadow_sample.c
1 /*
2  * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * Additions Copyright 2016 Espressif Systems (Shanghai) PTE LTD
4  *
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License").
7  * You may not use this file except in compliance with the License.
8  * A copy of the License is located at
9  *
10  *  http://aws.amazon.com/apache2.0
11  *
12  * or in the "license" file accompanying this file. This file is distributed
13  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14  * express or implied. See the License for the specific language governing
15  * permissions and limitations under the License.
16  */
17 /**
18  * @file thing_shadow_sample.c
19  * @brief A simple connected window example demonstrating the use of Thing Shadow
20  *
21  * See example README for more details.
22  */
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <unistd.h>
27 #include <limits.h>
28 #include <string.h>
29
30 #include "freertos/FreeRTOS.h"
31 #include "freertos/task.h"
32 #include "freertos/event_groups.h"
33 #include "esp_system.h"
34 #include "esp_wifi.h"
35 #include "esp_event_loop.h"
36 #include "esp_log.h"
37 #include "esp_vfs_fat.h"
38 #include "driver/sdmmc_host.h"
39
40 #include "nvs.h"
41 #include "nvs_flash.h"
42
43 #include "aws_iot_config.h"
44 #include "aws_iot_log.h"
45 #include "aws_iot_version.h"
46 #include "aws_iot_mqtt_client_interface.h"
47 #include "aws_iot_shadow_interface.h"
48
49 /*!
50  * The goal of this sample application is to demonstrate the capabilities of shadow.
51  * This device(say Connected Window) will open the window of a room based on temperature
52  * It can report to the Shadow the following parameters:
53  *  1. temperature of the room (double)
54  *  2. status of the window (open or close)
55  * It can act on commands from the cloud. In this case it will open or close the window based on the json object "windowOpen" data[open/close]
56  *
57  * The two variables from a device's perspective are double temperature and bool windowOpen
58  * The device needs to act on only on windowOpen variable, so we will create a primitiveJson_t object with callback
59  The Json Document in the cloud will be
60  {
61  "reported": {
62  "temperature": 0,
63  "windowOpen": false
64  },
65  "desired": {
66  "windowOpen": false
67  }
68  }
69  */
70
71 static const char *TAG = "shadow";
72
73 #define ROOMTEMPERATURE_UPPERLIMIT 32.0f
74 #define ROOMTEMPERATURE_LOWERLIMIT 25.0f
75 #define STARTING_ROOMTEMPERATURE ROOMTEMPERATURE_LOWERLIMIT
76
77 #define MAX_LENGTH_OF_UPDATE_JSON_BUFFER 200
78
79 /* The examples use simple WiFi configuration that you can set via
80    'make menuconfig'.
81
82    If you'd rather not, just change the below entries to strings with
83    the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
84 */
85 #define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
86 #define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
87
88
89 /* FreeRTOS event group to signal when we are connected & ready to make a request */
90 static EventGroupHandle_t wifi_event_group;
91
92 /* The event group allows multiple bits for each event,
93    but we only care about one event - are we connected
94    to the AP with an IP? */
95 const int CONNECTED_BIT = BIT0;
96
97
98 /* CA Root certificate, device ("Thing") certificate and device
99  * ("Thing") key.
100
101    Example can be configured one of two ways:
102
103    "Embedded Certs" are loaded from files in "certs/" and embedded into the app binary.
104
105    "Filesystem Certs" are loaded from the filesystem (SD card, etc.)
106
107    See example README for more details.
108 */
109 #if defined(CONFIG_EXAMPLE_EMBEDDED_CERTS)
110
111 extern const uint8_t aws_root_ca_pem_start[] asm("_binary_aws_root_ca_pem_start");
112 extern const uint8_t aws_root_ca_pem_end[] asm("_binary_aws_root_ca_pem_end");
113 extern const uint8_t certificate_pem_crt_start[] asm("_binary_certificate_pem_crt_start");
114 extern const uint8_t certificate_pem_crt_end[] asm("_binary_certificate_pem_crt_end");
115 extern const uint8_t private_pem_key_start[] asm("_binary_private_pem_key_start");
116 extern const uint8_t private_pem_key_end[] asm("_binary_private_pem_key_end");
117
118 #elif defined(CONFIG_EXAMPLE_FILESYSTEM_CERTS)
119
120 static const char * DEVICE_CERTIFICATE_PATH = CONFIG_EXAMPLE_CERTIFICATE_PATH;
121 static const char * DEVICE_PRIVATE_KEY_PATH = CONFIG_EXAMPLE_PRIVATE_KEY_PATH;
122 static const char * ROOT_CA_PATH = CONFIG_EXAMPLE_ROOT_CA_PATH;
123
124 #else
125 #error "Invalid method for loading certs"
126 #endif
127
128 /**
129  * @brief Default MQTT HOST URL is pulled from the aws_iot_config.h which
130  * uses menuconfig to find a default.
131  */
132 char HostAddress[255] = AWS_IOT_MQTT_HOST;
133
134 /**
135  * @brief Default MQTT port is pulled from the aws_iot_config.h which
136  * uses menuconfig to find a default.
137  */
138 uint32_t port = AWS_IOT_MQTT_PORT;
139
140 static esp_err_t event_handler(void *ctx, system_event_t *event)
141 {
142     switch(event->event_id) {
143     case SYSTEM_EVENT_STA_START:
144         esp_wifi_connect();
145         break;
146     case SYSTEM_EVENT_STA_GOT_IP:
147         xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
148         break;
149     case SYSTEM_EVENT_STA_DISCONNECTED:
150         /* This is a workaround as ESP32 WiFi libs don't currently
151            auto-reassociate. */
152         esp_wifi_connect();
153         xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
154         break;
155     default:
156         break;
157     }
158     return ESP_OK;
159 }
160
161 static void simulateRoomTemperature(float *pRoomTemperature) {
162     static float deltaChange;
163
164     if(*pRoomTemperature >= ROOMTEMPERATURE_UPPERLIMIT) {
165         deltaChange = -0.5f;
166     } else if(*pRoomTemperature <= ROOMTEMPERATURE_LOWERLIMIT) {
167         deltaChange = 0.5f;
168     }
169
170     *pRoomTemperature += deltaChange;
171 }
172
173 static bool shadowUpdateInProgress;
174
175 void ShadowUpdateStatusCallback(const char *pThingName, ShadowActions_t action, Shadow_Ack_Status_t status,
176                                 const char *pReceivedJsonDocument, void *pContextData) {
177     IOT_UNUSED(pThingName);
178     IOT_UNUSED(action);
179     IOT_UNUSED(pReceivedJsonDocument);
180     IOT_UNUSED(pContextData);
181
182     shadowUpdateInProgress = false;
183
184     if(SHADOW_ACK_TIMEOUT == status) {
185         ESP_LOGE(TAG, "Update timed out");
186     } else if(SHADOW_ACK_REJECTED == status) {
187         ESP_LOGE(TAG, "Update rejected");
188     } else if(SHADOW_ACK_ACCEPTED == status) {
189         ESP_LOGI(TAG, "Update accepted");
190     }
191 }
192
193 void windowActuate_Callback(const char *pJsonString, uint32_t JsonStringDataLen, jsonStruct_t *pContext) {
194     IOT_UNUSED(pJsonString);
195     IOT_UNUSED(JsonStringDataLen);
196
197     if(pContext != NULL) {
198         ESP_LOGI(TAG, "Delta - Window state changed to %d", *(bool *) (pContext->pData));
199     }
200 }
201
202 void aws_iot_task(void *param) {
203     IoT_Error_t rc = FAILURE;
204
205     char JsonDocumentBuffer[MAX_LENGTH_OF_UPDATE_JSON_BUFFER];
206     size_t sizeOfJsonDocumentBuffer = sizeof(JsonDocumentBuffer) / sizeof(JsonDocumentBuffer[0]);
207     float temperature = 0.0;
208
209     bool windowOpen = false;
210     jsonStruct_t windowActuator;
211     windowActuator.cb = windowActuate_Callback;
212     windowActuator.pData = &windowOpen;
213     windowActuator.pKey = "windowOpen";
214     windowActuator.type = SHADOW_JSON_BOOL;
215
216     jsonStruct_t temperatureHandler;
217     temperatureHandler.cb = NULL;
218     temperatureHandler.pKey = "temperature";
219     temperatureHandler.pData = &temperature;
220     temperatureHandler.type = SHADOW_JSON_FLOAT;
221
222     ESP_LOGI(TAG, "AWS IoT SDK Version %d.%d.%d-%s", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_TAG);
223
224     // initialize the mqtt client
225     AWS_IoT_Client mqttClient;
226
227     ShadowInitParameters_t sp = ShadowInitParametersDefault;
228     sp.pHost = AWS_IOT_MQTT_HOST;
229     sp.port = AWS_IOT_MQTT_PORT;
230
231 #if defined(CONFIG_EXAMPLE_EMBEDDED_CERTS)
232     sp.pClientCRT = (const char *)certificate_pem_crt_start;
233     sp.pClientKey = (const char *)private_pem_key_start;
234     sp.pRootCA = (const char *)aws_root_ca_pem_start;
235 #elif defined(CONFIG_EXAMPLE_FILESYSTEM_CERTS)
236     sp.pClientCRT = DEVICE_CERTIFICATE_PATH;
237     sp.pClientKey = DEVICE_PRIVATE_KEY_PATH;
238     sp.pRootCA = ROOT_CA_PATH;
239 #endif
240     sp.enableAutoReconnect = false;
241     sp.disconnectHandler = NULL;
242
243 #ifdef CONFIG_EXAMPLE_SDCARD_CERTS
244     ESP_LOGI(TAG, "Mounting SD card...");
245     sdmmc_host_t host = SDMMC_HOST_DEFAULT();
246     sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
247     esp_vfs_fat_sdmmc_mount_config_t mount_config = {
248         .format_if_mount_failed = false,
249         .max_files = 3,
250     };
251     sdmmc_card_t* card;
252     esp_err_t ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card);
253     if (ret != ESP_OK) {
254         ESP_LOGE(TAG, "Failed to mount SD card VFAT filesystem.");
255         abort();
256     }
257 #endif
258
259     /* Wait for WiFI to show as connected */
260     xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
261                         false, true, portMAX_DELAY);
262
263     ESP_LOGI(TAG, "Shadow Init");
264     rc = aws_iot_shadow_init(&mqttClient, &sp);
265     if(SUCCESS != rc) {
266         ESP_LOGE(TAG, "aws_iot_shadow_init returned error %d, aborting...", rc);
267         abort();
268     }
269
270     ShadowConnectParameters_t scp = ShadowConnectParametersDefault;
271     scp.pMyThingName = CONFIG_AWS_EXAMPLE_THING_NAME;
272     scp.pMqttClientId = CONFIG_AWS_EXAMPLE_CLIENT_ID;
273     scp.mqttClientIdLen = (uint16_t) strlen(CONFIG_AWS_EXAMPLE_CLIENT_ID);
274
275     ESP_LOGI(TAG, "Shadow Connect");
276     rc = aws_iot_shadow_connect(&mqttClient, &scp);
277     if(SUCCESS != rc) {
278         ESP_LOGE(TAG, "aws_iot_shadow_connect returned error %d, aborting...", rc);
279         abort();
280     }
281
282     /*
283      * Enable Auto Reconnect functionality. Minimum and Maximum time of Exponential backoff are set in aws_iot_config.h
284      *  #AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL
285      *  #AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL
286      */
287     rc = aws_iot_shadow_set_autoreconnect_status(&mqttClient, true);
288     if(SUCCESS != rc) {
289         ESP_LOGE(TAG, "Unable to set Auto Reconnect to true - %d, aborting...", rc);
290         abort();
291     }
292
293     rc = aws_iot_shadow_register_delta(&mqttClient, &windowActuator);
294
295     if(SUCCESS != rc) {
296         ESP_LOGE(TAG, "Shadow Register Delta Error");
297     }
298     temperature = STARTING_ROOMTEMPERATURE;
299
300     // loop and publish a change in temperature
301     while(NETWORK_ATTEMPTING_RECONNECT == rc || NETWORK_RECONNECTED == rc || SUCCESS == rc) {
302         rc = aws_iot_shadow_yield(&mqttClient, 200);
303         if(NETWORK_ATTEMPTING_RECONNECT == rc || shadowUpdateInProgress) {
304             rc = aws_iot_shadow_yield(&mqttClient, 1000);
305             // If the client is attempting to reconnect, or already waiting on a shadow update,
306             // we will skip the rest of the loop.
307             continue;
308         }
309         ESP_LOGI(TAG, "=======================================================================================");
310         ESP_LOGI(TAG, "On Device: window state %s", windowOpen ? "true" : "false");
311         simulateRoomTemperature(&temperature);
312
313         rc = aws_iot_shadow_init_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer);
314         if(SUCCESS == rc) {
315             rc = aws_iot_shadow_add_reported(JsonDocumentBuffer, sizeOfJsonDocumentBuffer, 2, &temperatureHandler,
316                                              &windowActuator);
317             if(SUCCESS == rc) {
318                 rc = aws_iot_finalize_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer);
319                 if(SUCCESS == rc) {
320                     ESP_LOGI(TAG, "Update Shadow: %s", JsonDocumentBuffer);
321                     rc = aws_iot_shadow_update(&mqttClient, CONFIG_AWS_EXAMPLE_THING_NAME, JsonDocumentBuffer,
322                                                ShadowUpdateStatusCallback, NULL, 4, true);
323                     shadowUpdateInProgress = true;
324                 }
325             }
326         }
327         ESP_LOGI(TAG, "*****************************************************************************************");
328         vTaskDelay(1000 / portTICK_RATE_MS);
329     }
330
331     if(SUCCESS != rc) {
332         ESP_LOGE(TAG, "An error occurred in the loop %d", rc);
333     }
334
335     ESP_LOGI(TAG, "Disconnecting");
336     rc = aws_iot_shadow_disconnect(&mqttClient);
337
338     if(SUCCESS != rc) {
339         ESP_LOGE(TAG, "Disconnect error %d", rc);
340     }
341
342     vTaskDelete(NULL);
343 }
344
345 static void initialise_wifi(void)
346 {
347     tcpip_adapter_init();
348     wifi_event_group = xEventGroupCreate();
349     ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
350     wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
351     ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
352     ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
353     wifi_config_t wifi_config = {
354         .sta = {
355             .ssid = EXAMPLE_WIFI_SSID,
356             .password = EXAMPLE_WIFI_PASS,
357         },
358     };
359     ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
360     ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
361     ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
362     ESP_ERROR_CHECK( esp_wifi_start() );
363 }
364
365
366 void app_main()
367 {
368     esp_err_t err = nvs_flash_init();
369     if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
370         ESP_ERROR_CHECK(nvs_flash_erase());
371         err = nvs_flash_init();
372     }
373     ESP_ERROR_CHECK( err );
374
375     initialise_wifi();
376     /* Temporarily pin task to core, due to FPU uncertainty */
377     xTaskCreatePinnedToCore(&aws_iot_task, "aws_iot_task", 16384+1024, NULL, 5, NULL, 1);
378 }