The setup script executed at step 1 installed the following required software packages that are specifically required for this step:
-
node: We are using Node.js for our code. This package is required to execute node programs.
-
npm: Node package manager. Used to install Node packages.
-
AWS IoT SDK for node (aws-iot-device-sdk): Used to communicate with the AWS IoT service.
-
onoff node package (onoff): Manage the Raspberry Pi GPIOs. It is quite simple and supports GPIO interrupts so we don’t need to continuously poll the button.
Let’s remember that we saved four certificate and key files during the IoT Thing creation in a folder: “/home/pi/iot”. Let’s open the folder, create a new file there, and call it “IoT_thing.js” or whatever you’d like. You will have something like this:

Your certificate names will be different, so please use your names in your code.
Open the IoT_thing.js file in your favorite text editor and write the following code.
var awsIot = require('aws-iot-device-sdk');
const Gpio = require('onoff').Gpio;
Here, we create two variables ‘ awsIot will be used to cooperate with the AWS IoT services, and Gpio based on the onoff library will be used to control the Raspberry Pi GPIOs. Then, let’s configure pins 11, 12, and 13 (PCM 17, 18 and 27) as outputs, and pin 15 (PCM 22) as an input:
const redLED = new Gpio(17, 'out');
const blueLED = new Gpio(18, 'out');
const greenLED = new Gpio(27, 'out');
const button = new Gpio(22, 'in', 'both', {debounceTimeout: 20});
The onoff library supports interrupts handling, so we configure it to react on both high-to-low and low-to-high edges. Also, it allows debounce by setting the debounce timeout. As you can see, I’ve set it to 20 ms which is enough for the average switch, but you can adjust it as needed.
Let’s declare two string constants for the two topics ‘ one is for receiving the messages about updating the Shadow state, and another for publishing updates of the reported Shadow state. You can read more about topics here.
update_docs_topic = "$aws/things/Raspberry_Pi_LED_Thing/shadow/update/documents";
update_topic = "$aws/things/Raspberry_Pi_LED_Thing/shadow/update";
Then let’s declare two variables to store the state and the color received from the Thing Shadow:
var LED_color;
var LED_state;
Next, we need to set the configuration of the device:
var device = awsIot.device({
keyPath: "abcdefghij-private.pem.key",
certPath: "abcdefghij-certificate.pem.crt",
caPath: "root-ca.pem.crt",
host: "abcdefghijklmn.iot.us-east-1.amazonaws.com"
});
Here we mention paths and filenames of the private key, certificate and root certificate files. Please make sure you type the name of your files. The host is the same URL address of the IoT Thing that we used for creating the Lambda function. Also make sure that you specify your host URL.
We will manage just two events of the IoT device ‘ connect and message. The first occurs on the launch of the application, and the second occurs when a new message appears in the topic we subscribe on.
Here is the code of the connect event handler:
device.on('connect', function() {
console.log('Connected');
update_shadow_on_start('red');
update_shadow_on_start('green');
update_shadow_on_start('blue');
device.subscribe(update_docs_topic);
});
First, we log the state Connected. Then, we update the Thing Shadow for all three LEDs, and finally subscribe to the topic specified in the constant update_docs_topic. The code of the function update_shadow_on_start is listed below.
function update_shadow_on_start (color)
{
if (color == 'red')
LED_state = redLED.readSync();
if (color == 'blue')
LED_state = blueLED.readSync();
if (color == 'green')
LED_state = greenLED.readSync();
LED_color = color;
var new_state = {'color':LED_color, 'state':(LED_state ? 'on' : 'off'), 'switch':(button.readSync() ? 'on' : 'off')};
var start_payload = {
'state': {
'reported': new_state,
'desired': new_state
}
}
device.publish(update_topic, JSON.stringify(start_payload), 0);
}
First, we define the state of the LED of the corresponding color and save it into the LED_state variable. Then we set the LED_color variable. And finally, update the Thing Shadow with new values, first reading the state of the switch ((button.readSync() ? 'on' : 'off')). Please notice that we update both desired and reported states. By the function device.publish we publish the updated state in the topic specified as update_topic, where AWS IoT service will read it and update the Shadow states.
Then we write the code for the message handler function:
device.on('message', function(topic, payload) {
var payload = JSON.parse(payload.toString());
console.log(payload.current.state.desired);
if(topic == update_docs_topic){
if(payload.current.state.desired.state == 'on')
LED_state = 1;
if(payload.current.state.desired.state == 'off')
LED_state = 0
LED_color = payload.current.state.desired.color;
if(LED_color == 'red')
read_pin_state = redLED.readSync();
if(LED_color == 'blue')
read_pin_state = blueLED.readSync();
if(LED_color == 'green')
read_pin_state = greenLED.readSync();
if (read_pin_state != LED_state)
{
if(LED_color == 'red')
redLED.writeSync(LED_state);
if(LED_color == 'blue')
blueLED.writeSync(LED_state);
if(LED_color == 'green')
greenLED.writeSync(LED_state);
var reported_payload = {
'state': {
'reported': payload.current.state.desired,
}
}
device.publish(update_topic, JSON.stringify(reported_payload), 0);
}
}
});
We parse the JSON payload of the incoming message and log it into the console. Then we check if we got the message from the correct topic (which is obvious because we’ve subscribed only on this single topic, but best to double check when things get busier). Then we check the values of the payload.current.state.desired.state and payload.current.state.desired.color which represent the state and the color of the LED which we want to change and save them into the LED_state and LED_color variables accordingly.
We then check if the current state of the pin is different from the desired state. If it is, we update the pin state with the function writeSync, and publish the updated reported state in the update_topic. If the pin state was the same as desired, we just skip it and do nothing.
Finally, we need to update the shadow state once the switch changes its state. To do this, it is more convenient to use the GPIO interrupt handler. onoff library provides this, so the interrupt handler is listed below:
button.watch(function (err, value) {
if (err) {
console.log ('switch read error');
throw err;
}
var new_state = {'switch':(button.readSync() ? 'on' : 'off')};
var new_payload = {
'state': {
'reported': new_state,
'desired': new_state,
}
}
device.publish(update_topic, JSON.stringify(new_payload), 0);
});
In this handler function returns the error err when something goes wrong or the switch state value otherwise. Thus, we define the new_state variable like we did for the update_shadow_on_start function but include only switch state into it. Then we specify the payload and publish it to the update_topic.
That is all the code for the Raspberry Pi! Now let’s try to run it.
Open the terminal window and switch to the folder with the just created file. Then type
node IoT_thing.js
If there are no errors you will see the following:
We have successfully connected to the AWS IoT Thing and updated the states of the Thing Shadow.
Now open the second terminal window and switch to the folder where Alexa is located. And launch it with the command
bash /home/pi/avs_run.sh
Now you have the Alexa client running on your Raspberry Pi. Try to say “Alexa, ask my raspberry to turn the red led on”. The red led should light up, and the corresponding message should appear in the terminal which runs IoT_thing.js file. Now try to ask “Alexa, ask my raspberry about the pin state” and hear the response, then change the switch state and ask again. You will see that the response differs.

Congratulations! You finally setup everything and now have the Alexa controlling your Raspberry Pi through the AWS services.
Below is all the Raspberry Pi code needed (with changes for your particular certificates and URLs as necessary):
var awsIot = require('aws-iot-device-sdk');
var Gpio = require('onoff').Gpio;
var redLED = new Gpio(17, 'out');
var blueLED = new Gpio(18, 'out');
var greenLED = new Gpio(27, 'out');
var button = new Gpio(22, 'in', 'both', { debounceTimeout: 20 });
update_docs_topic = "$aws/things/Raspberry_Pi_LED_Thing/shadow/update/documents";
update_topic = "$aws/things/Raspberry_Pi_LED_Thing/shadow/update";
var LED_color;
var LED_state;
var device = awsIot.device({
keyPath: "abcdefghij-private.pem.key",
certPath: "abcdefghij-certificate.pem.crt",
caPath: "root-ca.pem.crt",
host: "abcdefghijklmn.iot.us-east-1.amazonaws.com"
});
device.on('connect', function () {
console.log('Connected');
update_shadow_on_start('red');
update_shadow_on_start('green');
update_shadow_on_start('blue');
device.subscribe(update_docs_topic);
});
function update_shadow_on_start(color) {
if (color == 'red') LED_state = redLED.readSync();
if (color == 'blue') LED_state = blueLED.readSync();
if (color == 'green') LED_state = greenLED.readSync();
LED_color = color;
var new_state = { 'color': LED_color, 'state': LED_state ? 'on' : 'off', 'switch': button.readSync() ? 'on' : 'off' };
var start_payload = {
'state': {
'reported': new_state,
'desired': new_state
}
};
device.publish(update_topic, JSON.stringify(start_payload), 0);
}
device.on('message', function (topic, payload) {
var payload = JSON.parse(payload.toString());
console.log(payload.current.state.desired);
if (topic == update_docs_topic) {
if (payload.current.state.desired.state == 'on') LED_state = 1;
if (payload.current.state.desired.state == 'off') LED_state = 0;
LED_color = payload.current.state.desired.color;
if (LED_color == 'red') read_pin_state = redLED.readSync();
if (LED_color == 'blue') read_pin_state = blueLED.readSync();
if (LED_color == 'green') read_pin_state = greenLED.readSync();
if (read_pin_state != LED_state) {
if (LED_color == 'red') redLED.writeSync(LED_state);
if (LED_color == 'blue') blueLED.writeSync(LED_state);
if (LED_color == 'green') greenLED.writeSync(LED_state);
var reported_payload = {
'state': {
'reported': payload.current.state.desired
}
};
device.publish(update_topic, JSON.stringify(reported_payload), 0);
}
}
});
button.watch(function (err, value) {
if (err) {
console.log('switch read error');
throw err;
}
var new_state = { 'switch': button.readSync() ? 'on' : 'off' };
var new_payload = {
'state': {
'reported': new_state,
'desired': new_state
}
};
device.publish(update_topic, JSON.stringify(new_payload), 0);
});