50% OFF

ESP32-IDF Workshop

Blog/IoT Architecture

Connecting ESP32 to AWS IoT Core: A Production Setup Guide

Most AWS IoT tutorials skip the hard parts — certificates, device shadows, reconnect logic. Here's the production setup that survives in the field.

| Intermediate
Rajath Kumar
Rajath KumarEdge AI Engineer & Founder, Analog Data
2026-06-21·10 min read
Connecting ESP32 to AWS IoT Core: A Production Setup Guide

AWS IoT Core is the right backend for production IoT fleets — managed MQTT broker, device shadows, rules engine, and deep AWS ecosystem integration. But the official getting-started guides gloss over the parts that actually break in production: certificate management, reconnect storms, and device shadow sync.


Architecture Overview

text
1ESP32 Device
23  │  MQTT over TLS (port 8883)
4  │  X.509 mutual authentication
56AWS IoT Core (managed MQTT broker)
78  ├── Device Shadow ──► DynamoDB (state persistence)
9  ├── Rules Engine  ──► Lambda / S3 / Timestream (analytics)
10  └── Fleet Hub    ──► CloudWatch (monitoring)

Each device authenticates with a unique X.509 certificate. No username/password. No shared secrets. The private key never leaves the device.


Step 1: Certificate Provisioning

Generate and register a device certificate. Do this per device, per production unit:

bash
1# Generate private key and CSR on your provisioning workstation
2openssl genrsa -out device_001.key 2048
3openssl req -new -key device_001.key \
4  -out device_001.csr \
5  -subj "/CN=device-001/O=AnalogData/C=IN"
6
7# Register with AWS IoT and sign
8aws iot create-certificate-from-csr \
9  --certificate-signing-request file://device_001.csr \
10  --set-as-active \
11  --query 'certificatePem' \
12  --output text > device_001.crt
13
14# Attach a policy that allows this device to publish/subscribe
15aws iot attach-policy \
16  --policy-name ESP32DevicePolicy \
17  --target <certificate-arn>
18
19# Register the thing
20aws iot create-thing --thing-name device-001
21aws iot attach-thing-principal \
22  --thing-name device-001 \
23  --principal <certificate-arn>

Store the device_001.key and device_001.crt in ESP32's NVS (non-volatile storage), not in flash as header files — NVS allows certificate rotation via OTA.


Step 2: ESP-IDF MQTT Client with TLS

c
1#include "mqtt_client.h"
2#include "esp_tls.h"
3#include "nvs_flash.h"
4
5// Load certificates from NVS at runtime (not compiled-in)
6static char device_cert[2048];
7static char device_key[2048];
8static const char aws_root_ca[] =
9    "-----BEGIN CERTIFICATE-----\n"
10    /* Amazon Root CA 1 — embed this, it doesn't rotate */
11    "-----END CERTIFICATE-----\n";
12
13esp_mqtt_client_handle_t mqtt_client = NULL;
14
15void mqtt_init(void) {
16    load_cert_from_nvs("device_cert", device_cert, sizeof(device_cert));
17    load_cert_from_nvs("device_key",  device_key,  sizeof(device_key));
18
19    esp_mqtt_client_config_t mqtt_cfg = {
20        .broker = {
21            .address.uri = "mqtts://YOUR-ENDPOINT.iot.ap-south-1.amazonaws.com:8883",
22            .verification.certificate = aws_root_ca,
23        },
24        .credentials = {
25            .authentication = {
26                .certificate = device_cert,
27                .key         = device_key,
28            },
29        },
30        .session = {
31            .keepalive             = 60,
32            .disable_clean_session = true,    // retain subscriptions across reconnects
33            .last_will = {
34                .topic   = "$aws/things/device-001/shadow/update",
35                .msg     = "{\"state\":{\"reported\":{\"online\":false}}}",
36                .qos     = 1,
37                .retain  = false,
38            },
39        },
40        .network = {
41            .timeout_ms          = 10000,
42            .reconnect_timeout_ms = 5000,    // backoff handled in event handler
43        },
44    };
45
46    mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
47    esp_mqtt_client_register_event(mqtt_client, ESP_EVENT_ANY_ID,
48                                   mqtt_event_handler, NULL);
49    esp_mqtt_client_start(mqtt_client);
50}

Step 3: Reconnect Logic That Doesn't Storm the Broker

Naive reconnect causes broker overload when hundreds of devices reconnect simultaneously after a network outage. Use exponential backoff with jitter:

c
1static int reconnect_attempts = 0;
2
3static void mqtt_event_handler(void *handler_args, esp_event_base_t base,
4                                int32_t event_id, void *event_data) {
5    esp_mqtt_event_handle_t event = event_data;
6
7    switch ((esp_mqtt_event_id_t)event_id) {
8        case MQTT_EVENT_CONNECTED:
9            ESP_LOGI(TAG, "Connected to AWS IoT Core");
10            reconnect_attempts = 0;
11            publish_device_shadow_online();
12            subscribe_to_commands();
13            break;
14
15        case MQTT_EVENT_DISCONNECTED:
16            reconnect_attempts++;
17            // Exponential backoff: 1s, 2s, 4s, 8s... max 60s + random jitter
18            int base_delay = (1 << MIN(reconnect_attempts, 6)) * 1000;
19            int jitter      = esp_random() % 5000;  // 0-5s random jitter
20            int delay        = MIN(base_delay + jitter, 65000);
21
22            ESP_LOGW(TAG, "Disconnected. Reconnecting in %dms (attempt %d)",
23                     delay, reconnect_attempts);
24            vTaskDelay(pdMS_TO_TICKS(delay));
25            esp_mqtt_client_reconnect(mqtt_client);
26            break;
27
28        case MQTT_EVENT_DATA:
29            handle_incoming_message(event->topic, event->topic_len,
30                                    event->data, event->data_len);
31            break;
32
33        default:
34            break;
35    }
36}

Step 4: Device Shadow for State Synchronisation

Device Shadow is AWS IoT's mechanism for keeping device state in sync even when the device is offline:

c
1#define SHADOW_UPDATE_TOPIC  "$aws/things/device-001/shadow/update"
2#define SHADOW_GET_TOPIC     "$aws/things/device-001/shadow/get"
3#define SHADOW_DELTA_TOPIC   "$aws/things/device-001/shadow/update/delta"
4
5void publish_telemetry(float temperature, float humidity) {
6    char payload[256];
7    snprintf(payload, sizeof(payload),
8        "{"
9        "\"state\":{"
10        "  \"reported\":{"
11        "    \"temperature\":%.2f,"
12        "    \"humidity\":%.2f,"
13        "    \"firmware\":\"1.2.3\","
14        "    \"online\":true"
15        "  }"
16        "}"
17        "}",
18        temperature, humidity);
19
20    esp_mqtt_client_publish(mqtt_client, SHADOW_UPDATE_TOPIC,
21                            payload, 0, 1, 0);
22}
23
24// Handle desired state changes from cloud (e.g. config update)
25void handle_shadow_delta(const char *payload, int len) {
26    // Parse JSON delta and apply settings
27    // e.g. {"state":{"sampling_rate":10,"alert_threshold":0.025}}
28    cJSON *root  = cJSON_ParseWithLength(payload, len);
29    cJSON *state = cJSON_GetObjectItem(root, "state");
30
31    int new_rate = cJSON_GetNumberValue(cJSON_GetObjectItem(state, "sampling_rate"));
32    if (new_rate > 0) update_sampling_rate(new_rate);
33
34    cJSON_Delete(root);
35
36    // Report the new desired state as reported to close the delta
37    publish_shadow_reported_state();
38}

Key Takeaways

  1. One certificate per device — never share certificates across a fleet
  2. Store certs in NVS — not as compiled-in header files — enables OTA rotation
  3. Exponential backoff + jitter on reconnect — prevents reconnect storms at scale
  4. Device Shadow closes the offline gap — cloud can push config changes, device applies them on reconnect
  5. Last Will message marks device offline — free health monitoring without polling
Share
Live Workshop

Go from Arduino to Production Firmware

The ESP32-IDF Workshop covers ESP-IDF from scratch — tasks, queues, OTA, Wifi management, and deploying firmware that doesn't break at 3am.

Join the Workshop →

Frequently Asked Questions

Quick answers to common questions

Rajath Kumar

Written by

Rajath Kumar

Edge AI Engineer & Founder, Analog Data

I build things that run on chips and the software that talks to them. ESP32, STM32, FreeRTOS, FastAPI, TinyML — from bare-metal firmware to cloud backends to on-device inference. Based in Bengaluru. Founder of Analog Data.

More in IoT Architecture