Monday, July 13, 2020

Unit Testing and Arduino

When I worked in software, I was an early adopter of agile software development (eXtreme Programming) and so have always loved unit tests and TDD. Like many people from a software background, when I first started programming Arduino, my instinct was to write frameworks, patternize and objectify my code. Then I realised that I have 32k of program space to play with and a really big program might stretch to 100 lines of code (shock horror). So I adjusted my big-software thinking and like everyone else drew the line at functional decomposition and occasional library writing.


For the fun of it, I recently decided to make a calculator (pocket calculator if you happen to have huge pockets) from the schematic design up.

For this project I wanted to use my new favourite low cost microcontroller (the ATTINY816) with Arduino IDE using Spence Konde's rather fantasic Arduino core.

To my surprise, making a calculator that actually does arithmetic well is a lot harder than expected. One obvious hurdle is that float in Arduino C is only 32 bits. So once your numbers get to about 7 or 8 digits they become disconcertingly approximated. So, I thought, I'm (or used to be) a computer scientist, I'll just implement my own floating point representation with more precision than you can shake a stick at. So, I did, as a C++ library (I'll put it on Github when I've got it working and tidied up).

However, for the first time, since I had first started using Arduino, I felt the need to write unit tests, to make sure that my number class was doing arithmetic and generating an 8 character string plus decimal point position information that I could then easily map onto an 8 digit 7-segment display.

Unit Tests

If you haven't used unit testing before, then the basic idea is that you write a load of test functions (the more the better) that exercise the code being tested in some way and compare the outcome with the expected outcome. So, for example in a number class, you might want to check that when you add 2 and 2 you get 4, but also you write a test to make sure that when you add -123.45 and 0.01 you get -123.44 and not -123.46.

Having a unit test suite behind your code means that if you change your code to fix one bug, when you run the tests it will immediately tell you if you have broken something else in doing so. As you build up your suite of tests, you get more and more test coverage for the tricky situations that may uncover bugs.

My Solution for Arduino

The solution I came up with is very specific to the problem, so, I'll try and include some general principals rather than just the code.

Firstly, I created a separate test sketch, specifically for the purpose of testing my number class, without any of the other code related to keyboard scanning and display refresh.

The second thing I did was to get out an Arduino Uno (well actually I used a MonkMakesDuino) because one of the great things about the Uno, is that compiling and uploading a sketch is MUCH quicker than the likes of an ESP32 or indeed the ATTiny816 programmed using UPDI. So, the round-trip time when adding tests or fixing code is greatly reduced.

I used the Serial Monitor to view the results of the unit tests, and a test pass would simply marked by a single line giving the name of the test that passed. A test failure, would include as much information as possible to help with debugging.

Here's the start of my test sketch:

#include "CalcNum.h"

char digits[] = "........";
const int numDigits = 8;

void setup() {

void loop() {}

My number class is CalcNum (imaginative right!).
digits[] is a data structure used by CalcNum in its writeDigits() method that prepares a string for easy mapping onto an 8 digit 7-segment display.

All the test functions to be called are then listed out in the setup function, as we only need to run them once.

The first of these (testNums()) tests the representation of numbers themselves rather than arithmetic, so lets skip on to the test function testAdd1():

void testAdd1() {
  CalcNum x = CalcNum(12, 0);
  CalcNum y = CalcNum(3, 0);
  CalcNum z;
  CalcNum::add(x, y, &z);
  test("testAdd1", z, "      15", 7);

This function defines two numbers (x and y) using an exponent form (x = 12 x 10^0 = 12).
Adds them together and then calls the general purpose function test to see if the result was as expected.

As an aside, I haven't used C++ operator overloading in my number class, as this would inevitably lead to the need for dynamic memory allocation, which I avoid like the plague when working on embedded systems.

So, what are the parameters to test?

The first is a string, that is the name of the test, the second is the CalcNum to be checked. the third is the expected result string from calling writeDigits() - in this case 15 with leading spaces. The final parameter is the expected position of the decimal point on the display (zero indexed, left to right).

Here's what the function test looks like:

void test(char *testName, CalcNum z, char *expected, int expectedDP) {
  z.writeDigits(numDigits, digits);
  int dp = z.dpPos(numDigits);
  if (strcmp(digits, expected) == 0 && dp == expectedDP) {
    pass(testName, expected, expectedDP, z);
  else {
    fail(testName, expected, expectedDP, z);

As you can see, the test function compares the calculated and expected 8 digit string and decimal point positions and if the match, calls pass and if they don't calls fail.

void pass(char *testName, char *expected, int expectedDP, CalcNum z) {
  Serial.print("PASS: "); Serial.println(testName);
  //report(expected, dp, expectedDP, z);

The function pass just prints out a message that the test passed, along with the name of the passing test. Note the commented out call to report. Sometimes this gets commented back in to shed light on why one test passed when another didn't.

The fail function is much the same as pass, but with a different starting message.

void fail(char *testName, char *expected, int expectedDP, CalcNum z) {
  Serial.print("**** FAIL: "); Serial.println(testName);
  report(expected, expectedDP, z);

The report function just prints out as much useful information about the result as possible, to help me fix the bug.

void report(char *expected, int expectedDP, CalcNum z) {
  Serial.print("got digits["); Serial.print(digits); Serial.print("]dp="); Serial.print(z.dpPos(numDigits));
  Serial.print("\t expected ["); 
  Serial.print(expected);Serial.print("]dp="); Serial.println(expectedDP);
  Serial.print("float: "); Serial.println(z.toFloat(), 10);
  Serial.print("m: "); Serial.print(z.m); Serial.print(" e:"); Serial.println(z.e);


Once this project is written, I probably won't write any more tests until I meet a similar project for which 'it seems to work ok' is not sufficient.

However, its really easy just to put together some tests if you need to. For me, it wan't even worth looking to see if anyone had made a framework to do this and then taking the trouble to work out how to use it.

I hope this write-up will help you if you find yourself needing some Arduino unit tests!

Sunday, April 12, 2020

Making Protective Face Shields with a K40 Laser Cutter - Part 3

Part 1 Part 2

We finally had a chance for the household to go ahead and make some shields.


This was tricky, I don't have gloves, but do have a face mask each that covers our mouths and noses. So, I would not count these masks as sterile and as such have been careful to pass on that information to recipients.

So we had frequent hand washes and (for what its worth) baby wipes to wipe down the plastic that gets a bit of a dirty residue from the smoke produced during cutting. 

Fortunately the masks are easy to clean.

Finding Recipients

While waiting for the laser cutter to do its thing (about 8 mins / shield) there was plenty of time to open up Google Maps and search on care homes nearby. I emailed (where there was an email address on the website) or otherwise phoned a total of 5 homes within walking distance. 
One asked for 12 on the phone and was very grateful as they had no face shields at all. Another said the same on email (yesterdays delivery). And already we have two more deliveries to make tomorrow to care homes and GP surgeries. There is clearly a huge demand here in the UK.

What Next?

The next step is to find a cheaper source of material than eBay! and find out if there is a better way to distribute.

Although it seems that hospitals are the main focus and care homes and GP surgeries have rather been left to fend for themselves. So I feel that providing them with masks, fits both my small scale of production and a need that if it reduces infections will lighten the hospital load at the front end.

Tuesday, April 7, 2020

Making Protective Face Shields with a K40 Laser Cutter - Part 2

In part 1 of this post I looked at laser-cutting the plastic band. In this post, we can now attach the clear A4 sheet to the shield.

I used this from eBay with a thickness of 140 micros for the visor, which seems about right.

The A4 clear 'acetate' sheet fits in landscape aspect ratio and is punched with 4 holes. I used one of these:

.. and put one pair of holes in the middle, as you would a normal sheet of paper for a ring-binder. I then carefully put the other holes in at the edges by lining up one side of the hole punch puncher with an existing hole. This is fiddly, so 
Edit: This works fine,  didn't realize that if you push the guide rail on the side most of the way until it says A5 (on ours) punch one set of holes then flip the acetate sheet through 180 degrees and punch again, the holes are in the right places. Try this on paper first to avoid wasting acetate.

I have ordered one of these: which should make all the holes in one go.

The little plastic tabs on the strap fit snugly into the holes and hold the 'acetate' sheet in place.


The inner band holding the head away from the visor works well and prevents misting up. The whole thing was comfortable even wearing glasses. I'm sure its a way from rigorous professional protection equipment, but I think its a lot better than nothing, or just a face mask on its own.

It's also entirely plastic and I would have thought washable. I have three of these now, which we are going to send to medical and carer friends of ours who have expressed an interest.

Now I know its possible on A4, I'm ordering more materials and we'll get cracking. I'll come back with a part 3 on manufacturing cleanly and experiences in donating these.

Other Resources

This all stems from the work here https://community.andmirrors.co.uk/t/covid-19-laser-cut-face-shield/168 and https://www.kitronik.co.uk/blog/kitronik-make-nhs-frontline-ppe-visors/

There are lots of other projects and designs out there and Google will stay a lot more up to date than any list I put here.

Monday, April 6, 2020

Making Protective Face Shields with a K40 Laser Cutter - Part 1

This blog documents my efforts to make protective face masks using a low cost K40 laser cutter using Kitroniks visor design.

The A4 'acetate' sheets for the transparent shielding bit haven't arrived yet, so consider this post a Part 1 , dealing just with making the strap shown below. The clear visor part will come later.

Early indications: Yes, it's feasible - if you have a K40, give it a go.


These laser cutters are very low cost, but only have a small bed (about 330x220mm useable).

Their bed is much too small to take the files that Kitronik designed for their cutters, but if you have a bigger laser cutter then don't mess around with A4 it will be much more efficient to go to the full-size design here.

Design Files

Download the design files for A4 from github here or direct zip download here. The original DXF file was created by Dave at Kitronik and kindly passed on to me by Loraine Underwood, a fellow K40 owner (hers is in bit ATM)

The file A4 face shield band.dxf is the original file from Dave. I use the open source K40 Whisperer to control my K40, and this expects files to be an SVG, with vector cut lines to be in red 0.1mm wide. So the file A4 face shield band k40 whisperer.svg is an SVG version of the original with the lines made suitable for K40 Whisperer. I have also spaced things out a bit, because the K40 can be a bit 'melty' and close together lines can cause problems.

If you make a version of this for the default and highly suspect original K40 software, then please let me know and I'll add it to the repository.

The really nice thing about this design is that it just uses two materials, 0.5mm Polypropylene for the strap material and standard clear A4 OHP acetates (used full size without the need to cut). Whats more everything just clips together, no need for glue or staples.


I bought a few sheets of Polypropylene Plastic Sheets 0.5mm here. It seems to be quite widely available as A4 to try. I now need to wait for a load more. Kitronic also sell this material, but not as A4. However buying some big sheets and cutting them up may work out cheaper.

Eventually you will need the clear 'acetate' sheets to use as a visor - Kitronic recommend this. My sheets haven't arrived yet, they will be discussed in part 2 of this blog.

Cutting Parameters

Your parameters will vary so, please cut a small test piece before wasting material. This was what I found worked best for my K40. Speed for vector cut 15mm, power (set by the knob on the k40) about 50%. My beam seems to diverge at higher powers than that, but I probably don't have the focus right.

The material is quite light, and I have seen people use small magnets in this situation to keep the parts flat to the bed. I didn't use magnets and it worked ok for me. But if yours starts flapping about, you know what to do.


Please note that what I am describing here is making the first visor so, I am paying no attention to cleanliness at all. When making these for real, I think it would be sensible to be gloved and masked.

It took me a while to figure out how the pieces fit together, so here's what I learned. I have put numbers on each piece to make it easier to reference the parts.

1. Here's the cut sheet.

There is a bit of singing and in places the plastic had stuck itself together again. But I found it separated from the sheet easily enough.

2. The Pieces Labelled

For your first make, I'd suggest marking each piece like this (a sharpie will do). Or you may just be a lot better at puzzles than me and consider all this unnecessary!

3. Join Pieces 1 and 2
Bend the tabs of piece 1 and push them through the horizontal slots in part 2.

4. Join Pieces 2 and 3
Piece 3 feeds through the two end vertical slots in piece 2 like a belt through a buckle.

5. Join Pieces 1 and 4
Piece 4 fits on to piece 1 in much the same way.

6. Outer Strap
You should now have a long strap.

7. Inner strap
The inner strap is required to keep the clear part away from the wearer's face. Piece 5 accomplishes this by making a bow in the main strap.

Start by joining piece 5 to piece 2 and then to piece 1 using the horizontal slots.

8. Final part
The basic shape is there now, so it just remains to attach the buckle that connects the two ends of the strap together.
Join 4 and 6 together first. These have two positions, presumably for big and small heads. Fine adjustment of size uses piece 3.

My Donation Plans

Making these on a K40 is slow and so I don't expect to be making more than a few hundred of these visors at most. So, personally, we will be donating these to care-workers and other people we know who are desperate for protective wear and not too worried about the quality on the basis that something is better than nothing. I think, I'll leave supplying to the NHS as something for the big players (with big laser cutters) to do. But that's just what I think.

I hope to post part 2 of this tomorrow, if the clear plastic arrives.

Good luck and let me know how it goes.

Tuesday, February 11, 2020

Installing OpenCV 4 on a Raspberry Pi 4

I have recently been updating the Computer Vision chapter of the third edition of my Raspberry Pi Cookbook. Way back when I was writing the seconds edition, I decided to use the SimpleCV wrapper to OpenCV because it was -- well very simple to use.

SimpleCV is no longer maintained and in any case requires Python2. So the chapter's code needed to be changed to use OpenCV directly. Here's what I had to do on a Raspberry Pi 4 running Raspbian Buster (10).

To install OpenCV, first install the prerequisite packages using these commands:
$ sudo apt-get update
$ sudo apt-get install libhdf5-dev libhdf5-serial-dev libhdf5-103 
$ sudo apt-get install libqtgui4 libqtwebkit4 libqt4-test python3-pyqt5
You may also need to update pip using:
$ wget https://bootstrap.pypa.io/get-pip.py
$ sudo python3 get-pip.py
Then install OpenCV itself and Python image utilities using these commands:
$ sudo pip install opencv-contrib-python==
$ pip install imutils
After installation is complete, you can check that everything is working by starting Python 3, importing cv2 and checking the version:
pi@raspberrypi:~ $ python3
Python 3.7.3 (default, Apr  3 2019, 05:39:12) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2.__version__
>>> exit()

Friday, November 8, 2019

Making an SD card out of 1960s transistors

Reading about flash memory, I was staggered by the astronomical number of transistors in such a small place I decided to see what it would take to make one of these using 1960s technology.


Flash Memory: Lets go with a modest 32GB micro-SD card.

Transistor: I'll use a TO-18 package transistor. If like me you are from the UK and of a certain age, then the BC108 or BC109 will bring fond memories of Wireless World magazine and minor soldering injuries. In the US, I think the 2N2222 was more popular.

How many transistors are there in a 32GB SD Card?

Each bit of flash memory is made up of a single special type of transistor (called a floating gate transistor) that uses the quantum tunnelling effect. I choose to understand this to mean that you can write to the memory bit electrons get trapped in an insulated region and you can tell they moved (and so whether they are a 1 or a 0) by how much current flowed. That's my probably erroneous and over-simplified understanding, but hey, it works for me.

So, bipolar junction transistors such as the BC109 and 2N2222 have been around since the 1960s and can in fact still be bought today. These transistors are not capable of storing a bit of data on their own, to do that, you would need to arrange two as a flip-flop. But remember this is a thought experiment, as you've probably guessed, it isn't actually possible to build a 16GB flash memory from 1960s transistors.

And so, to the calculation:
32 GB means roughly (yes I know a GB is often taken to be 2^30) 32,000,000,000 bytes of data.
Each byte comprises 8 bits, so let's say there are 8 x 32,000,000,000 =  256,000,000,000 bits of data.

Not only that, but the card will contain a whole load of transistors for control logic and caching. For the sake of this thought experiment, I am going to ignore this. So the answer is there are something like:
256,000,000,000 transistors in a 32GB flash memory

Making this with TO-18 Transistors

Lets pretend we can make this thing with our TO-18 package transistors arranged in a square grid. To make a grid of 256,000,000,000,000 transistors is going to require a grid with the square root of 256,000,000,000,000 transistors on each side.

Thats roughly:

506,000 transistors per side

Each of our 1960s transistors is in a metal can with a diameter of about 5mm. If we assume that we need a bit of space around each transistor, then let's space them out to one transistor every 8mm. This means that each side of our array of transistors will have a size of 8mm x 506,000 or 4,048,000mm which is 4,048m which is roughly 4km (or roughly 2.5 miles)

A square 4km or 2.5 miles on each side

Let's superimpose a square 4km on each side on Manhatten.

Each of our little transistors weights about 0.3g so 256,000,000,000 would weigh:

76,800 metric tonnes 

- and thats ignoring the much greater weight of what it was mounted on.

When it comes to power consumption it's pretty hard to make anything useful up, especially as the distances involved mean that it would have no chance of actually working, even if our transistors were suitable. But let's just assume it would need its own power plant.

Friday, October 5, 2018

Arduino Air Quality Monitor

I've recently spent some time developing the MonkMakes CO2 Sensor for micro:bit and have spent a fair amount of time researching into indoor air quality. It struck me that I actually had no idea how healthy (or otherwise) the air is, in the house that I both sleep and work in. 

Time for a project! What's more I can reuse some of the sensors that I have accumulated while researching. These are low cost sensors, the whole lot costing probably less that GBP 50 (USD 50).

The project logs the following readings through USB back to the Arduino Serial Monitor, where you can copy and paste it into a spreadsheet:
  • True CO2 level using the serial interface to the MH-Z14A CO2 sensor.
  • tVOC (Total Volatile Organic Compounds) in parts per billion (ppb) using a low cost CCS811 MEMs sensor breakout board
  • eCO2 - equivalent CO2 (derived from the tVOC sensor by the CCS811. You don't need these readings if you have real CO2 measurement, but I wanted them for comparison.
  • Particulates - µg/cubic metre - this was measured using a Sharp GP2Y1010 optical particulate sensor.
It all squeezed onto a breadboard and I used a MonkMakesDuino Uno compatible as the 'Arduino'. 

The output in the Serial Monitor is in Tab separated fields that can just be copied and pasted into a spreadsheet.


MH-Z14A (CO2)

You can find the MH-Z14A on eBay pretty cheap. While not the nicest CO2 sensor (I like the COZIR Ambient) they do have the benefit of being really cheap (for a true CO2 sensor) and when compared with better sensors are accurate enough for this kind of project.

The device has a number of interfaces, but I used the TTL serial interface. This just requires 4 pins from the device (19-Tx, 18-Rx, 17-GND, 16-5V) shown left to right in the figure above.

The sensor has a serial protocol that requires a message to be sent from the Arduino, some-time after which a response will be received from which the PPM (parts per million) of CO2 can be extracted.

The sensor uses spectral absorption to optically measure the CO2 compensation. No compensation is made for temperature (indoor use, so stable temp assumed) or altitude (atmospheric pressure - but this is a small factor).

You can find the datasheet here but the datasheet for the older version of this product here goes into more detail.

CCS811 (tVOC)

This air-quality sensor chip measure Total Volatile Organic Compounds (unhealthy chemicals with a boiling point that makes them likely to get into our air). We breathe these out and they are also found from pollutants and chemicals we use in our lives. There are not many of them in good clean outdoor air - in most places anyway.

My sensor came from eBay, but Sparkfun also sell a breakout board based on the same chip and made an Arduino library that I use in the code below.

It uses an I2C interface, but operates at 3.3V. You can find the datasheet here.

GP2Y1010 (Particulate density)

This is another eBay purchase. It measures reflections from particles in the air from which you can derive an approximate measurement of the number of µg per cubic meter of particles in the air. 

Pin 1 is the rightmost connection (white wire) in the figure above.

This depends on so many factors, not least the unknown mass of the particles, how much light (IR) they reflect etc that the reading has to be taken with a good dose of skepticism. But is does give an idea of how dusty the air is. 

The device uses an analog output with a sensitivity of 0.65 Volts per 100µg per metre cubed.

You can find the datasheet for this device here.


Here's the schematic for the project.

The GP2Y1010 uses high current pulses of IR. To prevent the switching of the IR sender affecting the readings because of drops in the supply voltage, a 150Ω resistor charges a 200µF reservoir capacitor to supply the IR LED with a pulse of power. The internal amplifier of the GP2Y1010 must be supplied directly from the 5V rail (pin 6).

Note that the CCS811 is 3.3V supply. It also needs the WAK (Wake) and AD (I2C address option) pins tying to GND. The module I used has built-in I2C pullup resistors.


Here's the sketch. I've added lots of comments. Between the comments and the datasheets, it should make sense. You will need to add the Sparkfun library to your IDE, before it will compile.

#include <SoftwareSerial.h>
#include "SparkFunCCS811.h"

#define CCS811_ADDR 0x5A

const int ledPin = A3;
const int sensorPin = A0;
const long samplePeriod = 1000L;   // sample period in ms

SoftwareSerial co2Sensor(10, 11); // RX, TX
CCS811 vocSensor(CCS811_ADDR);

 *  Messages to be sent by serial to the MH-Z14 CO2 Sensor
const byte requestReading[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
const byte zeroCalibrate[] =  {0xff, 0x87, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2};

byte result[9];   // buffer into which serial messages from the MH-Z14 are received

 * Globals containing last sensor readings and the last time a set of readings was output
long lastSampleTime = 0;   // more accurately when data was last logged to serial.
int vocCO2;
int tVoc;
int co2;
int partics;

void setup() {
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH); // active low
  // write out headers for the columns of data
  Serial.println("CO2 (ppm)\teCO2 (ppm)\ttVOC(ppb)\tParts (µg/m3)");

 * Send the calibrate message to the MH-Z14 - setting it to 400ppm
void calibrate() {
  for (int i = 0; i < 9; i++) {

void loop() {
   * Send the Z command after leaving the project in fresh 400ppm air for 5 mins
  if (Serial.available()) {
    char ch = Serial.read();
    if (ch == 'z') {
   * If the tVOC sensor has data ready, update the global variable
  if (vocSensor.dataAvailable()) {
    vocCO2 = vocSensor.getCO2();
    tVoc = vocSensor.getTVOC();
   * If we are due to log another row of data, then take the CO2 and
   * partical readings and then send them all to serial
  long now = millis();
  if (now > lastSampleTime + samplePeriod) {
    lastSampleTime = now;
    co2 = readPPMSerial();
    partics = dustMicroGpm3();
    Serial.print(co2); Serial.print("\t"); 
    Serial.print(vocCO2); Serial.print("\t"); 
    Serial.print(tVoc); Serial.print("\t"); 

 * Send a serial message (9 bytes) to the MH-Z14 and wait for a response
 * Parse the resulting data and return the reported CO2 concentration in ppm
int readPPMSerial() {
  for (int i = 0; i < 9; i++) {
  //Serial.println("sent request");
  while (co2Sensor.available() < 9) {}; // wait for response
  for (int i = 0; i < 9; i++) {
    result[i] = co2Sensor.read(); 
  int high = result[2];
  int low = result[3];
  return high * 256 + low;

 * Return the particulates reading in µg/m3
int dustMicroGpm3() {
  digitalWrite(ledPin, LOW);  // active low pulse for the IR LED
  delayMicroseconds(280);     // wait before taking the analog reading
  int raw = analogRead(sensorPin);
  digitalWrite(ledPin, HIGH); // end the pulse
  delayMicroseconds(9680);    // give the capacitor time to recharge

  float sensitivity = 0.0065; // V/µg/m3   .from the datasheet
  float v = float(raw) * 5.0 / 1023.0;
  return int(v / 0.0065);


Here are the results for CO2, and eCO2, sampling every 10 seconds overnight in our bedroom. So, maybe time to sleep with the door open!


You can find information on healthy levels of CO2 here.

For information on tVOC concentration and what it means, see here.