Published © MIT

Voice Activated STEM Learning Robot

A robot to engage students in the process of learning STEM.

AdvancedWork in progress18 hours1,836

Things used in this project

Hardware components

Arduino Mega 2560
Arduino Mega 2560
×1
Echo Dot
Amazon Alexa Echo Dot
×1
ESP8266 ESP-12E
Espressif ESP8266 ESP-12E
×1
Servos (Tower Pro MG996R)
×6
4WD Smart Robot Car Chassis Kit
×1
Transparent acrylic sheet A4 (Letter) 3 mm
×1
Transparent acrylic sheet A4 (Letter) 1 mm
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Transparent acrylic sheet A4 (Letter) 3 mm
×2
Transparent acrylic sheet A4 (Letter) 3 mm1
×2
Blue led matrix 8x8 with MAX7219
×3
Metalic m3 screws
×1
Nylon m3 screws, bolts and spacers
×1
Servo brackets
×1
Cable ties (lots of them)
×1
Breadboard 5x5
×1
Dual H-Bridge motor drivers L298
SparkFun Dual H-Bridge motor drivers L298
×1
Ultrasonic Sensor - HC-SR04 (Generic)
Ultrasonic Sensor - HC-SR04 (Generic)
×3
Battery 12V
×1
DC 12V to DC 5V 3A 15W Step Down
×1
Mosfet driver module IRF520
×2
USB Type A Female Breakout Board
×4
Magnets
×8

Software apps and online services

Arduino IDE
Arduino IDE
Alexa Skills Kit
Amazon Alexa Alexa Skills Kit
AWS Lambda
Amazon Web Services AWS Lambda
ThingSpeak API
ThingSpeak API

Hand tools and fabrication machines

Laser cutter (generic)
Laser cutter (generic)
Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)
Dril tool

Story

Read more

Schematics

Messaging architecture

This is how things talk to each other

Main components of the system

Acrylic cutting template for the head and arms

The acrylic cutting shapes are simple

Acrylic cutting template for the body

Code

index.js

JavaScript
Lambda function
var request = require("request")

// 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 !== "") {
    //     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) {
    // add any session init logic here
}

/**
 * Called when the user invokes the skill without specifying what they want.
 */
function onLaunch(launchRequest, session, callback) {
    getWelcomeResponse(callback)
}

/**
 * Called when the user specifies an intent for this skill.
 */
function onIntent(intentRequest, session, callback) {

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

    // dispatch custom intents to handlers here
    if (intentName == "SayYesIntent") {
        SayYesIntent(intent, session, callback)
    } else if (intentName == "SayNoIntent") {
        SayNoIntent(intent, session, callback)
    } else if (intentName == "DontKnowIntent") {
        DontKnowIntent(intent, session, callback)
    } else if (intentName == "RightArmIntent") {
        RightArmIntent(intent, session, callback)
    } else if (intentName == "ArmsUpIntent") {
        ArmsUpIntent(intent, session, callback)
    } else {
         throw "Invalid intent"
    }
}

function SayYesIntent(intent, session, callback) {
    var url = 'https://api.thingspeak.com/apps/thinghttp/send_request?api_key=BJMH4C70DLQHTK69';
    request.get(url, function(error, response, body) {
        var speechOutput = 'Yes';
        callback(session.attributes, buildSpeechletResponseWithoutCard(speechOutput, "", true));
    })
}

function SayNoIntent(intent, session, callback) {
    var url = 'https://api.thingspeak.com/apps/thinghttp/send_request?api_key=2J13HH7WY21FOO7H';
    request.get(url, function(error, response, body) {
        var speechOutput = 'No';
        callback(session.attributes, buildSpeechletResponseWithoutCard(speechOutput, "", true));
    })
}

function DontKnowIntent(intent, session, callback) {
    var url = 'https://api.thingspeak.com/apps/thinghttp/send_request?api_key=8IORNIDNHIA3DGQ2';
    request.get(url, function(error, response, body) {
        var speechOutput = 'I DontKnow';
        callback(session.attributes, buildSpeechletResponseWithoutCard(speechOutput, "", true));
    })
}

function ArmsUpIntent(intent, session, callback) {
    var url = 'https://api.thingspeak.com/apps/thinghttp/send_request?api_key=STQ8OUIQUUI6NV04';
    request.get(url, function(error, response, body) {
        var speechOutput = 'My arms are up, now';
        callback(session.attributes, buildSpeechletResponseWithoutCard(speechOutput, "", true));
    })
}

function RightArmIntent(intent, session, callback) {
    var url = 'https://api.thingspeak.com/apps/thinghttp/send_request?api_key=89S4TOKRBLNG156U';
    request.get(url, function(error, response, body) {
        var speechOutput = 'Right arm activated.';
        callback(session.attributes, buildSpeechletResponseWithoutCard(speechOutput, "", true));
    })
}


/**
 * Called when the user ends the session.
 * Is not called when the skill returns shouldEndSession=true.
 */
function onSessionEnded(sessionEndedRequest, session) {

}

// ------- Skill specific logic -------

function getWelcomeResponse(callback) {
    var speechOutput = "Welcome to MemoSteam! What you want me to do?"

    var reprompt = "Do you want me to do anything?"

    var header = "Memosteam"

    var shouldEndSession = false

    var sessionAttributes = {
        "speechOutput" : speechOutput,
        "repromptText" : reprompt
    }

    callback(sessionAttributes, buildSpeechletResponse(header, speechOutput, reprompt, shouldEndSession))

}


// ------- Helper functions to build responses for Alexa -------


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

function buildSpeechletResponseWithoutCard(output, repromptText, shouldEndSession) {
    return {
        outputSpeech: {
            type: "PlainText",
            text: output
        },
        reprompt: {
            outputSpeech: {
                type: "PlainText",
                text: repromptText
            }
        },
        shouldEndSession: shouldEndSession
    };
}

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

function capitalizeFirst(s) {
    return s.charAt(0).toUpperCase() + s.slice(1)
}

Credits

Comments