By developers for developers.

Testing Arduino Code

An Everyday JRuby Adventure

by Ian Dees

Ian brings the testing power of the Ruby-based Cucumber testing library to the Arduino.

This article is the latest in my series on everyday JRuby. But it’s also an exploration in bringing serious software development tools to the Arduino.

People are using JRuby for amazing things worldwide. They’re also
using it at smaller scales to make their day-to-day work a little
easier. This series is a journey through everyday situations where
JRuby comes in handy.

In the previous installment, we implemented the Six Degrees of
Kevin Bacon game using a graph
database.
We then packaged it up for easy deployment to machines that don’t
have Ruby installed.

Today, we’ll use the Ruby-based Cucumber testing library to do a
quick smoke test of an Arduino-based project. The technique will
work in several different Ruby implementations; using JRuby here
will give us an excuse to talk about calling into a Java library
from Ruby.

The Hardware

The hardware we’ll be testing is the Mood Hat, a little demo I
threw together last summer as an excuse to play with
microcontrollers. It features the Lilypad, a washable
Arduino-based circuit board that can be sewn to
clothing.

The Mood Hat is a simple toy. It’s a baseball cap with five LEDs
representing five different moods from furious to ecstatic. At
any given time, one LED is lit. You press one of two buttons to
advance to the next happier or angrier state.

You can use nearly any cheap consumer LEDs and pushbuttons for
this; I bought a handful of tiny ones from Sparkfun that are
designed to go with the Lilypad.

Construction is simple. First, we’ll wire up the five LEDs to
pins 6 through 10 on the Lilypad. For each pin, connect a 1K
resistor to the pin, then connect the positive terminal of an
LED to the other side of the resistor. Wire the negative
terminal of the LED to the ground pin (marked as
- on the board).

Now, we’ll hook up the pushbuttons to pins 2 and 3. Connect each
button between its corresponding input pin and ground. When the
wearer presses a button, the voltage read at the pin will drop
to near zero. But you’ll need something to keep the voltage high
when the button isn’t being pressed. Connect a small resistor,
something on the order of 2.2K, between each output pin and the
supply pin (marked as + on the
board).

That’s it for the hardware—on to the tests!

The Tests

We’re going to write a series of tests using the Cucumber test
framework.
Each test will drive the hardware by sending commands to it over
the board’s built-in serial interface.

The Protocol

We’ll define a simple set of single-character commands we can
send from our test script to the Mood Hat over the serial
port:

?

Ask for the current mood, from 0 (furious) to 4 (ecstatic).

+ or -

Increase or decrease happiness by one notch.

0 ... 4

Set the current mood.

Those are all the commands we need to drive the hardware
during testing. We’ll need to implement them in both the tests
and in the firmware. Let’s start with the tests.

The Feature

Here’s what one piece of a Cucumber test might look like:

Given I am happy

When I increase my happiness

Then I should be ecstatic

This wording is going to get really old if we have to repeat
it for every combination of starting state and button
push. Let’s take advantage of Cucumber’s ability to define
several steps in a single ASCII art table. Here’s the full
Cucumber test, which should go in
features/mood_hat.feature in your project
directory:

Feature: Mood hat

In order to get my feelings across

As a passionate person

I want to display my mood on my hat

Scenario Outline: Changing mood

Given I am <feeling_now>

When I <change> my happiness

Then I should be <feeling_next>

Examples:

| feeling_now | change | feeling_next |

| furious | increase | unhappy |

| furious | decrease | furious |

| unhappy | increase | neutral |

| unhappy | decrease | furious |

| neutral | increase | happy |

| neutral | decrease | unhappy |

| happy | increase | ecstatic |

| happy | decrease | neutral |

| ecstatic | increase | ecstatic |

| ecstatic | decrease | happy |

Go ahead and install Cucumber, then run what you’ve got so far:

$ jruby -S gem install cucumber rspec-expectations

$ jruby -S cucumber features

You should see a copy of the test, plus a list of missing step
definitions:

...

10 scenarios (10 undefined)

30 steps (30 undefined)

0m1.360s

You can implement step definitions for undefined steps with these snippets:

Given /^I am furious$/ do

pending # express the regexp above with the code you wish you had

end

When /^I increase my happiness$/ do

pending # express the regexp above with the code you wish you had

end

Then /^I should be unhappy$/ do

pending # express the regexp above with the code you wish you had

end

...

For each line in the feature description, Cucumber expects to find a chunk of code implementing that particular test step. Since we haven’t defined any steps yet, Cucumber has supplied a template for us. Let’s create some step definitions based on the template.

Step Definitions

Cut and paste the first three empty step definitions into a new
file called
features/step_definitions/mood_hat_steps.rb. Adjust
them to contain the following code:

Given /^I am (.+)$/ do |mood|

@port.putc int_for(mood).to_s

end

When /^I ([^ ]+) my happiness$/ do |change|

char = (change == 'increase') ? '+' : '-'

@port.putc char

end

Then /^I should be (.+)$/ do |mood|

@port.putc '?'

@port.getc.to_i.should == int_for(mood)

end

Since each test step matches one of these three regular
expressions, these definitions are all we need. The
@port variable is an instance of
the SerialPort class (more on that in a
moment). We need to create this variable before the first test
runs; features/support/env.rb is the
place to put this kind of initialization code:

$: << File.dirname(__FILE__) + '/../../lib'

require'serialport'

port = SerialPort.new ENV['MOOD_HAT'], 9600, 8, 1, SerialPort::NONE

port.read_timeout = 1000

port.putc('?') until (port.getc =~ /\d/ rescuenil)

at_exit { port.close }

That code opens the serial port and keeps trying to contact the Mood Hat until it receives a reply. How to we make the connection visible to our test code? Cucumber provides the World method for this purpose. The following code (also in env.rb) will define a MoodWorld class to make the port available to Cucumber:

class MoodWorld

def initialize(port)

@port = port

end

# ... more methods here ...

end

World { MoodWorld.new(port) }

The MoodWorld class is also a good place for helper methods, like the int_for method that converts the names of emotions into numbers we can send over the port:

Emotions = %w(furious unhappy neutral happy ecstatic)

def int_for(emotion)

Emotions.index emotion

end

Now, on to the SerialPort class.

Supporting Code

In regular Ruby, we’d install a gem called serialport to
provide the SerialPort class. That gem
has C code in it, though. While some C-based gems are
compatible with JRuby, serialport isn’t one of them.

The good news is that we only need a tiny subset of
serialport’s functionality. We can easily write a
JRuby-specific file that provides this subset. If you’re
following along with regular Ruby, you can just do a
gem install serialport and
skip to the next section.

Java in Your Serial

To access the serial port from the JVM, we’re going to use the
JavaComm API. Although this is an official Java API, it
doesn’t actually ship with the JVM. Fortunately, there’s an
open source implementation called RXTX.

To use RXTX in this project, download the latest prerelease
version from the download page. Extract the
zip file. You’ll need two files
from this archive: a platform-specific library (e.g.,
rxtxSerial.dll on Windows) and the
cross-platform file RXTXComm.jar. Put the
platform-specific file in your project directory. Create a
lib subdirectory and put the
jar file into it.

Serial With Class

Now we need to define the SerialPort
class. Create a new file called
lib/serialport.rb, and place the
following code at the top of it:

require'java'

require'RXTXcomm.jar'

java_import('gnu.io.CommPortIdentifier')

java_import('gnu.io.SerialPort') { 'JSerialPort' }

This brings the RXTX classes we’re using into the Ruby
namespace, so we can use them just like any Ruby class. We
don’t want a name collision between the
SerialPort Java class defined in RXTX
and the one we’ll be writing in Ruby. By providing the name
JSerialPort in the block passed to
java_import, we prevent a clash.

Now, sketch the outline of the new class:

class SerialPort

attr_accessor :read_timeout

NONE = JSerialPort::PARITY_NONE

# ... methods go here ...

end

First, we’ve defined the
read_timeout= method, one of the
three SerialPort methods our test
script uses. Next, we’ve added a constant that needs to be
visible from outside the class. Now, let’s add a couple of
methods to open and close the serial port:

def initialize name, baud, data, stop, parity

port_id = CommPortIdentifier.get_port_identifier name

data = JSerialPort.const_get "DATABITS_#{data}"

stop = JSerialPort.const_get "STOPBITS_#{stop}"

@port = port_id.open 'JRuby', 500

@port.set_serial_port_params baud, data, stop, parity

@in = @port.input_stream

@out = @port.output_stream

end

def close

@port.close

end

There’s not much going on here. We just do a little
translation from the Ruby API to the Java one, then set up
the serial port. Now, add the putc
method, which sends a single character:

def putc(char)

@out.write char[0, 1].to_java_bytes

end

This method is simple; we just take the first character of
the string passed to us, convert it to a Java
byte array, and tell the underlying
output stream to send it. getc is
the other half of the equation:

def getc

if @read_timeout

deadline = Time.now + @read_timeout / 1000.0

sleep 0.1 until @in.available > 0 || Time.now > deadline

end

@in.to_io.read(@in.available)[-1, 1] || ''

end

If this port has a read timeout defined, we want to wait for
that amount of time until data becomes available. Otherwise,
we proceed straight to the read (which will block
indefinitely).

To do the actual read, we could allocate a fixed-size Java
byte array, pass it to the underlying input stream, then
copy it into a Ruby string. That’d be a lot of boilerplate,
though. Instead, we use a method provided by JRuby called
to_io. This presents the Java
stream as a Ruby IO object, which
just uses plain Ruby strings. We read all the bytes
available, then return the last one.

That’s all the serial support we need to get this test code
running. We can finally turn our attention to the firmware.

The Firmware

You can write the Mood Hat’s firmware using the official Arduino
IDE, or you can use the techniques Maik Schmidt describes in
this issue of
PragPub.
Before you start, you’ll need to install the open source Bounce
library for reading button states
accurately.

First, create a few definitions you’ll use elsewhere in the
code:

#include <Bounce.h>

#define NUM_MOODS 5

#define NEUTRAL ((NUM_MOODS - 1) / 2)

#define UP '+'

#define DOWN '-'

#define QUERY '?'

#define NONE -1

int ledPins[NUM_MOODS] = {6, 7, 8, 9, 10};

int heartbeatPin = 13;

int upPin = 2;

int downPin = 3;

Bounce upButton (upPin, 100);

Bounce downButton(downPin, 100);

int mood = NEUTRAL;

When the firmware loads, we need to open up serial
communications and designate our input and output pins. This
code goes into a setup function, which
will get called automatically after the hardware is done
powering up:

void setup() {

for (int i = 0; i < NUM_MOODS; ++i) {

pinMode(ledPins[i], OUTPUT);

}

pinMode(upPin, INPUT);

pinMode(downPin, INPUT);

Serial.begin(9600);

setMood(NEUTRAL);

}

Typical Arduino practice is to write a short, fast
loop function. The development kit will
generate code that calls our loop
repeatedly.

This project doesn’t need to do much work on each pass. We’ll
just check the physical buttons and serial port for incoming
commands, then change the mood accordingly:

void loop() {

upButton.update();

downButton.update();

int button = (upButton.fallingEdge() ? UP :

(downButton.fallingEdge() ? DOWN : NONE));

int serial = (Serial.available() > 0 ? Serial.read() : NONE);

int event = (button != NONE ? button : serial);

switch (event) {

case UP:

setMood(mood + 1);

break;

case DOWN:

setMood(mood - 1);

break;

case QUERY:

Serial.print('0' + mood, BYTE);

break;

default:

setMood(event - '0');

break;

}

digitalWrite(heartbeatPin, HIGH);

delay(50);

digitalWrite(heartbeatPin, LOW);

delay(50);

}

Let’s walk through this step by step. First, we check both of
our input pins for a signal falling to near zero—such a
condition would indicate a button press. We also check the
serial port for incoming commands. If we see both buttons and
serial characters, buttons take precedence.

Next, we change the current state based on what the incoming
request was. Finally, we flash the on-board LED to give an
indication that the firmware is still running.

The implementation of setMood just
needs to do a little range checking and activate the appropriate LED:

void setMood(int newMood) {

if (newMood < 0 || newMood >= NUM_MOODS) {

return;

}

digitalWrite(ledPins[mood], LOW);

mood = newMood;

digitalWrite(ledPins[mood], HIGH);

}

With this code loaded on the device, try rerunning your tests.
You’ll need to set the
MOOD_HAT environment variable to
your Lilypad’s serial port, which might be
COM3 on Windows or
/dev/tty-usbserial-... on a Mac:

$ export MOOD_HAT=/dev/tty-usbserial-...

$ jruby -S cucumber features

Now, bask in the green glow of a stream of passing tests on your
monitor.

Wrapping Up

What is it that we’ve done here? Well, I wrote this test suite
for fun, just to see what kind of hoops one would have to go
through to test an embedded device from Ruby. So, mission
accomplished.

But despite my best intentions to keep this test suite purely
theoretical, it ended up having practical value. Before all the
LEDs were wired up, I wrote a temporary user interface that made
just one LED brighter or dimmer to indicate the mood. Later, I
could freely change the user interface and retest the underlying
logic.

The tests were also a good place to explore different edge cases
for the interface. For example: should the moods wrap around at
the extremes, so that furious is one step happier than ecstatic?
Definitely not, and this became clear as the different examples
took shape.

Finally, there’s the more obvious benefit of using Cucumber to
drive the design: the project now has a basic smoke test that
can quickly be run against the firmware. As you write your own
variant Mood Hat that’s way better than mine, you’ll be able to
refactor fearlessly. When you do, please drop by the PragPub
forums and show us what you’ve
built.

The source code for this project is available here. You can see a
video of the tests in action here.
Happy hacking!

Special thanks to Maik Schmidt for reviewing this article.

By day, Ian Dees slings code, tests, and puns at a
Portland-area test equipment manufacturer. By night, he dons
a cape and keeps watch as Sidekick Man, protecting the city
from closet monsters. Ian is the author of Scripted GUI Testing With Ruby and co-author of the upcoming Using JRuby.