The flowchart in Figure 11 shows the program
structure. It starts by defining some constants and
performing block-level initializations.

The internal ADC is connected to the GP4 input and
set for 10-bit mode. The PWM is initialized and also set
for 10-bit mode so that its resolution is maximized and
matches the ADC resolution. As noted earlier, this
resolution is important for precise control over a wide
range of load currents. Unfortunately, there is a tradeoff
between resolution and PWM frequency in this
microcontroller (which is not uncommon).

Because of this tradeoff — while I would have
preferred using a PWM frequency above 40 kHz as it
would have made the LC filtering easier — I ended up
setting it to about 8 kHz. With the inductor and capacitor
values used, though, this switching rate is high enough for
effective filtering.

In the main body of the program, we enter a main
loop which starts by reading the ADC voltage (connected
to the potentiometer). For the ADC reading, I decided to
take eight samples and discard the
edge samples to avoid issues with
noise. This is done by sorting the
eight samples and then averaging
only the inner samples (you could
call this a “trimmed mean”). The
resulting (filtered) value is then
used to adjust the PWM duty
cycle proportionally to the
potentiometer/ADC voltage
reading.

Next, the program polls the
toggle switch input to detect if it
has been pressed. Note that
there’s debouncing code executed
here to avoid instability. If the
switch press is detected, the relay
output is toggled and the load
turned ON (or OFF).

Results

With the three paralleled light bulbs I used, I was able
to draw in excess of 3A at 3.3V and north of 4A with a 5V
supply input. This is more than adequate for most of my
uses. As I explained under “Incandescent Light Bulb
Selection,” the maximum current one can draw with this
adjustable load is determined by the light bulb’s power
rating and changes in a non-linear fashion with voltage.

Note that this curve would be different if different
light bulbs were used, and represents only the maximum
current the load is capable of drawing at each input
voltage. Lower currents — from zero up to the maximum —
are obtained by simply adjusting the potentiometer.

Conclusions and Future
Improvements

While I’m quite satisfied with the current
implementation and it has already proven very useful in
my hobby, there is always room for improvement in any
project. Here are some thoughts for future improvements
that the reader may consider:

Gate drive — For high power operation, level shifting
the MOSFET gate drive to 9V would result in higher VGS
voltage, thus lower on resistance and potentially even less
power dissipated in the MOSFET. This should be
considered if very high load currents are needed.