Alexa, Ask ESP8266 for Temperature Readings

Uttering Alexa voice commands to turn your ESP8266 connected device on or off is cool. There is plenty of information readily available explaining how to make that happen.

But what a custom command?

Like having Alexa tell you something unique. Using data gathered by reading your specific IoT device sensors?

Such as ESP8266 measured values that change over time.

For example, temperature or other weather sensors.

Having the ability to interact verbally with your IoT device in this manner opens up a wide range of possibilities. Yet, as I discovered, information on this topic was more challenging to find with a google search.

So here is how I did it.

And so can you.

The Problem

What is needed is a method of converting your verbal request into a command that can be sent to your ESP8266 (or other IoT device) to get the requested information, and to return the information to Alexa in a format that she can read back to you.

The solution presented here uses the Amazon Web Services (AWS) to do all the voice interfacing with Alexa, Things Speak as a repository for the ESP8266 sensor data, and a VPS CRON script to move the sensor data from the ESP8266 to Things Speak.

You might wonder why a VPS and Thing Speak are used. Why not communicate directly with the ESP8266 from the AWS Lambda Function?

Truth is, you can cut out these intermediate steps. But for security, I have added this layer to hide my IoT credentials within my heavily fortified Virtual Private Server (VPS). But do not be concerned, as you will see, this does not make the system overly complicated.

System Diagram

Test Scenario

We start with a voice command: “Alexa, ask ESP8266-12 for the indoor temperature”

Alexa responds with: “The indoor temperature is 73.2 degrees”

So what just happened?

Since we are dealing with the Amazon Echo device, Amazon lingo is needed (bold).

The voice command was received by the Amazon Echo device and recognized as an Alexa skill.

Invocation of the skill triggered the Lambda function which, in-turn, sent a request to ThingSpeak for the current ESP8266 sensor readings,

The readings returned were passed back to the Alexa skill which recites the values of the temperature sensors.

Implementation

We need three components to make this system function as intended.

Data Source (ESP8266 Sensor Data)

  • From ThingSpeak Channel or
  • Directly from ESP8266

Amazon Skill to convert your voice to a command to retrieve the requested information

Amazon Lambda function to receive the Skill request, query for the requested information and return a reply to the Amazon Skill

Let’s do it…

You will need to setup two accounts with Amazon, If you do not already have these accounts, create them now. No worries, they are FREE:

Create Amazon Developer Account (For Alexa Skill)

Start by pointing your browser to:

https://developer.amazon.com/

Select “Sign In” located on the page’s top-right header:

Next, select the “Create your Amazon Developer Account” button

Follow the instructions to create your free Amazon Developer Account

Create free AWS account (For Lambda Function)

Start by pointing your browser to:

https://portal.aws.amazon.com/billing/signup#/start

Follow the instructions to create your free AWS (Amazon Web Services) Account

Creating the Alexa Skill

Sign in to https://developer.amazon.com/ and go to the Developer Console.

Select the Alexa tab.

Select “Get Started” Alexa Skills Kit.

Select “Add a New Skill”

Note: The rest of the skill creation can be customized for your needs. It is suggested that you follow along with this guide for your first skill, and customize after getting this example to work. For this example, my publicly shared ThingSpeak Channel is used for the dynamic data source

For the first tab (Skill information), enter:

Name: ESP8266

Invocation Name: e s p eighty two sixty six dash twelve

Then click “Save” at the bottom.

For the Interaction Model tab, enter:

Intent Schema (copy/paste):

 
 
  1. {
  2. "intents": [
  3. {
  4. "slots": [
  5. {
  6. "name": "Sensor",
  7. "type": "ESP_SENSORS"
  8. }
  9. ],
  10. "intent": "GetEspInfoIntent"
  11. },
  12. {
  13. "intent": "AMAZON.HelpIntent"
  14. },
  15. {
  16. "intent": "AMAZON.StopIntent"
  17. },
  18. {
  19. "intent": "AMAZON.CancelIntent"
  20. }
  21. ]
  22. }

Custom Slot Types:

Enter Type: ESP_SENSORS
Enter Values:
every
indoor
outdoor
attic

Then click “Add” below the “Enter Values” box..

Now click “Save” at the bottom.

Sample Utterances: GetEspInfoIntent for {Sensor} temperature

Click “Save” at the bottom.

Then click “Next” at the bottom.

 

Creating the Lambda Function

Sign in to https://portal.aws.amazon.com

Make sure you are on the URL https://console.aws.amazon.com

From the Console, select Services > Compute > Lambda

Click “Create Function”

Click “Author from scratch”.

Enter the following Basic Information:

Name: ESP8266V2

Role: Create new role from templates

RoleName: myEspRole

Policy Templates: CloudFormation stack read-only permissions

Then click “Create Function”

Select Runtime: “Node.js 4.3”

Then paste the following code in the index.js field:

 
 
  1. /**
  2. Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at
  4. http://aws.amazon.com/apache2.0/
  5. or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
  6. */
  7. /*
  8. This file has been modified under the terms of the above license October 2017
  9. */
  10. /**
  11. * This code is dependant on data retreived from a ThingsSpeak channel
  12. * It is a Lambda function for handling Alexa Skill requests.
  13. *
  14. * Examples:
  15. * One-shot model:
  16. * User: "Alexa, ask ESP8266 for temperature readings"
  17. * Alexa: "Indoor temperature is 75.2 degrees and outdoor temperature is 86.2 degrees"
  18. */
  19. /**
  20. * App ID for the skill
  21. */
  22. var APP_ID = undefined; //OPTIONAL: replace with "amzn1.echo-sdk-ams.app.[your-unique-value-here]";
  23. //var APP_ID = "amzn1.ask.skill.018df58b-9e42-4e0f-927c-fd06ba2edb5b"; //OPTIONAL: replace with "amzn1.echo-sdk-ams.app.[your-unique-value-here]";
  24. /**
  25. * The AlexaSkill prototype and helper functions
  26. */
  27. var AlexaSkill = require('./AlexaSkill');
  28. var https = require('https');
  29. //ThingSpeak Data Access
  30. var channel_id = 33251;                        //ThingSpeak Channel ID
  31. var speak_key = '9VGQ4X79MQJC90RD';            //ThingSpeak Channel Read Key
  32. var p_at,p_ou,p_in;                            //Sensor Readings
  33. // Create URL to retrieve latest temperature reading from my ThingsSpeak channel (JSON format)
  34. var url = 'https://api.thingspeak.com/channels/' + channel_id + '/feed/last.json?api_key=' + speak_key;
  35. /**
  36. * ESP8266 is a child of AlexaSkill.
  37. * To read more about inheritance in JavaScript, see the link below.
  38. *
  39. * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript#Inheritance
  40. */
  41. var Esp8266 = function () {
  42. AlexaSkill.call(this, APP_ID);
  43. };
  44. // Extend AlexaSkill
  45. Esp8266.prototype = Object.create(AlexaSkill.prototype);
  46. Esp8266.prototype.constructor = Esp8266;
  47. Esp8266.prototype.eventHandlers.onSessionStarted = function (sessionStartedRequest, session) {
  48. //console.log("onSessionStarted requestId: " + sessionStartedRequest.requestId + ", sessionId: " + session.sessionId);
  49. // any initialization logic goes here
  50. };
  51. Esp8266.prototype.eventHandlers.onLaunch = function (launchRequest, session, response) {
  52. //console.log("onLaunch requestId: " + launchRequest.requestId + ", sessionId: " + session.sessionId);
  53. handleEsp8266Request(response);
  54. };
  55. /**
  56. * Overridden to show that a subclass can override this function to teardown session state.
  57. */
  58. Esp8266.prototype.eventHandlers.onSessionEnded = function (sessionEndedRequest, session) {
  59. //console.log("onSessionEnded requestId: " + sessionEndedRequest.requestId + ", sessionId: " + session.sessionId);
  60. // any cleanup logic goes here
  61. };
  62. Esp8266.prototype.intentHandlers = {
  63. "GetEspInfoIntent": function (intent, session, response) {
  64. var speechOutput;
  65. var cardTitle;
  66. var sensorSlot = intent.slots.Sensor,
  67. sensorName = "every";
  68. if (sensorSlot && sensorSlot.value){
  69. sensorName = sensorSlot.value.toLowerCase();
  70. }
  71. switch(sensorName) {
  72. default:
  73. case "every":
  74. speechOutput = "Indoor temperature is " + p_in + " degrees" +
  75. " and the Outdoor temperature is " + p_ou + " degrees and " +
  76. "the Attic temperature is " + p_at + " degrees";
  77. cardTitle = "Temperature Readings";
  78. break;
  79. case "indoor":
  80. speechOutput = "Indoor temperature is " + p_in + " degrees";
  81. cardTitle = "Indoor Temperature Reading";
  82. break;
  83. case "outdoor":
  84. speechOutput = "Outdoor temperature is " + p_ou + " degrees";
  85. cardTitle = "Outdoor Temperature Reading";
  86. break;
  87. case "attic":
  88. speechOutput = "Attic temperature is " + p_at + " degrees";
  89. cardTitle = "Attic Temperature Reading";
  90. break;
  91. }
  92. response.tellWithCard(speechOutput, cardTitle, speechOutput);
  93. },
  94. "AMAZON.HelpIntent": function (intent, session, response) {
  95. response.ask("You can say ask ESP8266 for indoor temperature, or, you can say exit... What can I help you with?", "What can I help you with?");
  96. },
  97. "AMAZON.StopIntent": function (intent, session, response) {
  98. var speechOutput = "Goodbye";
  99. response.tell(speechOutput);
  100. },
  101. "AMAZON.CancelIntent": function (intent, session, response) {
  102. var speechOutput = "Goodbye";
  103. response.tell(speechOutput);
  104. }
  105. };
  106. /* -------------------------------------------------------------
  107. * Get all sensors temperatures and return speech to the user.
  108. * -------------------------------------------------------------
  109. */
  110. function handleEsp8266Request(response) {
  111. // Create speech output
  112. var speechOutput = "Greetings, I did not understand what you want. How may I serve you?";
  113. var cardTitle = "Initial Request";
  114. response.tellWithCard(speechOutput, cardTitle, speechOutput);
  115. }
  116. // Create the handler that responds to the Alexa Request.
  117. exports.handler = function (event, context) {
  118. https.get(url, function(res) {
  119. // Get latest sensor data from home IoT SoC
  120. res.on('data', function(d) {
  121. p_in = JSON.parse(d).field1;
  122. p_ou = JSON.parse(d).field2;
  123. p_at = JSON.parse(d).field3;
  124. // Create an instance of the SpaceGeek skill.
  125. var Esp8266_skill = new Esp8266();
  126. Esp8266_skill.execute(event, context);
  127. });
  128. res.on('end', function() {
  129. });
  130. res.on('error', function(e) {
  131. context.fail("Got error: " + e.message);
  132. });
  133. });
  134. };

Now save this function (Click “Save and test”)

Exit out of the “Test” screen and click on the “Triggers” tab

Then click “Add Trigger” and click in the dotted box:

Select “Alexa Skills Kit”:

And click “Submit”.

Now Lets link the Lambda Function to the Alexa skill:

Return to the Alexa Skill developed earlier:

Click Alexa Skills Kit > Get Started

Click on the Name of the Skill created earlier

Click “Configuration”

Click the Endpoint “AWS Lambda ARN” radio button

Paste the ARN from the paste buffer in the “Default” box

Click bottom “Save” button

That’s it!

Now let’s test it…

The next tab on the Alexa Skills page is called “Test”. Select it and scroll down to the “Service Simulator”

Using the “Text” table, simply enter one of the following utterances, click “Ask ESP8266” and verify you get an appropriate “Service Response”:

every temperature
indoor temperature
outdoor temperature
attic temperature

For example, the utterance “attic temperature” should produce the following Service Response in JSON format:

 
 
  1. {
  2. "version": "1.0",
  3. "response": {
  4. "outputSpeech": {
  5. "text": "Attic temperature is 49.2 degrees",
  6. "type": "PlainText"
  7. },
  8. "card": {
  9. "content": "Attic temperature is 49.2 degrees",
  10. "title": "Attic Temperature Reading"
  11. },
  12. "speechletResponse": {
  13. "outputSpeech": {
  14. "text": "Attic temperature is 49.2 degrees"
  15. },
  16. "card": {
  17. "content": "Attic temperature is 49.2 degrees",
  18. "title": "Attic Temperature Reading"
  19. },
  20. "shouldEndSession": true
  21. }
  22. },
  23. "sessionAttributes": {}
  24. }

The actual temperature readings will vary, depending on the current temperature in my attic.

You should now be able to invoke this skill using your Amazon Echo using voice commands.

You can verify that this new skill is enabled on your Alexa echo at https://alexa.amazon.com

Select “Skills” and then “Your Skills”. This new skill should be listed and enabled.

The rest of the system has been covered in previous blog posts:

Things Speak Channel:

  • Write values
  • Read Values

Refer to my ThingSpeak Channel

Virtual Private Server (VPS):

  • Read from ESP8266
  • Write to Things Speak

Uses a Linux CRON script scheduled to run at the start of every hour.

See this post: PHP Script to move ESP8266 data

ESP8266 Sensor data source:

  • Read Sensors Continuously
  • Send Sensor Values on request as JSON string

ESP8266 Temperature Sensor Project

In Conclusion

As you can see, you can make the information collected by your IoT device, such as an ESP8266 accessible via audio commands/responses using the Amazon Echo device. The commands and responses can be customized to the exact words you wish to use. This opens up infinite possibilities.

Hope you find this information useful and fun to explore.

Loading

Share This:
FacebooktwitterredditpinterestlinkedintumblrFacebooktwitterredditpinterestlinkedintumblr
Social tagging: > >

7 Responses to Alexa, Ask ESP8266 for Temperature Readings

  1. Vinitha chinnam says:

    I'm following all the steps what you have mentioned. but when ever i run the lambda function I got the following error. Kindly help for this issue

    Response:
    {
    "errorMessage": "Cannot set property 'onSessionStarted' of undefined",
    "errorType": "TypeError",
    "stackTrace": [
    "Module._compile (module.js:409:26)",
    "Object.Module._extensions..js (module.js:416:10)",
    "Module.load (module.js:343:32)",
    "Function.Module._load (module.js:300:12)",
    "Module.require (module.js:353:17)",
    "require (internal/module.js:12:17)"
    ]
    }

    Request ID:
    "b218c5f0-7907-11e8-99bf-05ecde589166"

    Function Logs:
    START RequestId: b218c5f0-7907-11e8-99bf-05ecde589166 Version: $LATEST
    module initialization error: TypeError
    at Object.<anonymous> (/var/task/index.js:49:50)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Module.require (module.js:353:17)
    at require (internal/module.js:12:17)
    END RequestId: b218c5f0-7907-11e8-99bf-05ecde589166
    REPORT RequestId: b218c5f0-7907-11e8-99bf-05ecde589166 Duration: 152.76 ms Billed Duration: 200 ms Memory Size: 128 MB Max Memory Used: 31 MB
    module initialization error
    TypeError

    • facebook-profile-picture InternetOfHomeThings says:

      I just tested the Lambda function and found it to be working correctly. The error you have listed suggest there is a problem with the APP_ID. Please note that I have left this value undefined for this demo project:

      var APP_ID = undefined;

      Here is the response received from the test. You should see something similar:

      Response:
      {
      "version": "1.0",
      "response": {
      "outputSpeech": {
      "type": "PlainText",
      "text": "Indoor temperature is 79.2 degrees"
      },
      "shouldEndSession": true,
      "card": {
      "type": "Simple",
      "title": "Indoor Temperature Reading",
      "content": "Indoor temperature is 79.2 degrees"
      }
      },
      "sessionAttributes": {}
      }

      Request ID:
      "83ecf5a5-c972-4984-ba92-ff3b07c9a0d2"

      Function Logs:
      START RequestId: 83ecf5a5-c972-4984-ba92-ff3b07c9a0d2 Version: $LATEST
      2018-06-27T22:12:17.283Z 83ecf5a5-c972-4984-ba92-ff3b07c9a0d2 session applicationId: amzn1.ask.skill.5d4b0043-b8fb-4949-9480-af86709ec1db
      2018-06-27T22:12:17.308Z 83ecf5a5-c972-4984-ba92-ff3b07c9a0d2 dispatch intent = GetEspInfoIntent
      END RequestId: 83ecf5a5-c972-4984-ba92-ff3b07c9a0d2
      REPORT RequestId: 83ecf5a5-c972-4984-ba92-ff3b07c9a0d2 Duration: 272.61 ms Billed Duration: 300 ms Memory Size: 128 MB Max Memory Used: 22 MB

      This is my test configuration (requests indoor temperature):

      {
      "session": {
      "new": true,
      "sessionId": "SessionId.25ebb9d8-b285-491c-9952-7bbb3cb0045d",
      "application": {
      "applicationId": "amzn1.ask.skill.5d4b0043-b8fb-4949-9480-af86709ec1db"
      },
      "attributes": {},
      "user": {
      "userId": "amzn1.ask.account.AG3NZN2RSJPN2UPG7TJTMK7LP4DD3LKSRDGIIEUQTVT5PXWSPT4OSOPZGCMOUJPTX4RBSA6SUXD6W7UAEXHVRUEMCEKBNVGUVCLEWDN26DZVDPEBSAY2IGFWES6A2KSKF4PXEFN6GELGCEEIPHVAVADUMVFPCPBQXSJ3SAHEJUAQJNJLD3IBHMD7MHDNRDTMYCGI2IMRMFTXKGQ"
      }
      },
      "request": {
      "type": "IntentRequest",
      "requestId": "EdwRequestId.b31fffe1-59c0-450a-88f0-349590dfb8c2",
      "intent": {
      "name": "GetEspInfoIntent",
      "slots": {
      "Sensor": {
      "name": "Sensor",
      "value": "indoor"
      }
      }
      },
      "locale": "en-US",
      "timestamp": "2017-11-18T15:47:14Z"
      },
      "context": {
      "AudioPlayer": {
      "playerActivity": "IDLE"
      },
      "System": {
      "application": {
      "applicationId": "amzn1.ask.skill.5d4b0043-b8fb-4949-9480-af86709ec1db"
      },
      "user": {
      "userId": "amzn1.ask.account.AG3NZN2RSJPN2UPG7TJTMK7LP4DD3LKSRDGIIEUQTVT5PXWSPT4OSOPZGCMOUJPTX4RBSA6SUXD6W7UAEXHVRUEMCEKBNVGUVCLEWDN26DZVDPEBSAY2IGFWES6A2KSKF4PXEFN6GELGCEEIPHVAVADUMVFPCPBQXSJ3SAHEJUAQJNJLD3IBHMD7MHDNRDTMYCGI2IMRMFTXKGQ"
      },
      "device": {
      "supportedInterfaces": {}
      }
      }
      },
      "version": "1.0"
      }

  2. Provien Falcao says:

    I am getting following error. looks like There is no module called AlexaSkill.

    Response:
    {
    "errorMessage": "Cannot find module './AlexaSkill'",
    "errorType": "Error",
    "stackTrace": [
    "Function.Module._load (module.js:276:25)",
    "Module.require (module.js:353:17)",
    "require (internal/module.js:12:17)",
    "Object.<anonymous> (/var/task/index.js:28:18)",
    "Module._compile (module.js:409:26)",
    "Object.Module._extensions..js (module.js:416:10)",
    "Module.load (module.js:343:32)",
    "Function.Module._load (module.js:300:12)",
    "Module.require (module.js:353:17)"
    ]
    }

    Request ID:
    "6747ff5a-802f-11e8-abb1-97c597a1a034"

    Function Logs:
    START RequestId: 6747ff5a-802f-11e8-abb1-97c597a1a034 Version: $LATEST
    Unable to import module 'index': Error
    at Function.Module._resolveFilename (module.js:325:15)
    at Function.Module._load (module.js:276:25)
    at Module.require (module.js:353:17)
    at require (internal/module.js:12:17)
    at Object.<anonymous> (/var/task/index.js:28:18)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Module.require (module.js:353:17)
    END RequestId: 6747ff5a-802f-11e8-abb1-97c597a1a034
    REPORT RequestId: 6747ff5a-802f-11e8-abb1-97c597a1a034 Duration: 79.74 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 19 MB

    • facebook-profile-picture InternetOfHomeThings says:

      if the error you have noted is from the Lambda function, please refer to the comment reply from 2018/06/27 at 3:19 pm

      otherwise, more information is needed to understand and hopefully resolve the issue you are facing.
      What were you running when the error occurred? At what step in your Alexa Skill development did it occur?

  3. Icarusx says:

    Hi sir,
    have difficulty completing the skill.
    Where should we put the hutterance …

    Sample Utterances: GetEspInfoIntent for {Sensor} temperature

    If you can kindly tell me step by step.

    • facebook-profile-picture InternetOfHomeThings says:

      I see WordPress butchered my post around the utterance section. I will be unavailable to review and update the guidance until later December. If you need guidance now, your best resources are the AWS websites noted in the article.

  4. Phil says:

    Hi,

    Nodejs4.3 is obsolete, but finally , I succeeded.
    I found an AlexaSkill.js and made some adjustements and now, it's working.
    I

Leave a Reply

Press Ctrl+C to copy the following code.
"