phpoc_manhite000
Published © GPL3+

Cocktail Machine with Alexa and PHPoC

Ask Alexa to make a cocktail you want. Recipes are included in PHPoC script.

IntermediateFull instructions provided6,726

Things used in this project

Hardware components

PHPoC Blue
PHPoC Blue
×1
Echo Dot
Amazon Alexa Echo Dot
×1
PHPoC Stepper Motor Controller Ⅱ (S-type or T-type)
PHPoC Stepper Motor Controller Ⅱ (S-type or T-type)
×2
Stepper Motor
×2
Cocktail Machine DIY
×1

Software apps and online services

Alexa Skills Kit
Amazon Alexa Alexa Skills Kit
AWS Lambda
Amazon Web Services AWS Lambda
MQTT
MQTT

Story

Read more

Custom parts and enclosures

Lift Closer

Lift Part

Slide Moving Part

Slider Bearing Side

Slider Motor Side

Code

PHPoC code (task0.php)

PHP
<?php

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

define("SID_X",				13);
define("SID_Y",				14);
define("COCKTAIL_FLOW_TIME",	3); // time for cocktail flow into cup in second
define("COCKTAIL_WAIT_TIME",	3);

function step_wait($sid)
{
	while((int)spc_request_dev($sid, "get state") > 1)
		usleep(1);
}

function spc_check_did($sid, $did)
{
	$resp = spc_request_csv($sid, 0, "get did");

	if($resp === false)
	{
		echo "spc_check_did: sid$sid - device not found\r\n";
		return false;
	}

	if($resp[1] != "40002405")
	{
		echo "spc_check_did: unknown device ", $resp[2], "\r\n";
		return false;
	}

	return true;
}

function cooktail_get_id($drink_name)
{
	global $names;
	
	for($i = 0; $i < 4; $i++)
	{
		if($names[$i] == $drink_name)
			return $i;
	}
	
	return -1;
}

function cocktail_get($recipe)
{
	global $pos;
	
	spc_request_dev(SID_X, "goto -sw1 20000 1000000");
	step_wait(SID_X);
	spc_request_dev(SID_X, "reset");
	
	for($step_motor = 0; $step_motor < 4; $step_motor++) // get component one by one
	{
		$amount = $recipe[$step_motor]; 
		if($amount > 0)
		{
			$ps = $pos[$step_motor];
			
			spc_request_dev(SID_X, "goto +$ps 20000 1000000");
			step_wait(SID_X);
			
			for($i = 1; $i <= $amount; $i++)
			{
				if($i != 1)
					sleep(COCKTAIL_WAIT_TIME);

				spc_request_dev(SID_Y, "goto -sw0 30000 1000000");
				step_wait(SID_Y);
				sleep(COCKTAIL_FLOW_TIME);
				spc_request_dev(SID_Y, "reset");
				spc_request_dev(SID_Y, "goto 90000 30000 1000000");
				step_wait(SID_Y);
			}
			
			spc_request_dev(SID_Y, "goto 140000 30000 1000000");
			step_wait(SID_Y);
		}
	}
}

function cocktail_init()
{
	spc_reset();
	spc_sync_baud(460800);

	if(!spc_check_did(SID_X, "40002405"))
		return;
	if(!spc_check_did(SID_Y, "40002405"))
		return;

	spc_request_dev(SID_X, "set vref stop 4");
	spc_request_dev(SID_X, "set vref drive 15");
	spc_request_dev(SID_X, "set mode 32");
	spc_request_dev(SID_X, "set rsnc 120 250");
	
	spc_request_dev(SID_Y, "set vref stop 4");
	spc_request_dev(SID_Y, "set vref drive 15");
	spc_request_dev(SID_Y, "set mode 32");
	spc_request_dev(SID_Y, "set rsnc 120 250");
	
	spc_request_dev(SID_X, "goto +sw0 20000 1000000");
	step_wait(SID_X);
	
	spc_request_dev(SID_Y, "goto -sw0 30000 1000000");
	step_wait(SID_Y);
	
	spc_request_dev(SID_X, "reset");
	spc_request_dev(SID_Y, "reset");
	
	spc_request_dev(SID_Y, "goto 140000 30000 1000000");
	step_wait(SID_Y);
	
	spc_request_dev(SID_X, "goto +sw0 20000 1000000");
	step_wait(SID_X);
	spc_request_dev(SID_X, "reset");
	spc_request_dev(SID_Y, "reset");
}

$host_name = "iot.eclipse.org";
$port = 1883;
$will = "";
$username = "";
$password = "";
$subc_topics = array(array("alexa/phpoc/cocktail", 0));
$topic = "";
$content = "";
$retain = 0;

$names = array("SUMMER_RAIN", "SCREW_DRIVER", "BLACK_RUSSIAN", "BLACK_RUSSIAN_2", "SWEET_MARTINI", "MARTINI");

$recipe_list = array(//MALIBU | VODKA | OGRANGE_JUICE | KAHLUA | GIN    ||
				array(   2,       1,          0,          0,      0), //||SUMMER_RAIN
				array(   0,       1,          2,          0,      0), //||SCREW_DRIVER
				array(   0,       1,          0,          2,      0), //||BLACK_RUSSIAN
				array(   0,       2,          0,          1,      0), //||BLACK_RUSSIAN_2
			);

// motor pos: //MALIBU | VODKA | OGRANGE_JUICE | KAHLUA  |  GIN  | 
$pos = array(   1000,    22000,      41000,       63000,   82000);

mqtt_setup(0, "PHPoC-MQTT Sub Example",  $host_name, $port);
mqtt_connect(true, $will, $username, $password);

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

cocktail_init();

while(1)
{
	if(mqtt_state() == MQTT_DISCONNECTED)
		while(mqtt_reconnect() == false);
	
	if(mqtt_loop($topic, $content, $retain))
	{
		if($retain == 1)
			continue;// a stale message

		echo $content;
		$drink_name = $content;

		$id = cooktail_get_id($drink_name);

		if($id >= 0)
		{
			$recipe = $recipe_list[$id];
			cocktail_get($recipe);
		}
		else
			echo "cocktail: The drink is not found\r\n";

		spc_request_dev(SID_X, "goto -sw1 20000 1000000");
		step_wait(SID_X);
		spc_request_dev(SID_X, "reset");
	}
}
?>

Lambda function (index.js)

JavaScript
'use strict';

var mqtt = require('mqtt');

// --------------- 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,
        response: speechletResponse,
    };
}

// --------------- 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.
    const sessionAttributes = {};
    const cardTitle = 'Welcome';
    const 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.
    const repromptText = "How can I help you?";
    const shouldEndSession = false;

    callback(sessionAttributes,
        buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}

function handleSessionEndRequest(callback) {
    const cardTitle = 'Session Ended';
    const speechOutput = 'Thank you for trying the Alexa Skills Kit sample. Have a nice day!';
    // Setting this to true ends the session and exits the skill.
    const shouldEndSession = true;

    callback({}, buildSpeechletResponse(cardTitle, speechOutput, null, shouldEndSession));
}

function createAttributes(cocktail) {
    return {
        cocktail: cocktail
    };
}

/**
 * Send data to mqtt and prepares the speech to reply to the user.
 */
function cocktailInSession(intent, session, callback) {
	const cardTitle = intent.name;
	const drinkNameRequest = intent.slots.drinkName;
	let repromptText = '';
	let sessionAttributes = {};
	const shouldEndSession = true;
	let speechOutput = "";

	var drinkName = "";
	var requestName = "";

	if(drinkNameRequest) 
	{
		requestName = drinkNameRequest.value;
		console.log("cocktail: " + requestName);

		drinkName = requestName.toUpperCase();
		drinkName = drinkName.replace(/ /g, "_");
		
		switch(drinkName)
		{
			case "SUMMER_RAIN":
			case "SCREW_DRIVER":
			case "BLACK_RUSSIAN":
			case "BLACK_RUSSIAN_2":
			case "SWEET_MARTINI":
			case "MARTINI":
				break;

			default:
				drinkName = "";
		}
	}
	
	if(drinkName !== "")
	{
		speechOutput = "Ok, I will tell cocktail machine make " + requestName + " for you";

		//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 any mqtt topic
				client.publish('alexa/phpoc/cocktail', drinkName)
				client.end()
				resolve('Done Sending');
			});
		});
		mqttpromise.then(
			function(data) {
				console.log('Function called succesfully:', data);
				sessionAttributes = createAttributes(data);
				repromptText = speechOutput;
				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));
	}
}

// --------------- Events -----------------------

/**
 * 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}");

    const intent = intentRequest.intent;
    const intentName = intentRequest.intent.name;

    // Dispatch to your skill's intent handlers
    if (intentName === 'cocktail') {
        cocktailInSession(intent, session, callback);
    } else if (intentName === 'AMAZON.HelpIntent') {
        getWelcomeResponse(callback);
    } else if (intentName === 'AMAZON.StopIntent' || intentName === 'AMAZON.CancelIntent') {
        handleSessionEndRequest(callback);
    } else {
        throw new Error('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
}


// --------------- Main handler -----------------------

// Route the incoming request based on type (LaunchRequest, IntentRequest,
// etc.) The JSON body of the request is provided in the event parameter.
exports.handler = (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);
    }
};

Credits

phpoc_man

phpoc_man

39 projects • 185 followers
hite000

hite000

0 projects • 3 followers

Comments