I designed a printer robot using the EV3 kit a couple of years ago, but it always had limited use due to the difficulty in sending information to it for printing.
When I discovered the possibility of an Alexa device being able to communicate with an EV3, I immediately wanted to explore the possibilities of using that communication to send data to the printer.
My main objective was to be able to print out the lists that I had stored on my Alexa device. My grocery list, todo list, etc. While I was at it, I expanded it to print gift tags to everyone on a specified list, draw the Mindstorms EV3 logo, and print out words dictated to Alexa. It can easily be expanded to print even more items.
You can see the printer in action in the demonstration video, and continue reading to learn how it works.
Getting StartedThere are two main components to this project: the physical printer, built using the LEGO MINDSTORMS EV3 kit; and an Alexa powered device. On the coding side of things, there is a program that runs on the EV3 and an Alexa Skill that manages the voice interaction with the user. The EV3 can communicate with the Alexa device over Bluetooth to send data back and forth.
If you would like to recreate this project, I would highly recommend first going through the Setup Guide and the 4 Missions described on the LEGO MINDSTORMS Voice Challenge page - https://www.hackster.io/alexagadgets
These tutorials will go through all the details of setting up the development environment for your EV3 brick (python code running on ev3dev), writing an Alexa skill (Node.js code using the Alexa Skill Kit), and configuring your EV3 and Alexa skill to communicate with each other.
All of the code for this project can be found in the following github repository: https://github.com/jasonallemann/printbot
Building the PrinterThe printer can be built using the pieces that come in the LEGO Mindstorms EV3 kit (set 31313). The only thing you will need to add is a pen and paper, no additional LEGO pieces are required.
I have found that gel pens work best, as they glide very smoothly over the paper and don't require a lot of force to deposit ink.
For paper, I'm using sheets of 8.5x11 cut in half, but the tray is slightly larger than that, so it can accommodate A4 sheets cut in half as well.
I have attached a pdf file containing step by step building instructions to this project, but you can also find it, along with more information about the printer here: https://jkbrickworks.com/telegraph-machine-and-printer
Once the printer is built, you'll need to connect the motors and color sensor to the correct ports, as follows.
Motor Ports
- Port A - the large motor that moves the pen from side to side
- Port B - the large motor driving the paper feed wheels
- Port C - the medium motor, which lifts the pen assembly up and down
Sensor Ports
- Port 4 - the color sensor underneath the printer
The printer uses three motors and a color sensor. The two large motors control the position of the pen on the page, the medium motor raises and lowers the pen during the print operations and the color sensor is used to detect when paper is in position for printing.
Whenever you ask Alexa to print something, it will first feed the paper in to ensure it covers the color sensor. When it is finished printing, it will feed the paper out.
When the printer prints something, it issues a sequence of instructions to each of the motors in order to raise or lower the pen, move it from side to side or move the paper in and out.
For example, below is a code snippet illustrating how the letter A is printed. The pen module (imported as the alias p in this case) encapsulates the low level motor controls, and provides a friendly set of functions for manipulating the pen.
def printA():
p.down( FULL )
p.lowerPen()
p.up( FULL )
p.right( FULL )
p.down( FULL )
p.up( HALF )
p.left( FULL )
p.raisePen()
p.right( FULL )
p.up( HALF )
If we take a look a couple of these pen functions, we can see how they directly control the corresponding motors.
def lowerPen():
MediumMotor( OUTPUT_C ).on_for_degrees( SpeedPercent(25), -180 )
def left( distance ):
global CurrentPositionX
LargeMotor( OUTPUT_A ).on_for_degrees( SpeedPercent( -PrintSpeedPercent ), distance )
CurrentPositionX -= distance
And here is how a rectangle with cut corners is printed.
def cutRectangle( x, y, width, height, corner ):
cornerX = corner
cornerY = corner * 3 / 4
p.moveTo( x + cornerX )
p.down( y )
p.lowerPen()
p.right( width - 2 * cornerX )
p.angle( cornerX, -cornerY )
p.down( height - 2 * cornerY )
p.angle( -cornerX, -cornerY )
p.left( width - 2 * cornerX )
p.angle( -cornerX, cornerY )
p.up( height - 2 * cornerY )
p.angle( cornerX, cornerY )
p.raisePen()
p.carriageReturn()
Programming the PrinterThe code for the printer is written in Python and runs on ev3dev, an open source operating system that can run on the MINDSTORMS EV3 brick. The code is comprised of several files, which you can find in printbotEV3 folder in the github repository.
The file printer.py is the main program to run for the printer bot. It derives a class from the AlexaGadget class, which implements the code for responding to and sending messages between the EV3 and the Alexa device.
Below is a code snippet showing how the EV3 will process the directive for printing a list. You can see that the data associated with each directive is passed as a JSON formatted payload.
You can see that once the payload has been processed, the pen module is instructed to start printing (this will feed the paper in if necessary), the draw module is instructed to print each list item and the pen is instructed to finish printing (which will feed the paper out).
class MindstormsGadget(AlexaGadget):
# Handles the Custom.Mindstorms.Gadget control directive.
def on_custom_mindstorms_gadget_control( self, directive ):
try:
payload = json.loads( directive.payload.decode( "utf-8" ) )
controlType = payload["type"]
if controlType == "printList":
itemList = json.loads(payload["items"])
if not settings.getDialogMode():
p.startPrinting()
for item in itemList:
draw.listItem( item )
p.finishPrinting()
The
draw module code for printing a list item is as follows. It calls the text module to print the letter O followed by a space, which acts as a check box, then prints the text of the list. It finishes by moving the pen to the beginning of the page and perform a line feed.
def listItem( item ):
t.printText( "o " )
t.printText( item )
p.carriageReturn()
t.lineFeed()
The python files in the printBotEV3 folder contain all the code for operating the printer and drawing different items as follows.
- printer.py - The main program to be run for the print bot.
- offlineTests.py - Can be run as a separate program to test printer functionality independent of Alexa communication.
- pen.py - manages the position of the pen and paper. This includes functions for raising and lowering the pen and moving it around on the page. Also responsible for feeding the paper in and out.
- settings.py - manages the printer settings, which include the text size and dialog mode. These are described in the Text Size and Dialog Mode commands below.
- text.py - contains all the code for printing words and characters.
- draw.py - code for printing compound objects and graphical items, including rectangles, line art, gift tags and the MINDSTORMS logo.
The code for the skill is written in Node.js and can be found in the alexa-skill-nodejs folder in the github repository. This folder also contains the dialog model for the skill, in the file model.json.
These files can be copied into your skill once it has been created in the Alexa Developer Console. Again, it is recommended to go through the Setup Guide and Mission tutorials linked in the Getting Started section if you are unfamiliar with creating a skill.
The Alexa skill also contains a model for the dialog interaction with the user, which is described in JSON format. For example, here is the dialog definition for printing a list. It requires an utterance with the word list in it, as well as the name of the list.
{
"name": "PrintListIntent",
"slots": [
{
"name": "ListName",
"type": "ListType",
"samples": [
"{ListName}",
"the {ListName} list",
"my {ListName} list",
"{ListName} list"
]
}
],
"samples": [
"my {ListName} list",
"{ListName} list",
"the {ListName} list"
]
},
Below is a code snippet showing how the directive for printing a list is constructed by the Alexa skill and sent to the EV3.
let listName = Alexa.getSlotValue(handlerInput.requestEnvelope, 'ListName');
var listId = await getListNameSlotListId( handlerInput );
var items = null;
if( !listId )
{
return responseBuilder
.speak( `I could not find a list named ${listName}.` )
.reprompt( PrintPrompt )
.getResponse();
}
else
{
items = await getListItems( handlerInput, listId );
}
if( !items )
{
const permissions = ['read::alexa:household:list'];
return handlerInput.responseBuilder
.speak( 'Alexa List permissions are missing.' )
.withAskForPermissionsConsentCard( permissions )
.getResponse();
}
else if( items.length === 0 )
{
return responseBuilder
.speak( `The ${listName} list is empty.` )
.reprompt( PrintPrompt )
.getResponse();
}
var listItems = [];
for( let i = 0; i < items.length; i += 1 )
{
const itemValue = items[i].value;
listItems.push( itemValue );
}
// Build the directive for the EV3 to print the list items.
const directive = Util.build(endpointId, NAMESPACE, NAME_CONTROL,
{ type: 'printList', items: JSON.stringify(listItems) } );
return handlerInput.responseBuilder
.speak( `Printing the list items.` )
.addDirective( directive )
.getResponse();
Skill List PermissionsIn order to print your lists, you will need to give the skill permission to read your lists. Once your skill up and running, access the skill settings through the Amazon Alexa mobile app. The skill will request List Read Access, which you will need to grant.
Issuing Print CommandsOnce you have the printer.py program running on the EV3, you can launch the printer skill by saying "Alexa, open print bot". This invocation name is stored in the model.json file as shown below, so you can change it to whatever you want.
...
"languageModel": {
"invocationName": "print bot",
"intents": [
....
Once the skill is activated, Alexa will ask you what you would like to print, and all you have to do is tell her. In addition to the printing commands, there are commands for getting and setting the text size, calibrating the printer and performing diagnostics that can be issued. All of these commands are all outlined below.
Calibrating the PrinterExample: "Calibrate the printer."
The first thing you will probably want to do is calibrate the height of the pen, so that it prints correctly. This should be done whenever using the printer for the first time after inserting or adjusting the pen.
Issuing this voice command will put the printer in calibration mode. When in calibration mode, the pen will move back and forth repeatedly until the center button on the EV3 unit is pressed. While in this mode, you can use the up and down buttons on the EV3 unit to move the pen up and down until it is depositing ink on the paper.
For best results in calibrating the printer, follow these steps.
- Insert the pen into the pen holder. The pen should be positioned so that when the pen holder assembly is at its lowest position (the default position in the instructions), the tip of the pen is approximately just below the level of the paper.
- Manually slide the pen holder horizontally until the pen is roughly centered in the printer (if it isn’t already).
- Manually set the pen holder assembly to its highest possible position. To easily do this, disengage the 24 tooth gear from the worm gear by sliding it to the side along its axle. Raise the pen assembly, then slide the 24 tooth gear back to re-engage the worm gear.
- Ask Alexa to calibrate the printer. The pen will lower and start moving continuously from side to side above the page.
- Press the down button on the EV3 to lower the pen. Repeat pressing the down button until the pen lowers enough to touch the paper and start depositing ink.
- Press the center button on the EV3 to indicate that the pen is at the desired printing height. After its next full pass, the pen holder assembly will return to the raised position and the printer will be ready to print.
Example: "Print the grocery list."
The printer can print the items on any list you have on Alexa. Either the two built in lists (Shopping or To-Do) or any custom named list. Each item on the list is preceded with a check box.
Examples:
"Gift tags to everyone on the Christmas list."
"Gift tags from Jason to the Christmas list."
This will print a gift tag for each name on a list from the specified sender. If you don't specify either of the required parameters, Alexa will prompt you for them.
Example: "Print the word 'Hello'."
You can print out a word by asking to print it.
Example: "Draw the Mindstorms Logo"
This will have the printer draw the Mindstorms EV3 logo.
Examples:
"Set the text size to 6 millimeters."
"What is the current text size?"
You can ask Alexa to set and get the size of the text. The text size can be any value from 4 mm to 8 mm. The default value is 5 mm.
The text size, along with the dialog mode setting (see below), is stored in the file printerSettings.txt on the EV3, so it will be remembered across printing sessions.
The printerSettings.txt file is in JSON format. The internal text size is defined in degrees of rotation of the motor feeding the paper. 8 degrees roughly translates to 1 millimeter vertically, so the default of 40 degrees roughly translates to text that is 5 mm tall.
{ "textSize":40, "dialogMode":false }
Activating Dialog ModeExamples:
"Turn on dialog mode."
"Turn off dialog mode."
These commands will put the printer into and out of dialog mode. When in dialog mode, you can issue commands to the printer to test the dialog interaction. Alexa will respond appropriately to each command as if the printer was operating normally, it just won't actually print anything. This is useful for testing your dialog logic and slot validation.
Comments