1. Setup Your Pi for AVS

Note: you can skip this step if your Pi is already up and running.

Let’s begin by setting up the Pi. The Raspberry Pi 3 board includes -

  • 4 USB ports
  • HDMI port for video output (you’ll be using this to connect to your HDMI monitor)
  • 3.5mm audio jack for sound output (you’ll be using to connect your earphones)
  • Micro SD card slot
  • Ethernet port

Assembling Your Pi

  1. Install the heat sinks included in your kit. Peel off the blue plastic on the heat sinks, then place them on the two integrated circuits on the Pi, pressing gently.
  2. Check that your micro SD card is inserted into the micro SD card slot on your Pi.
  3. Plug in the USB microphone and 3.5mm earbuds.
  4. Connect the keyboard and mouse to the USB ports.
  5. Connect your monitor using the HDMI port.
  6. Insert ethernet cable into the Pi. (not shown in this picture)

Pi_setup

Booting Your Pi

  1. Plug in the power supply to the micro USB connector on the Pi. You should see a loading screen go through some startup steps before booting to desktop - if you run into any errors, request a new SD micro card to boot the OS from.
  2. Verify ethernet is working by clicking on the connectivity icon in the top right of the tool bar (next to the speaker icon). You should see an up/down arrow pair (not X’d out).
  3. Open a web browser by clicking on the globe icon in the top left toolbar.

Checkpoint 1

  1. Make sure you’re connected to the internet and are able to navigate to https://developer.amazon.com.

2. Get an Amazon Dev Account

Register for an Amazon Developer Account

Unless you already have one, go ahead and create a free developer account at developer.amazon.com. Click “sign in” at the upper right corner and create your account (or log in with your existing credentials). You can review the AVS Terms and Agreements here.

Note - you can open these links in a new tab - and if you lose track of the lab manual, it’s bookmarked in your chromium browser - just click on the “AVS workshop” tab!

Checkpoint 2

  1. Once logged in, navigate to https://developer.amazon.com/avs/home.html#/avs/home

3. Create a Device & Security Profile

Register your prototype and create a security profile

After you’ve created an Amazon developer account, you’ll need to create a product and security profile. This will enable your software client to connect to AVS.

Log in to developer.amazon.com. You should be in the Dashboard by default - click the ALEXA VOICE SERVICE button in the global navigation to start building products with Alexa built-in. If you don’t see this screen, try this link: https://developer.amazon.com/avs/home.html#/avs/homes

code

If this is your first time using AVS, you’ll see a welcome screen. Click the GET STARTED button, then click the CREATE PRODUCT button.

If you’re a returning developer that has already created products in your dashboard, click the blue CREATE PRODUCT button at the top right corner of the screen to start building a new device profile.

Fill in product information

  1. Product Name: Use AVS Tutorials Project.
  2. Product ID: Use PrototypePi. No spaces are allowed for the Product ID field.
  3. Select Device with Alexa built-in for Please Select Your Product Type. Select No for Will your device use a companion app?
  4. Choose Other for Product Category and write Prototype in the (please specify) and Brief product description field.
  5. Select Hands-free for How will users interact with your product?
  6. Skip the Upload an image step. This is not required for prototyping.
  7. Select No for Do you intend to distribute this product commercially?
  8. Select No for Will your device be used for Alexa for Business?
  9. Select No for Is this device associated with one or more AWS IoT Core Accounts?
  10. Select No for Is this a children’s product or is it otherwise directed to children younger than 13 years old?
  11. Click NEXT to continue.

Set up your security profile

  1. Click CREATE NEW PROFILE.

  2. Enter your own custom Security Profile Name and Security Profile Description for the following fields - or use the below example names:
    • Security Profile Name: AVS Tutorials Project
    • Security Profile Description: AVS Tutorials
    • Click NEXT.

    Security Profile ID will be generated for you.

  3. Select Other devices and platforms from the Web - Android/Kindle - iOS - Other devices and platforms options in the Platform Information section.

platforms

  • Write a name for your Client ID here - you can just use Prototype.
  • Click “Generate ID”. You should get a Client ID and an option to download it.
  • If you’re creating this product profile on your Raspberry Pi, click Download to get your credentials onto your AVS prototype. When you click download, the file will automatically be put in your /home/pi/Downloads folder. This file will be copied and used at a later step.
  • Check the box beside I agree to the AVS agreement and the AVS Program Requirements.
    • Click FINISH.

Congratulations! You now have access to the Alexa Voice Service APIs. Click OK on the prompt to continue. Your device should now be listed on your AVS dashboard.

4. Input Your Credentials

Download your credentials

If you didn’t already save it to your Pi when creating your product profile, it’s time to get your config.json file onto your client device. Start by opening a browser and logging into your AVS dashboard. Click on your Product Name, it should be AVS Tutorials Project or whatever you named it when creating the product profile.

If you don’t see your dashboard, click on “My Alexa Consoles” in the upper right corner and select “Alexa Built-in Products”.

This will take you to a product menu - on the left side you should see Product Details. Select Security Profile below that and choose Other devices and platforms from the Web - Android/Kindle - iOS - Other devices and platforms menu.

When you click the Download button on your Security Profile in your web browser, a file called config.json file will be placed in your /home/pi/Downloads folder. This file will be used in a later step to configure the Pi as an AVS device associated with your product.

Now that your Raspberry Pi has your own unique credentials loaded on it, it’s time to build your SDK.

5. Run the Install Script

Run the Install Script

You are now ready to run the install script. This will install all dependencies, including the Wake Word Engine (WWE) from Sensory. The WWE compares incoming audio to an onboard model of a wake word (“Alexa”) and will initiate the transmission of audio to the cloud when triggered. Note that this WWE is provided for prototyping purposes only and would need to be licensed for a commercial device. The AVS Device SDK is modular and flexible. When you’re ready to build your product, you can choose any WWE you prefer. Remember that for AVS products, the wake word must be Alexa so that your customers aren’t confused about how to interact with your device.

To run the install script, open a terminal by clicking on the console window in the Pi’s toolbar in the upper-left corner of the screen (or just use your existing terminal window). You should see a avs_install.sh script in your /home/pi/ directory. This pulls the credentials from your config.json file to run the install script. To launch the setup script, copy and paste the following command into your terminal window and hit return:

cd /home/pi
bash avs_install.sh

run_script

Type “AGREE” when it prompts you to accept the licensing terms from our third-party libraries. Unless, of course, you disagree!

This will kick off the installation process which could normally take over 20 minutes, but for reMARS we’ve precompiled the image to save you some time.

Once the installation is finished, you should see a return to the shell prompt and no abvious compilation errors. If your device freezes up - don’t worry, just restart by unplugging your Pi’s power cord. When you get back to your desktop, re-run the above avs_install.sh command to finish your install. If you need to recompile the sdk at some point post-workshop due to an update or manual modification, you should close any other processes like browsers on the pi as memory consumption during the compilation process is high.

Now you just need to launch the sample app and get a refresh token from AVS so your device can authenticate with the cloud via Login With Amazon (LWA).

6. Authorize your Device

When you run the sample app for the first time, you’ll need to authorize your client for access to AVS.

Initialize the sample app by pasting or typing the following command into your terminal:

cd /home/pi/
bash avs_run.sh

Wait for the sample app to display a message like the one in the picture below (but, a different code of course) - If you don’t see this, but you see “waiting for authorization” messages going by, then scroll up! It’s easy to miss the authorization code since the terminal window fills up pretty fast. Note - you need to leave this process running while you authorize, don’t close the window or otherwise stop the startsample script.

code

  1. Use a browser to navigate to amazon.com/us/code
  2. Authenticate using your Amazon user credentials. If you aren’t able to log in - try deleting all cookies or open a new tab in “guest mode” browser, then go to amazon.com/us/code. You can also just go to the link from your phone, laptop, or any connected device, and it should work.
  3. Enter the code specified in the message from sample app.
  4. Select “Allow”.
  5. Wait (it may take as long as 30 seconds) for CBLAuthDelegate to successfully get an access and refresh token from Login With Amazon (LWA).
  6. At this point the sample app will print a message informing you that you are now authorized!

Your raspberry pi is now ready to use the sample app. The next time you start the sample app, you will not need to go through the authorization process.

Note, this should autocomplete in your browser when you start typing - it’s also in the bookmarks as “Alexa Home Screen”.

Congrats on your progress so far! Now it’s time to talk with Alexa.

7. Talk to Alexa

Interact with Your Prototype

Say “Alexa” into the microphone on your Raspberry Pi to trigger the Wake Word Engine. Since you are using a single microphone in a noisy environment, you may want to speak closely into your microphone to ensure your voice is heard clearly. You should see scrolling debug messages on the terminal screen, indicating the wake word was recognized. Then say “tell me a joke.” If Alexa responds, congratulations! You have a working prototype.

If you cannot hear Alexa’s response, ensure your speaker/earbuds are turned on and plugged in to your Raspberry Pi’s 3.5mm audio jack. Check that your audio output on your Pi is set to Analog by right-clicking on the speaker icon in the top right corner of the screen.

Try the following interactions -

  • Say “Alexa”, then ask “What’s the weather in Las Vegas?”

  • Say “Alexa”, then say “Play Katy Perry on TuneIn.”

  • Say “Alexa”, then say “Add Milk, Eggs, Bread and Orange Juice to my shopping list.”

  • Say “Alexa”, then say “Who is Angela Merkel?”

Try a Multi-Turn interaction

  • Say “Alexa”, then ask “Set a timer”. You should receive a response asking you for how long. Respond to the question with “15 seconds”. In the terminal, scroll up until you see Listening… - right above that you’ll see that the state of the Audio Input Processor (AIP) has changed from IDLE to EXPECTING_SPEECH and then RECOGNIZING - without you speaking the wake word! Typically, the AIP is triggered by the Wake Word Engine running on the client - but in this case, it’s been activated via a Directive delivered down to your client from the cloud. You can turn off the timer when it goes off by saying: “Alexa, stop.”

AIP_multiturn

Multi-turn interactions can feel like a more natural method of communication, since you can continue to speak with Alexa as part of a continuing conversation without starting every phrase with “Alexa”.

Other interactions to Try

  • Say “Alexa, Wikipedia” - you’ll have the option of looking up any subject without having to speak the wake word before the subject.
  • Say “Alexa, let’s chat” to initiate a conversation with a chat bot!
  • Say “Alexa, play Yes Sire” to play a medieval-themed game using your voice.

8. Create your Custom Skill

Creation of the Alexa Skill

In order to accept commands and controls from Alexa, we’ll need to write a skill. Alexa skills let you add your own content and functionality to Alexa. Skills are a great way to engage with customers through a voice interface (VUI) and you can learn all about them on your own time at (https://developer.amazon.com/alexa-skills-kit).

For today, let’s start by logging into your developer account at Alexa Skill Kit Developer Console.

alt text

Clink on the blue button “Create Skill” button to get started. You will see the following page:

alt text

Enter the Skill name. I chose “AWS IoT Skill” but you can choose whatever you’d like. You can leave the skill setting on “Custom model” and under the “Choose a method to host your skill’s back end resources”, keep the default selection of “Provision Your Own”. Click on “Create Skill”. Keep the “Start from scratch” selected. You will be redirected to the following “build” page.

alt text

Now, we’re ready to build our skill. It looks complex, but we’ve broken it down into easy to follow steps below. Let’s get started!

Click on the “Invocation” link to define the key words you’ll need to say to Alexa in order to access your skill. Let’s choose the name “my raspberry” for the skill name. Later, if you’re having trouble invoking the skill or want to change the name, you can always come back and edit the name to something else. alt text

Now that we’re able to invoke our skill, we need to define what the skill can do. We are going to import our “interaction model”, which defines how we understand what a customer would say to our skill. On the left side, click on the “JSON Editor” button. Delete the JSON from the editor, then copy the JSON from the following URL and paste it in: https://raw.githubusercontent.com/avs-dvk-workshop/avs-dvk-workshop.github.io/master/assets/skill/interactionModel.json. Hit “Save” and then “Build Model”

alt text

So what did we do? On the left side of the intents you will see that we now have 7 total intents. Intents are an action that the customer would take using your skill. You can learn more at the following link: (https://developer.amazon.com/docs/custom-skills/create-the-interaction-model-for-your-skill.html#about-intents-slots-and-dialogs). For our workshop, we have created two Intents:

  • LEDControlIntent which will turn our LEDs on or off
  • SwitchCheckIntent which will check the status of our switch button.

Since we are going to be using 3 different LEDs (green, red, blue) and we have two different statuses for the LEDs, we need slots to set as variables when passing values to our back end logic. Scroll down on the left side and note that we have two different slot types:

  • LED_COLOR which has values of blue, green, and red
  • LED_STATE which has values of on and off

So we have the commands and some values, but how do we then hook that into an actual voice model? That’s where the sample utterances come into play.

Click on the LEDControlIntent, you should see something like the following:

alt text

Note how we have different combinations of what a user might say to trigger a change in color to the LED. Are there any other phrases a user might say to control the light? Try adding a few additional utterances.

Similar with the SwitchCheckIntent it will look similar to this this:

alt text

You can invent your own variants - whatever makes sense for your skill. After you finish, press the “Save Model” button to save the changes and “Build Model” for those to take effect on the skill.

We’ve almost finished but we need to add the ARN name of AWS Lambda function to complete the setup of the Alexa Skill. So let’s now switch to the Lambda function creation and then return to this page. Thus don’t close it.

9. Create a Lambda function

Creation of the Lambda function

Lambda function are computing on demand which is stored on the server and is invoked when some event occurs. Our goal is to invoke our Lambda function when we call previously created Alexa Skill and make it do some useful thing, like give a voice response and eventually update the state of our Raspberry Pi.

  1. First, if you haven’t already, register at the Amazon AWS console, it can take some time and requires entering your financial information, so be ready for this. Once you’ve registered, come back and use this link again.

  2. When you open the title page, make sure to select US East (N.Virginia) in the drop-down list in the upper right hand corner. Some other regions have restrictions and your Lambda function won’t work correctly: alt text 3. Now, click on the orange button “Create Function” and proceed to the next page: alt text Select “Author from scratch” option, as we will create our own function. Type the name of the function, I selected “RaspberryPi_LED_Control” but you can choose your own. Leave the Runtime as “Node.js 10.x” ‘ we will write our function in this language. Click on the orange button “Create function”. (Note that if it the “Execution role” dropdown appears, you want to have the “Create a new role with basic Lambda permissions” selected)

  3. Now let’s setup our function. First, since Lambda functions are run “on-demand”, they need a “trigger” to invoke the function execution. As it will be called by an Alexa Skill, let’s select “Alexa Skill Kit” in the “Add trigger” list in the left part of the page: alt text
    It requires some configuration (as is mentioned in the blue rectangle). Scroll down and see the following fields: alt text
    Leave “Skill ID verification” as “Enabled” but we need the skill ID. Also, we will need to pass the Lambda function ARN to the recently created Skill. Scroll up to the top of the page and find the function ARN in the top right: alt text
    Copy it and return to the Alexa Skill page. Find “Endpoint” in the menu on the left part of the page and select it. You will see the following page: alt text Select “AWS Lambda ARN” and paste the Lambda function ARN into the field “Default region”. Also copy the “Your Skill ID” value as you’ll need it in the Lambda page. alt text alt text
    Return to the Lambda function page and paste Skill ID in the appropriate field: alt text
    Then click Add. Then scroll to the top of the page and click orange button “Save” in the top right of the page.

  4. Let’s now finish configuration of our Skill. Return to the Alexa Skill page, scroll to the top of the page and click “Save Endpoints”. If there are no mistakes, the corresponding message will appear: alt text Now select “Invocation” in the menu at the left side of the page and use the “Save model” and “Build model” buttons in that order. The last action can take some time, so don’t worry about it. If there are no mistakes, you will see the following message finally: alt text And with that, we’ve finished creation of the Alexa Skill! We will test it after we complete the Lambda function.

  5. Go back to your Lambda function, let’s add some code to handle the different utterances and intents coming from our skill! First, let’s download a zip file that contains some starter code, download the following zip: https://github.com/avs-dvk-workshop/avs-dvk-workshop.github.io/blob/master/assets/skill/VoiceControlWorkshop-Skill.zip?raw=true Under the “Function Code” section of the Lambda screen, you should see a dropdown for “Code entry type”, click that dropdown and choose Upload a zip file. You’ll then click the “Upload” button and choose the VoiceControlWorkshop-Skill.zip file you just downloaded. Click “Save” in the upper-right corner of the screen and you should see your function populated! alt text.

  6. Scroll down on the page until you see the “Basic Settings” section on the right side and update the timeout from “3” to “8” seconds. Click the orange button “Save” in the top right of the page. alt text.

  7. Now let’s test it! At the top of the page you’ll see a “Test” button. Click that and it will ask you to configure a test event. From the dropdown of the Event template, choose “Amazon Alexa Start Session”. Give it a name like startSession, scroll down and click Create. alt text. Click the “Test” button and then the arrow next to Details in order to see the actual response. It should look something like the below image: alt text.

10. Test the ASK/Lambda connection

Testing the communication and cooperation between services

Let’s switch to the Alexa Skill page. Our skill is already saved and built. If not, please perform these operations as described earlier. Now switch from the “Build” page to the “Test” page from the menu in the top of the page: alt text
Enable the toggle “Test is enabled for this skill”. Now we can perform the test. You can press the microphone icon and say your request or simply type the request in the field to the left of the microphone. Let’s stick with the second option and type “open my raspberry”, and then press “Enter” on the keyboard. alt text.

You shoud see a response back from the skill. Give the command “stop” in response to end your skill session.

11. Create the IoT device

Creation of the IoT device

First, I would recommend creating the IoT device on the Raspberry Pi you are going to use. This is recommended due to the necessity of saving some files from the AWS site. These files appear just during registration, so don’t miss them. With that, let’s go.

  1. First, open the IoT console in your browser.

  2. Select the same region US East (N. Virginia) as we selected for the Lambda function: alt text

  3. In the left part of the page select “Manage” point. You will see the following page: alt text Click on the blue button “Register a Thing”. In the next page select “Create a single thing” in the top or in the bottom: alt text
    In the following window you need to enter the name of your thing. I selected “Raspberry_Pi_LED_Thing”, but you can choose any name you want. alt text Then scroll down and click on the blue button “Next” leaving everything else unchanged. You will see the following page: alt text
    Click the top blue button “Create certificate”. You will see the following window: alt text
    Now, be very careful. You need to download and save these three files in the “/home/pi/iot” on your Raspberry Pi. These files will be used to identify and securely connect to your IoT thing, in our case Raspberry Pi. In this folder is also a pre-installed file called “root-ca.pem.crt”. This is a certificate bundle used to validate the security of the connection to the AWS IoT service and other Internet destinations. Click on the blue button “Activate”. And then “Attach a policy”. In the next page it will warn you that there are no policies in your account. So, click on the button “Register Thing”, we will create and attach a policy later. alt text You will see the created thing: alt text

  4. Now it’s time to create a policy and gather all things together. Select the point “Secure” in the left menu and then “Policies” in the submenu. You will see the following: alt text
    Click the blue button “Create a policy” to see the following page: alt text We need to select the name for our policy - I selected iot_policy. In the field “Action” write iot:*, and in the box Resource ARN write *. Select “Allow” in the field “Effect”. This should look like the following: alt text
    Scroll down and click the blue button “Create”. You will see a successfully created policy: alt text
    Now select the point “Certificates” in the left menu. You will see your recently created certificate there. I have several, so my page looks like this, but you will probably only have one. If you have multiple, use the certificate you created for this IoT device. alt text Click on your certificate to see the following page: alt text
    You will have your own numbers, this is expected and would be bad if they’re not different. Now select the “Actions” in the top right and in the drop down list select “Attach policy”: alt text
    Select your “iot_policy” and click on the button “Attach”: alt text
    Now make sure that policy and thing are attached to the certificate. Select first “Policies” and then “Things” in the left menu to see your Policy and Thing. If everything is OK, click on the ”<-“ button in the top left and return to the previous menu.

And we’re done with this portion! That is all we need to do to make the Thing. Now we need to copy the thing’s URL so we can address it using our Lambda function. To find it, return to the menu point Manage and submenu Thing in the left part of the page. alt text
Click on your Thing and select the “Interact” point in the left menu. alt text
Copy the URL address under the HTTPS title; we will use it very soon. It’s time to write some code for our Lambda function.

12. Update Skill Lambda code to connect to AWS IoT

Update IAM Role to connect to AWS IoT

By default, most AWS services can only access themselves, primarily for security reasons. In order for your Lambda function to access AWS IoT, we need to add the permission to the existing Lambda function role. (Alternatively, we could have created a role first, but for the sake of this workshop it is a little simpler to go this route).

Now expand the “Services” in the top left corner and select IAM there:

alt text

In the page that comes up, select “Roles” on the left part of the page:

alt text

On the search box halfway down the screen, start typing in the name of your Lambda function (e.g. “Rasp”) and your role should appear

alt text

Click on the role, and then Attach Policies. Search and select checkbox for the policy “AWSIoTDataAccess” and “AWSLambdaBasicExecutionRole” and then click “Attach Policy”. It should look like the below when you are all finished.

alt text

Lambda function code

Let’s return to the Lambda function page for index.js. We need to update our function with the proper endpoint information from our IoT Thing.

var config = {};
config.IOT_BROKER_ENDPOINT = "xxxxxxxxxxx.iot.us-east-1.amazonaws.com";
config.IOT_BROKER_REGION = "us-east-1";
config.IOT_THING_NAME = "Raspberry_Pi_LED_Thing(USE YOUR UNIQUE NAME HERE)";

The variable IOT_BROKER_ENDPOINT contains the recently copied URL of your IoT device, so please insert your own URL there. IOT_BROKER_REGION is the region we selected when creating the IoT Thing and Lambda function. It should be “us-east-1 (N. Virginia)”. IOT_THING_NAME is the name of the IoT thing that you used.

And that’s it for now! Let’s set up IoT

13. Set up the Breadboard for AWS IoT

Hardware assembly

Refer to the pictures below for the hardware assembly.

  1. Shut down your Pi from the desktop menu, and disconnect the power cable.
  2. Attach the GPIO breakout board to the breadboard.
  3. Attach the ribbon cable to the GPIO breakout board.
  4. Attach the other end of the ribbon cable to the 40 pin header on the Pi.
  5. Place the red, green and blue LEDs on the breadboard. The shorter pin of the LEDs should be -5V line.
  6. Place the 220 Ohms (red-red-black-gold) LED resistors on the breadboard.
  7. Please the 3 red,greeen, and blue colored jumper wires linking the GPIO pins controlling the LEDs on the breadboard. Use the table below as a guide.
  8. Place the switch on the breadboard.
  9. Pleace the 10k Ohms (brown-black-orange-gold) pull-down resistor for the switch on the breadboard.
  10. Place the yellow jumper wire linking the switch to the GPIO pin on the breadbord. Use the table below as a guide.

Pi_sketch Pi_iot_board_1 Pi_iot_board_2

Breadboard connection table

Raspberry Pi Pin Number BCM pin number Module Pin Switch Pull-down resistor
6 - GND - Pin1
11 17 R - -
12 18 B - -
13 27 G - -
1 - - Pin 1 -
15 22 - Pin 2 Pin2

Here, in the table, in the first column, these are the physical pin numbers of the Raspberry Pi, and in the second column the pin numbers according to the BCM numeration as our GPIO library will use it.

14. Test the ASK/Lambda/IoT connection

Testing the communication and cooperation between services

Let’s switch back to the Alexa Skill page and go the “Test” page from the menu in the top of the page:

alt text

This time, instead of just launching the skill, let’s give a command to try to control the LED. In the skill simulator input, type “tell my raspberry to turn the red led on”, and then press “Enter” on the keyboard. Finally, if everything is good, you should see something like this:

alt text

You can see the JSON Input ‘ this is the request to the Lambda function, and JSON Output ‘ this is response from the Lambda function to the Alexa Skill. Let’s now see how invocation of this function affects the Thing Shadow. Let’s switch to the IoT Thing page and select “Shadow” in the menu in the left part of the page. You will see something like this:

alt text

If you can see all of this, that means that everything was set up correctly and all Amazon services work fine. You also can ask Alexa about the switch state “Alexa, ask my raspberry about the switch state”. But unless you don’t have your Raspberry Pi set up, it will say that the state is undefined, as you can see, there is no "switch" key in the Shadow state yet. Now, we need to setup the IoT software on the Raspberry Pi that will read the Shadow state and change the LEDs states accordingingly, and also to update the shadow state when the switch changes its state.

15. Setup the AWS IoT software on your Pi

The Raspbian OS Image for the workshop has the following required software packages pre-installed:

  • 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: alt text

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: alt text
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. alt text

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);
});

16. Extra Credit

All finished up with the material but want a new challenge?

Here are some ideas: