Photo: Diomidis Spinellis
If you want to explore coding with Lego bricks, there’s one major option: to use a kit from the company’s well-known Mindstorms robotics line. Mindstorms-based machines are built around the Intelligent Brick, which can be programmed using Lego’s graphical programming environment or one of a number of third-party alternative languages. But Lego also makes a collection of motors, connectors, lights, and infrared receivers collectively sold under the label of Power Functions. In place of a programmable brick, the Power Function line includes a handheld controller for transmitting command signals.
I wondered if it was possible to use a Raspberry Pi to replace the handheld controller, taking on the role of an Intelligent Brick. This would have some advantages. With programs being created on the same device used to control Lego constructions, it would eliminate the need to download the programs to the brick, speeding up development. The US $40 Pi is also a lot cheaper than the $190 Intelligent Brick. I also wondered if such a setup could be used with MIT’s Scratch, a free visual programming environment aimed at children. Scratch extensions are available for use with the Mindstorms brick, but they require altering the brick’s firmware, and I wanted to try something simpler.
As I discovered, most of the code required for controlling Lego toys using Scratch is already available as open source software. What was needed was integration, configuration, and some glue software.
Block by Block
Lego Power Functions allow motors to be controlled with infrared signals (top). Signals can be generated by connecting infrared LEDs to a Pi (middle images). A Lego enclosure holds the components (bottom).Photos: Diomidis Spinellis
First, I needed to build an infrared control link, which is basically two infrared LEDs operated via the Raspberry Pi’s general purpose input/output (GPIO) connector and Lego’s receiver. I used schematics and instructions by Alex Bain to build the hardware. For the software, I downloaded and installed LIRC, a package that has support for decoding and transmitting signals used by over 2,500 different infrared remote controls.
Getting the LIRC package to work with my home-brew infrared link was a simple matter of editing some configuration files and specifying which GPIO pins I had wired up for input and output.
Now I needed to get LIRC to send valid Lego command signals. This means specifying the waveform—a pattern of infrared pulses—that must be sent for each Lego command. Fortunately, Lego has released a document [PDF] specifying the protocol and format of all commands (for example, a binary value of 1 is transmitted by six pulses of IR light at a frequency of 38 kilohertz, followed by a pause of 553 microseconds). The Lego Power Functions system supports up to four receivers working on different channels, and each receiver has a red side and a blue side, each of which can independently control a motor.
Building on this information, Conor Cary created lego-lirc, a Java program that generates command waveforms, complete with the correct checksums, in a format that LIRC understands. I downloaded lego-lirc and, with the Lego documentation in hand, created additional waveforms that allow the transmission of PWM (pulse-width modulation) commands. These commands allow precise speed adjustment of Power Function motors without requiring timing loops in the application software. (To avoid the hassle of running lego-lirc, you can just download my file of generated LIRC waveforms directly from my GitHub repository under the username of dspinellis.) To configure LIRC to use the Lego commands, I copied the waveform to the LIRC configuration directory. I could then send Lego commands from the Pi’s command line through LIRC’s irsend program.
The final step was to issue the LIRC commands from the Scratch environment. I enabled “remote sensor connections” in Scratch. This makes Scratch behave like a local server running on the TCP port 42001. Client software can connect to Scratch using this port and listen for messages from Scratch programs. (It’s also possible to have the client software and Scratch environment run on separate machines, so you could have the Raspberry Pi–based infrared interface controlled by a Scratch program running on a desktop computer, for example.) I then installed Phillip Quiza’s excellent scratchpy library, which allows you to write Scratch clients in the Python programming language.
Finally, I wrote a Python script that receives Scratch broadcast messages specifying Lego remote commands, and runs the LIRC command-line client to send them (this is also available from my lego-power-scratch GitHub repository). To run the script, run the control.py program in a separate terminal window and launch the Scratch environment. While control.py is running, it will display on its standard output the remote control messages it sends or the errors it detects on the incoming Scratch messages.
In Scratch, programs are constructed by chaining together graphical blocks on screen. Blocks perform functions such as program-flow control and graphics manipulation. To send a message to a Lego Power Functions receiver, a “broadcast” block is used, with a simple text string of the form “Lego <channel> <Blue|Red> <power level>.” So, for example, the message “Lego 2 blue -7” will send a signal by way of the Python client and my transmitter to turn the motor connected to the blue side of the receiver on channel 2 at full speed, backward.
How does the system work in practice with its intended audience? I tried it out with a young budding engineer—who quickly wrote a Scratch program to control Lego’s Volvo Wheel Loader kit with a computer’s arrow keys.
This article appears in the November 2016 print issue as “A DIY Lego Controller.”
About the Author
Diomidis Spinellis is a professor of management science and technology at the Athens University of Economics and Business, in Greece. He is also editor in chief of the magazine IEEE Software and author of Effective Debugging: 66 Specific Ways to Debug Software and Systems (Addison-Wesley, 2016).