AWS IoT for ESP32 v1.0.0
An ESP-IDF based solution
Device-Shadow

AWS IoT Device Shadow

The device shadow service is offered by AWS IoT (AWS) that provides services to manage device shadow using MQTT APIs. The device shadow, as the name implies, represents a shadow of the actual device on AWS cloud. This shadow holds the information about the current state of the device.

Shadow

A shadow is used to store and retrieve information about the current state of the device. AWS uses JSON format to represent a device shadow.

A device shadow with led state and led color information:

// device shadow
{
"led" : 1, // shadow element 1, 1=on, 0=off
"color" : "green" // shadow element 2
}

Shadow Update

To change the state of a device a shadow update request must be sent to AWS.

AWS manages shadow updates and has defined three states that describe the aspects of shadow updates.

State Purpose
Desired App requests for a Desired state.
Reported Device reports its Current state.
Delta It exists when there is a difference between Desired & Reported states. It is Reported by AWS IoT as a way to indicate which shadow element has been updated.

The JSON document in the shadow section was a simplified version. The actual JSON used by AWS for handling shadow updates is shown below.

// actual device shadow on AWS
{
"state":
{
"desired": {
"led" : 1,
"color" : "green"
},
"reported": {
"led" : 1,
"color" : "green"
}
}
}

Let's consider a simple scenario to understand how AWS handles shadow update process.

Scenario

  • Let's assume the light bulb is off and let's omit the color from the device shadow to keep it simple. The current state of the device is now defined.

    Device shadow with bulb off:

    // device shadow, bulb is off
    {
    "state":
    {
    "desired": {
    "led" : 0
    },
    "reported": {
    "led" : 0
    }
    }
    }
  • Then we have a user who wants to turn on the bulb with the mobile app.

Update process

  1. The app sends a shadow update request indicating the user desired state.

    // shadow update request by app
    {
    "state":
    {
    "desired":{
    "led" : 1
    }
    }
    }

    As a result the desired state is updated in device shadow.

    // device shadow
    {
    "state":
    {
    "desired": {
    "led" : 1 // updated
    },
    "reported": {
    "led" : 0
    }
    }
    }
  2. On seeing this difference between the desired and reported state AWS reports delta state, which is to turn on the bulb.

    // device shadow
    {
    "state":
    {
    "desired": {
    "led" : 1
    },
    "reported": {
    "led" : 0
    },
    "delta": {
    "led" : 1 // due to change in desired state
    }
    }
    }

    The device actually subscribes to shadow delta topic. When a delta state exists AWS notifies the device by publishing a message on the delta topic. On receiving delta state device should perform desired action, here it turns on the bulb.

    To perform an action the library callbacks a delta handler which is registered using the AWS_shadowDeltaRegister API.

  3. After turning on the light bulb, the device reports that it is now on by updating its shadow.

    // shadow update request by device
    {
    "state":
    {
    "reported": {
    "led": 1 // device reports after turning on
    }
    }
    }

    The device updates reported state using AWS_shadowUpdateReported API.

The ligh bulb is now on and the device shadow now represents the current state of the bulb. Take a look at the device shadow and compare it with the shadow before the bulb was turned on.

Device shadow with bulb on:

// device shadow, after bulb is on
{
"state":
{
"desired": {
"led" : 1
},
"reported": {
"led" : 1
},
}
}

Callbacks

The library handles shadow update at element level. This means for shadow update with delta state you have to register a callback handler for each element using AWS_shadowDeltaRegister API. If your shadow contains two elements led and color, this means that you will need to register callbacks for both.

Implementation steps

The steps below explains the necessary configuration and initialization required to manage device shadow by using the library. Ensure your device is provisioned with certificates otherwise you have to manually configure the device certificates.

1. Define shadow elements

Define the shadow elements and callback handlers. You will also need variables to handle the desired and reported state values.

Sample variables to handle led state:

// led state 1=on, 0=off
uint32_t desiredLedState = 0;
uint32_t reportedLedState = 0;

Sample shadow element:

awsShadow_st led_Shadow = {
.keyStr = "led",
.valType_e = SHADOW_VALUE_TYPE_UINT,
.value_e.val_u32 = desiredLedState,
.callBackHandler = &led_ShadowHandler
};
@ SHADOW_VALUE_TYPE_UINT
Definition: lib_aws.h:41
AWS Shadow structure used by the library to handle shadow updates. You should initialize the callback...
Definition: lib_aws.h:93
char keyStr[LENGTH_AWS_SHADOW_KEY]
Definition: lib_aws.h:96

Sample shadow update callback handler:

void led_ShadowHandler(char *pKeyStr, void *pValue)
{
desiredLedState = *(uint8_t *)pValue;
print_info("Delta update desired Led : %d", desiredLedState);
}

2. Initialize the system configuration

Please refer to step #1 of Publish-Subscribe user guide to use thing certificates.

If you want to use other two methods then please read the Provisioning methods of Lib AWS under Overview section and configure the system configuration variable of type systemInitConfig_st.

3. Connect to AWS

Using the above configuration initialize the system and connect to AWS.

bool systemInitSuccess = SYSTEM_init(&s_sysConfig);
bool SYSTEM_init(systemInitConfig_st *s_pConfig)
Initiliaze the system with given configuration.

4. Update device shadow and register callbacks

if (systemInitSuccess)
{
SYSTEM_start();
// register callback for delta state
}
bool AWS_shadowDeltaRegister(awsShadow_st *ps_shadow)
Register a shadow element to receive callbacks whenever the shadow element is updated with a new valu...

5. Implement application logic

Refer step #2 of the update process, the device should perform some action.

// shadow updated? take action
if (desiredLedState != reportedLedState)
{
// action: turn on/off the bulb
GPIO_pinWrite(LED_PIN, desiredLedState);
// refer step #3 of update process
// update the shadow with reported-state
print_info("desiredLedState: %d, reportedLedState: %d", desiredLedState, reportedLedState);
reportedLedState = desiredLedState;
AWS_shadowUpdate("led", &reportedLedState, SHADOW_UPDATE_TYPE_REPORTED);
}
bool AWS_shadowUpdate(char *pKeyStr, void *pValue, shadowUpdateType_et updateType_e)
Update the shadow element.
@ SHADOW_UPDATE_TYPE_REPORTED
Definition: lib_aws.h:53

6. Create application task

Sample application task code:

void app_task(void *param)
{
while (1)
{
switch (SYSTEM_getMode())
{
{
// shadow updated? take action
}
break;
default:
break;
}
TASK_DELAY_MS(100);
}
}
TaskHandle_t tHandle_appTask = NULL;
xTaskCreate(&app_task, "app_task", 1024, NULL, 4, &tHandle_appTask);
bool AWS_isConnected()
Check if the device is connected state.
systemMode_et SYSTEM_getMode()
Get the system mode.
@ SYSTEM_MODE_NORMAL
Definition: lib_system.h:34