Mi FuFa Fu
Created November 16, 2019

Table Service Robot

This robot helps you to handover spices during lunch and diner.

BeginnerShowcase (no instructions)40

Things used in this project

Story

Read more

Code

EV3 runtime: table_service_v1.py

Python
runtime script to enable the EV3
import time
import logging
import json
import random
import threading

from enum import Enum
from agt import AlexaGadget

from ev3dev2.led import Leds
from ev3dev2.sound import Sound
from ev3dev2.motor import OUTPUT_A, OUTPUT_B, OUTPUT_C, MoveTank, SpeedPercent, MediumMotor, LargeMotor

# Set the logging level to INFO to see messages from AlexaGadget
logging.basicConfig(level=logging.INFO)


class Direction(Enum):
    """
    The list of directional commands and their variations.
    These variations correspond to the skill slot values.
    """
    FORWARD = ['forward', 'forwards', 'go forward']
    BACKWARD = ['back', 'backward', 'backwards', 'go backward']
    LEFT = ['left', 'go left']
    RIGHT = ['right', 'go right']
    STOP = ['stop', 'brake']


class MindstormsGadget(AlexaGadget):
    """
    A Mindstorms gadget that performs movement based on voice commands.
    Two types of commands are supported, directional movement and preset.
    """

    def __init__(self):
        """
        Performs Alexa Gadget initialization routines and ev3dev resource allocation.
        """
        super().__init__()

        # Gadget state
        self.patrol_mode = False

        # Ev3dev initialization
        self.leds = Leds()
        self.sound = Sound()
        self.drive = MoveTank(OUTPUT_A,OUTPUT_A)  # Band """
        self.deliver = MoveTank(OUTPUT_B,OUTPUT_B) # Deliver """

        # Start threads
        #threading.Thread(target=self._patrol_thread, daemon=True).start()

    def on_connected(self, device_addr):
        """
        Gadget connected to the paired Echo device.
        :param device_addr: the address of the device we connected to
        """
        self.leds.set_color("LEFT", "GREEN")
        self.leds.set_color("RIGHT", "GREEN")
        print("{} connected to Echo device".format(self.friendly_name))

    def on_disconnected(self, device_addr):
        """
        Gadget disconnected from the paired Echo device.
        :param device_addr: the address of the device we disconnected from
        """
        self.leds.set_color("LEFT", "BLACK")
        self.leds.set_color("RIGHT", "BLACK")
        print("{} disconnected from Echo device".format(self.friendly_name))

    def on_custom_mindstorms_gadget_control(self, directive):
        """
        Handles the Custom.Mindstorms.Gadget control directive.
        :param directive: the custom directive with the matching namespace and name
        """
        try:
            payload = json.loads(directive.payload.decode("utf-8"))
            print("Control payload: {}".format(payload))
            control_type = payload["type"]
            if control_type == "move":
                # Expected params: [direction, duration, speed]
                self._move(payload["direction"], int(payload["duration"]), int(payload["speed"]))
            if control_type == "deliver":
                # Expected params: [direction, duration, speed]
                self._deliver(payload["direction"], int(payload["duration"]), int(payload["speed"]),payload["spice"])
        
        except KeyError:
            print("Missing expected parameters: {}".format(directive))

    def _move(self, direction, duration, speed, is_blocking=False):
        """
        Handles move commands from the directive.
        Right and left movement can under or over turn depending on the surface type.
        :param direction: the move direction
        :param duration: the duration in seconds
        :param speed: the speed percentage as an integer
        :param is_blocking: if set, motor run until duration expired before accepting another command
        """
        print("Move command: ({}, {}, {}, {})".format(direction, speed, duration, is_blocking))
        if direction in Direction.FORWARD.value:
            self.drive.on_for_seconds(SpeedPercent(speed), SpeedPercent(speed), duration, block=is_blocking)
           # self.drive.run_timed(speed_sp=speed, time_sp=duration)  
           # self.drive.run_timed(speed_sp=750, time_sp=2500)
        if direction in Direction.BACKWARD.value:
           self.drive.on_for_seconds(SpeedPercent(-speed), SpeedPercent(-speed), duration, block=is_blocking)
           # self.drive.run_timed(speed_sp=-speed, time_sp=duration)  
  
        if direction in Direction.STOP.value:
            self.drive.off()
            self.patrol_mode = False

    def _deliver(self, direction, duration, speed, spice, is_blocking=False):

        #print("Move command: ({}, {}, {}, {})".format(direction, speed, duration, is_blocking))

        # Define the speed and duration for band and deliver motor depending on the spice
        if (spice == 'salt'):
            SpeedBand = 16
            Durationband = 2
            SpeedDeliver = 15
            DurationDeliver = 3

        if (spice == 'pepper'):
            SpeedBand = 0
            Durationband = 0
            SpeedDeliver = 15
            DurationDeliver = 3

        if spice == 'lemon':
            SpeedBand = -16
            Durationband = 2
            SpeedDeliver = 15
            DurationDeliver = 3


        if spice != '':
            self.leds.set_color("LEFT", "ORANGE")
            self.leds.set_color("RIGHT", "ORANGE")

            #Band position to correct spice
            self.drive.on_for_seconds(SpeedPercent(-SpeedBand), SpeedPercent(-SpeedBand), Durationband, block=is_blocking)   
            time.sleep(4)

            # deliver spice
            self.deliver.on_for_seconds(SpeedPercent(SpeedDeliver), SpeedPercent(SpeedDeliver), DurationDeliver, block=is_blocking)
            time.sleep(4)

            # # deliver motor to init
            self.deliver.on_for_seconds(SpeedPercent(-SpeedDeliver), SpeedPercent(-SpeedDeliver), DurationDeliver, block=is_blocking)
            time.sleep(4)

            #Band back to init
            self.drive.on_for_seconds(SpeedPercent(SpeedBand), SpeedPercent(SpeedBand), Durationband, block=is_blocking)  
            time.sleep(4)

            # make some noise and blinking after the delivery 
            self.sound.play_song((('E5', 'q'), ('E5', 'q')))
            self.sound.speak('Enjoy your meal')

            self.leds.set_color("LEFT", "GREEN")
            self.leds.set_color("RIGHT", "GREEN")

    

if __name__ == '__main__':

    # Startup sequence
    gadget = MindstormsGadget()
    gadget.sound.play_song((('C4', 'e'), ('D4', 'e'), ('E5', 'q')))
    gadget.leds.set_color("LEFT", "GREEN")
    gadget.leds.set_color("RIGHT", "GREEN")

    # Gadget main entry point
    gadget.main()

    # Shutdown sequence
    gadget.sound.play_song((('E5', 'e'), ('C4', 'e')))
    gadget.leds.set_color("LEFT", "BLACK")
    gadget.leds.set_color("RIGHT", "BLACK")

EV3 runtime: table_service_v1.ini

Python
confoig file for runtime script
# Copyright 2019 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
# 
# You may not use this file except in compliance with the terms and conditions 
# set forth in the accompanying LICENSE.TXT file.
#
# THESE MATERIALS ARE PROVIDED ON AN "AS IS" BASIS. AMAZON SPECIFICALLY DISCLAIMS, WITH 
# RESPECT TO THESE MATERIALS, ALL WARRANTIES, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING 
# THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.

[GadgetSettings]
amazonId = your key
alexaGadgetSecret =  your secret


[GadgetCapabilities]
Custom.Mindstorms.Gadget = 1.0

Alexa Skill lambda file: index.js

JavaScript
Main program of the alexa skill
const Alexa = require('ask-sdk-core');
const Util = require('./util');
const Common = require('./common');

// The namespace of the custom directive to be sent by this skill
const NAMESPACE = 'Custom.Mindstorms.Gadget';

// The name of the custom directive to be sent this skill
const NAME_CONTROL = 'control';

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
    },
    handle: async function(handlerInput) {

        let request = handlerInput.requestEnvelope;
        let { apiEndpoint, apiAccessToken } = request.context.System;
        let apiResponse = await Util.getConnectedEndpoints(apiEndpoint, apiAccessToken);
        if ((apiResponse.endpoints || []).length === 0) {
            return handlerInput.responseBuilder
            .speak(`I couldn't find an EV3 Brick connected to this Echo device. Please check to make sure your EV3 Brick is connected, and try again.`)
            .getResponse();
        }

        // Store the gadget endpointId to be used in this skill session
        let endpointId = apiResponse.endpoints[0].endpointId || [];
        Util.putSessionAttribute(handlerInput, 'endpointId', endpointId);

        return handlerInput.responseBuilder
            .speak("Welcome to table service, you can ask me for spices and I will deliver.")
            .reprompt("Which spice do you want?")
            .getResponse();
    }
};


// Move band to test the function

const MoveIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'MoveIntent';
    },
    handle: function (handlerInput) {
        const request = handlerInput.requestEnvelope;
        const direction = Alexa.getSlotValue(request, 'Direction');

        // Duration is optional, use default if not available
        const duration = Alexa.getSlotValue(request, 'Duration') || "2";

        // Get data from session attribute
        const attributesManager = handlerInput.attributesManager;
        const speed = attributesManager.getSessionAttributes().speed || "50";
        const endpointId = attributesManager.getSessionAttributes().endpointId || [];

        // Construct the directive with the payload containing the move parameters
        const directive = Util.build(endpointId, NAMESPACE, NAME_CONTROL,
            {
                type: 'move',
                direction: direction,
                duration: duration,
                speed: speed
            });

        const speechOutput = (direction === "brake")
            ?  "Applying brake"
            : `${direction} ${duration} seconds at ${speed} percent speed`;

        return handlerInput.responseBuilder
            .speak(speechOutput)
            .reprompt("awaiting command")
            .addDirective(directive)
            .getResponse();
    }
};

// Hnadling of the band and deliver the spice
const GetMeSpiceHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'DeliverSpice';
    },
    handle: function (handlerInput) {
        const request = handlerInput.requestEnvelope;
        const spicename = Alexa.getSlotValue(request, 'Spice');

        // Get data from session attribute
        const attributesManager = handlerInput.attributesManager;
        //const speed = attributesManager.getSessionAttributes().speed || "50";
        const speed = "5";
        const endpointId = attributesManager.getSessionAttributes().endpointId || [];
     
        
        //Default Parameter not used:
        let directionband  = "LEFT";
        let durationband = 5;
   
          const directive = Util.build(endpointId, NAMESPACE, NAME_CONTROL,
            {
                type: 'deliver',
                direction: directionband,
                duration: durationband,
                speed: speed,
                spice: spicename
                
            });    
            
       
            
        const speechOutput = spicename + "will be delivered";

        return handlerInput.responseBuilder
            .speak(speechOutput)
            .reprompt("too much spices are unhealthy")
            .addDirective(directive)
            .getResponse();
    }
};

// The SkillBuilder acts as the entry point for your skill, routing all request and response
// payloads to the handlers above. Make sure any new handlers or interceptors you've
// defined are included below. The order matters - they're processed top to bottom.
exports.handler = Alexa.SkillBuilders.custom()
    .addRequestHandlers(
        LaunchRequestHandler,
        GetMeSpiceHandler,
        MoveIntentHandler,
        Common.HelpIntentHandler,
        Common.CancelAndStopIntentHandler,
        Common.SessionEndedRequestHandler,
        Common.IntentReflectorHandler, // make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
    )
    .addRequestInterceptors(Common.RequestInterceptor)
    .addErrorHandlers(
        Common.ErrorHandler,
    )
    .lambda();

Github of Table service

all coding of table service skill

Credits

Mi Fu

Mi Fu

1 project • 1 follower
Fa Fu

Fa Fu

0 projects • 0 followers

Comments