This is only a preview of the February 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 12
I
must admit to feeling a tad smug
as I pen these words, because I just
received an email from a devoted
PE reader called David Humrich
(a.k.a., ‘The LED Baron’) who hails
from Down Under. As part of this
antipodean communication, David was
kind enough to say, ‘I just received the
November issue of PE. I particularly
enjoyed reading your ‘drip plus splash’
fun. I must confess that I start reading
the magazine from the back so that I
find your columns sooner!’
‘You’re making me blush,’ I thought.
‘Don’t stop,’ I thought. Suffice it to say
that, as a result of David’s message, I’m
currently bouncing around with a great
big smile on my face. Wait until I tell
my mother that we’ve finally found
someone who (intentionally) reads
my columns!
Prognostication procrastination
If I have one failing (and I’m not admitting to anything, you understand), it’s
that I’m easily distra… SQUIRREL!!! As
a result, many of my hobby projects take
years to come to fruition because something else appears on the scene to attract
my attention (‘Ooh, shiny!’). In the case
of my Pedagogical and Phantasmagorical Inamorata Prognostication Engine,
for example, I’ve probably been dabbling
around with this for close to 15 years at
the time of writing.
Some of my friends are prone to making
unkind remarks, even going so far as to
cast aspersions (which is something I
would never do because my throwing
arm isn’t what it used to be) and telling
me that I shouldn’t commence any new
projects until I finish some of the existing ones. Sometimes my lack of progress
R ac ing
pix el
5
6
7
P lodding
pix el
B ac kground
pix els
3
makes me feel sad but – for reasons I shall
shortly explain – now I don’t feel so bad.
On a trip to England to visit my mother
in 2018, I went to see an exhibition of
Leonardo da Vinci’s work, which was
being presented in downtown Sheffield. All I can say is that the man was
a genius. I’m currently reading his biography by Walter Isaacson (https://amzn.
to/33D0PGR) who – as always – does a
brilliant job. Previously, I read Walter’s
biographies on Steve Jobs and Einstein.
Regarding the former, I think he was a
brilliant guy who I wouldn’t have liked
very much. Regarding the latter, even
though we didn’t get into the math, it
wasn’t until I read this book that I fully
comprehended just how impressive an
achievement Einstein’s theory of General Relativity truly was (and still is,
for that matter).
The point is that, reading this biography on Leonardo, I discovered we share
a lot in common. It goes without saying
(although I’ll say it anyway) that both us
are renowned for our ready wit, dapper
dress, and sporty sense of style. Furthermore, I learned that the little rascal left
unfinished many more things than he
actually completed. He was constantly
making sketches with the intention of
turning them into pictures, and ceaselessly taking notes with the purpose of
writing treatises, but before he got around
to actually doing something, he became
preoccupied with something else.
Now, I’m certainly not comparing
myself with Leonardo da Vinci (I’m
happy to leave such comparisons to my
dear old mom, who would doubtless
opine that Leonardo came in a distant
second), but – still and all – it’s nice to
know that I’m not the only one who...
SQUIRREL!!!
The final countdown
I currently have the words and tune to
The Final Countdown rattling around in
2
my head. This song, which was released
by the Swedish rock band Europe in 1986,
reached number one in 25 countries. I
couldn’t help myself; I just took a break
to listen to it on YouTube (https://youtu.
be/NNiTxUEnmKI).
This trip down memory lane was triggered by thoughts of the scene toward the
end of the 1987 American science fiction
action movie Predator, which starred
Arnold Schwarzenegger. I’m thinking of
the part where Dutch (Arnold) disables the
alien’s cloaking device and then crushes the short-tempered creature under a
trap’s counterweight, thereby making
it a very unhappy alien indeed. In response, laughing maniacally, the beast
activates a self-destruct device, whose
countdown sequence is presented as a
series of rotating symbols on an alienarm-mounted display.
I was thinking we might implement
something similar on the NeoPixel
rings flaunted by the Prognostication
Engine, as discussed in my previous
column (PE, January 2021). Since the
engine sports a Big Red Button (‘Do
not press!’), our countdown sequence
could be associated with the activation
of this button.
Of course, we’ve got only a single ring
to play with on our prototype, but that
will provide a starting point. I’m thinking of implementing what I’m calling
a ‘plodding pixel’ and a ‘racing pixel’
(Fig.1). Remember that the pixel numbers shown here are the way we decided
we wanted to visualise and manipulate
things, with pixel 0 in the ‘south-west’
position and the pixel numbers increasing in a clockwise direction. The actual
pixel numbers on the physical ring are
completely different, so we employ a
simple cross-reference operation using
an array of integers called RingXref[]
to translate from our ideal world into the
real world (see last month’s column and
code for more details).
T im e
1
0
1 2
1 5
1 4
1 3
Fig.1. Countdown pixels.
60
( a) R ac ing pix el approac hes plodding pix el
( b) B um p & pause
( c ) Off we go again
Fig.2. First-pass countdown sequence
Practical Electronics | February | 2021
The idea is that the racing pixel proceeds in a clockwise direction (Fig.2a) until it reaches the plodding pixel, at which
point it ‘bumps’ the plodding pixel over into the next clockwise spot (Fig.2b). We then pause for a short time before the
racing pixel sets off again (Fig.2c).
In an earlier column (PE, October 2020), we noted that
it’s a good idea to make our code and functions as simple
and as general-purpose as possible. As part of this, the way
I’ve written the sketch (program) for this particular effect is
that we define four colours – one for the background pixels
(eg, white), one for the racing pixel (eg, yellow), one for the
plodding pixel (eg, red), and one we use when the racing
pixel bumps into the plodding pixel (eg, orange). Doing
things this way allows us to experiment with different scenarios quickly and easily, as illustrated in the video I just
took (https://bit.ly/3gXpueJ).
When it comes to the code itself, we’ve done things a
little differently this time (the full sketch is presented in file
CB-Feb21-01.txt – which is available on the February 2021
page of the PE website, and also the PE general downloads
page: https://bit.ly/3oouhbl).
For example, let’s consider our bump colour (the one we use
when the racing pixel crashes into the plodding pixel). Based
on our most recent programs (PE, January 2021), we know that
we are going to use our GetGammaCorrectedColor() function to correct the gamma value of our initial colour (COLOR_
BUMP_PIXEL, in this example). Next, we are going to use our
ModifyBrightness() function to control the intensity of
this colour as defined by our BRIGHTNESS constant. Previously, we might have broken this sequence out into a number
of discrete steps, as follows:
tmpColor = GetGammaCorrectedColor(COLOR_BUMP_PIXEL);
tmpColor = ModifyBrightness(tmpColor, BRIGHTNESS);
ColorBumpPixel = tmpColor;
We implemented things this way in order to make what we
were doing easier to understand, but now it’s time to ‘take off
the training wheels’ (well, just one wheel to start with) and
make things a tad more concise, as follows:
ColorBumpPixel =
ModifyBrightness(GetGammaCorrectedColor(COLOR_
BUMP_PIXEL), BRIGHTNESS);
I know this sort of thing can appear a little scary at first, but
it’s really not as bad as it seems. We can think of this as being
like the layers of an onion. First, let’s start at the outside
and work our way in. As we see, we are assigning the value
returned from our ModifyBrightness() function to our
ColorBumpPixel variable. Now, our ModifyBrightness()
function requires two arguments – a colour to be modified and
the amount by which it should be modified (the difference
between arguments and parameters was discussed in my Tips
and Tricks column in the October 2020 issue of PE). In the
case of the first argument, as opposed to specifying a colour
directly, we are using the colour value returned from our
GetGammaCorrectedColor() function. Now, that wasn’t
too bad, was it?
Another way to look at this is to think about how the C/
C++ compiler sees things, which is to start by looking at
the inner layers of the onion and working its way out. In
this case, it will first determine the return value from our
GetGammaCorrectedColor() function and subsequently use
this as the first argument to our ModifyBrightness() function.
Another thing we are going to do is define two global integer
variables called PtrPloddingPixel and PtrRacingPixel,
and we are going to use these to ‘point’ to the relevant pixels
Practical Electronics | February | 2021
(you’ll see what I mean in a minute). In the case of the racing
pixel, we want to turn the new (next) pixel on and the old
(last) pixel off, then update our PtrRacingPixel variable
to point at the next pixel, pause for a short delay, and then
do the whole thing again until our racing pixel bumps into
our plodding pixel.
In my previous column, we introduced a way of determining the number of the old pixel using the % modulo operator
so as to get around the boundary conditions when we transition from pixel 15 back to pixel 0 (or from 0 to 15 if our racing
pixel were travelling in an anticlockwise direction). We could
use a similar trick this time, but instead we’re going to employ
a slightly different technique.
You may recall that we introduced the concepts of typedef
(type definitions), enum (enumerated types), and struct
(structures) in my Tips and Tricks column in the December
2020 issue of PE. In this case, we’ll start by declaring a struct
called NextLastPair and then declare an array of these elements called PixelPtrs.
typedef struct
{
int next;
int last;
} NextLastPair;
NextLastPair PixelPtrs[NUM_NEOS];
Remember that the number of pixels in our ring, represented
by NUM_NEOS, is 16. The idea is that, for any element in this
array, which we will assume corresponds to the current pixel
of interest, the next value will be the number of the adjacent
pixel in the clockwise direction, while the last value will be
the number of the adjacent pixel in the anticlockwise direction.
The clever part is when we initialise this array in our
setup() function, as shown below. This initialisation is
based on the same modulo operator tricks we introduced in
my previous column.
for (int iNeos = 0; iNeos < NUM_NEOS; iNeos++)
{
PixelPtrs[iNeos].next = (iNeos + 1) % NUM_NEOS;
PixelPtrs[iNeos].last = (iNeos + (NUM_NEOS - 1))
% NUM_NEOS;
}
I know this can be a little demanding on the little gray cells, so
I’ve generated the results for you to peruse and ponder (Fig.3).
In the case of the next values, for each value of iNeos, we add
1 and then divide
the result by 16
iN eos + 1
% 1 6
iN eos + 1 5
% 1 6
(NUM_NEOS) using
0
1
1
0
1 5
1 5
the modulo opera1
2
2
1
1 6
0
tor to give us the re2
3
3
2
1 7
1
mainder, which – by
4
2
3
4
3
1 8
4
5
5
4
1 9
3
the magic of maths
6
4
5
6
5
2 0
– is the number
6
7
7
6
2 1
5
we want. Similar7
8
8
7
2 2
6
ly, in the case of
8
9
9
8
2 3
7
9
1 0
1 0
9
2 4
8
the l a s t values,
1 1
9
1 0
1 1
1 0
2 5
for each value of
1 1
1 2
1 2
1 1
2 6
1 0
iNeos, we add 15
1 3
1 3
2 7
1 1
1 2
1 2
(NUM_NEOS - 1)
1 4
1 4
2 8
1 2
1 3
1 3
and again divide
1 4
1 5
1 5
1 4
2 9
1 3
1 5
1 6
0
1 5
3 0
1 4
the result by 16
( a) T he nex t v alues
( a) T he last v alues
(NUM_NEOS) using
the modulo operator to give us the Fig.3. Generating the next and last values.
61
remainder, which – once again – provides us with the value we require.
The rest is easy-peasy. All we have to do
in our main loop() function is to cycle
around turning the racing and plodding
pixels on and off as required, including
(possibly) changing the colour of the
plodding pixel when the racing pixel
bumps into it. Once again, you can see
all of this code in file CB-Feb21-01.txt,
which you can access from the February
2021 page of the PE website.
Of course, our experiments here covered a single ring. The Prognostication
Engine has five such rings, which opens
the door to many more possibilities. For
example, every time the first (least-significant) ring experiences a ‘bump,’ this
could increment the racing pixel on the
next ring, and so on up the chain. My
head is buzzing with ideas here, but I’m
afraid they will have to wait their turn
because – you guessed it – SQUIRREL!!!
But where’s the GOL?
I fear I’ve let you down and led you astray.
At the close of my previous column, I
made mention of the fact that in this
month’s column we would be using our
12×12 ping pong-ball array to implement
a version of Conway’s famous Game of
Life (GOL) (https://bit.ly/pe-jan21-cgol).
Sad to relate, I got sidetracked by the
countdown sequence discussed above,
so I’m afraid that we will have to leave
the GOL until next time (I hang my head
in shame). Until then, as always, I (really
do) welcome your comments, questions,
and suggestions.
Want to build your own amazing array?
All the details are in previous Cool Beans
columns, starting in March 2020.
Cool bean Max Maxfield (Hawaiian shirt, on the right) is emperor
of all he surveys at CliveMaxfield.com – the go-to site for the
latest and greatest in technological geekdom.
Comments or questions? Email Max at: max<at>CliveMaxfield.com
Max’s Cool Beans cunning coding tips and tricks
T
he late great Stephen Hawkins (RIP) wrote a
ripping yarn called God Created the Integers: The
Mathematical Breakthroughs That Changed History
(https://amzn.to/3lKfV3e). It’s sitting on the bookshelf here
in my office. One day I intend to read it.
The reason this book just sprung into my mind is that a lot
of things in C/C++ programming in general, and programming
for the Arduino in particular, are, at their most fundamental
level, integers in one form or another.
Are you high?
Let’s consider the pin levels LOW and HIGH, for example. If
we’ve defined a pin as a digital output, we might assign it a
value of LOW or HIGH as follows:
digitalWrite(PinA, LOW);
digitalWrite(PinB, HIGH);
In the real world, assuming an Arduino Uno, these will appear
on the pin as values of 0V and 5V, respectively. But did you
know that we can achieve exactly the same effect using 0 and
1? For example:
digitalWrite(PinA, 0);
digitalWrite(PinB, 1);
How can this be? Well, if the truth were told, behind the scenes
(‘Pay no attention to that man behind the curtain’), the little
scamps who created the Arduino used something like #define
statements to equate the keywords LOW with 0 and HIGH with 1.
62
Knowing this tidbit of trivia opens the door to a lot of things
when we come to think about it. For example, consider the
following snippet of code:
int tmpValue;
tmpValue = digitalRead(PinA);
if (tmpValue == HIGH)
{
// Do some stuff
}
We now know that we could replace HIGH with 1, or with the
result of an integer expression, or with the integer value returned from a function, or... the list goes on.
We all have truths
As Pontius Pilate famously says (or sings) in the 1970 rock
opera Jesus Christ Superstar, with music by Andrew Lloyd
Webber and lyrics by Tim Rice: ‘But what is truth? Is truth
unchanging law? We both have truths – are mine the same
as yours?’ I know what he means and, in a moment, you
will too.
Way back in the 1850s, a largely self-taught English mathematician, philosopher, and logician called George Boole
(1815-1864) developed a form of mathematics that we now
call Boolean algebra. Bool’s intention was to use mathematical techniques to represent and test logical and philosophical arguments. Sad to relate, the significance of his work was
Practical Electronics | February | 2021
not fully appreciated until the late 1930s, when a graduate
student at MIT, Claude Shannon, revolutionized electronics
by submitting a master’s thesis showing how Boolean algebra
offered an ideal way for representing the logical operation of
digital systems.
In the case of the Arduino, we have a data type called bool
(or boolean). Variables declared using this type can be assigned values of false or true. Suppose we declare a Boolean variable called tmpBool and assign it a value of true or
false. Later, we might perform some tests like:
if (tmpBool == true)
{
// Do some stuff
}
if (tmpBool == false)
{
// Do some stuff
}
Note that the way I’m writing this code – as opposed to
using an if else construct – is to illustrate a point. Also,
as a point of interest, we could make the expressions above
more concise and achieve exactly the same effect by writing them as follows:
if (tmpBool)
{
// Do some stuff
}
if (!tmpBool)
{
// Do some stuff
}
The statements encompassed by the if() statement will only
execute if the condition returns a true (ie, non-zero) value.
For example, suppose we have something like the following:
if (tmpInt == 6)
{
// Do some stuff
}
If tmpInt does contain a value of 6, then the condition
will return a value of true (1) and the statements associated with the if() will execute; otherwise, the condition
will return a value of false (0), which will prevent the
statements associated with the if() from executing. The
reason I mention this here is that a common mistake is to
use a single ‘=’ (assignment) rather than a double ‘==’ (comparison); for example:
if (tmpInt = 6)
{
// Do some stuff
}
In this case, the compiler will first evaluate the expression
and assign a value of 6 to tmpInt (which is going to mess up
the rest of the program). Since the processor will see the end
result of the ‘comparison’ as being 6, it will treat this a being
true (non-zero), and subsequently execute the statements associated with the if().
We will continue to consider the ramifications of all this in
my next Tips and Tricks column. In the meantime, as always,
please feel free to reach out to me with comments, questions
or suggestions.
In the latter example, we understand !tmpBool to mean
‘Not tmpBool.’
It’s all about the integers
Under the hood, as it were, the C/C++ compiler – and hence
the Arduino – tends to treat Boolean operations as though
the variables were integers. Let’s start with the false value,
which equates to 0. Based on this, many people think that the
true value equates to 1. This is true as far as it goes (no pun
intended), but in fact any non-zero integer is considered to be
true, so 1, 2, 6, 10, 100… are all true in the Boolean sense.
In turn, assuming we’ve declared an integer variable called
tmpInt and a Boolean variable called tmpBool, this allows
us to do things like:
Your best bet since MAPLIN
Chock-a-Block with Stock
Visit: www.cricklewoodelectronics.com
Or phone our friendly knowledgeable staff on 020 8452 0161
Components • Audio • Video • Connectors • Cables
Arduino • Test Equipment etc, etc
tmpBool = true;
tmpInt = tmpBool;
tmpInt = false;
tmpInt = 6;
tmpBool = tmpInt;
tmpBool = 12;
Obviously, this is nonsense code, but it serves to give us an
idea what we can do. Now, let’s return to consider an if()
statement as follows:
if (condition)
{
// More statements
}
Practical Electronics | February | 2021
Visit our Shop, Call or Buy online at:
www.cricklewoodelectronics.com
020 8452 0161
Visit our shop at:
40-42 Cricklewood Broadway
London NW2 3ET
63
|