Skip to content

Overview

AWS IoT Core is the IoT platform in Amazon Web Services. Devices exchange messages with it over MQTT (the most mainstream lightweight messaging protocol in IoT), and authenticate with X.509 certificates (certificate = the "digital ID" the cloud platform issues to the device; combined with TLS encrypted transport, it's like putting messages in a tamper-proof encrypted envelope). This tutorial uses the official aws_iot_core example project to connect the Ai-WB2 to AWS IoT Core: after connecting, it loops publishing Light ON/OFF messages to a cloud topic and subscribes to cloud topics; the example also demonstrates the Shadow feature (the device's cloud-side "stand-in", always holding the device's latest state).

In plain words: AWS IoT is like the international version of a "WeChat server", but with a different login method — domestic platforms use "account and password" (the triple), AWS uses three keys: the device certificate (your ID card), the private key (a key only you have), and the root CA certificate (the "official stamp" that verifies identity). All three keys are embedded in the program so the board can get past AWS's "doorman" (the TLS encrypted connection) and log into the platform to send messages. This tutorial helps you issue these three keys in the AWS console and install them into the code.

This tutorial is based on the official Ai-Thinker SDK (Ai-Thinker-Open/Ai-Thinker-WB2, version release_bl_iot_sdk_1.6.40) example applications/iot-solution/aws_iot_core; the code can be found directly in the local SDK.

🎯Page GoalCreate a Thing and certificates in AWS IoT Core, connect the Ai-WB2 over MQTT + TLS, achieve topic publish/subscribe, and get familiar with the Shadow feature.
🧰Prerequisites① An Ai-WB2 development board (with a Type-C data cable) ② A 2.4GHz router (with public internet access) ③ An AWS account (registration requires card verification; free tier available) ④ Development environment set up per [SDK Installation](../../sdk/sdk_intro), and [Connect Wi-Fi](../wireless/wifi/wifi_connect) done.
🔗RelatedThe other two cloud platforms: [Connect Alibaba Cloud IoT](./ali_iot) and [Connect Tencent Cloud IoT](./qcloud); MQTT basics: [MQTT Communication](../wireless/wifi/mqtt).

Create an AWS Account, Thing and Certificates

This step “issues the three keys” on the cloud side: create the Thing (AWS calls a “device” a Thing), generate and download certificates, then install the keys into the code (step ③).

  1. Open the AWS official website, register and log in (card verification required; new accounts get free tier).
  2. Search IoT Core in the console and enter the AWS IoT Core console (pick us-east-1 or us-east-2 for the region — the region is where the servers live; use the same region for the endpoint later).
  3. Left menu “Manage → Things”, click “Create things” → “Create a single thing” → enter a thing name (e.g. Ai-WB2-M1, remember it — needed in the config later).
  4. Generate certificates for the thing: choose “1-Click certificate”; after generation immediately download the three files (generated only once, keep them safe):
    • xxx.cert.pem — the device certificate (ID card)
    • xxx.private.key — the private key (the key)
    • AmazonRootCA1.pem — the root CA certificate (the stamp)
  5. Create a Policy (= the “pass” issued to the certificate, declaring what it’s allowed to do): “Secure → Policies → Create policy”, add the four actions iot:Connect, iot:Subscribe, iot:Receive, iot:Publish, set Resources to * (enough for this tutorial’s testing), then “Secure → Certificates → select the certificate above → Attach policy”.
  6. Note the Endpoint (= server address): “Settings → Device data endpoint”, shaped like xxxxxxxx-ats.iot.us-east-2.amazonaws.com.
  7. Open the console’s “MQTT test client”, subscribe to topic $aws/things/<your thing name>/shadow/update, waiting for the board to connect and send messages (for verification later).
Enter the Example Project

Open a terminal and enter the official aws_iot_core example project directory:

cd ~/Ai-Thinker-WB2/applications/iot-solution/aws_iot_core

Note: cd is the “change directory” command — entering the official example project; all subsequent make commands must run in this directory.

📌 This project consists of multiple source files; the Full Code section only shows main.c — the rest can be found in the official project:

File Purpose
aws_iot_core/main.c Main program: Wi-Fi connection + starting the AWS control task
aws_iot_core/aws_control.c Main flow: init → connect → subscribe → loop publishing Light ON/OFF
aws_iot_core/demo_publish.c Standalone publish example (publish only, no subscribe)
aws_iot_core/demo_subscribe.c Standalone subscribe example (subscribe only, no publish)
aws_iot_core/demo_shadow.c Shadow example: syncs the light state to the cloud “stand-in”
aws_iot_core/aws_test_cert.h The certificate and endpoint configuration lives here (three keys + server address + topics)

💡 The official repo also has a certificate-file example project aws_iot_cert under applications/iot-solution/ (certificates as files read from the filesystem); this tutorial’s aws_iot_core compiles the certificates directly into the firmware, which is friendlier for beginners.

Modify Parameters (Use Your Own Wi-Fi, Certificates and Endpoint)

Open aws_iot_core/main.c and change the Wi-Fi account and password at the top (placeholder = the example values pre-written in the code; you need to replace them with your real ones):

#define ROUTER_SSID "your ssid"
#define ROUTER_PWD "your password"

Open aws_iot_core/aws_test_cert.h and do three things:

  1. Replace the content between -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- in TEST_ROOT_CA_FILENAME with the root CA certificate downloaded in step ①;
  2. Replace TEST_CERTIFICATE_FILENAME with the device certificate and TEST_PRIVATE_KEY_FILENAME with the private key (the -----END RSA PRIVATE KEY----- section at the end);
  3. Replace the endpoint with yours (shaped like xxxxxxxx-ats.iot.us-east-2.amazonaws.com):
#define TEST_MQTT_HOST "your endpoint"
#define TEST_MQTT_PORT 8883

Then replace the client ID and thing name with your thing name, and the AiThinker in the topics too (topic = channel name; messages are sent/received by channel):

#define TEST_MQTT_CLIENT_ID "Ai-WB2-M1"
#define TEST_MY_THING_NAME  "Ai-WB2-M1"

#define TEST_MYPUBTOPIC "$aws/things/AiThinker/shadow/update"
#define TEST_MYSUBTOPIC "$aws/things/AiThinker/shadow/update/accepted"

⚠️ Certificates are long text — when replacing, keep the surrounding double quotes and the \r\n line-break escape characters intact; use an editor’s whole-value replace for the two certificate macros. The most common symptom of broken certificate content is TLS handshake failure when connecting.

Write the Code

This tutorial uses the official example project — no new code needed; the complete aws_iot_core/main.c code has been moved to the end of this page:

📜 Full Code — in the “Full Code” section below, collapsed by default — click to expand, identical to the official example (applications/iot-solution/aws_iot_core/aws_iot_core/main.c). The main connect/send/receive flow is in aws_control.c, the Shadow in demo_shadow.c; the rest can be found in the official project.

Code highlights:

Code Purpose
wifi_mgmr_sta_connect(if, ssid, pwd, ...) Makes the board connect to the router in STA mode (as a “client”); nothing works without the internet
aws_iot_mqtt_init(&client, &params) Initializes the client, filling the endpoint/port/three certificates into the config — any mistake here and nothing connects
aws_iot_mqtt_connect(&client, &params) Establishes the TLS encrypted connection with AWS using the certificates (TLS = the encryption envelope for communication, anti-eavesdropping)
aws_iot_mqtt_subscribe(&client, topic, ...) Subscribes to a topic (topic = channel name); without subscribing you can’t receive cloud messages
aws_iot_mqtt_publish(&client, topic, ...) Publishes a message to a topic; the cloud/device subscribed to that channel all receive it
aws_iot_mqtt_yield(&client, 100) Loops send/receive processing and heartbeat keep-alive; without it you receive no messages and get kicked offline by the platform
aws_iot_shadow_update(&client, thing, ...) Syncs the device’s latest state to the cloud Shadow (the device’s “stand-in”)
Build the Project

Build in the project directory:

make -j8

Note: make is the “build” command, turning code into firmware (the program flashed into the board) the board can run; -j8 builds with 8 parallel CPU cores, faster.

On success a firmware build_out/aws_iot_core.bin is generated.

⚠️ If it reports riscv64-unknown-elf-gcc: command not found, the toolchain permissions aren’t configured — run cd toolchain/riscv/Linux && . chmod755.sh first, then rebuild.

Flash the Firmware

Keep the board connected via USB, confirm the serial device, and flash:

make flash p=/dev/ttyUSB0 b=921600

Note: make flash is the “flash” command, writing the compiled firmware into the board’s chip. After p= comes the serial device (often /dev/ttyUSB0 on Linux, COM3 and the like on Windows — use your actual one, check with ls /dev/ttyUSB*), b=921600 is the flash baud rate (serial transfer speed), keep the default.

⏳ During flashing, press and hold the EN button on the board when prompted to enter download mode; wait for the progress bar to complete — that means the flash succeeded.

Run and Verify

After flashing, the board automatically restarts: it connects to Wi-Fi first, then to AWS. Open the serial monitor (a serial debugging assistant app, baud rate 921600 — the baud rate is the “speaking speed” of the serial, both sides must match) and you should see:

[APP] [EVT] GOT IP ...
AWS IoT SDK Version 3.0.1-
Connecting...
...
Connect ok!!!, start Subscribing Topic=[$aws/things/AiThinker/shadow/update/accepted]...

Meanwhile go back to the AWS console’s “MQTT test client” (subscribed to $aws/things/<your thing name>/shadow/update) — you should receive a Light ON / LIGHT OFF alternating message every 1 second — the publish path is working.

Seeing Connect ok!!! means success; if you only see GOT IP without Connect ok, it hasn’t succeeded yet — check the FAQ at the end.


API Summary for This Tutorial

aws_iot_control(arg)

The AWS access main flow: init client → connect → subscribe TEST_MYSUBTOPIC → loop publishing Light ON/LIGHT OFF messages (project-custom interface, source in aws_iot_core/aws_control.c, called by main.c as a task after GOT IP).

Parameters:

  • arg: task parameter; this example passes NULL

Return: none (task function; disconnects and frees the client on exit)

aws_iot_demo_publish(arg)

Standalone publish example: publish only, no subscribe — loops sending Light ON/OFF to TEST_MYPUBTOPIC (project-custom interface, source in aws_iot_core/demo_publish.c).

Parameters:

  • arg: task parameter; pass NULL

Return: none (task function)

aws_iot_demo_subscribe(arg)

Standalone subscribe example: subscribe only, no publish — prints the topic and content of received messages (project-custom interface, source in aws_iot_core/demo_subscribe.c).

Parameters:

  • arg: task parameter; pass NULL

Return: none (task function)

aws_iot_demo_shadow(arg)

Shadow example: periodically syncs the lightstatus property (ON/OFF) to the cloud Shadow (Shadow = the device's cloud-side "stand-in", holding the device's latest state), and registers a delta callback to receive cloud-side modifications to the Shadow (project-custom interface, source in aws_iot_core/demo_shadow.c).

Parameters:

  • arg: task parameter; pass NULL

Return: none (task function)

aws_iot_mqtt_init(&client, &mqttInitParams)

Initializes the MQTT client: configures the server address (pHostURL), port, the three certificates (pRootCALocation/pDeviceCertLocation/pDevicePrivateKeyLocation), timeouts and disconnect callbacks (AWS official Device SDK interface).

Parameters:

  • client: AWS_IoT_Client struct pointer, holds client state
  • mqttInitParams: IoT_Client_Init_Params struct pointer; iotClientInitParamsDefault is the default, override members as needed

Return: SUCCESS (0) on success; error code on failure (IoT_Error_t enum, e.g. FAILURE)

aws_iot_mqtt_connect(&client, &connectParams)

Establishes the TLS encrypted connection with AWS IoT Core (TLS = encryption envelope, anti-eavesdropping); connection parameters include keep-alive interval, client ID, MQTT version, etc.

Parameters:

  • client: the client initialized by aws_iot_mqtt_init
  • connectParams: IoT_Client_Connect_Params struct pointer, containing keepAliveIntervalInSec, pClientID, MQTTVersion, isCleanSession, etc.

Return: SUCCESS (0) on success; error code on failure

aws_iot_mqtt_subscribe(&client, topicName, topicNameLen, qos, pApplicationHandler, pApplicationHandlerData)

Subscribes to a topic (topic = channel name); pApplicationHandler fires when the topic receives messages.

Parameters:

  • client: client struct pointer
  • topicName: topic string (this example TEST_MYSUBTOPIC)
  • topicNameLen: topic length
  • qos: QoS level; this example uses QOS1 (at-least-once)
  • pApplicationHandler: the message callback
  • pApplicationHandlerData: data passed through to the callback; pass NULL if none

Return: SUCCESS (0) on success; error code on failure

aws_iot_mqtt_publish(&client, topicName, topicNameLen, &params)

Publishes a message to a topic (this example publishes Light ON/OFF text messages).

Parameters:

  • client: client struct pointer
  • topicName: topic string (this example TEST_MYPUBTOPIC)
  • topicNameLen: topic length
  • params: IoT_Publish_Message_Params struct pointer, containing qos, isRetained, payload (message content), payloadLen

Return: SUCCESS (0) on success; error code on failure (publish ACK timeout returns MQTT_REQUEST_TIMEOUT_ERROR)

aws_iot_mqtt_yield(&client, timeout_ms)

Lets the SDK handle network send/receive and heartbeat keep-alive; must be called repeatedly in a loop, otherwise you receive no messages and get kicked offline by the platform. Returns NETWORK_ATTEMPTING_RECONNECT after a disconnect — skip business logic and wait for the reconnect then.

Parameters:

  • client: client struct pointer
  • timeout_ms: receive-wait timeout in milliseconds; this example uses 100

Return: IoT_Error_t values such as SUCCESS, NETWORK_ATTEMPTING_RECONNECT, NETWORK_RECONNECTED

aws_iot_shadow_update(&mqttClient, thingName, jsonDocument, callback, contextData, timeout_sec, isPersistent)

Syncs the device's latest state (JSON packet) to the cloud Shadow (Shadow = the cloud "stand-in"); the cloud/app reads the Shadow to get the device's latest state.

Parameters:

  • mqttClient: client struct pointer
  • thingName: the thing name (TEST_MY_THING_NAME)
  • jsonDocument: the state JSON packet (built by aws_iot_shadow_init_json_document/aws_iot_shadow_add_reported/aws_iot_finalize_json_document)
  • callback: update result callback (SHADOW_ACK_ACCEPTED means the cloud accepted it)
  • contextData: pass-through data; pass NULL if none
  • timeout_sec: timeout in seconds; this example 4
  • isPersistent: whether to keep the subscription; this example true

Return: SUCCESS (0) on success; error code on failure

📌 The AWS SDK headers (aws_iot_mqtt_client_interface.h etc.) come from the AWS official Device SDK for Embedded C v3.0.1, fetched automatically during the build; this project compiles the certificates into the firmware as macro strings — a "demo" approach; for mass production, the recommended practice is the aws_iot_cert project with certificates on the filesystem.


Full Code

Below is the complete aws_iot_core/main.c source, identical to the official example (applications/iot-solution/aws_iot_core/aws_iot_core/main.c) (send/receive and Shadow logic are in aws_control.c, demo_shadow.c, etc.; the rest can be found in the official project):

📜 Click to expand the full aws_iot_core/main.c code
c

#include <FreeRTOS.h>
#include <task.h>
#include <stdio.h>  
#include <string.h>
#include <aos/yloop.h>
#include <aos/kernel.h>
#include <lwip/tcpip.h>
#include <wifi_mgmr_ext.h>
#include <hal_wifi.h>

#define ROUTER_SSID "your ssid"
#define ROUTER_PWD "your password"

static wifi_conf_t conf =
{
    .country_code = "CN",
};

static void wifi_sta_connect(char* ssid, char* password)
{
    wifi_interface_t wifi_interface;

    wifi_interface = wifi_mgmr_sta_enable();
    wifi_mgmr_sta_connect(wifi_interface, ssid, password, NULL, NULL, 0, 0);
}

static void event_cb_wifi_event(input_event_t* event, void* private_data)
{
    switch (event->code)
    {
        case CODE_WIFI_ON_INIT_DONE:
        {
            printf("[APP] [EVT] INIT DONE %lld\r\n", aos_now_ms());
            wifi_mgmr_start_background(&conf);
        }
        break;
        case CODE_WIFI_ON_MGMR_DONE:
        {
            printf("[APP] [EVT] MGMR DONE %lld\r\n", aos_now_ms());
            wifi_sta_connect(ROUTER_SSID, ROUTER_PWD);
        }
        break;
        case CODE_WIFI_ON_DISCONNECT:
        {
            printf("[APP] [EVT] disconnect %lld\r\n", aos_now_ms());
        }
        break;
        case CODE_WIFI_ON_CONNECTING:
        {
            printf("[APP] [EVT] Connecting %lld\r\n", aos_now_ms());
        }
        break;
        case CODE_WIFI_CMD_RECONNECT:
        {
            printf("[APP] [EVT] Reconnect %lld\r\n", aos_now_ms());
        }
        break;
        case CODE_WIFI_ON_CONNECTED:
        {
            printf("[APP] [EVT] connected %lld\r\n", aos_now_ms());
        }
        break;
        case CODE_WIFI_ON_PRE_GOT_IP:
        {
            printf("[APP] [EVT] connected %lld\r\n", aos_now_ms());
        }
        break;
        case CODE_WIFI_ON_GOT_IP:
        {
            printf("[APP] [EVT] GOT IP %lld\r\n", aos_now_ms());
            printf("[SYS] Memory left is %d Bytes\r\n", xPortGetFreeHeapSize());
            void aws_iot_control(void *arg);
            xTaskCreate(aws_iot_control, (char*)"aws_iot_control", 4096, NULL, 10, NULL);            
        }
        break;
        case CODE_WIFI_ON_PROV_CONNECT:
        {
            printf("[APP] [EVT] [PROV] [CONNECT] %lld\r\n", aos_now_ms());
            wifi_sta_connect(ROUTER_SSID, ROUTER_PWD);
        }
        break;
        case CODE_WIFI_ON_PROV_DISCONNECT:
        {
            printf("[APP] [EVT] [PROV] [DISCONNECT] %lld\r\n", aos_now_ms());
        }
        break;
        default:
        {
            printf("[APP] [EVT] Unknown code %u, %lld\r\n", event->code, aos_now_ms());
            /*nothing*/
        }
    }
}

static void proc_main_entry(void* pvParameters)
{
    aos_register_event_filter(EV_WIFI, event_cb_wifi_event, NULL);
    hal_wifi_start_firmware_task();
    aos_post_event(EV_WIFI, CODE_WIFI_ON_INIT_DONE, 0);
    vTaskDelete(NULL);
}

void main()
{
    xTaskCreate(proc_main_entry, (char*)"main_entry", 1024, NULL, 15, NULL);
    tcpip_init(NULL, NULL);
}

FAQ & Troubleshooting

⚠️ Connect ok!!! never appears; logs show TLS handshake failure / certificate verify failed
Cause: the three certificates weren't replaced cleanly or the format is broken (extra/missing \r\n, misplaced quotes), wrong endpoint, certificate-device mismatch, or the certificate expired (AWS device certificates must be regenerated after expiry)
Fix: re-replace the three macros TEST_ROOT_CA_FILENAME/TEST_CERTIFICATE_FILENAME/TEST_PRIVATE_KEY_FILENAME in aws_test_cert.h character by character, keeping the surrounding quotes and \r\n; confirm TEST_MQTT_HOST is the address in your console's "Settings → Device data endpoint"; for expired certificates, recreate and download them in the console

⚠️ Connection rejected; logs report permission/policy errors
Cause: the certificate has no attached Policy, or the policy doesn't allow the Connect/Subscribe/Publish/Receive actions
Fix: in the console "Secure → Certificates", select your certificate → "Actions → Attach policy"; confirm the policy includes the four actions iot:Connect, iot:Subscribe, iot:Receive, iot:Publish

⚠️ Connected, but the test client receives no messages
Cause: the thing name in the topics doesn't match — the AiThinker in TEST_MY_THING_NAME/TEST_MYPUBTOPIC/TEST_MYSUBTOPIC wasn't replaced with your thing name, or the test client subscribed to a different topic than the published one
Fix: replace every AiThinker in the topics in aws_test_cert.h with your thing name; the test client subscribes to $aws/things/<your thing name>/shadow/update, matching TEST_MYPUBTOPIC

⚠️ Keeps printing Connecting, no GOT IP (can't connect to the router)
Cause: ROUTER_SSID/ROUTER_PWD wrong in main.c, the router is on 5GHz, or the signal is too weak
Fix: check the Wi-Fi account and password; confirm the router broadcasts 2.4GHz; move the board near the router; you can first run Connect Wi-Fi alone to verify internet access

⚠️ Serial device not found / can't open
Cause: USB-to-serial driver not installed, insufficient permission, or the cable only charges and can't transfer data
Fix: on Linux check with lsusb/dmesg; if permission denied run sudo usermod -aG dialout $USER and log back in, or sudo chmod 666 /dev/ttyUSB0; on Windows install the driver and check the COM port in Device Manager; try a data-capable cable

⚠️ Flashing keeps waiting / fails
Cause: didn't enter download mode, wrong baud rate, or wrong serial number
Fix: press and hold EN during flashing to enter download mode as prompted; confirm p=/dev/ttyUSB0 is your actual serial; try a different USB port or cable

Self-Check

The serial shows Connect ok!!!, and the AWS console's "MQTT test client" subscribed to $aws/things/<your thing name>/shadow/update keeps receiving Light ON / LIGHT OFF messages — the AWS IoT connection is verified.

Released under the MIT License. Build Time 2026-09-11 14:52:23