The story of the evolution of Chess will take us back to 6 AD. Chess is believed to be derived from the Indian game Chaturanga sometime before the 7th century. In 7th Century AD, it was adopted as Satranj in Sassanid Persia. Several variations of chess evolved in Persia. In these ancient forms of chess, the Queen would only move 1 step in any direction. The bishop could also move only 1 step diagonally. By 9 AD chess traveled to Europe through the Byzantine transmission.
The next big change in chess was around 1485 when the game of modern chess became popular and has almost the same rules. At some point, an unknown inventor in Italy or Spain bestowed the weakest non-pawn pieces on the board Bishop and Queen with new powers as in modern chess. The game became faster and much more intriguing. Though some variants in China and Japan had such pieces centuries earlier. It was a sexist society, so the excuse that Queen was now so much powerful than the King was explained with that she used her powers to protect her husband. The modern chess soon popularised as Chess of the Mad Queen. From the recent statistics around 600 million, active players are around the globe. And still, many passive players to be counted including me.
My grandfather was a very vigorous chess player and was always my opponent. He was a man of strategies and mystical moves. But due to his age-related problems and Parkinson's disease, he's not able to make a good move and he was retarding from the game. Then I thought of my newly bought Lego Mindstorms EV3 robotics kit and I thought of making a virtual hand for my grandpa. I searched for any chess playing robots with EV3 and I found Charlie. Unfortunately, it was not made up of EV3 kit and also I lacked many parts to build that. So I decided to build a robot of my own. So I started to think of another method. And I found it.
Hypothetical DesignNowadays almost all the chess boards include a metal sheet and all the pieces have a small magnet in them to avoid the fall of pieces due to external perturbations. I was interested in exploiting that feature of chess boards. So I designed a customized chessboard of my own. And I can use a magnet to move the pieces over the chessboard flawlessly.
XY Plotter
The hypothesis includes an XY plotter that can designate any position in the chessboard using an (x, y) co-ordinate. In a chessboard, the cells are designated using X co-ordinates using alphabets (A, B, C, D, E, F, G, H) and Y co-ordinates using numbers (1, 2, 3, 4, 5, 6, 7, 8). So the combination of this XY co-ordinates can uniquely identify a cell in a chessboard.
Magnet
A magnet is connected to the XY plotter which is activated only when needed to displace a piece from one position to another and is kept deactivated after movement.
Movements
We are designating the cell A1 as the origin. All other moves are based on the distance from the origin. We have four types of available moves,
1. Diagonal Move
2. Vertical Move
3. Horizontal Move
4. Offset Move
Let's try to simulate a move from C1 to D3
1. Magnet is moved to C1 using a diagonal move from A1
2. The magnet is turned on and the piece is moved to the corner of the cell C1 using Offset move.
3. The piece is moved vertically along the line as shown
4. The piece is then moved horizontally along the line as shown
5. Now the piece is in the corner of the target D3 cell. The piece is taken back to the center using OffsetNull move.
6. Now the piece is in the target position. The magnet is turned off and is taken back to A1 using a diagonal move.
Building InstructionsThe full build instructions can be seen in the Github repo. The final build will be look like this.
The connections from the ev3 brick to the motors are:
X axis Motor - Port B
Y axis Motor - Port D
Z axis Motor - Port A
We are only using one Lego mindstrorms ev3 31313 kit for building the whole project, so we have only 2 Large motors and one medium motor for the entire movements. Actually Gear Racks are very helpful in making this mechanism easier, unfortunately this kit doesn't contain the Gear Racks.
So I made three ideologies to move in X, Y and Z axis which you can seen in the below images. These are the key concepts in the brick building.
Here I used two gears for making precise movement in the X-Direction.
The Y-axis motor is centralized, having both sides axles fitted on tyres. Why we moving like that is ? because we have only one motor for each axis.
According to the rotations of the medium motor, the magnet will move upwards or downwards. The magnet(Neodymium) is attached on the brick with the double sided sticker. In the next step, let's build the case for this lego robot.
Building the CaseHere I actually made a customized chess board and case for the Caissa. 40x40 cm acrylic sheet is cutoff and a sticker of chess board is pasted on it. I made a larger chess board and will choose smaller magnetic chess pieces for smooth movement.
This was supported by the square pillars(Aluminium) having a bush attached on it's top.
These pillars should have at least, minimum length of chess robot. The whole things are attached in the plywood(Base). For restricting the Y-axis motion in 1D you can use the wooden strips(optional).
The case Building is over. Let's have a look on the software section.
SoftwareAlexa SkillDo the preliminary set up by doing the following steps. The setup guide is given here
- Install ev3dev for programming your EV3 Brick
- Install Visual Studio Code for editing code
- Install Alexa Gadgets Python Software on your EV3 Brick
Registering EV3 brick as Alexa Gadget
In order for your EV3 Brick to work with Alexa and your Echo device as an Alexa Gadget, you will need to register your gadget in the Alexa Developer Console. Registering your gadget ties your unique gadget to your account, and is required in order for your creation to work properly with Alexa. An Alexa Gadget is an Alexa connected accessory that interacts with compatible Amazon Echo devices over Bluetooth.
Hereit describes how it is done. Amazon ID and Alexa Gadget Secret should be noted up because it uniquely identifies our gadget. The following figure shows how a gadget interacts with an Echo device, and where the Alexa Gadgets interfaces fit in.
Schematics
If you need more knowledge about this, just have a look here. This documentation provides what exactly is
- Detail description of how gadget interact with Alexa
- Echo devices that support gadgets
- Things to be in mind, when creating Alexa gadgets.
When you've successfully registered your Alexa Gadget. Let's prepare the ev3 brick
Preparingev3 brick
Your EV3 Brick will be connecting to your Echo device using Bluetooth, which you'll need to enable on your Brick in order for the sample code to work correctly: For more instructions look here.
Note: If Bluetooth is “Not Available”, POWER OFF your Brick, power it back on, and try turning on Bluetooth again. If not yet available try re-flashing you OS.
Once Bluetooth is enabled, you’re ready to work with some code!
At this point, you can switch over to the Visual Code Workspace you created during the setup instructions. The Explorer panel should display the mission folders that are on your computer.
Registration and Settings
Within the caissa
folder, you will see an INI file and a Python file. Open up the caissa.ini
file, which includes the following configuration details:
[GadgetSettings]
amazonId = YOUR_GADGET_AMAZON_ID
alexaGadgetSecret = YOUR_GADGET_SECRET
[GadgetCapabilities]
Alexa.Gadget.StateListener = 1.0 - wakeword
The INI (or initialization) file defines parameters for how your EV3 Brick should work as a gadget:
Gadget Settings: Specifies the Amazon ID and Alexa Gadget Secret that you received when you created your gadget in the Amazon Developer Console. It authenticates your EV3 Brick and allows it to connect to your Echo device and Alexa.
Howto set upecho dot?
Tip: Before setup, download or update the Alexa app in your mobile device's app store.
- Open the Alexa app
- Go to the settings menu
- Select Add Device.
- Select Amazon Echo, and then Echo Dot.
- Plug in your device.
- Follow the instructions to set up your device.
Pairing Echo with Brick
In order to work with the Alexa, the EV3 Brick needs to establish a Bluetooth connection with the Echo device.
The Echo device and the gadget discover each other over Classic Bluetooth as follows:
- The user puts the gadget into pairing mode. This procedure depends on the gadget. For example, a button gadget might use a long press. If your gadget has a touchscreen, a user could enable pairing through an option on the screen.
- The user puts the Echo device into pairing mode. For Echo devices without a screen, a user can put an Echo device into pairing mode by using the Amazon Alexa app. In the Alexa app, the user goes to Settings, selects the Echo device, and then selects Pair Alexa Gadget under Connected Devices. If the Echo device has a screen, the user follows on-screen instructions to initiate pairing. (screenshots are given for reference)
Select bluetooth devices or pair a new device
Just click on the "pair a new device", please be patient it will take some time. Here I am using the echo dot 3rd generation.
Create your Alexa skillLet’s walk through the steps of creating your Skill:
1. Sign in to developer.amazon.com.
2. In the top header, hover over Alexa, and click on Alexa Skills Kit.
3. In the upper-right of the screen, click on Create Skill.
4. Enter a Skill Name, maybe “Caissa”. The name you give your Skill will also be the way you open the Skill. For example, “Alexa, open Caissa”,
5. Select your default language. Make sure the language select matches the language used on your Echo device.
6. Select Custom for the “Choose a model to add to your skill” option.
7. Select Alexa-Hosted for the “Choose a method to host your skill's backend resources” option.
8. Click Create skill in the upper-right.
9. Once you click Create skill, you will see a modal while your skill is being prepared to be customized.
Enable the Custom Interface Controller
The Custom Interface Controller allows you to send custom commands to your EV3Brick (Custom Directives), and receive events from your EV3 Brick (Custom Events). For this mission, you will need to enable the Custom Interface Controller, so you can use Custom Directives:
1. Click on Interfaces in the left navigation of the Alexa Developer Console.
2. Toggle Custom Interface Controller to ON.
3. Scroll to the top of the page and click Save Interfaces
That’s it! With Custom Interface Controller toggled on, you can write code that sends custom directives to your EV3 Brick and program how you want it to react. Learn more about Custom Interfaces.
Define the Skill Interaction Model
The Skill Interaction Model defines how you can speak to your skill, and what kind of commands it can expect to respond to. The interaction model includes intents, slots, sample utterances that you define, and program against in your skill’s code. Learn more about the Skill Interaction Model.
1. In the Alexa Developer Console, under Interaction Model, click on JSON Editor.
2. In the caissa
folder, you will see a folder called skill-nodejs
. Within that folder, there is a model.json
file. Copy the interaction model JSON from that file, and paste it into the editor, or drag and drop the JSON file onto the drop zone to upload it.
After pasting the JSON into the Alexa skill JSON Editor, click Save Model, and then Build Model presented at the top of the console interface. It may take some time for the model to build, so be patient. In the meantime, let’s explore the Intents and Slots in the JSON file.
An intent represents an action that fulfills a user's spoken request. Intents can optionally have arguments called slots. For example, in the JSON you will see a MoveIntent
with slots for IniY
, which maps to a predefined AMAZON.NUMBER
integer value. The combination of the intent and slots will allow us to write code that reacts to:
“Alexa, movefrom C3 to C4”
With your Intents and Slots defined, you’re ready to start implementing the Skill code.
There’s a lot to learn about creating skills, but for the purpose of this mission, we’ll guide you through using the Alexa-Hosted skill option you selected earlier, and share additional resources at the end. With an Alexa-Hosted skill, you can start writing code for your skill directly in the Alexa Developer Console:
1. Click on Code in the top navigation bar of the Alexa Developer Console.
2. In VS Code, open the index.js
file in the caisaa/skill-nodejs/lambda
folder.
3. Copy the code in the index.js
file into the index.js
file in the Alexa Developer Console Code Editor.
4. Copy the contents of the package.json
and util.js
files to the respective files in the Alexa Skill Code Editor.
5. Create a new file by clicking the New File icon in the upper-left of the Code Editor, and fill in the path and file name as /lambda/common.js
6. With the common.js
file created, make sure the file is open, and then copy the code in the common.js
file from the caissa/skill-nodejs/
folder in VS Code to the common.js
file in the Alexa Skill Code Editor.
Now, let’s take a look at the core elements of the index.js
file. The snippet below contains all of the handlers used by this skill. The last four handlers are lifted from the skill template — no modifications needed.
exports.handler = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestHandler,
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();
There are also three unique handlers that have been added.
LaunchRequestHandler
This handler is invoked when you launch the skill by saying, “Alexa, open chessmania”. When the skill is launched, the following occurs: The endpointID
of the MindstormsGadget
is obtained using:
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();
}
If there is no EV3 Brick connected to the Echo device, the skill replies with a message. Otherwise, the endpointID
is stored as a session attribute. Session attributes allow you to retain data during the skill session:
let endpointId = apiResponse.endpoints[0].endpointId || [];
Util.putSessionAttribute(handlerInput, 'endpointId', endpointId);
Once the endpointID
is stored, the skill continues by welcoming the person speaking to the Echo device:
let out = "Welcome to chess mania"
return handlerInput.responseBuilder
.speak(out)
.reprompt("Awaiting commands")
.getResponse();
MoveIntentHandler
This handler is invoked when you say, “Alexa, tell caissato movefrom C3 to C4”, or other similar supported utterance. When the skill is invoked using this utterance, the following code gets the values from the IniX
, IniY
, FinX
, Finy
slots to construct a command that will be sent to the EV3 Brick to make EV3RSTORM move.
let Inix = Alexa.getSlotValue(handlerInput.requestEnvelope, 'IniX');
let Iniy = Alexa.getSlotValue(handlerInput.requestEnvelope, 'IniY');
Iniy = Math.max(1, Math.min(8, parseInt(Iniy)));
let Finx = Alexa.getSlotValue(handlerInput.requestEnvelope, 'FinX');
let Finy= Alexa.getSlotValue(handlerInput.requestEnvelope, 'FinY');
Finy = Math.max(1, Math.min(8, parseInt(Finy)));
Once all the parameters are available, a control
custom directive is constructed to send to the MindstormsGadget
. This custom directive is then parsed on the EV3 Brick using code we’ll review later.
let directive = Util.build(endpointId, NAMESPACE, NAME_CONTROL,
{
type: 'move',
inix: Inix,
iniy: Iniy,
finx: Finx,
finy: Finy
});
return handlerInput.responseBuilder
.speak(`Moving `)
.addDirective(directive)
.withShouldEndSession(false)
.getResponse();
Now it is the time to test the skill, but you will find that you receive a response indicating that your EV3 Brick has not been detected:
1. Click Deploy in the upper-right of the Alexa Skill Code Editor. Wait for the deployment process to complete.
2. In the top navigation bar of the Alexa Developer Console, click on Test.
3. You can choose to enable the microphone on your computer or not.
4. Switch the testing from Off to Development using the dropdown under the navigation bar.
5. In the panel on the left, you can either type “open chessmania”, or say that utterance when you press the microphone button (you will have opted to enable the microphone on your computer for this to work).
6. You should get a response along the lines of, “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”, which you specified in the LaunchRequestHandler
.
Your skill is working! Let’s complete the other side of the experience by reviewing and running the EV3 code to get EV3RSTORM to react to your skill
Now let's build the python code.
Python CodePython is the working end that receives a directive (command) from the Alexa skill, elucidates the positions and building corresponding Lego instructions and hence performing visible movements.
Here I discuss all the functions that we need to customize for the flawless working of our chess player.
1. on_custom_mindstorms_gadget_control(self, directive)
It is the function that is invoked when a custom directive is sent from Alexa to Lego Mindstorms.
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"))
control_type = payload["type"]
if control_type == "move":
self._Move(payload["inix"],int(payload["iniy"]),payload["finx"],int(payload["finy"]))
except KeyError:
print("Missing expected parameters: {}".format(directive), file=sys.stderr)
It decodes the directive and decides the type of intent. It also decodes the slot values provided inside the directive. In our case if the type is 'move' then it invokes _Move() function else it returns an error.
2. ReadLetter(self, x)
For the easiness of movements, we want to convert the letter denoting the X coordinate to a number ranging from 1 to 8.
def ReadLetter(self,x):
x=x[0]
x=x.upper()
return ord(x) - 64
The ReadLetter(x) function takes input x (changes it to upper case) and then converts it into ASCII. After that from the ASCII value, 64 is reduced. In effect, it produces numbers from 1 to 8 for letters from A to H.
3. MoveDiagonal(self, xsteps:int, ysteps:int)
This function produces a diagonal movement combining horizontal and vertical movements. xsteps is the distance that to be moved in the X direction and ysteps is the distance that to be moved in the Y direction.
def MoveDiagonal(self,xsteps:int,ysteps:int):
self.XM.on_for_rotations(SpeedPercent(20),xsteps)
self.YM.on_for_rotations(SpeedPercent(10),-ysteps*0.3)
It is used only when the magnet is in a low position, else it may affect nearby pieces. It is used to move from origin to initial position and then to move back from the final position to the origin.
4. Offset(self)
It is the function used for the square correction. The piece that is at the center of the cell is taken to the corner of the cell.
def Offset(self):
self.XM.on_for_rotations(SpeedPercent(20),0.5)
self.YM.on_for_rotations(SpeedPercent(10),-0.15)
It is done so, because all the succeeding movements are along the borderlines, not to disturb any other pieces in the neighboring cells.
5. OffsetNull(self)
It is the function that nullifies the Offset() movement. After the horizontal and vertical moves, the piece will in the corner of the cell. To take back the piece to the center the OffsetNull() function is used.
def OffsetNull(self):
self.XM.on_for_rotations(SpeedPercent(20),-0.7)
self.YM.on_for_rotations(SpeedPercent(10),0.2)
6. MoveHorizontal(self, xsteps)
It is the function that produces a horizontal movement.
def MoveHorizontal(self,xsteps):
self.XM.on_for_rotations(SpeedPercent(20),xsteps)
The expected parameter is xsteps. It produces a horizontal movement corresponding to the value of xsteps.
7. MoveVertical(self, ysteps)
It is the function that produces a vertical movement.
def MoveVertical(self,ysteps):
self.YM.on_for_rotations(SpeedPercent(10),-ysteps*0.3)
The expected parameter is ysteps. It produces a vertical movement corresponding to the value of ysteps
8. MagOn(self)
In our case, we have connected our magnet to the medium motor which moves upward and downward. MagOn() is the function that turns the magnet upward, in effect which turns the magnet on.
def MagOn(self):
self.ZM.on_for_rotations(SpeedPercent(30),-3)
9. MagOff(self)
MagOff() is the function that turns the magnet downward, in effect which turns the magnet off.
def MagOff(self):
self.ZM.on_for_rotations(SpeedPercent(30),3)
10. _Move(self, inix:str, iniy:int, finx:str, finy:int, is_blocking=False)
It is the function that performs a complete movement. All the above functions (except the first one) are invoked accordingly.
def _Move(self,inix:str,iniy:int,finx:str,finy:int,is_blocking=False):
"""
Handles Move commands from the directive.
"""
print("Move command: ({})".format(inix), file=sys.stderr)
print("Move command: ({})".format(iniy), file=sys.stderr)
print("Move command: ({})".format(finx), file=sys.stderr)
print("Move command: ({})".format(finy), file=sys.stderr)
#Move from base position to the piece to be moved
xsteps = int(self.ReadLetter(inix)-1)
ysteps = int(int(iniy)-1)
if (xsteps==0 and ysteps==0):
print()
else:
self.MoveDiagonal(xsteps,ysteps)
#Turn Magnet Upwards
self.MagOn()
#Counting Steps
xsteps = int(self.ReadLetter(finx)-self.ReadLetter(inix))
ysteps = int(int(finy)-int(iniy))
if xsteps==0:
self.MoveVertical(ysteps)
elif ysteps==0:
self.MoveHorizontal(xsteps)
else:
#Square Correction
self.Offset()
#X Movement
self.MoveHorizontal(xsteps)
#Y Movement
self.MoveVertical(ysteps)
#Square Correction
self.OffsetNull()
#Turn Magnet downwards
self.MagOff()
#Return to origin
xsteps = int(self.ReadLetter(finx)-1)
ysteps = int(int(finy)-1)
self.MoveDiagonal(-xsteps,-ysteps)
print()
It uses the value of initial and final positions. First of all, it moves from 'A1' to the initial position. Then it turns the magnet on. Then performs an Offset movement for nonlinear movements. Then it produces horizontal and vertical movements and then an OffsetNull() move. After placing the piece in the target cell, it turns off the magnet and comes back to the initial position.
CalibrationNote: When calibrating the chess pieces, we need to take care of the poles of chess piece and the magnet on medium motor, should be of opposite poles(North-south or vice versa).
Even though we have built Caissa and given it properly working code there are many chances of misbehaviors. The rotation of the corresponding motor that can move 1 cell in the X direction is xwidth and that in the Y direction is ywidth, those should be perfectly measured. Calibration is the key for the perfect working of caissa. You can adjust Offset() and OffsetNull() functions for the proper working.
Demo VideoHere is some movements of Caïssa.
In this video, It is partially automated. It can also be fully automated.
I think that, this is a project which is having creative and engaging experiences that showcase the use of voice interaction with the lego mindstrorms.31313.
Comments