The project

This project serves as a sanity check that all is in order with the hardware, without the need to write on-board code using the IDE nor use the avr toolchain. What better tool than Ruby to do so?

The first thing we’ll do is to assure that the board and its built-in LED are responsive. Let’s define the behviour we would like, and implement it using Cucumber, in true BDD fashion:

[code language="ruby"]Feature:
Assure board led is responsive

Background:
Given the board is connected

Scenario: Turn led on
When I issue the led "On" command
Then the led is "On"

Scenario: Turn led off
When I issue the led "Off" command
Then the led is "Off"
[/code]

The step implementation follows:

[code language="ruby"]require 'driver'

Given(/^the board is connected$/) do
@driver ||= Driver.new
end

When(/^I issue the led "([^"]*)" command$/) do |command|
value = string_to_val command
expect(@driver.set_led_state value).to be value
end

Then(/^the led is "([^"]*)"$/) do |state|
expect(@driver.get_led_state).to eq string_to_val state
end

def string_to_val state
case state.downcase
when 'on'
my_state = ON
when 'off'
my_state = OFF
end
end[/code]

Some things to note:

  • We don’t have an assertion on @driver ||= Driver.new because the driver will simulate a connection in case the phyical board is disconnected or unavailable due to disrupted communications.
  • The user communicates using the words “on” and “off”, which are translated to ON and OFF for internal use.

This test will fail, of course, as we have yet to define the Driver class and we drop to rSpec, in TDD fashion:

[code language="ruby"]require 'driver'

describe "led functions" do
before(:each) do
@driver = Driver.new
end

it "turns the led on" do
expect(@driver.set_led_state ON).to eq ON
end

it "turns the led off" do
expect(@driver.set_led_state OFF).to eq OFF
end

it "blinks" do
@driver.blink 3
end
end[/code]

This too fails, of course, and we implement Driver thus:

[code language="ruby"]class Driver
def initialize
@arduino ||= ArduinoFirmata.connect nil, :bps => 57600
rescue Exception => ex
puts "Simulating. #{ex.message}" if @arduino.nil?
end
def set_led_state state
result = @arduino.digital_write(LED_PIN, state)
rescue Exception => ex
@state = state
state
end

def get_led_state
@arduino.output_digital_read(LED_PIN)
rescue Exception => ex
@state
end

def blink num
(0..num).each do
set_led_state ON
sleep 0.5
set_led_state OFF
sleep 0.5
end
end
end[/code]

 

Some things to note:

  • I am using the arduino_firmata gem, please see the Gemfile for details.
  • The initialize method catches the exception thrown when the Arduino is not connected, as the other methods do, in order to simulate the board in such circumstances. The simulation is always succeeds, by the way, and was coded to allow development without the board connected.
  • arduino.output_digital_read is a monkey-patch to the gem, as I could not find a way to query the board if an output pin was on or off:

[code language="ruby"]module ArduinoFirmata
class Arduino
def output_digital_read(pin)
raise ArgumentError, "invalid pin number (#{pin})" if pin.class != Fixnum or pin < 0
(@digital_output_data[pin >> 3] >> (pin & 0x07)) & 0x01 > 0 ? ON : OFF
end
end
end[/code]

All green

Having implemented the code, the tests should now pass and running rake again will run both Cucumber and rSpec, yielding:

[code language="plain"]~/Documents/projects/arduino (master)$ rake
/Users/ThoughtWorks/.rvm/rubies/ruby-2.2.1/bin/ruby -I/Users/ThoughtWorks/.rvm/gems/ruby-2.2.1/gems/rspec-support-3.3.0/lib:/Users/ThoughtWorks/.rvm/gems/ruby-2.2.1/gems/rspec-core-3.3.1/lib /Users/ThoughtWorks/.rvm/gems/ruby-2.2.1/gems/rspec-core-3.3.1/exe/rspec --pattern spec/\*\*\{,/\*/\*\*\}/\*_spec.rb
...

Finished in 7.56 seconds (files took 0.27749 seconds to load)
3 examples, 0 failures

/Users/ThoughtWorks/.rvm/rubies/ruby-2.2.1/bin/ruby -S bundle exec cucumber
Feature:
Assure board led is responsive

Background: # features/initial.feature:4
Given the board is connected # features/step_definitions/initial_steps.rb:3

Scenario: Turn led on # features/initial.feature:7
When I issue the led "On" command # features/step_definitions/initial_steps.rb:7
Then the led is "On" # features/step_definitions/initial_steps.rb:12

Scenario: Turn led off # features/initial.feature:11
When I issue the led "Off" command # features/step_definitions/initial_steps.rb:7
Then the led is "Off" # features/step_definitions/initial_steps.rb:12

2 scenarios (2 passed)
6 steps (6 passed)
0m4.579s
[/code]

 

Make this better!

The project is here. Please feel free to fork and contribute.

Conclusion

How much is “good enough”? If you notice, the assertions are implemented using the data structure exposed by arduino_firmata, not with a call to the board itself. This is always a tradeoff in testing. How far should we go? For this project, testing via data structure is “good enough”. For a medical application, or something that flies a plane, it’s obviously not good enough and we would have to assert on an electric current flowing to the LED. And again, who is to assure us that the LED is actually emitting light?

There’s not much else we can do with a standalone Arduino without any periferals connected, but it’s enough to make sure that everything is set up correctly for future development.

Disclaimer

This installment was to show a quick-and-dirty sanity check without bothering to flash the device.

Afterword

The testing and writing of this installment were made while flying to Barcelona, hoping that fellow passengers would not freak out seeing wires and blinking lights mid-flight.

Happy Arduinoing!