Amazon Echo - LED Strip

Amazon Echo - LED Strip

Postby Khanh » Fri May 12, 2017 1:49 pm

Demonstration
phpBB [video]


Introduction

This project can use voice command to:
- Turn on/off one, a range or all of LED strip
- Change color of one, a range or all of LED strip
- Change effect (blink, changing color or normal) of one, a range or all of LED strip
This project is an expansion of the project https://www.hackster.io/phpoc_man/amazo ... poc-037b11
Every step is almost the same, there are only some difference on interaction model configuration and source code.
You can do step by step on that link. I only show the differences in this article.

Things used in this project
- 1 x Amazon Alexa Echo Dot
- 1 x PHPoC Blue
- 14 x PHPoC 4-Port Relay Expansion Board http://www.phpoc.com/smart_expansion_board.php
- 14 x Adafruit RGB LED Strip https://www.adafruit.com/product/285

RGB LED strip required 12V power source. Therefore we need to use relay to control the LED .
Each strip need 3 relays to control three colors lines.
I use 14 strips, so I need 42 relays.
Each PHPoC 4-Port Relay Expansion Board has 4 relays.
In order to simplify the wiring and source code, I use 14 Expansion Boards. Each board connect to one LED strip.
led_strip.png
led_strip.png (545.87 KiB) Viewed 464 times


Step 2: Creating and configuring Alexa Skill (on Alexa Service)

- In “Interaction Model”:

• Intent Schema
Code: Select all
{
   "intents": [
      {
        "slots": [
         {
           "name": "ColorState",
           "type": "COLOR_STATE"
         },
         {
           "name": "LedState",
           "type": "LED_STATE"
         },
         {
           "name": "FromLed",
           "type": "LED_ID"
         },
         {
           "name": "ToLed",
           "type": "LED_ID"
         },
         {
           "name": "Effect",
           "type": "EFFECT_STATE"
         }
        ],
        "intent": "LedStrip"
      }
   ]
}


• Custom Slot Types

+ COLOR_STATE
Code: Select all
red
green
blue
cyan
yellow
magenta
white


+ LED_STATE
Code: Select all
on
off


+ LED_ID
Code: Select all
one
two
three
four
five
six
seven
eight
night
ten
eleven
twelve
thirteen
fourteen
all


+ EFFECT_STATE
Code: Select all
normal
blink
color


• Sample Utterances
Code: Select all
LedStrip {FromLed} {ColorState}
LedStrip {FromLed} to {ToLed} {ColorState}
LedStrip number {FromLed} {ColorState}
LedStrip number {FromLed} to number {ToLed} {ColorState}
LedStrip led number {FromLed} {ColorState}
LedStrip led number {FromLed} to number {ToLed} {ColorState}
LedStrip led number {FromLed} to led number {ToLed} {ColorState}
LedStrip change color to {ColorState}
LedStrip change number {FromLed} to {ColorState}
LedStrip change number {FromLed} to number {ToLed} to {ColorState}
LedStrip change led number {FromLed} to {ColorState}
LedStrip change led number {FromLed} to number {ToLed} to {ColorState}
LedStrip change led number {FromLed} to led number {ToLed} to {ColorState}
LedStrip change led from number {FromLed} to number {ToLed} to {ColorState}
LedStrip {FromLed} {LedState}
LedStrip {LedState} {FromLed}
LedStrip {FromLed} to {ToLed} {LedState}
LedStrip turn {LedState} {FromLed}
LedStrip turn {LedState} number {FromLed}
LedStrip turn {LedState} led number {FromLed}
LedStrip turn {FromLed} {LedState}
LedStrip turn led number {FromLed} {LedState}
LedStrip turn {LedState} number {FromLed} to number {ToLed}
LedStrip turn {LedState} led number {FromLed} to number {ToLed}
LedStrip turn {LedState} led from number {FromLed} to number {ToLed}
LedStrip turn number {FromLed} to number {ToLed} {LedState}
LedStrip turn led number {FromLed} to number {ToLed} {LedState}
LedStrip turn led from number {FromLed} to number {ToLed} {LedState}
LedStrip {Effect}
LedStrip {FromLed} {Effect}
LedStrip {Effect} {FromLed}
LedStrip {Effect} number {FromLed}
LedStrip {Effect} led number {FromLed}
LedStrip {Effect} {FromLed} to {ToLed}
LedStrip {Effect} number {FromLed} to {ToLed}
LedStrip {Effect} number {FromLed} to number {ToLed}
LedStrip {Effect} led number {FromLed} to {ToLed}
LedStrip {Effect} led number {FromLed} to number {ToLed}
LedStrip number {FromLed} {Effect}
LedStrip {FromLed} to {ToLed} {Effect}
LedStrip number {FromLed} to {ToLed} {Effect}
LedStrip number {FromLed} to number {ToLed} {Effect}
LedStrip led number {FromLed} {Effect}
LedStrip led number {FromLed} to {ToLed} {Effect}
LedStrip led number {FromLed} to number {ToLed} {Effect}
LedStrip effect {Effect}
LedStrip effect {FromLed} {Effect}
LedStrip effect {Effect} {FromLed}
LedStrip effect {Effect} number {FromLed}
LedStrip effect {Effect} led number {FromLed}
LedStrip effect {Effect} {FromLed} to {ToLed}
LedStrip effect {Effect} number {FromLed} to {ToLed}
LedStrip effect {Effect} number {FromLed} to number {ToLed}
LedStrip effect {Effect} led number {FromLed} to {ToLed}
LedStrip effect {Effect} led number {FromLed} to number {ToLed}



Step 3: Writing source code to handle data from Alexa Skill (on AWS Lambda)

This code will handle "intent" from Alexa Skill and extract the necessary data, format it in json format and publish on a MQTT topic.
<index.js>
Code: Select all
/**
 * This sample demonstrates a simple skill built with the Amazon Alexa Skills Kit.
 * The Intent Schema, Custom Slots, and Sample Utterances for this skill, as well as
 * testing instructions are located at http://amzn.to/1LzFrj6
 *
 * For additional samples, visit the Alexa Skills Kit Getting Started guide at
 * http://amzn.to/1LGWsLG
 */

var mqtt = require('mqtt');

// Route the incoming request based on type (LaunchRequest, IntentRequest,
// etc.) The JSON body of the request is provided in the event parameter.
exports.handler = function (event, context) {
   try {
      console.log("event.session.application.applicationId=" + event.session.application.applicationId);

      /**
       * Uncomment this if statement and populate with your skill's application ID to
       * prevent someone else from configuring a skill that sends requests to this function.
       */
      /*
      if (event.session.application.applicationId !== "amzn1.echo-sdk-ams.app.[unique-value-here]") {
          context.fail("Invalid Application ID");
      }
      */

      if (event.session.new) {
         onSessionStarted({requestId: event.request.requestId}, event.session);
      }

      if (event.request.type === "LaunchRequest") {
         onLaunch(event.request,
            event.session,
            function callback(sessionAttributes, speechletResponse) {
               context.succeed(buildResponse(sessionAttributes, speechletResponse));
            });
      } else if (event.request.type === "IntentRequest") {
         onIntent(event.request,
            event.session,
            function callback(sessionAttributes, speechletResponse) {
               context.succeed(buildResponse(sessionAttributes, speechletResponse));
            });
      } else if (event.request.type === "SessionEndedRequest") {
         onSessionEnded(event.request, event.session);
         context.succeed();
      }
   } catch (e) {
      context.fail("Exception: " + e);
   }
};

/**
 * Called when the session starts.
 */
function onSessionStarted(sessionStartedRequest, session) {
   console.log("onSessionStarted requestId=" + sessionStartedRequest.requestId +
      ", sessionId=" + session.sessionId);
}

/**
 * Called when the user launches the skill without specifying what they want.
 */
function onLaunch(launchRequest, session, callback) {
   console.log("onLaunch requestId=" + launchRequest.requestId +
      ", sessionId=" + session.sessionId);

   // Dispatch to your skill's launch.
   getWelcomeResponse(callback);
}

/**
 * Called when the user specifies an intent for this skill.
 */
function onIntent(intentRequest, session, callback) {
   console.log("onIntent requestId=" + intentRequest.requestId +
      ", sessionId=" + session.sessionId);

   var intent = intentRequest.intent,
      intentName = intentRequest.intent.name;

   // Dispatch to your skill's intent handlers
   if ("LedStrip" === intentName) {
      setLightInSession(intent, session, callback);
   } else if ("AMAZON.HelpIntent" === intentName) {
      getWelcomeResponse(callback);
   } else if ("AMAZON.StopIntent" === intentName || "AMAZON.CancelIntent" === intentName) {
      handleSessionEndRequest(callback);
   } else {
      throw "Invalid intent";
   }
}

/**
 * Called when the user ends the session.
 * Is not called when the skill returns shouldEndSession=true.
 */
function onSessionEnded(sessionEndedRequest, session) {
   console.log("onSessionEnded requestId=" + sessionEndedRequest.requestId + ", sessionId=" + session.sessionId);
   // Add cleanup logic here
}

// --------------- Functions that control the skill's behavior -----------------------

function getWelcomeResponse(callback) {
   // If we wanted to initialize the session to have some attributes we could add those here.
   var sessionAttributes = {};
   var cardTitle = "Welcome";
   var speechOutput = "Welcome to P H P o C. How can I help you?";
   
   // If the user either does not reply to the welcome message or says something that is not
   // understood, they will be prompted again with this text.
   var repromptText = "How can I help you?";
   var shouldEndSession = false;
   
   callback(sessionAttributes,
      buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}

function handleSessionEndRequest(callback) {
   var cardTitle = "Session Ended";
   var speechOutput = "Thank you for trying the P h p o c  example. Have a nice day!";
   // Setting this to true ends the session and exits the skill.
   var shouldEndSession = true;
   
   callback({}, buildSpeechletResponse(cardTitle, speechOutput, null, shouldEndSession));
}

/**
 * Sets the led in the session and prepares the speech to reply to the user.
 */
function setLightInSession(intent, session, callback) {
   var cardTitle = intent.name;
   
   var colorStateRequest = intent.slots.ColorState;
   var ledFromRequest = intent.slots.FromLed;
   var ledToRequest = intent.slots.ToLed;
   var ledStateRequest = intent.slots.LedState;
   var effectRequest = intent.slots.Effect;

   var repromptText = "";
   var sessionAttributes = {};
   var shouldEndSession = true;
   var speechOutput = "";
   
   if(colorStateRequest || ledFromRequest || ledToRequest || ledStateRequest || effectRequest)
   {
      var colorState = colorStateRequest.value;
      var ledFrom = ledFromRequest.value;
      var ledTo = ledToRequest.value;
      var ledState = ledStateRequest.value;
      var effect = effectRequest.value;
      
      if (colorState === undefined)
         colorState = "";
      
      if (ledFrom === undefined)
         ledFrom = 0;
      
      if (ledTo === undefined)
         ledTo = 0;
      
      if (ledState === undefined)
         ledState = "";
      
      if (effect === undefined)
         effect = "";
      
      var responseAlexa = "";
      
      if(colorState != "")
      {
         responseAlexa = "Ok, changing color of led strip ";
         
         if(ledFrom != 0 && ledTo !=0)
            responseAlexa += "from number " + ledFrom + " to number " + ledTo + " to " + colorState;
         else if(ledFrom != 0)
            responseAlexa += "number " + ledFrom + " to " + colorState;
         else
            responseAlexa += " to " +  colorState;
      }
      else if(ledState != "")
      {
         responseAlexa = "Ok, turning ";
         if(ledFrom != 0 && ledTo !=0)
            responseAlexa += ledState + " led strip from number " + ledFrom + " to number " + ledTo;
         else if(ledFrom != 0)
         {
            if(ledFrom == "all")
               responseAlexa += ledState + " all led strip";
            else
               responseAlexa += ledState + " led strip number " + ledFrom;
         }
      }
      else if(effect != "")
      {
         responseAlexa = "Ok, change effect of led ";
         
         if(ledFrom != 0 && ledTo !=0)
            responseAlexa += "from number " + ledFrom + " to number " + ledTo + " to " + effect;
         else if(ledFrom != 0)
            responseAlexa += "number " + ledFrom + " to " + effect;
         else
            responseAlexa = "Ok, change effect to " + effect;
      }
      else
      {
         responseAlexa = "Please try again";
      }
      
      if (ledFrom === "all")
         ledFrom = 15;
      
      var paramsUpdate = ' {"color" : "' + colorState + '", "from" : ' + ledFrom + ', "to" : ' + ledTo + ', "state" : "' + ledState + '", "effect" : "' + effect + '"}';
      
      //Update
      var mqttpromise = new Promise( function(resolve,reject){
         var client = mqtt.connect({port:1883,host:'iot.eclipse.org'})
         
         client.on('connect', function() { // When connected
            // publish a message to a topic
            client.publish('phpoc/alexa/ledstrip', paramsUpdate)
            client.end()
            resolve('Done Sending');
         });
         
      });
      mqttpromise.then(
         function(data) {
            console.log('Function called succesfully:', data);
            sessionAttributes = createcolorStateAttributes(colorState);
            speechOutput = responseAlexa;
            repromptText = responseAlexa;
            callback(sessionAttributes,buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
         },
         function(err) {
            console.log('An error occurred:', err);
         }
      );
      
   } else {
      speechOutput = "Please try again";
      repromptText = "Please try again";
      callback(sessionAttributes,buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
   }
}

function createcolorStateAttributes(colorState) {
   return {
      colorState: colorState
   };
}

// --------------- Helpers that build all of the responses -----------------------

function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
   return {
      outputSpeech: {
         type: "PlainText",
         text: output
      },
      card: {
         type: "Simple",
         title: "SessionSpeechlet - " + title,
         content: "SessionSpeechlet - " + output
      },
      reprompt: {
         outputSpeech: {
            type: "PlainText",
            text: repromptText
         }
      },
      shouldEndSession: shouldEndSession
   };
}

function buildResponse(sessionAttributes, speechletResponse) {
   return {
      version: "1.0",
      sessionAttributes: sessionAttributes,
      response: speechletResponse
   };
}


Step 5: Writing source code on PHPoC to handle commands and control the devices.

This code subscribe a MQTT topic and handle MQTT message which contains LED state, effect and LED numbers.
<task0.php>
Code: Select all
<?php

if(_SERVER("REQUEST_METHOD"))
    exit; // avoid php execution via http request

include_once "/lib/sd_spc.php";
include_once "/lib/sn_dns.php";
include_once "/lib/sn_json.php";
include_once "/lib/vn_mqtt.php";

define("RED",      1);
define("GREEN",      2);
define("BLUE",      4);
define("YELLOW",   RED | GREEN);
define("CYAN",      GREEN | BLUE);
define("MAGENTA",   RED | BLUE);
define("WHITE",      RED | GREEN | BLUE);

define("EFFECT_NORMAL",      1);
define("EFFECT_BLINK",      2);
define("EFFECT_COLOR",      3);
define("EFFECT_DOMINO",      4);

define("ON",      1);
define("OFF",      0);

function led_strip_get_tick()
{
   while(($pid = pid_open("/mmap/st0", O_NODIE)) == -EBUSY)
      usleep(500);

   if(!pid_ioctl($pid, "get state"))
      pid_ioctl($pid, "start");

   $tick = pid_ioctl($pid, "get count");
   pid_close($pid);

   return $tick;
}
function get_color_code($color_name)
{
   $color_name = strtoupper($color_name);
   
   if($color_name == "RED")
      return RED;
   if($color_name == "GREEN")
      return GREEN;
   if($color_name == "BLUE")
      return BLUE;
   if($color_name == "YELLOW")
      return YELLOW;
   if($color_name == "CYAN")
      return CYAN;
   if($color_name == "MAGENTA")
      return MAGENTA;
   if($color_name == "WHITE")
      return WHITE;

   return 0;
}

spc_reset();
spc_sync_baud(115200);

//$host_name = "test.mosquitto.org";
$host_name = "iot.eclipse.org";
//$host_name = "broker.hivemq.com";
//$host_name = "broker.mqttdashboard.com";
//$host_name = "[112.171.138.90]";
$port = 1883;

mqtt_setup(0, "PHPoC-MQTT Sub Example",  $host_name, $port);

$will = "";
$username = "";
$password = "";

mqtt_connect(true, $will, $username, $password);

$out_topics = array(array("phpoc/alexa/ledstrip", 0));

if(mqtt_state() == MQTT_CONNECTED)
    mqtt_subscribe($out_topics);

$in_topic = "";
$in_content = "";
$is_retain = 0;

$led_from = 0;
$led_to = 0;
$led_effect = array("normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal", "normal");
$led_color = array(WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE, WHITE);
$led_state = array(OFF, OFF, OFF, OFF, OFF, OFF, OFF, OFF, OFF, OFF, OFF, OFF, OFF, OFF);

$update = true;

while(1)
{
   if(mqtt_state() == MQTT_DISCONNECTED)
      while(mqtt_reconnect() == false);
   
   if(mqtt_loop($in_topic, $in_content, $is_retain))
   {
      //TODO , procees the received publish packet here
      if($is_retain == 1)
         echo "<<a stale message\r\n";
      
      echo "<<topic:$in_topic\r\n";
      echo "<<content: $in_content\r\n";
      
      $color   = json_text_value(json_search($in_content, "color"));
      $from   = (int)json_search($in_content, "from");
      $to      = (int)json_search($in_content, "to");
      $state   = json_text_value(json_search($in_content, "state"));
      $effect = json_text_value(json_search($in_content, "effect"));
      
      if($from > 15 || $to > 15)
         continue;
      
      if($from == 15)
      {
         $led_from = 1;
         $led_to = 14;
      }
      else if($from != 0)
      {
         $led_from = $from;
         $led_to = $to;
      }
      
      if($color != "")
      {
         $color_value = get_color_code($color);
         
         if($from != 0 && $to == 0)
            $led_color[$from - 1] = $color_value;
         else if($from != 0 && $to != 0)
         {
            for($i = $led_from; $i <= $led_to; $i++)
            {
               $led_color[$i - 1] = $color_value;
            }
         }
         else if($from == 0 && $to == 0)
         {
            for($i = 1; $i <= 14; $i++)
            {
               $led_color[$i - 1] = $color_value;
            }
         }
      }
      
      if($state != "" && $led_from != 0)
      {
         $state_value = ($state == "on") ? ON : OFF;
         
         if($led_to == 0)
         {
            $led_state[$led_from - 1] = $state_value;
            $led_effect[$led_from - 1] = "normal";
         }
         else
         {
            for($i = $led_from; $i <= $led_to; $i++)
            {
               $led_state[$i - 1] = $state_value;
               $led_effect[$i - 1] = "normal";
            }
         }
      }
      
      if($effect != "")
      {
         if($from != 0 && $to == 0)
         {
            $led_effect[$from - 1] = $effect;
            if($effect != "normal")
               $led_state[$from - 1] = ON;
         }
         else if($from != 0 && $to != 0)
         {
            for($i = $led_from; $i <= $led_to; $i++)
            {
               $led_effect[$i - 1] = $effect;
               if($effect != "normal")
                  $led_state[$i - 1] = ON;
            }
         }
         else if($from == 0 && $to == 0)
         {
            for($i = 1; $i <= 14; $i++)
            {
               $led_effect[$i - 1] = $effect;
               if($effect != "normal")
                  $led_state[$i - 1] = ON;
            }
         }
      }
      
      $update = true;
   }
   $curr_time = led_strip_get_tick();
   
   for($i = 0; $i < 14; $i++)
   {
      $effect = $led_effect[$i];
      switch($effect)
      {
         case "normal":
            //$update = true;
            break;
         case "blink":
            if(($curr_time - $update_time) > 1500)
            {
               //if($led_state[$i] == ON)
                  $led_state[$i] = ($led_state[$i] + 1) % 2;
               
               $update = true;
            }
            break;
         case "color":
            if(($curr_time - $update_time) > 1500)
            {
               $led_color[$i] = ($led_color[$i] + 1) % 8;
               
               $update = true;
            }
            break;
         case "domino":
            
            break;
      }
   }
   
   if($update)
   {
      for($i = 1; $i <= 14; $i++)
      {
         if($led_state[$i - 1] == OFF)
         {
            spc_request($i, 4, "set 0 output low"); usleep(10);
            spc_request($i, 4, "set 1 output low"); usleep(10);
            spc_request($i, 4, "set 2 output low"); usleep(10);
         }
         else
         {
            $color = $led_color[$i - 1];
            
            $state = ($color & RED) ? "high" : "low";
            spc_request($i, 4, "set 0 output $state"); usleep(10);
            $state = ($color & GREEN) ? "high" : "low";
            spc_request($i, 4, "set 1 output $state"); usleep(10);
            $state = ($color & BLUE) ? "high" : "low";
            spc_request($i, 4, "set 2 output $state"); usleep(10);
         }
      }
      
      $update_time = led_strip_get_tick();
      $update = false;
   }
}
//mqtt_unsubscribe("sensors/temperature");
   
mqtt_disconnect();

?>
Khanh
 
Posts: 72
Joined: Fri Mar 11, 2016 10:57 am

Return to Project

Who is online

Users browsing this forum: No registered users and 1 guest

cron