Braill3 – An EV3-based Braille Bricks Reader

Several months ago I had been thinking on building a robot that could read the Braille bricks developed by LEGO. They have had them available in a more educational context via their website for some time now. Unfortunately, these ideas came to nought at that time, so I put the idea to bed.

Roll on a few months and the group of RobotMak3rs that I’m a member of had one of their regular remix challenges – i.e. to mix an existing set with some robotics to come up with a new idea. By then the domestic LEGO Braille Bricks set (40656 in the UK) was out, so I figured it was time to revisit my earlier ideas.

Initial Ideas

Right at the start I wanted the bot to read out the Braille using text-to-speech (TTS). I wanted to do this as the bricks are intended for visually impaired users so having just display of text would be inappropriate. The Spike/Robot Inventor doesn’t have the ability to generate complex sounds on the hub itself and would have relied on an external app running on a mobile or table to perform that task. Instead I decided it would be far better to use an EV3 running Pybricks micropython as that has the ability to perform TTS output. In addition to wanting the Braille read out, I wanted all the prompts that would appear on screen to have an audio equivalent.

My initial idea for the mechanics was to have three medium EV3 motors each with 3×5 L-beam attached. As the bot moved along the line of Braille it would rotate the motors such that the tip of an L-beam touched either the brick’s stud or the top of the brick. The difference in angle of the motor would indicate a dot or not. However, very quickly this idea was discarded due to the fact that the stud height is only 1.7mm. The height, and therefore angle change was not sufficient to accurately distinguish the presence of a stud or not. Also this would have required three motors, only allowing one remaining to move the bot along a row. Since I wanted to have it be able to read multiple rows of text, I’d have needed 5 motors (X, Y, +3 for touch) which is not possible with an EV3. So I discarded this approach.

My next idea was for a bot that had an arm with three touch switches mounted on it and have the arm lift up and down. This way the angle of the motor would be irrelevant. The arm would need to move up and down on to each column of studs so that it wouldn’t get snagged on the next column as it moved sideways.

I went through various arrangements of the switches, settling on something similar to below for a number of prototypes:

The principle here was that the stud would push against the pin, which in turn via rotation of the 3×3 quarter circle beam would press in the button. The motors would have had to be mounted at 90° to each other due to the width of the switches (initially) preventing them being next to each other. The big problem with all of these designs is that the springs in the switches are remarkably firm. The motor, pushing the arm down, would have to apply a significant force – akin to trying to hold out a 1kg mass at arms length. Also, it looked ugly. I tend to work on the principle that if it’s ugly then it’s likely to be wrong.

The ‘Fingertip’

After going through several iterations of the ideas above, I had a brainwave. It was possible to mount the motors in parallel and ‘dog-leg’ the pins such that they could also touch the studs. To counter the issue of the required force to press in the switches, linear actuators would be used instead. Although this would slow down the sensing action it would trade speed against accuracy. I ended up with the mechanism below:

This mechanism worked perfectly, with an unexpected discovery on the switches, discussed further on.

Bridge, Switches, and Calibration

The Braille sensing mechanism (the ‘lift’ as I think of it) needed to move in both X axis and Y axis, so that the several rows of bricks could be placed on the baseboards supplied with the kit. The lift would be mounted on a bridge, allowing for Y-axis movement, and the bridge itself would move in the X-axis. The bridge took a few attempts to get right. Due to a combination of the mass of the lift and the force required to press in the switches resulted in flexing of the bridge, so this required a few revisions to get it rigid enough but not too bulky

One thing I had never realised about the EV3’s switches is that they trigger at the start of their travel, i.e. they don’t need to be pushed all the way in to trigger. Had they needed to be depressed all the way, it’s quite possible this model would never have worked. Due to LEGO being plastic, the parts are never perfectly aligned. This could have meant that one of the switches may have reached the end of its travel before either/both of the other switches had triggered. No amount of extra downward force could have pressed the other two as this switch would have blocked any more movement. Thankfully they trigger at the start, so it’s still possible to push down, thus enabling the neighbouring switches to also trigger.

Due to slight flex in the model, it’s not possible to have the motor wind the linear actuators to the same place per row of Braille. The middle two rows can require a little more force. To solve this the bot requires calibration on first use, and offers to calibrate on start up as well. Calibration requires that an L (⠇) brick is placed at the start of each row, then the bot tests each of those bricks. For each row the motor position for the last switch to activate is stored on disk, for repeat use, then when in use it drives the motor to just beyond the motor angle to ensure that all switches could be activated.


As I said at the start, I wanted this model to be accessible to the target users, so all instructions are read out as well as displayed on screen. All button operations have a small click to provide audio feedback, along with a relevant audio prompt, e.g. saying which row has been selected to read. To aid in placing the Braille bricks on the baseboard there are tiles on the first column. Subsequent bricks are simply placed next to the previous bricks. The EV3 has been oriented so that the speaker is facing the user so that it can be heard.


As part of our group’s remix challenge we have to produce a video relating to our build. I opted to have the video show parts of the robot and it in operation. Since the bot converts the Braille to speech, I figured I’d have the voice-over performed by the bot as well (I’m never a fan of speaking on camera, and being a Brit I always feel that I sound sarcastic 😆). I also thought that it would be a fun little feature to have the subtitles show in Braille first and the wipe over to the actual text. The resulting video is below:

Build Instructions and Code

Build instructions:

Python code:

The Python code needs to be run under Pybricks micropython, under ev3dev. This requires a microSD card. The official LEGO ev3dev installation can be found at:

Operating Instructions

  • On first ever run of the program, it will pre-generate all the pre-coded speech prompts. This is so that the program doesn’t have to perform the TTS step every time a common speech prompt is needed. It will only do this the once.
  • On first run of the program it will insist on a calibration step. It will offer to perform a calibration on every other run, but it defaults to ‘no’. To calibrate perform the following:
    1. Put an L brick at the start of each of the 4 rows
    2. Press the centre button. This will then test each of the sensors and motor and store for future use
  • Lay out the Braille as wanted. There are 4 rows that can be used. Select the row to be read with the left and right buttons, centre to read. Spaces between words can be either one or two columns of studs wide. Three or more empty columns will end the row of text.

Updated EV3g Mailbox Messaging in Python

Previously I posted an article on handling EV3g binary Mailbox messages under Python3. Since then I have carried on working on this class, along with adding a handler class.

Improved Mailbox Handling

One of the things I wasn’t so keen on with my implementation was the need to specify the type of Mailbox value, i.e. BOOL, NUMBER, or TEXT. Python’s variables have their own type, so the code has been adjusted to use the value’s own type to determine the binary payload format. It is still possible to coerce the type:

from ev3mailbox import EV3Mailbox

float_msg  = EV3Mailbox("Pi", 3.1415)

# Coerce to a string
string_msg = EV3Mailbox("Pie", 3.1415, str)

These changes have made the use of this side of the code much cleaner.

Mailbox I/O Handler

Whilst working on my use case for the original code, I had been working on the principle that I’d be using it in a simple synchronous send/receive pattern. This worked well, until I started using threads at both sides of the ev3dev <-> EV3g link. Once threads were in the mix, there’s a risk that the bt_socket.recv(…) call could actually receive a message that wasn’t destined for that particular call, but for another area of the program.

The solution to the above problem was to implement a receiving thread that deals with all the socket.recv(…) calls. Each message is decoded, and then each Mailbox name has its own FIFO of message objects. It’s a deliberate choice to maintain the list of objects, rather than just their values, so that they can be forced to floats if it’s known they may be very small – see my previous post about that problem.

The new class implements a handler that will deal with all the Bluetooth and thread side of things. All that’s then required to do is call send(…), get(…), or stop() on the class instance:

from ev3messages import EV3Messages

handler = EV3Message(bt_mac_address)

handler.send("Name", value)
msg = handler.get("ANOther")
value = msg.value


The calls to send(…) and get(…) should (!) be thread safe, so calls to get(…) wait on receiving a message of the requested name.

Code Repo

The repo is available from: and is released under the GPLv3.

EV3 Mailboxes in Python

Recently I wanted to enter the Alexa / LEGO MINDSTORMS challenge:

My idea required being able to send EV3 Mailbox messages via Bluetooth between ev3dev and a stock EV3 running the EV3g language – from Python. I’m new to Python, I’m a Perl programmer at heart, so this was somewhat of a learning curve moment. I had to get to grips with Bluetooth (not that difficult as I’m used the IP networking) and Python at the same time.

I figured that one of the major selling points of Python was its extensive library of support functions, so set to looking for something providing EV3 Mailbox handling. My research wasn’t as fruitful as I’d hoped for. I could find various chunks of code but either they were flawed in their behaviour, or much more heavyweight than I wanted. So I decided to jump in feet first and write my own library.

I’d written a library for App Inventor 2 [0] [1] [2] [3] [4] that would encode & decode EV3 Mailbox payloads before, so this wasn’t too daunting a task. One aspect of Mailbox messages is that there isn’t an identifier within the payload that identifies the content: String, Float (IEE754 32 bit), Boolean. Normally this is handled in EV3g by expecting a specific type relating to the message name – i.e. a message called “status” would be defined to always be a Boolean, but “command” would always be a String – the code forces the type to remain constant. However, the type of the payload can be deduced to some extent, so I decide that I’d implement that within the Python class:

  • Payload length = 1 byte => Boolean
  • Payload length = 4 bytes
    • Last byte != NULL or NULL in the other bytes => Float
    • Otherwise => String
  • All other payloads => String

The only issue with the logic above is that really, really, small numbers may get decoded as strings, e.g. “@@@\x00” would get seen as a string, not 5.90052e-39 which is also a valid decode of it. As such I also implemented a .force_float() method which will re-decode the payload.

The git repo for this library can be found at:

To use it do something like:

from ev3mailbox import EV3Mailbox as Mailbox

message = EV3Mailbox.encode("Name", "Message value", Mailbox.Type.TEXT)

# Data from Bluetooth
 mailbox = EV3Mailbox.decode(payload)

Hopefully this will prove useful to others. The code has been released under the GPLv3: