One of the kids at the Pi club is making a dice game using a Raspberry Pi and a GertBoard. This has been a great little project because it involves both software and hardware. We started a few weeks ago by just plugging in the GertBoard with a view to flashing a couple of LEDs. and it is now nearing completion.
The GertBoard, as designed by Gert Van Loo of Broadcom
The GertBoard is the gold standard of plug in I/O cards for the Raspberry Pi. It has an impressive array of facilities such as a whole load of LEDs, buffers that can be made to be input or output buffers, the LEDs monitor inputs as well as outputs, and there are some switches.
Over one side is an Atmel chip which can be used as an Arduino – more about that in another blog post. Down the other side is a motor driver with external power supply, a relay driver for a number of relays with external supply, and an optional fit regulator to provide more power to the board to power all those hungry devices.
There are also A2D and D2A converters (Analogue/Digital converters).
You can buy the GertBoard pre-assembled from Farnell (which is how I bought it), and I heard the other day that you can get it in kit form too, so you can assemble it yourself. Most of the components on the board are through-hole components, so it’s not to hard to assemble.
I met Gert Van Loo a few weeks ago and he explained the design of the GertBoard to us – he said one of the hardest things was designing a manual that could be understood by everyone, and he enlisted the help of a friend to painstakingly check and double check all the exercises, all the test scripts, the diagrams, and everything, to make sure it all made sense.
Configuring the GertBoard
The GertBoard being very flexible, is highly configurable, and comes with a set of jumper links and patch cables to set up all the different combinations. A nice feature of the GertBoard manual is that Gert uses annotated PCB plots to show where to fit jumpers and links to use.
With flexibility comes power, and with power comes complexity. My one criticism of the GertBoard (if you can call it a criticism) is that it needs configuring. If you just plug it in to your Raspberry Pi, it doesn’t do anything useful. You have to fit jumpers and patch wires to configure it.
Actually, this caused the most problems, because although the manual is very well written, I have discovered that today’s kids are very impatient and don’t tend to read instructions. If something doesn’t work when they plug it in, they assume it is broken. It’s up to us adults to show the wisdom of patience and reading the manual, and systematically working through things.
In this case, to get the LEDs to work, you have to fit the OUT jumpers for the buffers, so that all the LEDs are driven by the 74 series buffers. Then, you have to patch from the bottom Raspberry Pi GPIO connector through to the BUF1..BUF12 connectors to route the (by default unconneced) Raspberry Pi GPIO pins through to the buffers.
One mistake we made was to incorrectly patch some wires through to the IN/OUT jumpers – this had the unusual side effect that it sort of worked, but we could not correctly sense inputs. Reading the manual (and for me, just checking the schematic) made it obvious what we had done wrong.
The other thing that we discovered much further down the line on another project, was that the 3V3 jumper is not fitted by default, and the buffers seem to find their power from the GPIOs – this causes some occasional reliability problems with the Pi, and also the LEDs are not as bright. Again, looking at the schematic and testing with a voltmeter proved this was the case and it was easily fixed. It does actually in the manual say that you have to fit this 3V3 jumper to bring the Pi power through to the board (and the Atmel chip), but the manual is quite big and the example patch diagrams don’t show it (as you might be using one of the alternative powering methods).
The need for a cookbook
This brings me to a small diversion – I find in the software industry that with tools like javadoc and doxygen (that automatically generate HTML documentation from the code), there is a similar problem. You get a lovely clickable manual of several thousand pages telling you all the functions you can call, what their parameters are and what their return types are. But, you don’t get a small self contained example that you can run from end to end to prove you understand how it works.
A lot of examples on the stackoverflow and other forum websites are like this – especially with java and python where you need to import libraries to get access to various code. Examples that advertise themselves as complete working examples are not really complete, you still have to work out how to top and tail it.
Myself I prefer a nice cookbook – I learnt to program in Java almost entirely from the O’Reilly Java Cookbook, which was so well thumbed that it fell apart and I had to buy another one. But the examples were self contained, small, not too contrived, and worked out of the box. I think hardware needs such a cookbook.
Controlling the GPIO
The real purpose of the dice game was to do something real, rather than just flashing an LED. We flashed an LED on day one, and that worked once we got all the jumpers correct – we needed a real project. Enter stage left…. the dice game.
The dice game is quite simple. You have 7 LEDs in a typical dice pattern, one GPIO driving each LED. The python program writes the patterns for 1 to 6 in sequence in a loop. You read a button, while the button is pressed it spins, when you let go of the button the spinning slows down and stops on a random number. That’s it.
Actually there were many steps to this and many real world learning opportunities and lessons learnt. The process we followed with this was identical to how I “breathe life” into a new bit of hardware when working in my day job.
Step 1: Hello World
Hello World is such an important first step. And it should be “hello world” without any hardware. A python program with a print statement:
This is important, because you discover how to do all of these things:
- turn the computer on and boot it up and login
- find the editor and create a new file
- work out how to enter and edit programs
- work out what statements you need to type in
- work out how to save the file
- work out how to run the program
- work out what a syntax error is and how to fix it
- work out where the output of your program comes
Step 2: LEDs on the dev kit (GertBoard)
The next step was to prove we could configure and drive 7 LEDs from a python program. Actually, the first step is “Hello Embedded World” – i.e. driving a single LED.
import RPi.GPIO as GPIO import time GPIO.setmode(GPIO.BCM) GPIO.setup(25, GPIO.OUT) #pin22 works while True: GPIO.output(25, True) time.sleep(0.25) GPIO.output(25, False) time.sleep(0.25)
We misread the manual at this point and use BCM mode (pin numbering on the BCM2835 chip) rather than BOARD mode, and that has filtered right through the program, so it’s too late to change at the moment. Go with it for now.
7 LEDs is a natural progression from this, but means we have to map out all the GPIO numbers and work out which GPIO in python controls which LED. Confusingly only some GPIO numbers are supported, and there are two numbering schemes – the pin number of the BCM chip, and the GPIO number on the Raspberry Pi (an they are different). Then there is the pin number on the Raspberry Pi header connector (which is different from the GPIO number), then we have to map this through to BUF1..BUF7 on the GertBoard (another numbering scheme)…. you get the picture.
At this point I always draw up a little spreadsheet mapping all the possible names through from end to end. We didn’t do this (well, to be fair, I suggested it, but the lad doing the project didn’t think it was necessary as it was working). Later in the project when the wires fell out, he decided it was a good idea to document all the pins!
Step 3: From LEDs to patterns
At this point the program started to get bigger than one page, so it was an excellent opportunity to introduce functions as a way to modularise the program. I believe that learning should be “problem lead” – so we had a problem (the program was too big to understand) so we used a technique (refactoring) to introduce a new concept (function modularisation), and it simplified the program.
The first thing to push into a function was the mapping from horrible GPIO numbers to LED numbers. We might have done this by constants, but decided to write a function first
def write(led, status): print(led, status) if (led == 1): GPIO.output(25, status) elif (led == 2): GPIO.output(23, status) elif (led == 3):
There are many advanced ways in python of doing this with lists, but a function is fine for now, we don’t want to introduce too many concepts in one go.
Now it is possible to turn on any LED by name:
But of course, we never turn them off, so another little utility function becomes useful at this point…
def alloff(): for x in range(1,8): write(x, False)
The original version of this was 7 lines of code, but it was a great opportunity to take a problem (lots of repeated similar code) and simplify it into a loop. Note how the python range() function counts from 1 to 7 even though we ask for 8 in the range (it stops at 8)
The last set of functions are now obvious:
def pattern3(): write(1, True) write(4, True) write(7, True)
Again, lots of things we could do here with python lists, but let’s not go there yet.
We now have a lovely little “domain specific language” – we have written our own vocabulary, and can say things like this:
alloff() pattern1() pattern3()
Step 4: Using our own vocabulary
Actually, we think we’re all set, then the first time we go to use our vocabulary of functions we realise there is still a bit missing. We want to spin our LEDs from 1 to 6 in a loop, which comes out like this:
while True: for x in range(1,7): alloff() if (x == 1): pattern1() elif (x==2): pattern2()
It’s a shame to have all that detail in the main program (I like to show people that the main program should really just tell the main story of what the program does, and push the detail into functions).
Another bit of refactoring gives us a new function:
def pattern(n): alloff() if (n==1): pattern1() elif (n==2): pattern2() ....
And our spinning main loop is now lovely:
while True: for x in range(1,7): pattern(x) time.sleep(0.25)
It just goes to show, a simple program that flashes a few LEDs not only can become quite large very quickly, but there are a number of great learning opportunities in there, and here’s the computer science we have already covered in this simple game:
- Hello World
- Hello Embedded World
- Controlling GPIOs
- Generating time delays
- The need to import pre-written libraries
- While loops (infinite)
- For loops
- If and elif statements
- Functions with parameters
- Abstraction (separating “what” and “how”)
Out of all of these, I think my favorite of all time is “separating the what from the how”. This, in my opinion, is the single simplest technique that I have learnt as a software engineer, that has had the biggest impact. It takes a minute to learn, but a lifetime to master. With it, you can take small simple programs and turn them into huge complex programs, and they are still readable, easy to maintain and extend, easy to debug, and easy to reuse parts on other projects.
Abstraction is a key computer science concept – don’t teach programming, teach computer science!
Our dice game is nearly finished, but there’s still a lot to write about, such as getting the button to work, breaking and re-fixing the hardware (and how we tested it with a little manufacturing-test script), adding randomisation, adding a “slow down and stop” feature, and then attaching it to our web server. That’s far too much to talk about here, so we’ll follow up later with another installment.