Ruby + Arduino + LEGO® = RuBot

Share this article

A while back I had a long weekend to kill and was in the mood for some just-for-fun hacking. The weather was predicted to be awful and I remembered that I always wanted to build a robot. Of course, I had built simple line follower robots and the like years ago, but I was aiming higher this time. My idea was to build a Linux controlled robot with a Ruby brain and the capability to provide the operator with a live video stream. Well, at least this is my first milestone. An artificial intelligence module and a doomsday weapon need to stay in the backlog for now. At first I thought about getting some LEGO® Mindstorms for the task, but then I quickly remembered a few things:

  1. Lego Mindstorms are still pretty expensive even though they are a cool toy.
  2. The NXT is programmed in a weird LabView‘esque language.
  3. I have an unused Arduino and a couple LEGO® Power Functions Motors along with some generic LEGO® Technic. That should do the trick.

Hardware

I did what every sane tinkerer would do and ordered a Motor Driver Shield from Adafruit. The specs of this nice little shield convinced me to just get it as a finished and tested product and not to start soldering L293D chips myself. After all, I am mostly a software developer, not an electronics engineer. While I waited for the delivery I started to play around with some LEGO® blocks to build a chassis that is strong enough to carry a small laptop and the Arduino around. I used my trusty old Asus EeePC 701 netbook that I keep around mainly for hacking purposes and as a backup PC. This quirky little fellow would sit on top and do all the heavy lifting CPU-wise: running Ruby, providing live video data (640×480 pixel), WIFI connection. Even with it weighing in at only 922g (~2 pounds) and dimensions of 225x165mm (~8.86×6.5 inches), it is not trivial task to build something out of LEGO® is strong enough to hold it without looking like a giant block. It is kind of funny how similar elegant programming is to elegant mechanical engineering. The basic principles of “DRY” or “YAGNI” and the like can all be applied, along with the high level essence of “less is more”. In order to become alive and kicking, the robots brain would talk via USB to the Arduino, which would be equipped with Adafruits Motor Driver Shield. Connected to the Motor Driver Shield would be the LEGO® Power Functions Motors. The finished robot bigger version
  1. Arduino equipped with Adafruit Motor Driver Shield.
  2. Two LEGO® Power Functions M-Motors for driving.
  3. Slightly modified LEGO® Power Functions Battery Box connected to the Motor Driver Shield used as external power supply (the power from the USB port alone is not enough to drive the 9V motors).
  4. LEGO® Power Functions M-Motor for turning the gripper.
  5. Tiny red LEGO® Micromotor for opening and closing the gripper.
  6. Caster wheel able to turn 360°.
  7. Modified LEGO® Power Functions wires connected to the Motor Driver Shield, providing connectors for the motors.
  8. USB connection to the Arduino.
  9. Camera (640×480 pixel, 30 fps).
No soldering required at all. Although I did a little soldering to create a smaller, thinner USB cable in order to achieve a clutter free appearance.

Software

Knowing that I would not need a fancy frontend and that the application would turn out pretty small in general, I decided to use Sinatra as the base framework accompanied with a small set of Gems and libraries: I have been looking for a good reason to play around with websockets for quite some time now, and finally took the opportunity to use one to reduce the delay of the robot controls. I can tell you in cases like this, a websocket connection really gives you just the extra snappiness you need when compared to Ajax calls. All the magic is done by the wonderful Sinatra-Websocket Gem and all you have to do in Sinatra is something like this:
get '/' do
if request.websocket?
request.websocket do |ws|
ws.onopen do
ws.send("CONNECTED #{Time.now}")
settings.websockets << ws
end
ws.onmessage do |msg|
handle_commands(msg)
EM.next_tick { settings.websockets.each{|s| s.send(msg) } }
end
ws.onclose do
settings.websockets.delete(ws)
end
end
else
haml :index
end
end
Here we mount the websocket right under “/”, handling both a websocket request and a normal GET for the index page. The three callback functions onopen, onmessage and onclose are used to handle all the events dealing with websockets. The handle_commands method takes the websocket message, which is a JSON string in this case, and proxies the valid commands to the Arduino via the Ruby-Serialport Gem. I wrote a thin wrapper that makes it a little easier to register several motors and associate them with a name and port (on the Motor Driver Shield) respectively. It also lets you define which direction is “forward” or “backward” for each motor in your model.
$arduino = Arduino.new
$motordriver = MotorDriver.new(
$arduino,
{ :left => Motor.new(2, $arduino, { :forward => Motor::BACKWARD, :backward => Motor::FORWARD }),
:right => Motor.new(1, $arduino, { :forward => Motor::BACKWARD, :backward => Motor::FORWARD }),
:gripper => Motor.new(3, $arduino, { :forward => Motor::FORWARD, :backward => Motor::BACKWARD }),
:rotator => Motor.new(4, $arduino, { :forward => Motor::FORWARD, :backward => Motor::BACKWARD }),
}
)

def handle_commands(params={})
params = (JSON.parse(params) unless params.class == Hash rescue {})
Thread.new{`espeak '#{params['speak'].tr(''','')}' 2< /dev/null`} if params['speak']
$motordriver.left(*params['left']) if params['left']
$motordriver.right(*params['right']) if params['right']
$motordriver.gripper(*params['gripper']) if params['gripper']
$motordriver.rotator(*params['rotator']) if params['rotator']
rescue
p $!
end
Noticed the line that utilizes eSpeak? It is an primitively implemented but an incredibly fun gimmick that adds voice output to the robot! The other parameters are for the actual controls and handled by the $motordriver. It simply translates all the motor commands into 3 byte long messages to the Arduino conforming to the very simple binary protocol that I created. The first byte defines the motor (1-4), the second byte defines the direction (forward = 1, backward = 2, brake = 3, release = 4) and the third byte defines the speed (0-255). The complete Arduino sketch (a sketch is a program in Arduino-speak) looks like this:
#include <AFMotor.h> // Adafruit Motor shield library

AF_DCMotor motors[] = {AF_DCMotor(1), AF_DCMotor(2), AF_DCMotor(3), AF_DCMotor(4)};
int i = 0;

void setup() {
Serial.begin(9600);
Serial.println("Motor test!n");
for(i=0;i<4;i++){ motors[i].run(RELEASE); }
}

void loop() {
uint8_t motor;
uint8_t direction;
uint8_t speed;
while (Serial.available() > 2) {
motor = Serial.read();
direction = Serial.read();
speed = Serial.read();
if(motor > 0 && motor < 5) {
if(direction < 1 || direction > 4){ direction = 4; }
motors[motor-1].setSpeed(speed);
motors[motor-1].run(direction);
}
}
}
For the video connection, I wanted to use a real audio-/video stream with some fancy codec like H.263 but gave up after several attempts with VLC, FFMPEG and MPlayer as streamer. I just could not get the latency below 3-4 seconds. I know this does not sound like a lot, but when you are operating a remote robot that executes your commands nearly instantly but delivers the images with such a delay, you’re going to have a bad time
and crash into objects a lot. I decided to try simple Motion JPEG streaming which is basically just sending JPEG images to the browser without closing the connection. Somehow, despite the much bigger bandwidth usage, the delay shrunk down to half a second or so. My theory is that the Asus EeePC 701 is just a little too slow for fancier codecs. But still, if someone of you had success with “real” low latency streaming besides Red5 (since I’m not looking for Flash solutions), please feel free to leave a comment and point me in the right direction. My code to handle the M-JPEG streaming is a bit more cluttered but after all it is a hack anyways ;)
get '/mjpgstream' do
fps = (params['fps'] || 10).to_i
ms = (1000 / fps.to_f).to_i
headers('Cache-Control' => 'no-cache, private', 'Pragma' => 'no-cache',
'Content-type' => 'multipart/x-mixed-replace; boundary={{{NEXT}}}')
stream(:keep_open) do |out|
if !$mjpg_stream || $mjpg_stream.closed?
puts "starting mjpg stream with #{fps} fps. #{ms} ms between frames."
$mjpg_stream = IO.popen "./uvccapture/uvccapture -oSTDOUT -m -t#{ms} -DMJPG -x640 -y480 2> /dev/null"
end
settings.video_connections << out
out.callback {
settings.video_connections.delete(out)
if settings.video_connections.empty?
puts 'closing mjpg stream'
$mjpg_stream.close
end
}
buffer = ''
buffer << $mjpg_stream.read(1) while !buffer.end_with?("{{{NEXT}}}n")
while !$mjpg_stream.closed?
begin
out << $mjpg_stream.read(512)
rescue
end
end
end
end
Basically I modified/extended the Uvccapture program to …
  • … allow “STDOUT” for the -o option
  • … use milliseconds instead of seconds for the -t option
  • … have a -D option to specify the delimiter or use "nn--{{{NEXT}}}nnContent-type: image/jpegnn" when -D is “MJPEG”
and utilized it with a IO.popen call to provide me with an easy to handle M-JPEG stream that could be transported to the browser with Sinatra’s built in stream function (requires Thin as a webserver, as far as I know). The Uvccapture stream is buffered and reused in case more than one client is requesting the resource and also gets opened and closed on demand. I am aware that this is probably not the most elegant solution but it worked well enough for me in this case. The web frontend for the robot is pretty simple as promised and lingers within the main file itself in the form of two tiny Haml templates: [haml] @@ layout !!! 5 %html{:lang => ‘en’} %head %meta{:charset => ‘utf-8′} %link{:rel=>’shortcut icon’, :type=>’image/gif’, :href=>’favicon.gif’} %title RuBot %body{:style=>’background-color:black; text-align:center;’} = yield(:layout) %script{:src => ‘jquery.js’} %script{:src => ‘app.js’} @@ index %div %img{:src=>’/mjpgstream’} %p{:style=>’color:#555′} Help: Use arrow keys to drive, q/w to open/close gripper a/s to turn gripper left/right hold shift for slow-mode, space to speak [/haml] All the frontend magic happens within “app.js”, where the robot controls are simply mapped to keyboard events. This is a bit unlucky in hindsight because it makes it much harder to control the robot via a mobile phones browser but I hope you will forgive me. The other thing that is notable within these few lines of Haml code is the line %img{:src=>'/mjpgstream'}. That is all there is to it to display the M-JPEG stream. The content-type multipart/x-mixed-replace; boundary={{{NEXT}}} is enough for the browser to know what to do and handles the replacement of the images within the image tag by itself. Pretty cool if you ask me. You can find all the code including the modified Uvccapture version in this repository. As always I hope you enjoyed this little project and if so, feel free to tell your friends :)

Frequently Asked Questions (FAQs) about Ruby, Arduino, and Lego Rubot

What is the Ruby programming language and how is it used with Arduino and Lego Rubot?

Ruby is a dynamic, open-source programming language that focuses on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write. In the context of Arduino and Lego Rubot, Ruby is used to write the software that controls these devices. This includes tasks such as reading sensor data, controlling motors, and implementing the logic for the robot’s behavior. Ruby’s simplicity and readability make it a great choice for this purpose, especially for beginners or those who are new to programming.

How can I get started with Arduino and Lego Rubot?

To get started with Arduino and Lego Rubot, you’ll first need to acquire the necessary hardware. This includes an Arduino board, a Lego Mindstorms kit, and any additional sensors or components you want to use. Once you have the hardware, you can start learning about how to program it using Ruby. There are many online tutorials and resources available, including the article on our website, which provides a step-by-step guide to getting started.

What are some of the best Arduino and Lego projects?

There are countless Arduino and Lego projects that you can undertake, depending on your interests and skill level. Some popular projects include building a weather station, a home automation system, or a robot that can navigate its environment. The possibilities are truly endless, and the best project for you will depend on what you’re interested in and what you hope to learn.

How can I use the Legoino library with Arduino?

The Legoino library is a powerful tool for working with Lego Mindstorms sensors and motors using an Arduino board. To use it, you’ll first need to install the library in your Arduino IDE. Once installed, you can use the functions provided by the library to control your Lego devices. The Legoino library provides a simple and intuitive interface for working with Lego devices, making it a great choice for beginners and experienced users alike.

Can I use other programming languages with Arduino and Lego Rubot?

Yes, while this article focuses on using Ruby, Arduino and Lego Rubot can be programmed using a variety of languages. Arduino itself is typically programmed using a language based on C++, but there are libraries and tools available that allow you to use other languages, such as Python, JavaScript, and more. Similarly, Lego Mindstorms can be programmed using a variety of languages, including but not limited to Python, Java, and C#.

What are some common challenges when working with Arduino and Lego Rubot?

Some common challenges when working with Arduino and Lego Rubot include troubleshooting hardware issues, understanding the syntax and structure of the programming language, and implementing complex logic for the robot’s behavior. However, with patience and practice, these challenges can be overcome. There are also many resources available online to help you, including forums, tutorials, and documentation.

Can I use Arduino and Lego Rubot for educational purposes?

Absolutely! Arduino and Lego Rubot are excellent tools for teaching a variety of subjects, including programming, robotics, and engineering. They provide a hands-on, interactive way to learn these subjects, making them a great choice for classrooms, after-school clubs, and other educational settings.

What are the system requirements for running Ruby, Arduino, and Lego Rubot?

The system requirements for running Ruby, Arduino, and Lego Rubot will depend on the specific hardware and software you’re using. However, in general, you’ll need a computer with a USB port to connect to the Arduino board, and you’ll need to install the appropriate software for programming (such as the Arduino IDE and a Ruby interpreter).

Can I customize my Arduino and Lego Rubot project?

Yes, one of the great things about working with Arduino and Lego Rubot is the ability to customize your project to suit your needs. This can include adding additional sensors or components, modifying the software to change the robot’s behavior, or even designing and building your own custom robot.

Where can I find more resources on Arduino and Lego Rubot?

There are many resources available online for learning more about Arduino and Lego Rubot. This includes tutorials, forums, documentation, and more. Some good places to start include the official Arduino and Lego Mindstorms websites, as well as online communities like Stack Overflow and Reddit.

Marc BerszickMarc Berszick
View Author

Marc is a freelance software engineer and technical consultant with a dedication to web technologies and open standards who has over 10 years of experience. He loves traveling, mountain biking and climbing.

arduinoEmerging Tech
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week