Cloud Home Automation Series Part 2 : Use AWS Device Shadow to Control ESP32 with Arduino Code

Written by gourav-das | Published 2020/02/13
Tech Story Tags: aws-iot | esp32 | home-automation | arduino | aws-iot-tutorial | device-shadow | cloud-home-automation | cloud-top-story

TLDR Part 2: Use AWS Device Shadow to Control ESP32 with Arduino Code. Use AWS MQTT Client to activate Device Shadow service and control ESP32 inbuilt led led. Use the shadow to get and set the state of a device over the device, regardless of whether the device is connected to the Internet. The shadow service maintains a shadow for each device you connect to AWS IoT. The following series split into four parts (refer below) with very simple and clear instructions to provision a home automation system.via the TL;DR App

Welcome to SIMPLE LEARNING AWS Cloud Home AutomationZero to Hero Series.
In the second part, we will turn ON/OFF the inbuilt led of ESP32 using AWS Device Shadow Service
. Before starting, let's do a recap, in Part 1 we have connected the ESP32 MCU with AWS and able to publish Messages to IoT Core.
The following series split into four parts (refer below) with very simple and clear instructions to provision a home automation system to control house appliances through the web.
Everything covered from scratch you won't face any difficulty understanding. In case of any clarification, drop me a note on LinkedIn. Feel free to explore them with ease, skip to the one which is relevant to you.
A device's shadow is a JSON document that is used to store and retrieve current state information for a device. The Device Shadow service maintains a shadow for each device you connect to AWS IoT. You can use the shadow to get and set the state of a device over MQTT or HTTP, regardless of whether the device is connected to the Internet. Each device's shadow is uniquely identified by the name of the corresponding thing. [Ref: AWS Documentation]
Prerequisites:
  1. Prior knowledge to connect ESP32 to AWS.
    If you are unaware, go to 
    Part 1 of the series
    .
  2. This tutorial mostly covers hands-on, prior knowledge of AWS IoT is an advantage. Click here to check AWS Official IoT Documentation.
Learning Objectives:
  1. Using Device Shadow to control the ESP32 built-in led, a stepping stone to achieve the final objective to control your home appliance anywhere from the world.
  2. Use AWS MQTT Client to publish messages to activate Device Shadow service and control ESP32 inbuilt led.

So let's see something happen now.

We will copy the below Arduino code, do some minor changes in Arduino IDE and make the code functional. Alternatively, you can download the Arduino Code as ino file here
/*********
  Gourav Das
  Complete project details at https://hackernoon.com/cloud-home-automation-series-part-2-use-aws-device-shadow-to-control-esp32-with-arduino-code-tq9a37aj
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/
#include <AWS_IOT.h>
#include <WiFi.h>
#include <ArduinoJson.h> //Download latest ArduinoJson 6 Version library only by Benoît Blanchon from Manage Libraries

AWS_IOT shadow;
#define ledPin 2 // LED Pin

char WIFI_SSID[]="Wifi-Name";
char WIFI_PASSWORD[]="Wifi_Password";
char HOST_ADDRESS[]="************-***.iot.us-east-1.amazonaws.com";  //AWS IoT Custom Endpoint Address
char CLIENT_ID[]= "ESP32";
char SHADOW_GET[]= "$aws/things/{Thingname}/shadow/get/accepted";
char SENT_GET[]= "$aws/things/{ThingName}/shadow/get";
char SHADOW_UPDATE[]= "$aws/things/{ThingName}/shadow/update";

int status = WL_IDLE_STATUS;
int msgReceived=0;
char payload[512];
char reportpayload[512];
char rcvdPayload[512];

void mySubCallBackHandler (char *topicName, int payloadLen, char *payLoad)
{
    strncpy(rcvdPayload,payLoad,payloadLen);
    rcvdPayload[payloadLen] = 0;
    msgReceived = 1;
}

void updateShadow (int power)
{ 
  sprintf(reportpayload,"{\"state\": {\"reported\": {\"power\": \"%d\"}}}",power);
  delay(3000);   
    if(shadow.publish(SHADOW_UPDATE,reportpayload) == 0)
      {       
        Serial.print("Publish Message:");
        Serial.println(reportpayload);
      }
    else
      {
        Serial.println("Publish failed");
        Serial.println(reportpayload);   
      }  
} 

void setup() {
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);
  delay(2000);  

    while (status != WL_CONNECTED)
    {
        Serial.print("Attempting to connect to SSID: ");
        Serial.println(WIFI_SSID);
        status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD); // Connect to WPA/WPA2 network. Change this line if using open or WEP network:        
        delay(5000);// wait 5 seconds for connection:
    }

    Serial.println("Connected to wifi");

    if(shadow.connect(HOST_ADDRESS,CLIENT_ID)== 0) //Connect to AWS IoT COre
    {
        Serial.println("Connected to AWS");
        delay(1000);

        if(0==shadow.subscribe(SHADOW_GET,mySubCallBackHandler)) //Subscribe to Accepted GET Shadow Service
        {
            Serial.println("Subscribe Successfull");
        }
        else
        {
            Serial.println("Subscribe Failed, Check the Thing Name and Certificates");
            while(1);
        }
    }
    else
    {
        Serial.println("AWS connection failed, Check the HOST Address");
        while(1);
    }
  
  
  delay(3000); /*Sent Empty string to fetch Shadow desired state*/ 
    if(shadow.publish(SENT_GET,"{}") == 0)
      {       
        Serial.print("Empty String Published\n");
      }
    else
      {
        Serial.println("Empty String Publish failed\n");
      }  /*Sent Empty string to fetch Shadow desired state*/   
}    

void loop() {
  
 if(msgReceived == 1)
    
  {
        msgReceived = 0;
        Serial.print("Received Message:");
        Serial.println(rcvdPayload);
        StaticJsonDocument<256> doc;
        deserializeJson(doc, rcvdPayload);
        
    
        if (doc.isNull()) { /* Test if parsing succeeds. */
        Serial.println("parseObject() failed");
        return;
        } /* Test if parsing succeeds. */
    
        int power = doc["state"]["desired"]["power"];
        Serial.println(power);
        if(power == 1)
        digitalWrite(ledPin, HIGH);
        else if(power == 0)
        digitalWrite(ledPin, LOW);
    updateShadow(power);  
    }  
}
We need to add a library in Arduino IDE, go to Tools → Manage Libraries. Search for ArduinoJson (by Benoit Blanchon), select the latest 6.x.x version only and click install. The code won't work on 5.x.x and below versions. Also, remember that we have used AWS_IOT library in the code to integrate ESP32 with AWS. If you are unaware, go to Part 1 of the series. Also, check the Certificates enrollment section under Hardware integration.

Code Modifications

From the AWS region selector in the navigation bar (top right corner), choose the US East (N. Virginia) Region only throughout the series.
Update the necessary details, skip
CLIENT_ID[]= "ESP32"
(Keep it as it is). In
HOST_ADDRESS[]
put the AWS IoT Endpoint Address which you can retrieve from IoT Dashboard, settings(check sidebar). Next, we will replace
{ThingName}
in the code with the actual thing name (e.g. ESP32) we gave at the time of thing creation in Part 1 of the series. To check the
Thing Name
, Click Here to go to AWS IoT Console, select Things under the
Manage
option from the Sidebar and check for the name as highlighted below.
Now plug your ESP32 board with your Desktop/Laptop using a USB cable and upload the code. Next, we will cover the Code workflow and Test the functionality.
Code Workflow

Testing

Plug your ESP32 board to your Desktop/Laptop using a USB cable. Click Here to go to AWS IoT Console. Choose Test from the Sidebar it will connect the MQTT client. Under Subscription topic, subscribes to the following Topics:
$aws/things/{ThingName}/shadow/update
 and  
$aws/things/{ThingName}/shadow/get
 [Do replace {ThingName} with the Thing name you provided in AWS]. Here we are using 1 as ON flag and 0 as OFF flag.
Now in 
$aws/things/{ThingName}/shadow/update
  publish the following msg in JSON format:
{"state": {"desired": {"power": "1"}}}
and publish
{}
to topic 
$aws/things/{ThingName}/shadow/get
. By doing so it will switch ON the led. Now, repeat the same just replace 1 with 0 in JSON it will switch OFF the led.
Congratulations on completing the 2nd part :)
. In the next part, we will build Web client to interact with device shadow and switch on/off the led by using Node-RED by IBM.
Kindly Share & Comment only if you find it useful and help me on my mission to educate and familiarize people in the world of digitization 💪 #This is a Free tutorial and all my upcoming tutorials will be free and accessible from Public forums.# Appreciate if you drop me a note on 
LinkedIn
 & share your opinion. Don't worry, I don't bite 👻 so don't shy away 🏃🏻‍♀️ 🏃🏻. Your feedback will help me to come up  with more awesome contents on the internet. 

Written by gourav-das | Tech Enthusiast and Clouder. AWS 6x & Azure 2x Certified. & I still watch One piece and spongebob
Published by HackerNoon on 2020/02/13