This is only a preview of the March 2020 issue of Practical Electronics. You can view 0 of the 80 pages in the full issue. Articles in this series:
|
Max’s Cool Beans
By Max the Magnificent
Flashing LEDs and drooling engineers
I
’ve said it before, and I’ll say it again, ‘Show me
a flashing LED, and I’ll show you a man drooling.’ The
simple truth is that you cannot have too many LEDs in
your hobby projects or electronic systems.
For example, take my Inamorata Prognostication Engine
(Fig.1.), whose task it is to predict the mood of my wife (Gina
the Gorgeous) as I’m leaving my office and heading for home.
(By an ironic twist of fate, should Gina ever discern the Engine’s purpose, I won’t need it to predict her mood of the
moment.) In addition to the tricolour LEDs illuminating the
vacuum tubes on top and powering the furnace in the upper
cabinet, the main control panel is festooned with the little rascals: two for each toggle switch and pushbutton switch, and
16 for each potentiometer.
At the moment, I’m just using these LEDs to present a rainbow effect. Now that I know everything works as planned,
I’m poised to start programming them in earnest, but this has
caused me to reflect on how we might enhance our projects
using only a single unicoloured LED, and then – of course –
working our way up to grander things.
A single unicolour LED
Just saying ‘single unicolour LED’ brings a little tear to my eye,
but I’m trying to be brave, and I bet that with a little thought
we can make even this interesting. Let’s start by considering
a single-pole, single-throw (SPST) toggle switch that we’re
using to control something or other.
Note that we are going to be playing with a very ordinary,
standard, individually packaged LED that’s distinct from the
switch; that is, we aren’t considering a component with an
integrated LED looking like a halo surrounding the center of
the switch, for example.
The first thing is to consider is whether the switching action
is orientated vertically or horizontally (both options are used
in the Inamorata Prognostication Engine); also, if our LED is
to be mounted above, below, left, or right of the switch (Fig.2).
There are two reasons I’m portraying the LED as green in
these images. The first is that the human eye is most sensitive to a yellowish-green colour. The second is that the original LED I plucked out of my treasure chest of spare parts
when I set to writing this column happened to be green (such
Vertical switching action
Prognostication Engine.
Practical Electronics | March | 2020
Horizontal switching action
Fig.2. Alternative switch orientations and LED locations.
65
are the unpredictable
whims of fate).
Whichever switchR1
Side view
a = anode
orientation/LED-lok = cathode
a
cation combo we
a
k
D1
choose, let’s start
k
by considering the
To something
like a relay or a
switch driving the
SW1
microcontroller
LED directly without the aid of a mi0V (GND)
crocontroller (Fig.3).
Fig.3. A switch driving a LED directly.
Note that the reason
I’m showing a 5V supply here is that I’m planning on using
an Arduino Uno in later experiments. Also note that the LED’s
positive terminal, the anode (a), is the longer lead, while its
negative terminal, the cathode (k), is indicated by the flat side
on the plastic package.
Just to remind ourselves as to how these things work, and
to ensure we’re all tap dancing to the same drumbeat, two of
the parameters on an LED’s data sheet are its forward voltage drop (Vr) and its maximum forward current (Ir). Let’s
assume that the V r and I r for our LED are 2V and 20mA
(0.02A), respectively.
Using Ohm’s law, we know that V = IR, which means that R
= V/I. Since our power supply is 5V and our diode drops 2V,
this means that our resistor R1 = (5 − 2) / 0.02 = 3/0.02 = 150Ω.
For the purposes of these discussions, we will assume our
switch is orientated so as to have a vertical switching action
and that the LED is mounted below the switch. We will also
assume that if the lever on the switch is pointing towards the
LED, then we will regard the switch as being in its On/Closed/
Active state; contrawise, if the lever is pointing away from
the LED, we will regard the switch as being in its Off/Open/
Inactive state (Fig.4).
This brings us to an interesting point, which is that in the UK
one typically presses a toggle switch down to make it active
and up to make it inactive. In the case of rocker switches, one
presses the bottom of the rocker to activate the switch and the
top to deactivate it. (One exception to this rule is in aviation,
where toggling the switch up turns it on.) By comparison,
things are usually the other way around in the US, and you
take your chances in the rest of the world. You can do what
you want in the case of your own projects, but consistency is
probably more important than orientation, and it’s always a
good idea to make your systems ‘local friendly.’
D1
Top view
Adding an Arduino
We’re going to have to agree on some form of terminology to
help us keep track of things as we proceed. Let’s start with
the following to describe the previous example of the switch
driving the LED directly:
Switch -> On; LED -> On
Switch -> Off; LED -> Off
While this is functional, it’s hardly inspiring. Is there any way
we can add a little pizazz? Well yes, and the easiest way to do so
is by bringing a microcontroller into the picture. For the purposes
of these discussions
we’ll use an Arduino
Uno. Furthermore, we
need to plan ahead, because as the sophistication of our effects in(a) Switch is On/Active
(b) Switch is Off/Inactive
creases, switch bounce
Fig.4. Our switch’s active/inactive states. may become an issue.
66
5V
5V
Arduino
LS 18
1
2
SW1
3
4
VSS
SW0 OUT0
SW1 OUT1
SW2 OUT2
VDD
8
7
D4
D9
6
5
R1
D1
Fig.5. Using an LS18 to remove switch bounce and an Arduino
to control our LED.
As a reminder, when we turn a switch on or off, it can actually bounce on-off-on-off… multiple times before settling in
its new state. This applies to toggle switches, rocker switches,
pushbutton switches, etc. This isn’t a problem in the case of a
light switch in a house, for example, because any flickering of
the light that may ensue happens far too quickly for our eyes
and brains to perceive it. However, it’s a different matter when
you have microprocessors that can sample their inputs millions of times a second, because they could see what we think
of as a single switch transition as comprising multiple events.
There is a wide variety of hardware and/or software techniques we can use to debounce our switches. In this case, I’m
using an LS18 dual in-line (DIL) chip from LogiSwitch.net
(Fig.5). This is a 3-channel device, which means it can handle
three switches. Excluding noise spikes on its inputs, which
it rejects, the outputs from the LS18 follow their respective
inputs, delayed by 20ms after the final bounce (Fig.6).
The ‘D4’ and ‘D9’ annotations in Fig.4 represent the Arduino’s digital input/output (I/O) pins 4 and 9, respectively.
Observe that we aren’t using pull-up resistors on the LS18’s
SW0 input or the Arduino’s D4 input because the LS18 takes
care of this for us (I love this chip, and I now use it – and its
LS118 cousin – in all of my projects).
A word to the wise
This is just a little bit of advice from me to you. It doesn’t
matter how good you think you are or how simple is
the circuit that you are building, your life will be a lot
less frustrating if you do the following:
1) Draw your circuit diagram on a piece of paper.
2) Compare your physical circuit to the circuit diagram
and mark off the wires one-by-one.
When I was wiring up the breadboard for the first program, I forgot to include one little jumper wire. If I’d
followed my own advice above, I would have saved
myself a lot of hassle. As it was, I spent an inordinate
amount of time tracking this little scamp down. That’s
a very simple example – just imagine troubleshooting
Fig.3 from last month. Yes, I know, you’d rather not,
but following my advice will help you stay sane(-ish).
Forgetting to include this wire caused a lot of frustration!
Practical Electronics | March | 2020
I fear that when you look at my program, you might think the
way I’ve done things is ‘overkill.’ For example, I ended up
using around 50 lines of code, but I could have written this
first sketch much more concisely as follows:
Noise spikes
are rejected
SW0
OUT0
20ms
20ms
Output follows input 20 ms
after last bounce
Fig.6. Operation of the LS18 switch debouncer chip.
For your delectation and delight, I will make any code I
create for the Arduino as part of this series of articles available for you to download, peruse, and ponder. Just to provide
a point of reference, I’ve created an Arduino sketch (program)
that replicates the effects of the switch driving the LED directly (https://bit.ly/2tfezZ0).
#define SWITCH_ON
#define SWITCH_OFF
LOW
HIGH
#define LED_ON
#define LED_OFF
HIGH
LOW
int PinSwitch = 4;
int PinLed
= 9;
int OldSwitchState;
int NewSwitchState;
void setup ()
{
pinMode(PinSwitch, INPUT);
pinMode(PinLed,
OUTPUT);
OldSwitchState = digitalRead(PinSwitch);
if (OldSwitchState == SWITCH_ON)
{
digitalWrite(PinLed, LED_ON);
}
else
{
digitalWrite(PinLed, LED_OFF);
}
}
void loop ()
{
NewSwitchState = digitalRead(PinSwitch);
if (NewSwitchState != OldSwitchState)
{
if (NewSwitchState == SWITCH_ON)
{
digitalWrite(PinLed, LED_ON);
}
else
{
digitalWrite(PinLed, LED_OFF);
}
OldSwitchState = NewSwitchState;
}
}
Practical Electronics | March | 2020
int PinSwitch = 4;
int PinLed
= 9;
void setup() {
pinMode(PinSwitch, INPUT);
pinMode(PinLed,
OUTPUT);
}
void loop() {
if (digitalRead(PinSwitch) == LOW)
digitalWrite(PinLed, HIGH);
else digitalWrite(PinLed, LOW);
}
Actually, we could remove the if() statement and reduce the
body of our loop() function to a single line if we were feeling particularly bold (email me if you aren’t sure how to do
this). There are several reasons why I created the first sketch
the way I did, the most important being to set the scene for
what is to come, like having a special start-up sequence when
power is first applied to the system, for example.
By now you must think I’m a coding genius – my good manners prevents me from contradicting you. I have learnt a few
tricks along the way, and if you are new to all of this then at the
end of the article I’ve put together a few little tips, hints and
handy rules of thumb that may make your life a little easier.
Pretentious? Moi?
You may call me pretentious if you will (although the radiance of my mother’s smile will no longer fall upon you), but
I feel that simply turning our LED hard on and hard off lacks
a little je ne sais quoi.
The thing is, now that we have added a microcontroller
into the picture, we can do all sorts of capriciously cunning
things. ‘The world is our oyster,’ as they say (or octopus, or
any mollusc of your choice). For example, when the switch
closes, we could flash the LED three times before pausing and
then going hard on (https://bit.ly/2FbmWqY):
Switch -> On; LED -> Flash-Flash-Flash-Pause-On
Switch -> Off; LED -> Off
If you look at my code, you’ll see I’m using calls to the delay()
function. I’m doing this to make the code as easy to understand as possible. In practice, however, using this function
would not be a good idea in this situation. This is because
delay()is a blocking function, which means it prevents the
Arduino from doing anything else (apart from servicing interrupts). Happily, there are better ways of doing things, but
they’re a bit more complicated so we’ll leave them as a topic
for another day.
Returning to our discussions, another possibility is that –
instead of turning it hard on and off – we fade the LED up and
down, respectively:
Switch -> On; LED -> Fade-On
Switch -> Off; LED -> Fade-Off
So, how do we perform this fade? It is possible to dim an LED
by lowering its supply voltage, but this typically doesn’t work
as well as one might hope, not the least because things will go
pear-shaped once your supply voltage approaches the LED’s
forward voltage-drop value. The alternative is to turn the LED
67
One
cycle
5V
0V
5V
0V
5V
0V
5V
0V
Each cycle is divided
into 256 ‘time slices’
0% Duty Cycle
analogWrite(<pin>, 0)
LED off all of the time
33% Duty Cycle
analogWrite(<pin>, 85)
LED on 1/3 of the time
66% Duty Cycle
analogWrite(<pin>, 170)
LED on 2/3 of the time
100% Duty Cycle
analogWrite(<pin>, 255)
LED on all of the time
Fig.7. Using PWM to control the brightness of an LED.
fully on and fully off very quickly. If the LED is only on for
50% of the time, for example, then it will only appear to be
half as bright as if it were on for 100% of the time.
Fortunately, microcontrollers can turn things on and off very
quickly indeed, thereby allowing us to implement something
we call pulse-width modulation (PWM). In the case of the Arduino, we have a function called analogWrite() that allows
us to specify a pin number and a PWM parameter, where this
parameter is an 8-bit unsigned integer that can represent values
between 0 (fully off) and 255 (fully on) – see Fig.7.
In the case of the Arduino, each complete cycle shown in
Fig.7 consumes only around 1/500th of a second. Using this
technique, the LED is turned on and off so quickly that no
flicker is perceptible to the human eye. The Arduino Uno has
six pins that support PWM via the analogWrite() function:
3, 5, 6, 9, 10, and 11 (thank goodness we decided to use pin 9
to drive our LED – it’s almost as if we had a plan).
Let’s commence with a ‘clunky’ approach where we ‘fade
up’ by increasing the brightness in three big steps, starting at
0% and proceeding to 33%, then 66%, then 100%. We’ll fade
down in the same clunky way (https://bit.ly/2u7GACa). Once
we’ve seen this in action, we’ll modify our program to make
the fades occur much more smoothly (https://bit.ly/2MKPqfp).
Feel the power!
Something else we might consider is the possibility of implementing a start-up sequence. That is, when power is first applied to the system, we want to do something a little bit special
– perhaps something like the following (https://bit.ly/2QGlGBQ):
Power-up (Switch is on): LED -> ((flash-flash-flash-pause) * 3) -> On
Power-up (Switch is off): LED -> extended-flash -> Off
Now imagine a system that boasts a bunch of switches and LEDs,
like the Prognostication Engine, for example. In this case, we
can make our start-up sequence a feast for the eyes. Furthermore, since this bodacious beauty is equipped with tricolour
LEDs, we might vary the entire colour scheme depending on
its evaluation of the current state of play in the world (bright,
cheerful colours when things are going well; more foreboding colours when the situation is turning ominous – DEFCON
anyone? https://en.wikipedia.org/wiki/DEFCON).
Breathing, snoozing, and snoring
One more thing to consider is what happens if no one activates a switch for a
certain period of time – say 10 minutes –
which we might call the ‘Inactive Timeout Period.’ It might be nice to indicate
this in some way.
68
There are two primary scenarios for when this period times
out: either the switch and LED are currently on or they are
currently off. Let’s start with the case where they are on. One
thing we could do would be to enter a mode where we give a
really short periodic flash:
Inactive Timeout (Switch is on): LED -> Flash-Pause-Flash-Pause…
Inactive Timeout (Switch is off): LED -> Stays off
Another alternative is that the systems enters a ‘breathing’
mode (I’ve also heard this called ‘snoozing’ and ‘snoring’ on
the basis that the system is sort of asleep) where the LED gradually fades up and down... and up and down:
Inactive Timeout (Switch is on): LED -> Breathing mode
Inactive Timeout (Switch is off): LED -> Stays off
Last but not least, if we are using a breathing mode when the
switch is on, maybe we could use an intermittent flash when
the switch is off:
Inactive Timeout (Switch is on): LED -> Breathing mode
Inactive Timeout (Switch is off): LED -> Flash-Pause-Flash-Pause…
When something occurs to wake the machine up again, maybe
we could run the start-up sequence we discussed earlier.
There are a couple of reasons I haven’t provided any code
examples for these snoozing and snoring cases. The first is
that we currently have only a single switch, so the only way
to wake the system up would be to toggle our switch into its
opposite state. If the switch were on when we entered the
snoozing mode, for example, then the only way to wake the
system would be to turn the switch off. Thus, implementing
this mode would make a lot more sense if our system boasted
a bunch of switches.
Another problem is our use of the delay() function, which
makes it difficult for us to detect when some action occurs
in the outside world and respond to that action in a timely
manner. We will discuss techniques to work around this in
a future column.
The excitement mounts!
On the off chance you’re interested, I’ve created a video showing all of the effects we’ve discussed thus far in action (https://
bit.ly/2ZUvL1W) – I’d love to hear what you think.
Also, remember that all of the effects presented above are
just first-pass ideas that popped into my head as I was writing this column. It would be great if you could perform some
experiments of your own and then email me if you come up
with any cunning effects you would care to share. (Such generosity will be handsomely rewarded with fame, but no fortune – none.)
The really exciting thing is that, thus far, we’ve only considered a single toggle switch with a single unicolour LED. What
about pushbutton switches (latching and momentary/non-latching)? What about bicolour and tricolour LEDs? And what about
having two LEDs associated with each switch or pushbutton?
I don’t know about you, but I’m becoming light-headed
considering the multifarious potentials, prospects, and possibilities. I can’t wait to see what we come up with in next
month’s column. In the meantime, I welcome your comments,
questions, and suggestions. Until next time, have a good one!
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
Practical Electronics | March | 2020
Max’s Cool Beans cunning coding tips and tricks
I
’ll be providing downloadable Arduino sketches
(programs) to accompany my next few Cool Beans
columns, so I thought it might be a good idea to provide
a rationale for the coding style I use.
The first point to note is that you use the Arduino’s integrated
development environment (IDE) to capture your sketches using
the C/C++ programming languages. C++ is essentially C with
extensions (like C on steroids). C++ is classed as an object-oriented language (OPP), but we really don’t need to worry about
what this means at this moment in time. What we think of as the
‘Arduino Language’ is a set of pre-defined C/C++ functions that
someone has written for us and that can be called from our code.
The main() function
One thing that trips some people up if they already know C/
C++ is that programs written in these languages always have
top-level function called main(). In the case of the Arduino,
however, there are two functions that are placed in your sketch
by default, setup() and loop(), but no main() function.
(You can also create your own functions, but your sketch must
include the setup() and loop() functions.)
The IDE also adds a comment inside the setup() function
saying, ‘Put your setup code here, to run once.’ Similarly, there’s
a comment inside the loop() function saying, ‘Put your main
code here, to run repeatedly’ (their use of the word ‘main’ is
unfortunate in this context).
Behind the scenes, when you click the ‘Compile’ icon, the IDE
generates a temporary file that includes all of your code along
with a main() function looking something like the following:
main ()
{
setup();
while(1) loop();
}
Comments really help you and others to understand and maintain your programs, so don’t be miserly with these little scamps,
but don’t add comments when there’s no need to do so (having
a statement that says a = a + 1; followed by a comment that
says: ‘// Add 1 to a’ is the sign of someone who needs a little
guidance). Use common sense.
Got style?
Do you remember the song Style, as sung by Frank Sinatra, Dean
Martin, and Bing Crosby (https://bit.ly/39qLpGG)? There are a
couple of lines that go: You’ve either got or you haven’t got style.
(If you got it, you stand out a mile.). This is certainly true in the
case of writing code.
A lot of coding style comes down to personal preference. If
you work for a big company, you are expected to follow their inhouse style. If you are doing this for yourself, you can develop
your own way of doing things. The main thing is to be consistent so that you (and others) can more easily understand and
maintain your code in the future.
I’m a hardware design engineer by trade. I’m self-taught regarding software, so you really shouldn’t listen to anything I say
regarding coding. On the other hand, I’ve been exposed to a lot
of code and seen a lot of things that I like (along with a lot that
I don’t). My own style has evolved over the years as I’ve been
exposed to different techniques. What follows is a subset of the
coding techniques that work for me:
#define
I always use uppercase alphanumeric characters and underscores for my constant names; for example:
#define START_COUNT
#define END_COUNT
0
100
Rationale: This allows me to easily identify global constants in
the body of the program.
This calls the setup() function a single time, and then it
calls the loop() function over and over and over again.
Don’t use ‘magic numbers’
Cases and spaces
Take a look at the following statement:
C/C++ are case-sensitive languages; for example, variables with
names like fred, Fred, and FRED are treated by the compiler
as being completely different entities.
C/C++ don’t care whether you use whitespace characters or
not (where whitespace characters include spaces, tabs, and blank
lines). The main reason for using whitespace characters is to
make your code more readable; they are stripped out when the
code is compiled, so they won’t make the final program that’s
loaded into the Arduino any bigger.
Speaking of tabs... don’t use them. Yes, of course it’s easier to
press the <tab> key a single time rather than the <space> bar
multiple times. The problem is that you don’t know how many
spaces other systems will associate with a tab, which may make
your printouts look strange, for example. And let’s not forget
the 2017 article on the StackOverflow.blog website: Developers Who Use Spaces Make More Money Than Those Who Use
Tabs (https://bit.ly/37iB0eg).
for (i = 0; i < 10; i++)
{
// Do stuff here
}
Both the 0 and the 10 would be considered to be ‘magic numbers’ because they appeared from nowhere as if by magic. Generally speaking, it’s fine to use 0 (or 1) because we all know that
counts usually start or terminate at 0 (or 1), but it’s best to use
a constant value for any other number; for example (assuming
MAX_COUNT was previously declared using a #define statement):
for (i = 0; i < MAX_COUNT; i++)
{
// Do stuff here
}
Comments
Next time
Anything on a line following two forward slashes (//) characters
is seen as a comment. Multi-line comments can be started with a
slash and a star (/*) and terminated with a star and a slash (*/).
In my next column we’ll look at variable names, function names,
and function calls. In the meantime, if you have any coding
tips and tricks you’d care to share, I’d love to hear about them.
Practical Electronics | March | 2020
69
|