Demonstration




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 Amazon Echo – DIY Project with PHPoC
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

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.

Click image for larger version

Name:	led_strip.png
Views:	213
Size:	490.3 KB
ID:	673


Step 2: Creating and configuring Alexa Skill (on Alexa Service)
  • In “Interaction Model”
    • Intent Schema
      Code:
      		{
      		"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:
        			red
        			green
        			blue
        			cyan
        			yellow
        			magenta
        			white
      • LED_STATE
        Code:
        			on
        			off
      • LED_ID
        Code:
        			one
        			two
        			three
        			four
        			five
        			six
        			seven
        			eight
        			night
        			ten
        			eleven
        			twelve
        			thirteen
        			fourteen
        			all
      • EFFECT_STATE
        Code:
        			normal
        			blink
        			color
    • Sample Utterances[/i][/b]
      Code:
      		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:
/**
 * 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>
PHP Code:
<?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(WHITEWHITEWHITEWHITEWHITEWHITEWHITEWHITEWHITEWHITEWHITEWHITEWHITEWHITE);
$led_state = array(OFFOFFOFFOFFOFFOFFOFFOFFOFFOFFOFFOFFOFFOFF);

$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 != && $to == 0)
$led_color[$from 1] = $color_value;
else if(
$from != && $to != 0)
{
for(
$i $led_from$i <= $led_to$i++)
{
$led_color[$i 1] = $color_value;
}
}
else if(
$from == && $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 != && $to == 0)
{
$led_effect[$from 1] = $effect;
if(
$effect != "normal")
$led_state[$from 1] = ON;
}
else if(
$from != && $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 == && $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($i4"set 0 output low"); usleep(10);
spc_request($i4"set 1 output low"); usleep(10);
spc_request($i4"set 2 output low"); usleep(10);
}
else
{
$color $led_color[$i 1];

$state = ($color RED) ? "high" "low";
spc_request($i4"set 0 output $state"); usleep(10);
$state = ($color GREEN) ? "high" "low";
spc_request($i4"set 1 output $state"); usleep(10);
$state = ($color BLUE) ? "high" "low";
spc_request($i4"set 2 output $state"); usleep(10);
}
}

$update_time led_strip_get_tick();
$update false;
}
}
//mqtt_unsubscribe("sensors/temperature");

mqtt_disconnect();

?>