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
2015-07-07
Previous article
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!

Previous article
Filed under
BDD
Cucumber
Embedded
Ruby
- Alexa on Rails - how to develop and test Alexa skills using Rails
- How to reconnect to a database when its connection was lost
- Oh, the places you'll go...
- Quick AWS IoT Setup and test
- Weekend warrior - MacRuby and rSpec, Mac OS X Lion, Xcode V4.3.2
- Writing an Alexa skill using Ruby and AWS Lambda (Part 0)
TDD
rSpec
Other Tags
API GW
AWS
- Programming ESP32 using MQTT with AWS and FreeRTOS
- Quick AWS IoT Setup and test
- Set up AWS API GW with a Typescript authorizer and logging
- Use AWS CodePipline to execute CloudFormation templates
- Use GitHub Actions to deploy your SPA hosted on Amazon S3
- Use an AWS CloudFormation script to create and host an SPA on S3 with SSL and apex/subdomain redirection using CloudFront
- Writing an Alexa skill using Ruby and AWS Lambda (Part 0)
ActiveRecord
Agile
- A review of software development metrics
- Agile programme management brief
- An alternative to current product development metrics
- An alternative to the current product development governance model
- Command & Control Management - The Party Killer
- Document Driven Development
- Inceptions revisited
- Managing multiple stakeholders
- Returns Driven Development
- The tip of the (good) iceberg
Alexa
Analysis
Ansible
BDD
BLE
C
CAB
CloudFormation
- Set up AWS API GW with a Typescript authorizer and logging
- Use AWS CodePipline to execute CloudFormation templates
- Use GitHub Actions to deploy your SPA hosted on Amazon S3
- Use an AWS CloudFormation script to create and host an SPA on S3 with SSL and apex/subdomain redirection using CloudFront
- Writing an Alexa skill using Ruby and AWS Lambda (Part 0)
CloudFront
CloudWatch
Cross-compile
Cucumber
DevOps
Devops
DotNet
Embedded
Fitbit
GNU
GitHub Actions
Governance
How-to
Inception
IoT
Javascript
Jest
Lambda
Mac OS X
- Bluetooth Low Energy (BLE) Implementing a peripheral on Mac OS X
- Cross-compiling for Raspberry Pi on a Mac and debugging using NetBeans
- Drobo will not mount in Finder
- Quickie - ssh dynamic port forwarding to avoid unsecured public networks
- Remote compilation, execution and debugging Raspberry Pi from a Mac using NetBeans
- Weekend warrior - MacRuby and rSpec, Mac OS X Lion, Xcode V4.3.2
MacRuby
Metrics
MySQL
NetBeans
Objective-C
PMO
Product Management
- A path to accelerating value realization
- A review of software development metrics
- Agile programme management brief
- An alternative to current product development metrics
- An alternative to the current product development governance model
- Express initiative kickoff formula
- Inceptions revisited
- Managing multiple stakeholders
- Plan for value delivery
- Pre-prod activity - Futurespective
- Value Stream Mapping
- When planning, it's not only about relative complexity
Programme management
Project Management
- A path to accelerating value realization
- A review of software development metrics
- Agile programme management brief
- An alternative to current product development metrics
- An alternative to the current product development governance model
- Command & Control Management - The Party Killer
- Express initiative kickoff formula
- Inceptions revisited
- Managing multiple stakeholders
- Plan for value delivery
- Pre-prod activity - Futurespective
- Value Stream Mapping
- When planning, it's not only about relative complexity
Quality Assurance
Rails
Raspberry Pi
Remote compilation
Remote debugging
Remote execution
Risk Assessment
Route 53
Ruby
- Alexa on Rails - how to develop and test Alexa skills using Rails
- Arduino programming using Ruby, Cucumber & rSpec
- How to reconnect to a database when its connection was lost
- Oh, the places you'll go...
- Quick AWS IoT Setup and test
- Weekend warrior - MacRuby and rSpec, Mac OS X Lion, Xcode V4.3.2
- Writing an Alexa skill using Ruby and AWS Lambda (Part 0)