ECE 4180 Alexa Controlled Mini Basketball
Introduction
The Amazon Echo is a great tool for accessible IoT devices, as it can serve as a Voice User Interface(VUI) for a number of creative applications! In this project an Alexa skill was developed to communicate to a server. An internet-connected mbed also communicates with that server, and upon indication from the server, starts a game of tabletop basketball!
This project was made by Cameron Allotey and Taylor Dingler
Electronics & Technologies
mbed
The mbed is responsible for running the actual game upon receiving data from server. It also sends the final score to the server after the game. Using the RTOS library, the mbed uses I/O devices to create effects for the game. The roles of specific I/O devices are detailed below. The mbed code to control the I/O devices and communicate using Ethernet is featured in the Appendix. Also note that this project uses mbed OS 5, which has a slightly different implementation of RTOS and has several built-in network libraries.
Amazon Echo Dot
An Alexa skill was developed to start the game on the mbed and report the score to the user. The skill communicates with an AWS Lambda function to send HTTP requests to a backend server when the user requests to start a game. Follow the guide here to create a custom Alexa skill. https://developer.amazon.com/en-US/alexa/alexa-skills-kit/get-deeper/custom-skills. The Lambda function showing the necessary intents is featured in the Appendix.
Backend Server
A locally hosted NodeJs server is made publicly reachable using ngrok, a service that enables a server running locally to be accessed outside of the local network.
ngrok wrote:
How it works You download and run a program on your machine and provide it the port of a network service, usually a web server.
It connects to the ngrok cloud service which accepts traffic on a public address and relays that traffic through to the ngrok process running on your machine and then on to the local address you specified. Postman was used to develop the endpoints prior to implementation.
Read more at https://ngrok.com/
The server uses simple GET and POST requests to manage the status of the mbed system using only Alexa voice commands.
HC-SR04 sonar distance sensor
The distance sensor was used to track the player's scoring, using a change in detected distance as an indicator of a made shot.
Ethernet
Ethernet was used to create a network bridge allowing for the mbed to access the internet. This is imperative in the process of communicating with the backend server.
Other I/O Devices
The project also utlized an RGB LED, speaker, amplifier, and LCD screen to enhance the presentation and game experience
- https://os.mbed.com/users/4180_1/notebook/rgb-leds/
- https://os.mbed.com/users/4180_1/notebook/using-a-speaker-for-audio-output/
- https://os.mbed.com/users/4180_1/notebook/ulcd-144-g2-128-by-128-color-lcd/
Connections
The wiring for the key project components is shown below
| mbed | RJ45 Ethernet Port | uLCD | HC-SR04 Sonar Sensor | External 5V |
|---|---|---|---|---|
| p9 | trig | |||
| p10 | echo | |||
| RU- | p8 | |||
| RU+ | p7 | |||
| TU- | p2 | |||
| TU+ | p1 | |||
| p30 | RES | |||
| p28 | TX | |||
| p27 | RX | |||
| +5V | VCC | x |
Images & Videos
Project Progress in Images:
Demo Video:
Presentation Video:
Possible Improvements
- Inclusion of multiple game modes
- Make high score carry over between sessions
- Use Alexa speaker to play music during game, this would also allow for a single session to be used for an unlimited number of games. This addition requires significant knowledge of Alexa Skills development.
- Improve quality of physical device and order supplemental parts for a more professional final product
- Wifi (either Huzzah or Pi) rather than Ethernet
Appendix
mbed Code
Import program4180miniBball
ece 4180 final project
Server Code
index.js
const WebSocket = require('ws');
var express = require('express');
var app = express();
var port = process.env.PORT || 8000
app.use(express.static('public'));
var bodyParser = require('body-parser')
//app.use(bodyParser.urlencoded({ extended: true }));
app.listen(port);
var game = 0;
app.route('/')
.get(function (req, res) {
res.send(String(game));
game = 0;
})
.post(function (req, res) {
game = 1;
res.end();
})
var score = 0;
app.route('/game')
.get(function (req, res) {
res.send(String(score));
})
.post(function (req, res) {
score = req.param('score')
console.log(score)
res.end();
})
Alexa Skill Lambda Function Code
lambda_function.py
# -*- coding: utf-8 -*-
# This sample demonstrates handling intents from an Alexa skill using the Alexa Skills Kit SDK for Python.
# Please visit https://alexa.design/cookbook for additional examples on implementing slots, dialog management,
# session persistence, api calls, and more.
# This sample is built using the handler classes approach in skill builder.
import logging
import ask_sdk_core.utils as ask_utils
# importing the requests library
import requests
from ask_sdk_core.skill_builder import SkillBuilder
from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.dispatch_components import AbstractExceptionHandler
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model import Response
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# api-endpoint
URL = "http://bf360b94.ngrok.io/"
class LaunchRequestHandler(AbstractRequestHandler):
"""Handler for Skill Launch."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_request_type("LaunchRequest")(handler_input)
def handle(self, handler_input):
# type: (HandlerInput) -> Response
speak_output = "Welcome to E C E 41 80 Tabletop Basketball. To begin, say start game. To get your previous score, say score."
return (
handler_input.response_builder
.speak(speak_output)
.ask(speak_output)
.response
)
class StartGameIntentHandler(AbstractRequestHandler):
"""Handler for Start Game Intent."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_intent_name("StartGameIntent")(handler_input)
def handle(self, handler_input):
# type: (HandlerInput) -> Response
speak_output = "When the lights turn on, score as many points as possible!"
# sending get request and saving the response as response object
r = requests.post(url = URL)
return (
handler_input.response_builder
.speak(speak_output)
# .ask("either start another game or say exit to quit")
.response
)
class GetScoreIntentHandler(AbstractRequestHandler):
"""Handler for Start Game Intent."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_intent_name("GetScoreIntent")(handler_input)
def handle(self, handler_input):
# sending get request and saving the response as response object
r = requests.get(url = URL + 'game/')
x = int(r.content)
txt = "You Scored {} Points Last Round!"
speak_output = txt.format(x)
return (
handler_input.response_builder
.speak(speak_output)
# .ask("either start another game or say exit to quit")
.response
)
class HelpIntentHandler(AbstractRequestHandler):
"""Handler for Help Intent."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_intent_name("AMAZON.HelpIntent")(handler_input)
def handle(self, handler_input):
# type: (HandlerInput) -> Response
speak_output = "You can say play game to start a game."
return (
handler_input.response_builder
.speak(speak_output)
.ask(speak_output)
.response
)
class CancelOrStopIntentHandler(AbstractRequestHandler):
"""Single handler for Cancel and Stop Intent."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return (ask_utils.is_intent_name("AMAZON.CancelIntent")(handler_input) or
ask_utils.is_intent_name("AMAZON.StopIntent")(handler_input))
def handle(self, handler_input):
# type: (HandlerInput) -> Response
speak_output = "Goodbye!"
return (
handler_input.response_builder
.speak(speak_output)
.response
)
class SessionEndedRequestHandler(AbstractRequestHandler):
"""Handler for Session End."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_request_type("SessionEndedRequest")(handler_input)
def handle(self, handler_input):
# type: (HandlerInput) -> Response
speech_output = "Thanks for playing!"
# Any cleanup logic goes here.
return handler_input.response_builder.response
class IntentReflectorHandler(AbstractRequestHandler):
"""The intent reflector is used for interaction model testing and debugging.
It will simply repeat the intent the user said. You can create custom handlers
for your intents by defining them above, then also adding them to the request
handler chain below.
"""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_request_type("IntentRequest")(handler_input)
def handle(self, handler_input):
# type: (HandlerInput) -> Response
intent_name = ask_utils.get_intent_name(handler_input)
speak_output = "You just triggered " + intent_name + "."
return (
handler_input.response_builder
.speak(speak_output)
# .ask("add a reprompt if you want to keep the session open for the user to respond")
.response
)
class CatchAllExceptionHandler(AbstractExceptionHandler):
"""Generic error handling to capture any syntax or routing errors. If you receive an error
stating the request handler chain is not found, you have not implemented a handler for
the intent being invoked or included it in the skill builder below.
"""
def can_handle(self, handler_input, exception):
# type: (HandlerInput, Exception) -> bool
return True
def handle(self, handler_input, exception):
# type: (HandlerInput, Exception) -> Response
logger.error(exception, exc_info=True)
speak_output = "Sorry, I had trouble doing what you asked. Please try again."
return (
handler_input.response_builder
.speak(speak_output)
.ask(speak_output)
.response
)
# The SkillBuilder object 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.
sb = SkillBuilder()
sb.add_request_handler(LaunchRequestHandler())
sb.add_request_handler(StartGameIntentHandler())
sb.add_request_handler(GetScoreIntentHandler())
sb.add_request_handler(HelpIntentHandler())
sb.add_request_handler(CancelOrStopIntentHandler())
sb.add_request_handler(SessionEndedRequestHandler())
sb.add_request_handler(IntentReflectorHandler()) # make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers
sb.add_exception_handler(CatchAllExceptionHandler())
lambda_handler = sb.lambda_handler()
Please log in to post comments.






