I'm a keen electronics hobbyist, but I get frustrated using breadboards to prototype simple Arduino projects. The chaotic rats nest of jumper wires obscures the design, and many of my components don't fit into standard breadboards.
What I want is a Lego-style set of building blocks that can be slotted together. mCookie looks excellent - it is certainly has Lego-like modules, that can be easily connected together. However, I don't like it. If you get a prototype working with an mCookie kit, there's no obvious way to convert it into a real project (with component on a PCB or Veroboard).
So how about using the myriad "Shields" available for the Arduino? Stackable shields sound like a good idea, but there are some obvious problems. What if I want a "button" shield and an OLED shield. I can't stack them, because they must both be on top!!!
Also, my project often "run out of pins", and I need some kind of IO expansion. Maybe some shift registers chained together for more outputs than you'd ever need! Or maybe use multiplexers so that one analog pin can read from many analog inputs.
Arduinos aren't the only microcontrollers about, there are a huge range of similar products. Get down to the bare bones with a PIC chip. WiFi connection with a range of ESP8266 based boards. Just need more speed, and more pins, then perhaps the Blue Pill fits the bill. I want to prototype with every microprocessor.
My solution is to chain modules in a plane, so that every module is visible. Unlike the mCookie, my modules connect using simple angled header pins, so its easy to create your own using veroboard, or a PCB. All of the modules have 12 pin : VIN, VCC, GND, NC (not connected), and then 8 data pins. Isn't 8 data pins too few? Yes! So the master module has three such connectors, with all the analog pins on one edge, 8 digital pins on another edge and the third edge has the remaining pins, which I've labelled "Serial", because there are the SPI, I2C, UART.
Let's take a look at an example project : A home-brew universal remote control for your TV, Hi-Fi etc.
We start with a DIYduino module, which has the AT Mega 328 (the same microcontroller as the Arduino UNO). We will need some buttons, so we add a four button module to the "Digital" edge of the DIYduino (don't worry, we'll later add more buttons later!) For a flashy remote control, we can add an OLED display, so that it can show you which device is currently selected (TV, Amp DVD player etc). Slot in an I2C module to the "Serial" edge, and attack an I2C OLED display.
So far so good, everything is Lego-like, everything slots together without fuss and we can begin programming it using the standard Arduino IDE.
How about adding a real time clock, so that the OLED display can show the time? We could also write code to lock the controller after a curfew unless a special code is entered preventing the little ones watching TV when they should be asleep! We can use an I2C based real time clock, so that that it can be chained to the "Serial" edge after the module with the OLED display.
However, I haven't created a module specifically for a real time clock chip. I suggest two options :
- Add a mini breadboard module, and put the real time clock there.
- Add a "prototype" module, and solder the real time clock in place. I prefer the second approach, because then I can reuse the module when prototyping other projects, and I really don't like using breadboard. YMMV.
There's a problem though, the real time clock chip that I have uses 3.3V, and my DIYduino board uses 5V. No problem, just slot a level-shifter module between the two, and it will take care of the level conversions in both directions.
But we need more than four buttons. There are numerous solutions once again. The best long-term solution for large number of buttons is to create a "scanned" array of buttons, but for simplicity, I'll ignore that! Instead, I'll use a MCP23017 module. This is an IO expander, which adds 16 additional data lines, and is controlled via I2C. Using I2C, this means it co-exists with the OLED display and the real time clock, without using any more of the Arduino's pins.
The datasheet says that its operating voltage is 1.8V to 5.5V, so we could plug it in after the level converter module, or before it - it makes no difference. I'll insert it before the level shifter, so it will be operating at 5V.
This is where the modular nature of the system really pays off. The extra 16 data lines shoot off at 90 degrees, so we can add two sets of the four button modules on both sides making a cross shape (i.e. an extra 16 buttons, giving a total of 20 buttons).
Its starting to look big and cumbersome, and certainly not useful for a real world solution. For me, that's fine, because I only want to prototype the system and when I have everything working, I'll convert what I've built onto a PCB or vero board, and design a 3D printed case to house it.
Note, it's possible to add up to 8 of these modules, giving a ridiculous 8x16 = 512 extra data lines!
How about the infra red LED to control the TV? The simplest solution is to use a standard LED module, but instead of using a regular (visible) LED, use an infra red one instead. We can add that to the end on the "Digital" edge, as we have spare pins there.
At this point, I could take the project apart, throw it in a box, and it would take less than a minute to rebuild it. Yay!
Keen observers will have noticed that the 20 buttons are connected very differently. Four of them are connected directly to the Arduino's pins, and the other 16 are connected via an I2C interface.
Won't this make the code ugly? Won't it need vastly different code to test the state of the buttons? No of course not! Clever software can make all of the buttons appear identical. Instead of accessing the buttons directly using :
digitalRead( pinNumber )
I've created a set of input classes, which all share the same interface. At the beginning of the code, we create 4 "direct" buttons, and the 16 I2C buttons, and then put them into a single array (of size 20), and then we can loop over all 20 buttons.
As a bonus, these classes keep track of the button's state, and there are three distinct tests :
- Pressed (when the button changes from up to down)
- Down (when the button is down, regardless of the state the last time we checked)
- Released (when the button changes from down to up).
So, we can change the channel when a button is pressed (but don't keep changing the channel if the button is held down). We can also change the volume, when a button is down, and continue changing the volume while it is held down.
Have you ever used an old calculator and pressing a number sometimes acts like you pressed it twice? Well that's "bounce". The "modern" way of getting rid of bounce uses software (it can also be done in hardware, but that costs more). These classes also have an option of adding software anti-bounce. If a button appears to have been pressed twice in super human speed, then it just ignores the second press. FYI, when I wrote the code, I couldn't get even the cheapest of buttons to bounce, so to test it, I had to replace the button with a piece of frayed wire, which bounced like crazy. So I'm not sure if anti-bounce is really needed. Maybe when the switches get old it will be useful. Hmmm.
BTW, buttons can either have pull-up resistors, or pull-down resistors. In our "init" code, when we create the Button objects, we say whether they use pull-ups or pull-downs, and from then on, all of our code doesn't care how they are wired.
There are down sides to this approach. Firstly, the library adds overheads - it uses more program memory (both flash memory and RAM). Secondly, to add objects of different types into a single array, we have to use pointers. That's scary and complex for programmer newbies, so feel free to ignore the library if you want - you can still do things the long winded (but pointer-free) way if you prefer ;-) However, I highly recommend you use the library, because IMHO, it is the "right" way to do things!
But what happens if we do run out of memory? (We won't for this project, but let's pretend we do!) Then I suggest we upgrade from an Arduino to a beefier microcontroller. Let's replace the DIYduino with a "Blue Pill" module. Note that the blue pill has more pins, so instead of 3 edge connectors, it has 5. However, the format of the connections are the same, and the same modules can be plugged in just as before. Well actually, that's not quite true, because the blue pill uses 3.3 volts, so we can remove the level conversion module. Fortunately the OLED display and the IO Expander both handle 5V as well as 3.3V, so we don't need to change them.
The blue pill uses the same IDE as the Arduino (but you'll need to download extra code - google it!)
Similarly, we could have replaced the DIYduino with a WiFi enabled ESP8266, and all of the modules would still work :-)