This is only a preview of the November 2021 issue of Practical Electronics. You can view 0 of the 72 pages in the full issue. Articles in this series:
|
Max’s Cool Beans
By Max the Magnificent
Flashing LEDs and drooling engineers – Part 21
I
live in a maelstrom of weird and
wonderful happenings. For example,
I often get questions pertaining to my
projects like, ‘What on earth possessed
you to come up with that?’ (I can hear
my mother’s voice ringing in my ears
as clearly as if she was in the room
with me, and now my wife – Gina the
Gorgeous – has taken over for her).
Well, if you are at all interested in
discovering the answer to this question,
you’ll be delighted to discover that I
was recently interviewed in a Fish Fry
Special Edition: Makers Today! podcast
hosted by the inimitable Amelia Dalton
(https://bit.ly/3C6bIiS).
Have you ever paid any attention to the
indexes in technical books? I don’t know
about you, but I find it to be incredibly
frustrating when I return to a book looking
to find something to use as a reference but
fail to find what I’m searching for in the
index. The reason I mention this here is
that, at the time of this writing, three of
my chums – Adam Taylor, Dan Binnun
and Saket Srivistava — recently finished
writing a book called A Hands-On Guide
to Designing Embedded Systems (https://
bit.ly/3hnu9If). They kindly sent an early
PDF copy for me to peruse and ponder.
I immediately had a quick skim before
taking a deep dive, and my eye was caught
by the index, whose sole contents were
a note saying, ‘Index Goes Here.’ Since
I spend a humongous amount of time
hand-crafting the indexes for my own
books, I was moved to pen a column on
this topic: Just Call Me an Indexing Fool
(https://bit.ly/3C0prba).
I was just chatting to Matt Pulzer who
is the illustrious publisher-editor of
Practical Electronics. Matt is a font of
esoteric erudition, so I was delighted
to add to his treasure trove of trivia by
informing him of a nugget of knowledge
pertaining to the phrase ‘It was a dark and
stormy night...’ As you may recall, this
was used to comic effect in the Peanuts
cartoons by Charles Schulz (https://bit.
ly/2XfgOdS). In fact, Snoopy wrote so
many stories starting this way that these
openings ended up forming a book in
their own right (https://amzn.to/3C5XVJ9).
This archetypal example of a florid,
melodramatic style of fiction writing
comes from the opening sentence in
the 1830 novel Paul Clifford, which
was penned by English novelist Edward
Bulwer-Lytton. In fact, this opening
sentence is so bad that it led to the
annual Bulwer-Lytton Fiction Contest,
which challenges participants to write
an atrocious opening sentence to the
worst novel never written. Personally,
I class this competition (https://bit.
ly/2Xh8bQ3) alongside the Darwin
Awards, which salute the improvement
of the human genome by honoring those
who accidentally remove themselves
from it in a spectacular manner (https://
bit.ly/2YNUb0N), and the Ig Nobel
awards, whose stated aim is to honor
achievements that first make people
laugh, and then make them think (https://
bit.ly/3C4Qj9Q).
Are you nuts?
As I’ve mentioned on occasion, I’m a big
fan of steampunk, which is a subgenre
of science fiction that incorporates retrofuturistic technology and aesthetics
inspired by 19th-century industrial steam-
Round
Dome
Pan
Flat
Oval
Hexaganol
Flat
(Slotted)
Phillips
Torx
(6-lobe)
Square
(Robertson)
Hexagonal
(Allen)
Hexagonal
(External)
Fig.1. Machine-screw heads (top) and drives (bottom).
56
powered machinery. Many of my projects
include brass control panels or facias, in
which case I will also use brass fasteners.
For some of my projects, like my Victorian
Spectrum Analyser and my Countdown
Timer (https://bit.ly/3yYmypv), whose
job is to display the years, months, days,
hours, minutes and seconds to my 100th
birthday celebrations, which will kick
off at 11:45am British Summer Time on
29 May 2057 (mark your calendar), I’ve
used brass acorn nuts to good effect. With
other projects, I’ve used machine screws
with a variety of head shapes and drive
styles as appropriate.
There is, of course, an incredible variety
of head shapes available, where each
of these head shapes may be available
with a diversity of drive styles, only a
small selection of which are presented
here (Fig.1). Just to increase the fun and
frivolity, combinations of drives are also
available, like Phillips + slotted and
hexagonal + slotted. If you are aiming for
historical authenticity and attempting to
avoid the appearance of anachronisms,
then you can’t go wrong with slotted
or hexagonal heads (also, acorn nuts,
which are not shown here). Just about
everything else is an artifact of the 20th
century, including Square (Robertson)
drives, which were invented by Peter
Robertson in 1907, Hexagonal (Allen)
drives, which were invented by William
Allen circa 1910, Phillips drives, which
were invented by Henry Phillips in 1936,
and Torx screws, which were invented
by the Camcar Textron company in 1967.
For my own projects, I predominantly
use slotted screws, although I’m not
averse to Allen or Robertson drives, and
Fig.2. Two pseudo robot heads showing different types of ‘eye’ effects.
Practical Electronics | November | 2021
( a)
0
1
2
3
4
5
6
7
1
2
3
All f our SMAD s ( 8 patterns 0 - 7 clock wise)
( b)
0
1
2
3
0
All f our SMAD s ( 4 patterns 0 - 3 clock wise)
( c)
(Above) Fig.3. SMAD segment map.
(Right) Fig.4. Variations on a simple
windmill effect.
0
1
sometimes even hexagonal heads should the occasion demand.
On the other hand, although I love them for household tasks,
you will never, ever find me using a Phillips screw on one of
my steampunk extravaganzas.
Of course, how you create your own projects is totally up
to you; it’s not for me to cast aspersions (not the least that
my throwing arm isn’t what it used to be). Having said this, I
think a little attention to detail goes a long way. In the case of
my two pseudo robot heads, each featuring two of our SMADs
(Steve and Max’s Awesome Displays), for example, I’m using
eight slotted drive, pan-headed machine screws to attach
each of the SMADs to the front panels (Fig.2). Furthermore,
although the side panels are actually attached to the main
bodies of the heads (if you see what I mean) using hot glue,
as seen in the image on the front cover of this issue, I’ve still
augmented each panel with seven screws, just to convey the
impression that they are more robust and sophisticated than
is, in fact, the case.
One last point on this subject: the silver head has black
SMAD shells and facias while the black head has silver (well,
nickel-coloured) SMAD shells and facias. As we just saw, the
side panel screws on the silver head are a regular steel colour,
while their equivalents on the black head are black. Similarly,
the screws holding the silver SMADs on the black head are
steel, while the screws holding the black SMADs on the silver
head are black. In the not-so-distant past, I used to spend an
exorbitant amount of time trying to track down black versions
of various fasteners. More recently, unless I’m using brass, I
simply purchase steel versions of everything and then use
Gun Blue if I need any to be black (https://amzn.to/391DKj6).
Feast your eyes
In previous columns, I’ve mentioned the idea of using different
SMAD segments and colours to represent eyes. In Fig.2. we
see two versions I just threw together. On the left, I’m using
blue to represent the eyeball and red to denote the pupil; on
the right, I’m using white to signify the eyeball and black to
act as the pupil.
I’m going to continue experimenting with different
representations, including making the eyes look left (west), right
(east), up (north), and south (down). I’ll probably experiment
with looking northwest, southwest, northeast, and southeast, also.
Furthermore, when the eyes are looking directly forward,
as shown in Fig.2, it might be interesting to see if we can
achieve some sort of blinking effect. All of this is something
I’m noodling over in the back of my mind, but I’d love to hear
your suggestions.
Lest we forget
As a reminder, our SMAD segment map is shown in Fig.3.
The numbers, which are applicable to both types of SMAD
Practical Electronics | November | 2021
2
3
L ef t 2 SMAD s ( 4 patterns 0 - 3 clock wise)
0
1
2
3
R ight 2 SMAD s ( 4 patterns 0 - 3 anticlock wise)
shell (29-segments and 45-segments), refer to the positions
of the LEDs in the string, while the letter combinations
are the names we use to identify the segments in just the
29-segment versions.
In my previous column (PE, October 2021), we ended up
with all four SMADs displaying a simple windmill effect
comprising eight patterns (Fig.4a). The key part is where we
create an array containing the LEDs we wish to light up in
each pattern. For this first effect, we defined NUM_PATTERNS_
IN_EFFECT as being 8 (this corresponds to the fact that we
have 8 ‘spokes,’ as illustrated in Fig.3), and MAX_LEDS_IN_
EFFECT as being 4 (there are two LEDs in each spoke). We
then created a two-dimensional array of 8-bit integers called
EffectMap[][], that is comprised of NUM_PATTERNS_IN_
EFFECT (ie, 8) rows, each containing MAX_LEDS_IN_EFFECT
+ 1 (ie, 5) items, and we initialised this array as follows:
{
{2, 1, 17, 0, 0}, //
BA
{4, 0, 2, 18, 41}, // AA BB EA
{2, 3, 19, 0, 0}, //
BC
{4, 0, 4, 20, 42}, // AA BD EB
{2, 5, 21, 0, 0}, //
BE
{4, 0, 6, 22, 43}, // AA BF EC
{2, 7, 23, 0, 0}, //
BG
{4, 0, 8, 24, 44} // AA BH ED
};
Remember that the fi rst value in each row tells us how
many LEDs we wish to light for this pattern. The remaining
values in the row are the numbers of the LEDs (any 0s on
the right are just placeholders). You can remind yourself as
to how all this worked by downloading the code (file CBOct21-05.txt) from the October 2021 page of the PE website
at: https://bit.ly/3oouhbl
Take your turn
At the end of my column last month, I invited you to
perform some thought experiments of your own. The first
was to consider how we might go about modifying our latest
program so that it has four patterns, each with two arms
at 180° to each other. You can find my solution in file CBNov21-01.txt on the November 2021 page of the PE website
at: https://bit.ly/3oouhbl
Although not part of our main mission, I added a third ‘Deep
Background’ colour of purple just to make life a little more
interesting (Fig.4b). As you will see, implementing the new
effect is easy peasy lemon squeezy. First, we change NUM_
PATTERNS_IN_EFFECT to be 4 and MAX_LEDS_IN_EFFECT
to be 6. Next, we redefine the contents of our EffectMap[]
[] array as follows:
57
{
{5,0, 1,17, 5,21, 0}, // AA+BA+BE
{6,2,18, 6,22,41,43}, //
BB+BF+EA+EC
{5,0, 3,19, 7,23, 0}, // AA+BC+BG
{6,4,20, 8,24,42,44} //
BD+BH+EB+ED
};
Our next thought experiment was to ponder how we might modify
this latest incarnation such that the windmill patterns on the
left-hand robot head’s eyes rotate clockwise while the patterns
on the right-hand robot head’s eyes rotate anticlockwise (Fig.4c).
You can find my solution in file CB-Nov21-02.txt. To be
honest, although my code functions as planned, it feels a little
‘clunky’ to me and I have a sneaking suspicion that this could
be implemented in a more elegant fashion. Perhaps you might
take a look and offer some suggestions.
The final countdown
The last problem I posed was to implement a sort of counter
that I think of as an ‘Alien Countdown’ (I had a disturbed
childhood). The idea here is to return to a version of our
original 8-pattern windmill effect (Fig.4a) but with our new
colour scheme (Fig.4b and Fig.4c).
What we want to do is to create a program involving two
or more SMADs that commences with them each displaying
a single windmill arm pointing upwards. We start with the
least-significant SMAD (the one on the right) spinning its
arm clockwise, completing four full rotations each second (I
originally specified one full rotation a second, but the effect
looks more exciting and interesting when we speed it up).
Every time this arm returns to its vertical position, the arm
on the adjacent SMAD advances by one position (pattern).
Similarly, every time the arm on the second SMAD returns
to its vertical position, the arm on the third SMAD advances
by one position, and so on for all of the SMADs in the chain.
You can find my solution in file CB-Nov21-03.txt. In this
case, I have to admit that I’m rather ‘chuffed’ with the way
things turned out. Stripped of the nitty-gritty code, the main
loop is as follows:
int iSmad = 0;
bool done = false;
do
{
int smadOffset = iSmad * NUM_NEOS_PER_SMAD;
// For current SMAD
// Set Neos in old pattern to background color
// <code goes here>
// For current SMAD
// Increment SMAD pointer to next pattern
// <code goes here>
// For current SMAD
// Set Neos in new pattern to foreground color
// <code goes here>
// For current SMAD
// Test to see if we’ve wrapped around
if (SmadPtrs[iSmad] != NORTH)
done = true;
else
iSmad = iSmad + 1;
} while ( (done == false) && (iSmad < NUM_SMADS) );
This means that all we have to do is change our definition of
NUM_SMADS (the number of SMADs in the chain) to accommodate
any number of ‘digits’ in our counter. As usual, for your
delectation and delight, I’ve captured a quick video showing all
of the effects discussed in this column (https://bit.ly/3lJk1uN).
What next?
There are so many things we can do with SMADs in general,
and with my robot heads in particular, that I’m undecided as to
which direction to take things next. Do we continue playing with
variations on the simple effects we’ve seen thus far, building
our (my) confidence that we (I) have a clue what we are (I am)
doing? Alternatively, do we take the plunge
and start experimenting with some eyeCool bean Max Maxfield (Hawaiian shirt, on the right) is emperor
wateringly cunning colour effects? Do you
of all he surveys at CliveMaxfield.com – the go-to site for the
have any thoughts you’d care to share at
latest and greatest in technological geekdom.
this point in the proceedings? As always,
I welcome your sage comments, insightful
Comments or questions? Email Max at: max<at>CliveMaxfield.com
questions, and helpful suggestions.
Max’s Cool Beans cunning coding tips and tricks
I
fear this column is going to be
reminiscent of one of my mother’s
tortuous tales, which typically kick
off along the lines of: ‘I bumped into
Mrs. Greebles at the fishmongers the
other day. You remember, she was the
oldest of three sisters; the youngest,
Beryl, was a strumpet, while the
middle girl eloped with an Australian
taxidermist and they had two sons who
were terrified of bananas and...’
58
The amazing thing is that, after
wandering so far out into the weeds that
her audience starts to consider sending
out a search party, she somehow manages
to bring the story home to a triumphant
conclusion: ‘And that’s why you should
never name a walrus Wally!’ (And people
wonder why I drink.)
In an earlier Tips and Tricks (PE, July
2021), we noted that microcontrollers like
the Teensy (which we are using to drive
our 10-character, 21-segment Victorian
Display) and the Arduino (which I use
for all sorts of things) contain three types
of memory: Flash, SRAM, and EEPROM.
The Flash, which is non-volatile (it
remembers its contents when power is
removed from the system), is used to store
the program. By comparison, the SRAM,
which is volatile (it forgets its contents
when power is removed from the system),
is where the program creates, stores,
Practical Electronics | November | 2021
Let’s also remind ourselves that we
plan on having three copies of these
settings bytes. First, we’ll have a default
set called DefSettings that’s stored in
the Flash memory as part of the main
program. Next, we’ll have a customised
set that’s been tweaked by the user and
is stored in the EEPROM. Finally, we’ll
have a working set called WrkSettings
that’s stored in the SRAM; it is this latter
set that will ultimately be used by the
program once it’s up and running.
Let’s start by considering the default
set of values stored in DefSettings
using a generic visualisation (Fig.5).
First, we have a magic number, whose
purpose will be explained shortly,
#include <EEPROM.h>
and whose value I’ve set to be 0x42
in hexadecimal (01000010 in binary),
Just to remind ourselves how this works,
which is, as we all should know by now,
suppose we declare an integer variable
The Answer to the Ultimate Question
called Address to which we assign a
of Life, the Universe, and Everything
value of 0, along with an 8-bit (byte(https://bit.ly/pe-nov21-42).
sized) variable called Data to which we
This is followed by a version number,
assign a value of 128. In this case, we can
which I’m considering to be presented in
write the data into our EEPROM using:
the form of two 4-bit nybbles (remember,
‘two nybbles make a byte’), where the
EEPROM.write(Address, Data);
most-significant nybble holds the primary
version number and the least-significant
Contrariwise, we can read the data back
nybble holds the secondary version
out of our EEPROM using:
number. Since our nybbles contain 0001
and 0000, this means we are at version
Data = EEPROM.read(Address);
1.0 in our example.
Next, we have the time format (0 = 12In the aforementioned Tips and Tricks
hour, 1 = 24-hour) followed by the date
column, we also made mention of the fact
format 0 = YYYY/MM/DD, 1 = MM/DD/
that we are going to use the EEPROM to
YYYY, 2 = DD/MM/YYYY). These are
store a set of byte-sized unsigned integer
followed by the number of modes and
values (we’ll use the data type uint8_t
the number of effects. For the purposes
that we introduced in the September
of these discussions, let’s assume that
2020 Tips and Tricks) to keep track of
we have three modes numbered 0, 1,
a variety of user settings, such as the
and 2 that display the time, date, and
preferred formats in which to display
some scrolling text message, respectively.
the date and time.
Observe that, for the purpose of this
example, only two of these (the time and
Setting the scene
date) have associated format settings.
In the real world, we are going to have a
When it comes to effects, let’s assume
bunch of ‘settings bytes.’ For the purposes
we have sixteen (numbered from 0 to 15).
of these discussions, however, let’s assume
For example, effect 0 could be static white
we are working with a very limited subset
text on a black background, effect 1 could
comprising just ten bytes, which we can
be static black text on a white background,
think of as being numbered from 0 to 9.
effect 2 could be an animated rainbow
As part of this, we have defined NUM_
of coloured text on a black background,
SET_BYTES as being equal to 10.
and so forth. Next, we have three bytes
N ames f or struct v ariables
containing the effects associated with
I ndex f or array
each of our modes. As we see, we’re
V alues in settings by tes
assuming that the default will be to
associate effect 0 (white text on a black
0 01000010 Magic N umber
v d M a g i c N u m
background) with each mode.
1 00010000 V ersion N umber
v d V e r s i o N u m
Finally, we have a special value called
00000000 T ime F ormat
2
v d T i m e
a
checksum,
which will primarily be
00000000 D ate F ormat
3
v d D a t e
of use when we wish to determine if
4
00000011 N umber of Modes
v d N u m M o d e s
the contents of the EEPROM are valid
00001111 N umber of E f f ects
5
v d N u m F x
or not. We will return to this point in
00000000 F x Mode 0 ( D ate)
6
v d F x M o d e 00
a little while, but first...
7
00000000 F x Mode 1 ( T ime)
v d F x M o d e 01
and manipulates variables when it runs.
Also, we have a small amount of nonvolatile EEPROM (electrically erasable
programmable read-only memory) in
which we can store modest quantities
of long-term information.
In the case of the Teensy 3.6, we have
4KB of EEPROM (that’s 4,096 bytes
numbered from 0 to 4,095 in decimal
or 0x000 to 0xFFF in hexadecimal). If we
want to use this EEPROM in our programs,
we first need to include a special library
that’s provided as part of the Arduino’s
integrated development environment
(IDE) using the following statement:
v d F x M o d e 02
8
00000000
F x Mode 2 ( T ex t)
v d C h e c k s u m
9
? ? ? ? ? ? ? ?
C heck sum
Fig.5. Example contents of DefSettings.
Practical Electronics | November | 2021
Let’s form a union!
As we previously discussed (yes,
I’m afraid you really, really should
re-read the July 2021 Tips and Tricks),
we are going to define a new data type
in the form of a union that we will call
Settings. A union offers multiple ways
to visualise and manipulate the same
area of memory. In our simple case,
sometimes we wish to think of things as
a structure of byte-sized variables we’ve
called vds, while other times we wish to
think of the same memory locations as an
array of byte-sized variables we’ve called
vda. (The ‘vd’ part of these identifiers
stands for ‘Victorian Display,’ while the
‘s’ and ‘a’ parts stand for ‘structure’ and
‘array’, respectively.)
typedef union
{
struct
{
uint8_t vdMagicNum;
uint8_t vdVersionNum;
uint8_t vdTime;
uint8_t vdDate;
uint8_t vdNumModes;
uint8_t vdNumFx;
uint8_t vdFxMode00;
uint8_t vdFxMode01;
uint8_t vdFxMode02;
uint8_t vdChecksum;
} vds;
uint8_t vda[NUM_SET_BYTES];
} Settings;
Next, we declare our DefSettings
and WrkSettings variables as being
of this union type, as shown below. The
clever thing here is the way in which we
initialise the values in our DefSettings
variable as follows:
Settings DefSettings =
{
.vds =
{
.vdMagicNum
= 0x42,
.vdVersionNum = 0x10,
.vdTime
= 0x00,
.vdDate
= 0x00,
.vdNumModes
= 0x03,
.vdNumFx
= 0x0F,
.vdFxMode00
= 0x00,
.vdFxMode01
= 0x00,
.vdFxMode02
= 0x00,
.vdChecksum
= 0x00
}
};
Settings WrkSettings;
The 0x00 that we’ve assigned to the
checksum value is for completeness only
because we will be calculating a new
value almost immediately (although this
0x00 value will never be used, neglecting
to assign a default value here would bug
me incessantly – also bugging me, why
isn’t there a word ‘cessantly’?).
59
Check my checksum
Address in E E P R O M
0x 000
X X X X X X X X
01000010
01000010
Magic N umber
0x 001
X X X X X X X X
00010000
00010000
V ersion N umber
0x 002
X X X X X X X X
00000000
00000001
T ime F ormat
0x 003
X X X X X X X X
00000000
00000010
D ate F ormat
0x 004
X X X X X X X X
00000011
00000011
N umber of Modes
0x 005
X X X X X X X X
00001111
00001111
N umber of E f f ects
0x 006
X X X X X X X X
00000000
00000111
F x Mode 0
0x 007
X X X X X X X X
00000000
00001001
F x Mode 1
0x 008
X X X X X X X X
00000000
00000101
F x Mode 2
0x 009
X X X X X X X X
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ?
C heck sum
( a) U ninitialised
( b) D ef ault
( c) C ustom
Fig.6. Contents of EEPROM at different stages.
Stay constant
Actually, this provides another teachable moment. If we never
intended to change any of the values in our DefSettings
variable, we should (would) have declared it as being a
constant value as follows:
const Settings DefSettings = {etc.}
This would confer several advantages, the first being that
the compiler would ‘throw a wobbly’ if our code attempted
to change any of the values. Also, any initialised variables
that are not declared as being of type const are stored in
the Flash program memory and then copied by the start-up
code into SRAM (all this takes place before the user program
is executed). By comparison, in the case of any initialised
variables that are declared as being of type const, the system
knows these values aren’t going to change, which means they
don’t need to be copied into SRAM, thereby saving valuable
SRAM resources.
Having said all this, we are planning on changing the
checksum value in DefSettings, which explains why we
didn’t use the const keyword in this case.
The proof of the pudding
Remember that the structure and the array are intended to
provide two different ways to manipulate and display the
same ten bytes of memory in our union. But how do we know
that this actually works? Well, as I’ve mentioned before, I’m
a hardware design engineer by trade, and writing code is not
my strong suit, so I performed a little test as follows:
for (int i = 0; i < NUM_SET_BYTES; i++)
{
WrkSettings.vda[i] = DefSettings.vda[i];
}
for (int i = 0; i < NUM_SET_BYTES; i++)
{
Serial.print(i);
Serial.print(“
“);
Serial.println(WrkSettings.vda[i]);
}
Let’s remind ourselves that our last settings byte is used to
contain a value called a ‘checksum.’ All we need to know at
this point in our discussions is that a checksum is a small
block of digital data that is derived from a larger block of
digital data for the purpose of detecting errors that may
have been introduced during the storage or transmission of
the larger block of data. In our case, we are going to derive
a byte-sized checksum value based on the contents of all of
our other settings bytes.
The procedure by which we generate our checksum value is
referred to as a ‘checksum function’ or a ‘checksum algorithm.’
A good checksum algorithm will generate a significantly
different output value for even a small change made to the
input data. I hate to do this to you again (I might remind you
as to how we introduced this column), but I think you know
what I’m going to say, which is that we will return to this
point shortly, but first...
Meet my shell game
Have you ever seen someone perform a shell game? This
involves sleight of hand in which three inverted cups, or
nutshells, are moved about and the contestant must identify
which one ends up with a pea or other object underneath.
If you are the contestant in question, you are convinced you
know where the pea is hiding… right up to the point where
the shell you selected is shown to be empty and your hardearned dosh bids you a fond farewell. Well, we are poised to
do something similar with our settings bytes in general and
our checksum byte in particular.
Before we proceed, let’s make a decision that one of the very
first things we will do when we initially run our program is to
use our checksum function to generate a checksum byte based
on all of the settings bytes (except the checksum byte itself)
that are stored in our DefSettings variable. We will store the
result in the checksum byte associated with DefSettings.
Now, let’s assume that we’ve just taken a new microcontroller
out of the box, plugged it into our system, and loaded our
program into it. Since this is a new microcontroller, the
EEPROM is going to be uninitialised, which means it will
contain unknown values (Fig.6a). We’ve indicated these
values as ‘X’ in this figure, but they would typically be all 1s
(or, more rarely, all 0s).
When we run our program, one of the first things we’ll
do (after generating and storing the checksum byte in the
DefSettings variable as discussed above) will be to check
to see if the EEPROM contains valid settings data. How do we
do this? Well, do you recall the magic number byte in Fig.5?
We decided to load this byte with a value of 0x42, but any
value other than 0x00 and 0xFF would suffice.
E E P R O M
D ef Settings
W rk Settings
( a) P ower- up with empty E E P R O M
E E P R O M
D ef Settings
W rk Settings
When we initialised the values in our DefSettings
variable, we used its structure aspect. Now, in the first
( c) P ower- up with loaded E E P R O M
loop above, we use its array aspect to copy these values
U ser
from the DefSettings variable into the WrkSettings
D
ef
Settings
E
E
P
R
O
M
W rk Settings
variable, after which we use the second loop to print
the values from the WrkSettings variable. It works!
(Phew!). If you wish, you can peruse and ponder this
sketch in all its glory (file CB-Nov21-04.txt, which is
( e) U ser decides to change settings
available on the November 2021 page of the PE website
at: https://bit.ly/3oouhbl).
Fig.7. Alternative action sequences.
60
E E P R O M
D ef Settings
W rk Settings
( b) L oad E E P R O M and W rk Settings
E E P R O M
D ef Settings
W rk Settings
( d) L oad W rk Settings f rom E E P R O M
U ser
E E P R O M
D ef Settings
W rk Settings
( f ) U ser changes settings
Practical Electronics | November | 2021
01000010
01000010
Magic N umber
00010000
00010000
V ersion N umber
00000001
00000001
T ime F ormat
00000010
00000010
D ate F ormat
00000011
00000011
N umber of Modes
00001111
00001111
N umber of E f f ects
00000111
00000111
F x Mode 0
00001001
00001001
F x Mode 1
00000101
00000101
F x Mode 2
01010110
10000100
C heck sum
( a) P arity by te
( b) Sum complement
Fig.8. Simple checksum algorithms.
What we are going to do is read the byte
from address 0x000 in the EEPROM to
see if it matches the magic number byte
in DefSettings. However, as we’ve
already discussed, since this is the first
time we’ve run the program, we are
powering up with an empty EEPROM
(Fig.7a), so our test to read the magic
number will fail. In this case, we will load
the default settings values, including our
recently calculated checksum byte, from
DefSettings into both the EEPROM
and WrkSettings (Fig.6b and Fig.7b).
OK, suppose we play with our Victorian
Display and then power it off again
without changing any of the settings. In
this case, when we next power the system
up again, the EEPROM will contain a
copy of the default settings (Fig.6b and
Fig.7c). As before, one of the first things
we do is to read the byte from address
0x000 in the EEPROM to see if it matches
the magic number byte in DefSettings.
In this case, the test passes, so we copy
the values stored in the EEPROM into
WrkSettings (Fig.7d).
At this point, we would use our
checksum function to verify that the
data we’ve copied into our WrkSettings
variable is good (we’ll discuss how we
do this in a moment). If this test fails,
we know that the data in the EEPROM
has become corrupted in some way, in
which case we will have to mediate the
situation (perhaps by regressing to Fig.7b).
Alternatively, if the test passes, then we
know we are ready to rock and roll.
Now, suppose that while our program
is running, we decide to change some
of the settings (Fig.7e). For example, we
might decide to modify the time format
from 12-hour to 24-hour. Similarly, we
might decide to modify the date format
from YYYY/MM/DD to DD/MM/YYYY.
Also, we might decide to change the
effects associated with the various modes.
Obviously, we are going to have to
equip our program with the ability to
allow the user to select new values for
everything. Each time the user does
change one of the values then: a) the
new selection will be copied into the
appropriate byte in both the EEPROM and
WrkSettings and b) a new checksum
Practical Electronics | November | 2021
value will be generated, and this too
will be stored in both the EEPROM and
WrkSettings (Fig.6c and Fig.7f).
Checksum Redux
The reason the checksum bytes in Fig.6
and Fig.7 are shown as ‘????????’ is that
we haven’t yet decided what algorithm we
are going to use. The simplest checksum
algorithm is called the ‘longitudinal parity
check’ (Fig.8a). This involves breaking the
data into ‘words’ with a fixed number (n)
of bits and then computing the exclusive
or (XOR) of all those words, excluding
the as-yet-unknown checksum value
itself. In our case, of course, our words
are 8-bits wide.
The easiest way to think about this
is that we want to end up with an even
number of 1s in each column. In the leastsignificant (bit 0) column we have six 1s
(even), so the corresponding checksum bit
will be 0. In the bit 1 column we have five
1s (odd), so the corresponding checksum
bit will be 1. In the bit 2 column we have
three 1s (odd), so – once again – the
corresponding checksum bit will be 1.
And so on for the rest of the bits, resulting
in a checksum value of 01010110.
In order to check the integrity of the
data once we’ve read it out of the EEPROM
and loaded it into WrkSettings, we will
use our checksum function to compute
the exclusive or (XOR) of all the settings
bytes in WrkSettings, including the
checksum byte. If the result is not 0x00
(all zeros), then we know an error must
have occurred.
Another simple technique is the
‘sum complement’ algorithm (Fig.8b).
In this case, we treat the settings bytes
as unsigned binary numbers and add
them all together, excluding the asyet-unknown checksum value itself.
In this case, 01000010 + 00010000 +
00000001 + 00000010 + 00000011 +
00001111 + 00000111 + 00001001 +
00000101 = 01111100. If the result had
been bigger than our word size of eight
bits, we would have simply discarded
any overflow bits. Next, we generate the
twos complement of this value (swap all
the 0s for 1s, swap all of the 1s for 0s,
and add 1 to the result), which gives us
a checksum value of 10000100.
When we wish to check the integrity
of the data once we’ve read it out
of the EEPROM and loaded it into
WrkSettings, we add all the bytes,
including the checksum byte, and discard
any overflow bits. Once again, if the result
is anything other than 0x00 (all zeros),
then we know an error has occurred.
Earlier we noted that, ‘A good
checksum algorithm will generate a
significantly different output value for
even a small change made to the input
data.’ Is this true of the two algorithms
we just discussed? I fear not. Try flipping
a single bit in one of our settings bytes
and see what effect it has on the output.
In my next Tips and Tricks column, we
will consider a much more cunning
algorithm, but first...
Mission critical
While fighting your way through this
column, you may have wondered why we
need to use checksum values to verify the
integrity of our EEPROM contents. ‘Is this
form of memory really that unreliable?’
you may be asking yourself. Well, the
answer is ‘Yes’ and ‘No’ (I bet you saw
that coming). It’s certainly true that the
typical Arduino EEPROM has a specified
life of only 100,000 write/erase cycles, but
that would become an issue only if we
did something silly like write to it every
millisecond (how many times do we really
expect a user to modify the settings for
something like our Victorian Display?).
One problem is that writing data to
the EEPROM takes a relatively long
time in the scheme of things, not that
you would notice anything yourself, of
course, especially since we are writing
only a handful of bytes in the case of our
Victorian Display. Still and all, we have
to worry what might happen if a brownout (low-power) condition was to occur
while writing to the EEPROM. Similarly,
for a poorly timed user-initiated powerdown or reset occurring while we were
writing to the EEPROM. In such a case,
the checksum will not match on readback and we’ll know to treat the data as
junk. Contrariwise, if we simply assume
the data is fine, we could end up trying
to work with junk, which will almost
invariably end in tears.
As an aside, one way to overcome
data corruption caused by cell wearing
or power failure or whatever is to
sequentially write two (or more) identical
sets of data. If one checksum is bad,
we still have one good copy; if both
checksums are bad, then – speaking in the
engineer’s vernacular – we’re up a certain
creek without motive power. Of course,
we could also employ error-correcting
strategies like using Hamming codes,
which can detect up to two-bit errors or
detect-and-correct one-bit errors, but this
is outside the scope of these columns.
Finally, something else you might be
thinking is, ‘Do we really need to go to
all this effort for the Victorian Display,
which – after all – is only really a glorified
clock, for goodness’ sake?’ Ah, well,
that’s a very good question. My reasoning
is that what we learn here will prove
useful later in life should we ever find
ourselves in the position of having to
design real-world mission-critical or
safety-critical systems.
OK, that’s all for this Tips and Tricks.
As always, I welcome your comments,
questions and suggestions.
61
|