This tutorial is part two of the AWS IoT series for integrating RAK473 based board from RAK Wireless with the various cloud IoT services provided by Amazon web services.
The HardwareWe will be focussing on the RAK473 based board, namely the Creator pro ans Dash button. These boards are based on the Realtek RTL8711AM chipset and are quite powerful chipsets providing additional features like TLS support, mDNS API support and much more over the standard 802.11 features.
For setting up the Arduino IDE for the RAK 473 based boards please visit the URL here: https://github.com/RAKWireless/CREATOR-Arduino-SDK
I have also covered the details in the post here: https://www.hackster.io/naresh-krish/diy-remote-wifi-switch-using-the-rak-dashbutton-09b0de
For setting up your Raspberry pi 3 for Node-red please follow the post here: https://www.hackster.io/naresh-krish/node-red-integration-with-rak-wireless-dash-button-4d2efe
Once these two are setup. We can proceed further.
AWS IoT: Shadows servicesIn our previous tutorial, we touched up the concept of the AWS IoT services and how Things can be made to have a virtual presence called Shadow in the cloud.
The purpose of having a device shadow is simple. IoT devices are not meant to be always on devices. They may switch on for short bursts of time to relay information. Even if they are always-on it is not necessary for them to be always connected. Network disturbances, disconnects may result in devices loosing connectivity with the radio communication and drop data packets or even fail to send data packets from their side of operations.
During such scenarios, your apps (web application, mobile application) shouldn't try to access the things devices directly to make changes to their configuration (push data) or gather sensor data (pull data). The Shadow service is a perfect solution to this problem. The device Shadow is a virtual representation of the most recent stare of the Thing which it represents. It can have attributes and fields that map to certain measurements/configuration on the device that can be updated/read by our application without actually failing with a connection failure.
Since the AWS IoT shadows are always on virtual entities, you can be gauranteed that when you read the Shadow for attributes and confiuguration, you will ALWAYS get the most recent update.
Note: Although the update may not be the most recent w.r.t to the device as issues like network disconnects discussed above may have hampered the device from sending an update.
The AWS Shadow services provide endpoints for both devices as well as apps to read//write attribute for a Thing on the AWS IoT service.
MQTT endpoints
The Thing Shadows service uses reserved MQTT topics to enable applications and things to get, update, or delete the state information for a thing (thing shadow). The names of these topics start with $aws/things/thingName
/shadow. Publishing and subscribing on thing shadow topics requires topic-based authorization. AWS IoT reserves the right to add new topics to the existing topic structure. For this reason, we recommend that you avoid wild card subscriptions to shadow topics. For example, avoid subscribing to topic filters like $aws/things/thingName/shadow/#
because the number of topics that match this topic filter might increase as AWS IoT introduces new shadow topics. For examples of the messages published on these topics see Thing Shadows Data Flow.
The following are the MQTT topics used for interacting with thing shadows.
REST endpoints:A thing shadow exposes the following URL for updating state information:
Copy
https://
The endpoint is specific to your AWS account. To retrieve your endpoint, use the describe-endpoint command. The format of the endpoint is as follows:
Copy
identifier
API ActionsEach link above in turn links to the API documentation on AWS so feel free to research into the topic/endpoints for more information.
The MQTT endpoints a are for devices to update their attributes and to subscribe to secondary enents like update success, update rejected etc. These are essential to take remedy action when a Shadow operation fails.
The HTTPs REST endpoints are for apps (web services, mobile apps) to read/write to the Things Shadow instance in the cloud.
AuthorizationUpdating a thing shadow requires a policy that allows the caller to perform the iot:UpdateThingShadow
action. The Thing Shadows service accepts two forms of authentication: Signature Version 4 with IAM credentials or TLS mutual authentication with a client certificate.
The following is an example policy that allows a caller to update a thing shadow:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "iot:UpdateThingShadow",
"Resource": ["arn:aws:iot:
Generate Device CertificatesSign in to the AWS Management Console and open the AWS IoT console at https://aws.amazon.com/iot. On the Welcome page, choose Get started.
If this is your first time using the AWS IoT console, you see the Welcome to the AWS IoT Console page. In the left navigation pane, choose Manage to expand the choices, and then choose Things.
On the page that says You don't have any things yet, choose Register a thing. (If you have created a thing before, choose Create.)
A thing represents a device whose status or data is stored in the AWS Cloud. This stored status or data is called the device's shadow. The Device Shadow service maintains a shadow for each device connected to AWS IoT.
- Type a name for the thing, and then choose Create thing.
Choose Create certificate. This generates an X.509 certificate and key pair.
Choose Activate to activate the X.509 certificate, then choose Attach a policy.
Now connect the policy that you will generate below to this certificate. and your done.
The Amazon ROOT CA is available in this page:
https://docs.aws.amazon.com/iot/latest/developerguide/managing-device-certs.html
Its the VeriSign certificate: VeriSign Class 3 Public Primary G5 root CA certificateAWS IoT Policies for Things
AWS IoT policies are JSON documents. They are very similar to IAM policies in structure but have some spcialised sections for resources to be assocuated with proper resource access/ permission etc. AWS IoT supports named policies so many identities can reference the same policy document. Named policies are versioned so they can be easily rolled back.
AWS IoT defines a set of policy actions that describe the operations and resources to which you can grant or deny access. For example:
iot:Connect
represents permission to connect to the AWS IoT message broker.
iot:Subscribe
represents permission to subscribe to an MQTT topic or topic filter.
iot:GetThingShadow
represents permission to get a thing shadow.
AWS IoT policies allow you to control access to the AWS IoT data plane. The AWS IoT data plane consists of operations that allow you to connect to the AWS IoT message broker, send and receive MQTT messages, and get or update thing shadows. For more information, see AWS IoT Policy Actions.
An AWS IoT policy is a JSON document that contains one or more policy statements. Each statement contains an Effect
, an Action
, and a Resource
. The Effect
specifies whether the action will be allowed or denied. The Action
specifies the action the policy is allowing or denying. The Resource
specifies the resource or resources on which the action is allowed or denied. The following policy grants all devices permission to connect to the AWS IoT message broker, but restricts the device to publishing on a specific MQTT topic:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action":["iot:Publish"],
"Resource": ["arn:aws:iot:xx-east-1:xxxx:topic/topic1"]
},
{
"Effect": "Allow",
"Action": ["iot:Connect"],
"Resource": ["*"]
}]
}
Lets connect to AWS ShadowOk now that we have some in depth knowledge with the AWS shadow services, lets see what our RAK473 code can do to connect to the AWS IoT account and updatee its shadow via MQTT:
/*
A simple Amazon AWS IoT example
*/
#include <WiFi.h>
#include <PubSubClient.h>
// Update these with values suitable for your network.
char ssid[] = "yourNetwork"; // your network SSID (name)
char pass[] = "secretPassword"; // your network password
int status = WL_IDLE_STATUS; // the Wifi radio's status
WiFiSSLClient wifiClient;
PubSubClient client(wifiClient);
#define THING_NAME "ameba"
char mqttServer[] = "a2zweh2b7yb784.iot.ap-southeast-1.amazonaws.com";
char clientId[] = "amebaClient";
char publishUpdateTopic[] = "$aws/things/" THING_NAME "/shadow/update";
char publishGetTopic[] = "$aws/things/" THING_NAME "/shadow/get";
char publishPayload[MQTT_MAX_PACKET_SIZE];
char *subscribeTopic[5] = {
"$aws/things/" THING_NAME "/shadow/update/accepted",
"$aws/things/" THING_NAME "/shadow/update/rejected",
"$aws/things/" THING_NAME "/shadow/update/delta",
"$aws/things/" THING_NAME "/shadow/get/accepted",
"$aws/things/" THING_NAME "/shadow/get/rejected"
};
/* root CA can be download here:
* https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem
**/
char* rootCABuff = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB\n" \
"yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\n" \
"ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp\n" \
"U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW\n" \
"ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\n" \
"aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL\n" \
"MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW\n" \
"ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln\n" \
"biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp\n" \
"U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y\n" \
"aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1\n" \
"nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex\n" \
"t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz\n" \
"SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG\n" \
"BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+\n" \
"rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/\n" \
"NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E\n" \
"BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH\n" \
"BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy\n" \
"aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv\n" \
"MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE\n" \
"p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y\n" \
"5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK\n" \
"WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ\n" \
"4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N\n" \
"hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq\n" \
"-----END CERTIFICATE-----\n";
/* Fill your certificate.pem.crt wiht LINE ENDING */
char* certificateBuff = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIDWTCCAkGgAwIBAgIUE1UsPqN2mfvCGh2DLX2HWs3NOIYwDQYJKoZIhvcNAQEL\n" \
"BQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20g\n" \
"SW5jLiBMPVNlYXR0bGUgU1Q9V2FzaGluZ3RvbiBDPVVTMB4XDTE2MDYyNzA2MzQ0\n" \
"NVoXDTQ5MTIzMTIzNTk1OVowHjEcMBoGA1UEAwwTQVdTIElvVCBDZXJ0aWZpY2F0\n" \
"ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANM8/fzFJeZoS2yIf0Yy\n" \
"seFqUlnwNwNsg0G4U+3mUPj47ogs3jLDFA208P85S3qnfDaUY553wYY3BcIlBzpp\n" \
"y7vstrgapTOxH2c/Nrk/QfDqg/gUBSZs24c12WvyqlfDnKcIsRxALbfO0yMWKATB\n" \
"fFfnnZRCOVO6eKcW2O3ptfYDH8tIhrzCAEAAAF6CYmlSzyw9KrWh7ypYCp3AcJ/Y\n" \
"Fp4+SsF6xxLRuLOk37NJL8HfuIappkUIN7seGU9Y6Bo2YgOj9yBXyW/dmV3IxKVW\n" \
"SUSMSdIP+pc/b8lFfnE14yFtfK7jKhdy4XOh9LpOMJYs0i74UVQKJs7NYL8MXHIu\n" \
"FlUCAwEAAaNgMF4wHwYDVR0jBBgwFoAUoTuxNa4LQJd07hfP4se/TevgQ0AwHQYD\n" \
"VR0OBBYEFAQ9fAKopGZ52+f+w0dJMAwTi/hVMAwGA1UdEwEB/wQCMAAwDgYDVR0P\n" \
"AQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IBAQB6YQ+w6YTCZYWOlZfH09D8WhwV\n" \
"zSAzcEUdWH5T7bNK1N5r7/5zVIedseFxPUqtuC8j+C7CWDuLykpI8A2WAnayXOXn\n" \
"UPc/SNIet0nGts50cd4zzg7xMMqseGVdSMLAjFwqI8npJp9Ij8inrsf5f1hY0nhb\n" \
"Zb6FkVghlYhgmWv9p949kBQ6ODrtiyQqAYjzzZa957HwR7ajZfMIWm+HaV6f+NYq\n" \
"O43eQbt14xrRQNjAhSQaoVT64PH1TA6XiyoF25xlnrrxDXGAo5cxVDa1MmXu249X\n" \
"9z2+uzKgCYuugQk+w+8JmC6hR0EH4q4+ydsNkYTC0LK3MndHumA1Dj3OwUfA\n" \
"-----END CERTIFICATE-----\n";
/* Fill your private.pem.key wiht LINE ENDING */
char* privateKeyBuff = \
"-----BEGIN RSA PRIVATE KEY-----\n" \
"MIIEpAIBAAKCAQEA0zz9/MUl5mhLbIh/RjKx4WpSWfA3A2yDQbhT7eZQ+PjuiCze\n" \
"MsMUDbTw/zlLeqd8NpRjnnfBhjcFwiUHOmnLu+y2uBqlM7EfZz82uT9B8OqD+BQF\n" \
"JmzbhzXZa/KqV8OcpwixHEAtt87TIxYoBMF8V+edlEI5U7p4pxbY7em19gMfy0iG\n" \
"vMIAQAAAXoJiaVLPLD0qtaHvKlgKncBwn9gWnj5KwXrHEtG4s6Tfs0kvwd+4hqmm\n" \
"RQg3ux4ZT1joGjZiA6P3IFfJb92ZXcjEpVZJRIxJ0g/6lz9vyUV+cTXjIW18ruMq\n" \
"F3Lhc6H0uk4wlizSLvhRVAomzs1gvwxcci4WVQIDAQABAoIBAQCObSVjdRokzFVu\n" \
"jGokTrIZJrL36Ttul4+4lCwiz5PxCwbp0jbPSzEOPN3xeBQoUx0xP8QbaOuXLyo2\n" \
"yPiirgqsXuKkJ3MT820VFE41gS1Y3wa0EeuXCPbLp8c9PZUVL9ND3Fxui+dHc7Bw\n" \
"i9PXwQ2xx308JZq5lZUcNA93+oixohfoN4kgPfT3je7yPvn0j1rMHfjQPrSvpIvk\n" \
"P2XA66BZf8YS9k6lpJLzslntusHO4ZoqdQxaqa3a79qawUjkJ4G4qx3aDvzqdjXU\n" \
"DTFgTLI8sicWl4Icx3vqCgZfGWYuzRX5Ka5eUZYkwv4PgUOSynVvc6n3fjX/2F1X\n" \
"aPEqLuIBAoGBAO+Akr7F5LnRyLxLEeywjruGxvG5vHS41YvRcHSukrNLoK4mRMRA\n" \
"fTqJe8oZCRuOGsDBkRujgnSEUJhqt2+kGf9dvv5pCS35PFvTzawbpe8HX/JjitDb\n" \
"kKxAd3edsbOo3ZCOwYLFwMPMIMzemqFX67cXyQwOUhcGf6N7YBKhA6JhAoGBAOHK\n" \
"AarQJXo3C69SpWMvei3zmV8pUdVLPiiiNb1q/pustt+hykRhXNrngV3ncB7mJBET\n" \
"Q+36c5F04A6EBSp5Nr8EdiPnsFwk+ILKx7wQ6y0E29xIRLxryyu27rMAGpk8D1Oy\n" \
"8F2mX23qGWQAsMi2sxKk7o+2EGoUF1f/HKeDcOB1AoGBAM5johG8P2rSGYYJuxyY\n" \
"2adIcdCFGp4LWhrvFVW3yruvhHwOhlwIpuH28DIseOjCANPy+rUypoz6KOnvrLwM\n" \
"Ukr54kkjAsIXcahAUZDrEod1d31NwqZRT87gjxMJVcVY0/Zqzt9+wqr4EZv6iI5Z\n" \
"UcuqN5qoDJ3C/+NFwnjLQHKBAoGAQI5zX5VXwdPPQXeN1ggTFORba7vyq9txkEig\n" \
"uOHInlYJi3NE07xKwkQC1wh/JDaFBWTOvVIojOQv07ani3dQ0djCto1d/VqMu0ij\n" \
"RwBHXX3QJvF6xazEUGFjakaTVFC5ySKWWxBgpJqUW+VepmSmWqRRmUFi/BF2gzBr\n" \
"zvFj6qkCgYANcCc6wXqWNwmAnH/bfTRXunUgt3AAxmMXtkakhXAjElgFymgmXhTN\n" \
"Huu00WjTeZqOSl8bE0Ki/bcRojlx2QMMaEHGSVRL28uPJCJ4HeBAH3m22dtceFgF\n" \
"+ZHhF5b6NFe6EeLBcd/TGLtuKvc/jeru8tciNrPj9MTSJR9T0TYxhw==\n" \
"-----END RSA PRIVATE KEY-----\n";
int led_pin = 10;
int led_state = 1;
void updateLedState(int desired_led_state) {
printf("change led_state to %d\r\n", desired_led_state);
led_state = desired_led_state;
digitalWrite(led_pin, led_state);
sprintf(publishPayload, "{\"state\":{\"reported\":{\"led\":%d}},\"clientToken\":\"%s\"}",
led_state,
clientId
);
printf("Publish [%s] %s\r\n", publishUpdateTopic, publishPayload);
client.publish(publishUpdateTopic, publishPayload);
}
void checkLedState() {
printf("try to get led_state\r\n");
sprintf(publishPayload, "{\"state\":{\"reported\":{\"led\":%d}},\"clientToken\":\"%s\"}",
led_state,
clientId
);
printf("Publish [%s] %s\r\n", publishGetTopic, publishPayload);
client.publish(publishGetTopic, publishPayload);
// After publish "get" command AWS IoT would send "get/accepted" message and we can check led state in callback
}
void callback(char* topic, byte* payload, unsigned int length) {
char buf[MQTT_MAX_PACKET_SIZE];
char *pch;
int desired_led_state;
strncpy(buf, (const char *)payload, length);
buf[length] = '\0';
printf("Message arrived [%s] %s\r\n", topic, buf);
if ((strstr(topic, "/shadow/update/accepted") != NULL) || (strstr(topic, "/shadow/get/accepted") != NULL)) {
// payload format: {"state":{"reported":{"led":1},"desired":{"led":0}},"metadata":{"reported":{"led":{"timestamp":1466996558}},"desired":{"led":{"timestamp":1466996558}}},"version":7,"timestamp":1466996558}
pch = strstr(buf, "\"desired\":{\"led\":");
if (pch != NULL) {
pch += strlen("\"desired\":{\"led\":");
desired_led_state = *pch - '0';
if (desired_led_state != led_state) {
updateLedState(desired_led_state);
}
}
}
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect(clientId)) {
Serial.println("connected");
for (int i=0; i<5; i++) {
printf("subscribe [%s]\r\n", subscribeTopic[i]);
client.subscribe(subscribeTopic[i]);
}
checkLedState();
updateLedState(led_state);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup()
{
pinMode(led_pin, OUTPUT);
digitalWrite(led_pin, led_state);
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
if (status == WL_CONNECTED) break;
// retry after 1 second
delay(1000);
}
wifiClient.setRootCA((unsigned char*)rootCABuff);
wifiClient.setClientCertificate((unsigned char*)certificateBuff, (unsigned char*)privateKeyBuff);
client.setServer(mqttServer, 8883);
client.setCallback(callback);
// For publish qos1 that server will send ack
client.setPublishQos(MQTTQOS1);
// For subscribe or publish, wait ack can keep command sequence in order
client.waitForAck(true);
// Allow the hardware to sort itself out
delay(1500);
}
void loop()
{
if (!client.connected()) {
reconnect();
}
client.loop();
}
Lets go through the details of the code:#include <WiFi.h>
#include <PubSubClient.h>
This part includes the AMEba wifi and MQTT pubsub library for usage.
char ssid[] = "yourNetwork"; // your network SSID (name)
char pass[] = "secretPassword"; // your network password
int status = WL_IDLE_STATUS; // the Wifi radio's status
Here we can edit the variables to store our wifi SSID and password for connecting to later.
Note: The Ameba SDK also supports the simple config protocol to update its SSID and password OTA directly via a supported app on mobile or desktop: more information here: https://www.amebaiot.com/en/standard-sdk-simple-config/
#define THING_NAME "ameba"
This defines the name of the Thing which we gave in the AWS IoT console.
char mqttServer[] = "a2zweh2b7yb784.iot.ap-southeast-1.amazonaws.com";
char clientId[] = "amebaClient";
char publishUpdateTopic[] = "$aws/things/" THING_NAME "/shadow/update";
char publishGetTopic[] = "$aws/things/" THING_NAME "/shadow/get";
char publishPayload[MQTT_MAX_PACKET_SIZE];
char *subscribeTopic[5] = {
"$aws/things/" THING_NAME "/shadow/update/accepted",
"$aws/things/" THING_NAME "/shadow/update/rejected",
"$aws/things/" THING_NAME "/shadow/update/delta",
"$aws/things/" THING_NAME "/shadow/get/accepted",
"$aws/things/" THING_NAME "/shadow/get/rejected"
};
This section is mostly just more configuration, specifically related to the Shadow MQTT endpoint.
The MQTT server is the broker server to which you want you devices to connect to. The clientID should be unique string fro each devices that connects to AWS IoT account. we have two update topics to publish to:
- shadow/update
- shadow/get
The topics are explained in detail above.
The url for the mqtt broker can be found in the Things registry device entery. Simply click on the device and then in the left pane click Interact to find out the endpoint details.
The subscribe topics are for getting a callback mechanism for the things for its various life-cycle events, like update accepted, update rejected etc. This will help the devices take alternate code paths if the update or get operation failed for some reason.
char* rootCABuff
char* certificateBuff
char* privateKeyBuff
These three Certificates are the ones that support the TLS communication with the AWS IoT MQTT gateway/broker.
- rootCABuff: stores the AWS Root CA key:
- certificateBuff: stores the client certificate information
- privateKeyBuff : stores the private key from the key-pair generated for your Thing devices on AWS IoT things console.
The RAK 473 is powerful enough to support TLS communication with AWS IoT which makes it a lucrative board for secure operations.
void updateLedState(int desired_led_state) { }
This function is responsible for updating the state of the LED. In our implementation, this function takes a int desired state (1 or 0) Since this is normal Arduino API style, passing 1 would turn on the LED and passing 0 would turn off the led.
Here is the integration details. This function is called from 'update/accepted' section of the callback function (explained below). So when our Shadow update services is called either via MQTT endpoint of HTTP endpoint, our devices would get the '/update/accepted' MQTT topic relay and it will then set the led to the same state that is mentioned in the Shadow response present in the MQTT payload.
This makes the operation a two way dynamic behaviour. You can set the state of the LED from this function (edits required) and update the shadow service. Or any other app editing the shadow service via mqtt/https can influence the state of the LED.
Excercise: try to edit the above function to set the led state directly as well to the GPIO pin.
void checkLedState() {}
This function checks the state of the LED. It does this by sending a request to the '/update/get' mqtt endpoint. The response will be available in the get/accpeted mqtt topic.
void callback(char* topic, byte* payload, unsigned int length)
The callback function is the MQTT pubsub library callback function which is called every time the device gets a message on the topics it has subscribed to. In our case we have subscribed to the topic stored in the 'subscribeTopic' array variable.
The callback function has three variables that are of importance.
- Topic: the topic from where the message is coming from
- payload: The incoming message payload. This will be json string for AWS iOT services.
- length: length of the message payload
void reconnect()
This function is a very basic reconnect mechanism if the MQT library returns the state of the client as disconnected. In such case the function will re-trigger a MQTT broker connection and subscribe back to the topics of desire.
The Setup functionThis needs a little bit more deeper analysis.
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
if (status == WL_CONNECTED) break;
// retry after 1 second
delay(1000);
}
After doing the basic GPIO mapping and setting the inital state of the led. We come across the above code section. If the wifi status is disconnected, we try to the connect the wifi client ot he SSID we provided with the required password.
wifiClient.setRootCA((unsigned char*)rootCABuff);
wifiClient.setClientCertificate((unsigned char*)certificateBuff, (unsigned char*)privateKeyBuff);
Here is the TLS magic. Such a complex thing to achieve, but so elegant in AMeba SDK :). Here we set the ROOT CA for our TLS connection to the AWS IoT mQTT endpoint. And we also set the private key and client key pair for the communication encryption.
It is important to specify the AWS root CA as well because AWS IoT allows developers to use their own ROOT CA if they don't feel the AWS Root CA is sufficient for their requirement. In such scenario you have to provide the certificate details of your Root CA the associated variable explained above.
client.setServer(mqttServer, 8883);
client.setCallback(callback);
These two lines let the MQTT client object to connect to our AWS IoT cloud broker and set the callback function explained above.
client.setPublishQos(MQTTQOS1);
It is important to understand that we are connecting to the mqtt broker with a QOS request of 1. Which means deliver 'atleast once'. This could mean that the message delivery may happen atleast once (or multiple times) This QOS is a fine balance between speed of delivery and making sure that message is delivered concretely to the end device.
More explanation about QOS 1 is provided in detail here:
https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels
Note: Amazon IoT doesntr suppoort QOS of 2. This is because the MQTT broker doesn't send the PUBACK or SUBACK words back to the receiver for message acknowledgement.
The loop function is pretty self explanatory. We just need to reconnect if we get disconnected from the MQTT broker.
Now push this firmware to the devices and see the led state change when you update the LED state from a MQTT topic or from a web/mobile application.
For making a sample mqtt request, you can use the AWS IoT console MQTT client.
- Login into thew AWS Console and proceed to the AWS IoT services.
- Click on the Test menu on the left pane
You will be greated with a page that shows you have a mqtt agent connected and available for subscribing or publishing to the the topic:
- Now you can publish to the topic :
"$aws/things/<THING_NAME>/shadow/update";
Replace the <THING_NAME> with the name of your thing. Send this JSON to the service:
{
"state":{
"reported":{
"led":"1"
}
},
"clientToken":"xxx"
}
This allows you to set the state of the led to 1. you should see that your device gets the message in the update/accepted callback endpoint and the led state should change.
Comments
Please log in or sign up to comment.