- 2 x 1kΩ 1/4W linear pot with SPST switch (from eBay)
- 2 x large knobs to fit the above
- 2 x 470Ω 1/4W resistors
- 8 x high brightness red LEDs (Vf 2.2V If 20mA)
- 3xAA 4.5V batter box
- 2 x A4 sheets of 3mm pearlescent acrylic
Dr. Monk's DIY Electronics Blog
Open Source hardware, Arduino, Raspberry Pi, micro:bit, BeagleBone, DIY electronic construction, reviews, projects, how-tos and recipes.
Friday, January 14, 2022
Pimping a Toy Kitchen
Tuesday, August 25, 2020
Testing the micro:bit's Analog Inputs
The BBC GPIO connection rings (labelled 0, 1 and 2) of a micro:bit can all be used as analog inputs.
You might think, that as long as you are careful to to exceed the 3V input voltage limit, then you can measure any low voltage whatever the source. Perhaps a photoresistor in a voltage divider arrangement with a fixed resistor.
While this is basically true, if the source of the voltage to be measured has a high output impedance, at some point the voltage measured by the micro:bit will diverge from reality as the impedance of the voltage source being measured increases.
In reality, you can't measure something without altering it. The best we can do is to make the measurement errors small, so that they can be ignored.
This blog post determines the extent of this measurement error with the micro:bit's analog inputs.
Equipment Used
Here's a handy test program that reports the voltage at P0 when button A is pressed, that I used in this experiment.
Use a multimeter to measure the voltage between the 3V and GND ring connectors on the micro:bit and put this value in for Vanalog. I was powering the micro:bit from USB, so Vanalog was about 3.2V.
You will also need:
- A digital multimeter on DC volts range
- Resistors 100Ω, 1kΩ, 10kΩ, 100kΩ, 1MΩ, 10MΩ
- A low-impedance voltage source such as bench power supply (set to 2V) or just an AA battery
- An alligator lead
Setup
Results
R (Ω) | V1 (V) | V2 (V) |
0 | 1.99 | 2.05 |
100 | 1.99 | 2.05 |
1,000 | 1.99 | 2.05 |
10,000 | 1.99 | 2.02 |
100,000 | 1.99 | 1.77 |
1,000,000 | 1.93 | 1.17 |
10,000,000 | 1.75 | 0.92 |
Conclusion
Monday, July 13, 2020
Unit Testing and Arduino
Background
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() {
Serial.begin(9600);
testNums();
testAdd1();
testAdd2();
testSub1();
testMult1();
testMult2();
testMult3();
testDiv1();
testDiv2();
}
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);
Serial.println();
}
Conclusion
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
We finally had a chance for the household to go ahead and make some shields.
Cleanliness
Fortunately the masks are easy to clean.
Finding Recipients
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
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:
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.
Review
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
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.
K40
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
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.
Material
Cutting Parameters
Assembly
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
Tuesday, February 11, 2020
Installing OpenCV 4 on a Raspberry Pi 4
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-pyqt5You may also need to update pip using:
$ wget https://bootstrap.pypa.io/get-pip.py $ sudo python3 get-pip.pyThen install OpenCV itself and Python image utilities using these commands:
$ sudo pip install opencv-contrib-python==4.1.0.25 $ pip install imutilsAfter 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__ '4.1.0' >>> exit()