August 2017 Archives

How the RPi::WiringPi distribution is tested

So I'm gearing up to write a "Howto Raspberry Pi with Perl" multi-chapter tutorial, and as I finalize a few last things, I thought I'd put together how all of the software is laid out and is continuously tested.

The RPi::WiringPi distribution is essentially a class that provides access to external sub distributions, and provides several benefits such as maintaining a registry of in-use GPIO pins, and ensuring your Pi is cleaned up back to default on exit, or if an error or signal is caught. The sub modules do none of those things.

I use the automation and dispatching capabilities of my Test::BrewBuild software to handle the test management. This software runs your unit tests on any/all Perlbrew and/or Berrybrew installed instances, with the ability to dispatch your test requests over the network to remote machines. I'm not getting into those details, just know I use the bbtester binary to listen for test requests, and the bbdispatch binary to send them, both on the same Raspberry Pi hardware.

So, I have my base hardware platform layout (full image here, real-world pic here):

RPi::WiringPi test platform

I'll explain a couple of things. First, there are no "sensors" or the like being tested. I test the core aspects that do sensor work.

I'll start from top-left to top-right, circle down to bottom-right, then go back left. Top-right is an Arduino Uno. It's sole purpose is to test the I2C communication functionality, which is provided by the RPi::I2C distribution. There's a basic hardware-emulation Arduino sketch that presents a hardware-like situation, to allow testing of base I2C functions.

Next we have the Pi. I don't have the pins connected exactly like this, but this was the easiest method to portray it. Below to the bottom right, there's a 74HC595 shift register, which effectively turns three GPIO pins into eight, and can be cascaded four times, for a total of 32 extra GPIO using only three physical GPIO on the board. I have only one here for testing.

To the left of that is an MCP4922 Digital-to-Analog converter (DAC), which takes a single digital input, and translates that into two analog outputs. RPi::WiringPi provides the RPi::MCP4922 object for this.

Then we've got an MCP3008 Analog-to-Digital converter (ADC), which does the opposite of the DAC, it provides eight analog inputs that can be read through a single digital channel. RPi::WiringPi handles this via the RPi::ADC::MCP3008 distribution.

Then, next to that, the blue "breakout" is another ADC, the ADS1115 by Adafruit, which has four analog inputs (compared to the MCP3008 above which has eight). This is an I2C device, so it provides me further testing in that manner, and its also used for reading Pulse Width Modulation (PWM) testing from the Pi itself, and for the servo() method built into RPi::WiringPi. It's fronted by the RPi::ADC::ADS distribution.

To the breadboard left of the ADS1115 is a two row, 16 column LCD, managed by RPi::LCD. This distribution can handle both the 16x2 and 20x4 models. The purpose of this hardware is to test the output, but also displays overall test output provided by the Test::BrewBuild distribution, as it has a small hack in it specifically to do this not just for the RPi::WiringPi dist, but any distribution you send to the Test::BrewBuild tester.

Now, to how things are tested. First, I start the tester in auto mode:

bbtester start -a

That puts the test listener in the background as a service (although this is Unix, it also works on Windows), then I bbdispatch an automated test run to that tester, specifying the rpi-wiringpi Github repository in the most simplistic of ways (putting this in crontab is the way to go). I've got a single Perl instance installed, and the result line will repeat continuously when run from the CLI (default is wait 60 seconds between run checks). In the bbtester start line above, it will check Github to see if any new commits have been pushed, and only run the tests if there's been an update:

bbdispatch -t localhost -r stevieb9/rpi-wiringpi -a
5.24.1 :: PASS

...install a couple more perls on the Pi:

brewbuild -i 5.26.0,5.16.3

Stop the dispatcher, and restart it:

bbdispatch -t localhost -r stevieb9/rpi-wiringpi -a

5.16.3 :: PASS
5.24.1 :: PASS
5.26.0 :: PASS

The Test::BrewBuild software is much more capable than these simple examples, but I digress. What I will say that running bbdispatch with the --rpi flag allows you to test any distribution (that's available on Github), and have your output displayed on an LCD screen (including date/time, PASS/FAIL, run count as well as the commit sum).

I run this setup 24 hours a day, seven days a week, and against eight versions of Perl, the Pi hardware test platform can do about 16-20 passes an hour (in a mode that tests regardless if new commits have been pushed).

Both the bbtester and bbdispatch software have extensive logging, and both can be run in "foreground" mode, particularly for troubleshooting. Here's a full-blown example of the dispatcher output, and the tester output.

I have not enabled debug levels for the actual brewbuild command which is the command that actually does the testing (called from within bbtester), as this output will be long enough for this example. In this run, I'm using the --csum flag to bbtester, so it tests on each pass, even if a new commit hasn't been pushed.

The bbdispatch debug output:

bbdispatch -t localhost -r stevieb9/rpi-wiringpi -a -d 7 --rpi

[2017-08-17 18:29:44.231][lvl 5][Dispatch.new] instantiating new object
[2017-08-17 18:29:44.232][lvl 5][Dispatch.auto] Commencing auto run dispatch sequence
[2017-08-17 18:29:44.233][lvl 7][Dispatch.auto] continuous integration mode enabled
[2017-08-17 18:29:44.233][lvl 6][Dispatch.auto] COMMENCING RUN: 1

[2017-08-17 18:29:44.233][lvl 7][Dispatch.dispatch] working on testers: localhost
[2017-08-17 18:29:44.234][lvl 5][Dispatch.dispatch] configured localhost with port 7800
[2017-08-17 18:29:44.235][lvl 7][Dispatch._fork] spinning up tester: localhost
[2017-08-17 18:29:44.241][lvl 7][Dispatch._fork.localhost] tester localhost socket created ok
[2017-08-17 18:29:44.242][lvl 7][Dispatch._fork.localhost] syn "localhost" sent
[2017-08-17 18:29:44.243][lvl 7][Dispatch._fork.localhost] ack "localhost" received
[2017-08-17 18:29:44.243][lvl 7][Dispatch._fork.localhost] sent command: brewbuild
[2017-08-17 18:29:44.244][lvl 7][Dispatch._fork.localhost] received "ok"
[2017-08-17 18:29:44.245][lvl 5][Dispatch._fork.localhost] repo was sent in, and set to: https://github.com/stevieb9/rpi-wiringpi
[2017-08-17 18:29:44.245][lvl 6][Dispatch._fork.localhost] dispatching out to and waiting for tester: 'localhost'...
[2017-08-17 18:30:28.997][lvl 7][Dispatch._fork.localhost] tester work has concluded
[2017-08-17 18:30:29.288][lvl 5][Dispatch._fork] tester: localhost finished
[2017-08-17 18:30:29.290][lvl 7][Dispatch.dispatch] returning results if available...
5.24.1 :: PASS
[2017-08-17 18:30:29.290][lvl 5][Dispatch.auto] auto run status: PASS
[2017-08-17 18:30:29.291][lvl 7][Dispatch.auto] RPi LCD test result output enabled
[2017-08-17 18:30:30.211][lvl 6][Dispatch.auto] auto run complete. Sleeping for 60 seconds, then restarting if more runs required

Here's the bbtester output:

bbtester --fg --stdout -a -c -d 7

[2017-08-17 18:29:34.312][lvl 7][Tester] logging to STDOUT
[2017-08-17 18:29:34.313][lvl 5][Tester.new] instantiating new Test::BrewBuild::Tester object
[2017-08-17 18:29:34.313][lvl 7][Tester.new] args:
[2017-08-17 18:29:34.313][lvl 7][Tester.new] 
auto: 1
stdout: 1
debug: 7
csum: 1

[2017-08-17 18:29:34.315][lvl 6][Tester.listen] successfully created network socket on IP 0.0.0.0 and port 7800
[2017-08-17 18:29:34.316][lvl 7][Tester.listen] 0.0.0.0 now accepting incoming connections
[2017-08-17 18:29:34.316][lvl 7][Tester.listen] work dir is: /home/pi/brewbuild
[2017-08-17 18:29:34.317][lvl 7][Tester.listen] chdir to work dir: /home/pi/brewbuild
[2017-08-17 18:29:34.317][lvl 7][Tester.listen] TESTER: 0.0.0.0 PLATFORM: armv7l-linux
[2017-08-17 18:29:34.317][lvl 7][Tester.listen] waiting for a connection...

[2017-08-17 18:29:44.242][lvl 7][Tester.listen] received ack: localhost
[2017-08-17 18:29:44.243][lvl 7][Tester.listen] returned ack: localhost
[2017-08-17 18:29:44.243][lvl 7][Tester.listen] received cmd: brewbuild
[2017-08-17 18:29:44.244][lvl 7][Tester.listen] sending 'ok'
[2017-08-17 18:29:44.246][lvl 7][Tester.listen] received repo: https://github.com/stevieb9/rpi-wiringpi
[2017-08-17 18:29:44.246][lvl 5][Git.new] instantiating new object
[2017-08-17 18:29:44.247][lvl 6][Git.git] git command set to 'git'
[2017-08-17 18:29:44.247][lvl 7][Tester.listen] using Git: git
[2017-08-17 18:29:44.247][lvl 7][Tester.listen] before all checks, repo set to https://github.com/stevieb9/rpi-wiringpi
[2017-08-17 18:29:44.248][lvl 6][Git.name] converting repository link to repo name
[2017-08-17 18:29:44.248][lvl 6][Git.name] repo link converted to rpi-wiringpi
[2017-08-17 18:29:44.248][lvl 7][Tester.listen] chdir to: /home/pi/brewbuild/rpi-wiringpi
[2017-08-17 18:29:44.249][lvl 7][Tester.listen] repo rpi-wiringpi exists
[2017-08-17 18:29:44.249][lvl 6][Tester.listen] in auto mode
[2017-08-17 18:29:44.259][lvl 6][Git.link] found https://github.com/stevieb9/rpi-wiringpi for the repo
[2017-08-17 18:29:44.260][lvl 7][Git.status] checking git status
[2017-08-17 18:29:44.280][lvl 6][Git.link] found https://github.com/stevieb9/rpi-wiringpi for the repo
[2017-08-17 18:29:44.281][lvl 6][Git.revision.revision] initiating git revision
[2017-08-17 18:29:44.281][lvl 6][Git.revision] local: 'rev-parse HEAD' sent
[2017-08-17 18:29:44.291][lvl 5][Git.revision] commit checksum: b21c66e27e3972eacee1acb9a71def478c66b869
[2017-08-17 18:29:44.301][lvl 6][Git.link] found https://github.com/stevieb9/rpi-wiringpi for the repo
[2017-08-17 18:29:44.301][lvl 6][Git.revision.revision] initiating git revision
[2017-08-17 18:29:44.301][lvl 6][Git.revision] remote: 'ls-remote https://github.com/stevieb9/rpi-wiringpi' sent
[2017-08-17 18:29:44.946][lvl 5][Git.revision] commit checksum: b21c66e27e3972eacee1acb9a71def478c66b869
[2017-08-17 18:29:44.946][lvl 7][Tester.listen] 
Git check:
    status: 1
    local:  b21c66e27e3972eacee1acb9a71def478c66b869
    remote: b21c66e27e3972eacee1acb9a71def478c66b869
[2017-08-17 18:29:44.947][lvl 7][Tester.listen] commit checksums are equal; no need to pull
[2017-08-17 18:29:44.949][lvl 5][Tester.listen] COMMENCING TEST RUN; no args (default)
[2017-08-17 18:29:44.954][lvl 7][Tester.listen] executing test()
[2017-08-17 18:30:28.994][lvl 7][Tester.listen] chdir to: /home/pi/brewbuild/rpi-wiringpi/bblog
[2017-08-17 18:30:28.994][lvl 7][Tester.listen] no log files generated, nothing to process
[2017-08-17 18:30:28.995][lvl 7][Tester.listen] chdir to: /home/pi/brewbuild/rpi-wiringpi
[2017-08-17 18:30:28.995][lvl 7][Tester.listen] removing log dir: /home/pi/brewbuild/rpi-wiringpi/bblog
[2017-08-17 18:30:28.996][lvl 5][Tester.listen] storing and sending results back to dispatcher
[2017-08-17 18:30:28.999][lvl 7][Tester.listen] work dir is: /home/pi/brewbuild
[2017-08-17 18:30:28.999][lvl 7][Tester.listen] chdir to work dir: /home/pi/brewbuild
[2017-08-17 18:30:29.000][lvl 7][Tester.listen] TESTER: 0.0.0.0 PLATFORM: armv7l-linux
[2017-08-17 18:30:29.001][lvl 7][Tester.listen] waiting for a connection...

Of course, normally output isn't displayed in this regard, but that's a general overview of how my RPi::WiringPi collection of distributions is tested all day, every day, on my custom CI test platform.

Here's the full list of RPI:: related distributions:

  • RPi::ADC::ADS provides access to the ADS1xxx family of analog to digital converters
  • RPi::ADC::MCP3008 provides access to MCP300x series analog to digital converters
  • RPi::BMP180 easy reading of the x80 series barometric pressure sensors
  • RPi::Const standard RPi and other typical constant names
  • RPi::DAC::MCP4922 access to the MCP4xxx series digital to analog converters
  • RPi::DHT11 provides sampled and converted results from DHTxx hygrometer/temperature sensors
  • RPi::MCP4xxxx provides control over the MCP4xxxx series digital potentiometers
  • RPi::HCRS04 read distances with this ultrasonic distance sensor
  • RPi::I2C access devices through I2C
  • RPi::LCD control 16x2 or 20x4 LCD screens
  • RPi::Pin complete management of GPIO pins
  • RPi::Serial read/write to/from the serial interface
  • RPi::SPI comms with devices on the Serial Peripheral Interface

Here's a pic of what the hardware looks like. I was ready to get boards printed, but had to back out at the last minute, so it's still on a breadboard for now.

About Steve Bertrand

user-pic Just Another Perl Hacker