Comparing Options
In this article, Jeff takes a closer look at LED technology, and he compares the respective advantages of linear, non-addressable controlled LED strips and digitally addressable strips.
How light-emitting diodes (LEDs) are used is constantly evolving, yet they remain basically unchanged. This semiconductor emits light when current flows through it. When electrons recombine with electron holes, they can release energy in the form of photons. The energy required for electrons to cross the band gap of the semiconductor determines the color of this light. Invisible IR may have been the first radiant energy produced by a semiconductor device, but the red LED wasn’t far behind. That led to the seven-segment displays used in watches and calculators of the ‘60s.
My first four-leaded, multicolored LED was quite expensive. Both common anode and cathode devices were available, thus only four connections were required (Figure 1). Now, surface mount devices (SMDs) are available in 4-pin devices (sharing common anodes or cathodes) and 6-pin devices (with discrete connections). We know that white is the combination of red, green, and blue (RGB). These devices contain these three distinct colors on separate visual axes, so their visual output is less than optimal. Many RGB devices have an additional element, a white LED, for truer color output. Most of today’s HDTVs use some kind of LED technology.
You’d think it was enough to produce a good, discrete LED device, but controlling individual LEDs requires several physical connections, so it would be handy to reduce the number of connections necessary. RGB LEDs are often required to work together to produce a continuous blanket of a single color. RGB LEDs, as in the SMT RGB 5050 LED from Adafruit ($0.50), shown in Figure 2b, can be arranged in groups of three to reduce current and run from a 12V source. There are separate grounds for each color, for a total of four connections. Each color must have its own series resistor to limit the current and balance the color outputs. This circuit of three devices is duplicated in parallel to create longer lengths that still only require four connections. In this arrangement, each group of three LEDs produce the same color, as do all the parallel groups that extend the bus (if you will). We’ll look at this shortly.
These colorful strips quickly became popular, but as we all know, the status quo doesn’t stand still for anyone. A cry for individually addressable colors was first answered by embedding a clockless shift register into each RGB device, as in Adafruit’s NeoPixel RGB LED ($0.50). The timing requirements are pretty strict for these devices. The pulse width of an applied 800kHz clock determines each bit’s logic level. These devices operate on 5V, which means that though the supply voltage is lower than the aforementioned devices, the current is three times greater (for three LEDs, as opposed to the previously mentioned devices). Because of their speed requirements, not all processors can handle the timing requirements. However, these devices require only three connections.
A serial peripheral interface (SPI) improved this scheme by eliminating the tight timing constraints. DotStar Addressable 5050 RGB LEDs (Figure 2a) require an additional connection—back to four connections—but they are easier to use.
Adafruit’s DotStar (a) is an addressable RGB LED. The internal RGB LEDs are controlled by some internal smarts. It requires a data stream to set individual colors, and so I call this a “digital RGB LED.” Adafruit’s discrete RGB LED (b) has three independent LEDs. This device has separate anode and cathode connections for each LED. A color can be set with a series resistor for each LED. I call this an “analog RGB LED.”
I wanted to use LEDs to create a numerical digit or a seven-segment display. In this article, I’ll take a closer look at the respective advantages of the linear, non-addressable controlled strips and the digitally addressable strips, and I’ll explain my choice between these two options. But first, it makes sense to understand why an LED is more efficient than an incandescent bulb as a light source.
— ADVERTISMENT—
—Advertise Here—
WATTS, LUMENS, AND CANDELAS
Historically, we used the term “watt” to describe the light output of an incandescent bulb, even though a watt is actually a measure of power consumed, not brightness. This was meaningful when comparing incandescent bulbs of different wattages, but fell short when comparing incandescents with other types of radiant-energy producers, such as sodium vapor lights, compact fluorescent light bulbs (CFLs) and LEDs. For a while now, incandescent packaging also has contained a specification of light output in “lumens” (lm). The lumen is a measure of the total light emitted by a bulb, no matter what type of bulb it is or how many watts it consumes. This terminology specification allows a better comparison between different bulb technologies. Watt-lumen approximations for some common incandescent bulbs are given in Table 1.
An incandescent bulb uses a filament to produce radiant energy, and the current passing through the filament’s resistance creates heat energy. Although the desired effect is light, light is merely a byproduct of the heated and glowing filament. More current equals more heat and light and a higher wattage. It’s no wonder that wattage was used as an indication of brightness. To get there, the majority of the energy is heat, so the efficiency of the incandescent bulb is relatively low. A typical relationship between watts and lumens is 12lm per watt for an incandescent bulb. This conversion can be seen as the incandescent bulb’s efficiency: 1W will get you 12lm of light, with lumens being the total light energy emitted.
The candela (cd) and millicandela (mcd), the standard units of luminous intensity, refer to the amount of light emitted by a device in a particular direction. A conical shape with an inclusion angle of about 65.54º produces a circular surface area of r2 on a sphere of radius r, which is defined as 1 steradian. This turns out to be 1/4π of the total surface area of the sphere. The candela is based on the lumens that hit this area. Thus, while the lumen is a measure of total light, candela is only a fraction of the total light, or about 0.08lm. So 12.57lm can be said to produce 1cd.
When we want to light up a room, we may want to light every area in the room equally, or perhaps just the floor, or a picture on the wall. Incandescent bulbs come in other styles such as flood and spot. A floodlight directs all the lumens into a cone of about 60-120º, whereas a spotlight directs all the lumens into a cone of less than 60º. So an incandescent bulb of 100W that produces 1,600lm of light will have a lower candela rating (~130cd) than an incandescent floodlight (~400cd), and both will have a lower candela rating than a spotlight (~2,000cd).
5050 RGB LED
Each sub-circuit of an analog 5050 RGB LED string contains three identical RGB LEDs connected in series with a current-limiting resistor that limits each color to 20mA when powered by a 12V supply. You can separate these strings between each group of three RGB LEDs.
A 5050 RGB LED is a generic RGB analog device that gets its name from its physical size—0.5cm2. At 60mA (20mA/color) it has a minimum output of 2,000mcd (three colors ON). A 1m strip contains 60 RGB LEDs in 20 parallel groups of three series devices. Figure 3 shows how these are arranged. We can calculate the wattage required for this generic RGB analog LED strip of 60 LEDs driven by a 12V source:
20 LED groups x 0.06mA = 1.2A
12V x 1.2A = 14.4W
Now we can calculate the efficiency by converting the total candela to lumens and dividing by the wattage:
20 LED groups x 2,000mcd/group x 3 LEDs/group = 120,000mcd or 120cd
120cd x 12.57 = 1508lm
1508lm/14.4W = 105lm/W
If we look at a few specifications in a 5050 RGB datasheet shown in Table 2, we can get an idea of how this stacks up to the incandescent bulb in terms of efficiency.
— ADVERTISMENT—
—Advertise Here—
Pertinent specifications from the 5050 RGB datasheet. Based on a 12V supply voltage and three of these devices in series, the current through each color LED is set by a single resistor for each color. Green and Blue = 150Ω, and Red = 330Ω. Based on the minimum voltage drops that’s 20mA/color.
When the supply voltage is 12V, a set of three series devices will draw a total of 60mA with 100% duty cycle. As before, we can calculate the wattage for each generic RGB analod group of 3 LEDs as follows:
12V x 0.06A = 0.72W
The efficiency should be the same:
2,000mcd/device x 3devices/group = 6,000mcd or 6cd
6cd x 12.57 = 76lm
76lm / 0.72W = 105lm / W
As you can see, the efficiency of this LED device is just over 105lm/W. That’s about eight times more efficient than an incandescent bulb.
DOTSTAR ADDRESSABLE 5050 RGB
Adafruit’s DotStar (SK9822) is a smart 5050 RGB LED. It includes constant current sources for the three internal LEDs, a 2-wire SPI-type interface, and multiple PWMs—an 8-bit PWM for each color and a 5-bit PWM for the constant current. While three of the analog devices previously discussed can be stacked to make use of a higher supply voltage and a reduced current, these smart devices have additional circuitry that requires a 5V supply. Therefore, they do not have the “groups of three” requirement, and you can have a string with any number of devices. Figure 4 shows how these are arranged. The LEDs also have the same maximum current of 20mA per LED. We calculate the wattage required for a single RGB digital LED device, at 60 ma per device, driving by a 5V source:
5V x 0.06A = 0.3W
And we calculate the efficiency:
2,000mcd/device or 2cd
2cd x 12.57 = 25lm
25 lm/0.3W = 83 lm/W
This device is about six times more efficient than an incandescent bulb. It is a bit less efficient than the analog device, however; though the loss at 5V is pretty much the same as the loss at 12V, at 12V the loss is divided over three devices instead of one. Being able to control the color of each individual device may be a necessity for your project, so you can deal with a slightly lower efficiency to gain the addressable advantage.
Since the DotStar RGB LEDs are digital serial devices, they require a 5V supply. They have separate power/ground, clock/data, input/output connections. You can separate DotStar strings between each RGB LED.
CIRCUIT REQUIREMENTS
To handle a single, analog 5050 RGB LED, we must ground the three LED colors’ connections. We need the ability to sink up to 20mA per LED chain, and have access to open drain outputs, because the chain of three devices is run from 12V. The Arduino does not offer “open collector” as an output definition, but it can be simulated by using the input mode when the logic is high and the output mode when the logic is low. Since I will eventually be using more than one chain of three devices, I’ll need a higher current sink capability, so adding three external high-current devices now will simplify all experimentation. The circuitry I used for this project is shown in Figure 5. For the analog strips I used a metal-oxide semiconductor field-effect transistor (MOSFET) as an external control device. These have a low series resistance, even with logic level gate voltages, and can handle multiple Amps. The 1m string discussed above requires 1.2A at 12V. You can apply power to both ends of the strip, if necessary, to eliminate voltage drop.
This is an Arduino Shield schematic. This circuit was built on an Arduino prototyping shield. Three outputs—one for Red, one for Green, and one for Blue—can sink 20mA for each single analog RGB LED. MOSFETs are drivers from each output that handle the higher currents of an analog string. }The digital DotStar strings require just two output pins; the SPI-type output uses just CLK and MOSI. A 1A 12V power supply can supply power for the analog strips (60 LEDs) and the Arduino. A 2A 5V power supply will be needed for the DotStar strip (60 LEDs) and the Arduino.
Although the digital strip requires no additional circuitry, the system’s 5V supply must handle all the current for the LED strip via the strip’s PCB trace. With 60 devices/meter and 60mA/device that’s 60 x 60mA = 3,600mA/meter or 3.6A/meter. At maximum illumination, the copper voltage drop on a 1m strip can be pretty high and may cause color distortion at the far end. Remember—the Blue and Green LEDs may need up to 3.4V to operate correctly, as shown in Table 2. You can apply power to both ends of the strip, with sufficiently large wires, and eliminate voltage drop.
STRIP CODING
Using the Arduino to drive these strip LED devices takes very little code. Let’s start off with a single, analog RGB device. It can run on 5V, since the device is merely three individual LEDs. As mentioned earlier, many micros can sink 20mA/pin, so if you’re using a single RGB device, you can drive one without any external high-powered devices. In fact, since the outputs are current-limited, you can get away with no series resistors. That’s bad practice, but you can save three resistors.
When using an analog strip, the minimum number of devices you can cut the strip into is three. Each three-device sub-circuit contain three stacked devices, and each color in a device is wired in series with its own series resistor to limit the current flow through the three devices to 20mA/color, with a supply of 12V. The current for a triplet is small. However, since these are driven from a 12V supply, its not good practice to sink this current using a 5V logic output, even if it has protection diodes. Instead, an external device should be used to interface the logic output with the higher supply voltages. You’ll want to choose an external device that can handle the total current used by all the devices in your string. For a few triplets, a small transistor can be used, but for more devices, you’ll want to use an FET with a small ON-resistance (Ron), which will keep wasted heat to a minimum.
— ADVERTISMENT—
—Advertise Here—
Each color is controlled by a single bit, so just turning ON or OFF, a bit can get you six basic colors (R, G, B, and cyan, magenta, and yellow) plus white. If you want more control, either over color tuning or brightness, you’ll need to use PWM on each output bit; so check with your device to see which pins support hardware PWM. As an option, you could use just about any output pin with a software PWM routine. Make sure this is timer-interrupt driven, so the interrupt’s high priority assures consistent pulse width of the LEDs.
The hardware PWM is simple to use. The command analogWrite(pin,value)generates a PWM output at approximately 500Hz, with ON/OFF times equal to value/255 (ADC resolution)—anywhere from 0/255 (0%) ON, to 255/255 (100%) ON. While this is not really an analog command, when a low-pass filter is added to its PWM output, an analog level (average voltage) is generated on the capacitor. Here we won’t need an analog voltage, because the value you provide (1-254) will produce a pulse width proportional to the value. A value of zero creates a constant logic low, whereas 255 produces a constant high. The first program uses a number of defined colors and asks what color you want to set the analog RGB device. Based on your answer, a look-up table determines the appropriate value for each of the PWM outputs.
#define RedPWM 5
#define GreenPWM 6
#define BluePWM 3
// color value = RRGGBB
#define Black 0x000000
#define White 0xFFFFFF
#define Red 0xFF0000
#define Lime 0x80FF00
…
The PWM output pins are identified and some colors are defined. You can add or modify values here for your particular needs. The setup function initializes a few things (Listing 1).
In the loop() function we make an inquiry and let the user enter a color, return the resultant RGB values, and update the PWM commands with those values. When an input ends with a <CR> (carriage return), I do a color check to see if it has been defined. If so, the defined value is split into its R, G, and B values. The color check also tests for an integer between 1 and 255. This sets the variable brightness to a percentage (float), so each color value is affected by that amount. These are the “new” values that are used in the updateColor() function (Listing 2).
That’s it! The color and brightness of each LED in the device has now been changed. You can measure the 12V current and see how the color and brightness (PWM values) affects the this. Whether you have one or a string of LEDs connected to the three PWM color outputs, all the LEDs will be affected and respond in the same way, because they are all being driven off the same three outputs. If you need to have individual control of each LED, this analog system will not work for you. So let’s look at the digital version, the DotStar.
The DotStar addressable RGB LEDs are a second generation addressable LED. NeoPixels are similar, but require a single output pin to control all LEDs. With NeoPixels, data is sent as a serial bit stream at a rate of 400kHz. A 20% high pulse equals a “1,” and a 50% high pulse equals a “0.” A pause (low) of >50µs resets or locks in the data. You can see how any timing issue can inadvertently cause misinterpreted data or an incomplete transmission, resulting in wrong data latching into the devices. The DotStar system is faster and more reliable, so I will skip NeoPixels and present the DotStar application.
We can start off with a single DotStar device to get the feel of how to communicate with it. You can use hardware or software SPI to produce the clock and data necessary. We begin by selecting an SPI, the color definitions, and the initial color values which will be used to initialize the LED string (Listing 3).
In a similar fashion as with the analog application, initialize all the peripherals being used, and set the color data and brightness in the setup() function (Listing 4).
This is pretty much the same, except for the sendLEDFrame() function, which handles sending the data stream through the SPI. Hardware SPI handles sending the data we feed to it, using the SPI.transfer(value) function in the SPI library. For our software SPI (no library), I created a transfer(value) function that will send the data using digital writes to my CKI and SDI pins (Listing 5).
Listing 1
To control an analog LED device we need to define the three outputs that will be used to control each color LED within the device. Initializing each color to 0 (0% ON) assures that the LEDs will be totally OFF, even though the brightness is 100%. Brightness has overall control of the three individual colors, which allows dimming while keeping the original color proportions. The routine updateColor() handles this.
void setup()
{
// initialize Serial
Serial.begin(115200);
Serial.println(SignOn);
// initialize PWM pins
pinMode(RedPWM,OUTPUT);
pinMode(GreenPWM,OUTPUT);
pinMode(BluePWM,OUTPUT);
// initialize colors
BlueColor = 0;
GreenColor = 0;
RedColor = 0;
Brightness = 100;
// update the PWM outputs
updateColor();
}
Listing 2
In this routine the the PWMs are set for each color. Each PWM value is based on the initial color value multiplied by the brightness percentage (brightness/100).
void updateColor()
{
analogWrite(RedPWM,RedColor * Brightness/100);
analogWrite(GreenPWM,GreenColor * Brightness/100);
analogWrite(BluePWM,BlueColor * Brightness/100);
}
Listing 3
To control a digital LED device, we need to define two outputs that will be used as the SPI's serial clock (SCL) and serial data out (SDO). Additionally, we need to know how many of these serial devices will be addressed, the LED string length.
#define softSPI
#ifndef softSPI
#include <SPI.h>
#else
#define CKI 13
#define SDI 11
#endif
#define NUMPIXELS 1 // Number of LEDs in strip
// color value = RRGGBB
#define Black 0x000000
#define White 0xFFFFFF
#define Red 0xFF0000
...
String SignOn = “DotStar Single”;
byte Brightness;
byte BlueColor;
byte GreenColor;
byte RedColor;
String myString;
Listing 4
The initialization here is similar to the analog device, except we are setting up the pins for the SPI. The routine sendLEDFrame() formats the SPI data and clocks it out the SPI port to the devices in the connected string of LEDs.
void setup()
{
// initialize Serial:
Serial.begin(115200);
Serial.println(SignOn);
#ifndef softSPI
// initialize SPI:
SPI.begin();
Serial.println(“Hardware SPI Ready”);
#else
pinMode(CKI,OUTPUT);
pinMode(SDI,OUTPUT);
Serial.println(“Software SPI Ready”);
#endif
Brightness = 31;
BlueColor = 0;
GreenColor = 0;
RedColor = 0;
sendLEDFrame();
}
Listing 5
The actual work is handled here in the sendLEDFrame() routine. The format for each Data frame is four bytes. Note: the minimum transmission consists of a Start frame, the Data frame and an End frame. The Start and End frames are unique and signal the LEDs that a new transmission is beginning or ending. Each LED will use the last Data frame it receives as its 'new' data.
void sendLEDFrame()
{
sendStartFrame();
#ifndef softSPI
SPI.transfer(0xE0 + Brightness);
SPI.transfer(BlueColor);
SPI.transfer(GreenColor);
SPI.transfer(RedColor);
#else
transfer(0xE0 + Brightness);
transfer(BlueColor);
transfer(GreenColor);
transfer(RedColor);
#endif
sendEndFrame();
}
void transfer(byte myData)
{
for(int i=0; i<8; i++)
{
digitalWrite(CKI,LOW);
digitalWrite(SDI,myData & 1<<i);
digitalWrite(CKI,HIGH);
}
}
In the loop() function, we make an inquiry, let the user enter a color, return the resultant RGB values, and send the appropriate SPI data with those values. When an input ends with a <CR> (carriage return), I do a color check to see if it has been defined. If so, the defined value is split into its R, G, and B values. The color check also tests for an integer between 1 and 255. This sets the variable brightness value 1-31; this affects all colors in the same way. These are the “new” values that are used in the sendLEDFrame() function.
The data must be sent to the DotStar device in a particular format. There are two aspects of this: the individual DotStar data, and the stream format. The stream format begins with a start packet, contains n data packets, and ends with a stop packet. Each packet contains 4 bytes. The start packet has all bytes = 0x00. The end packet has all bytes = 0xFF. A data packet contains a brightness byte followed by three color bytes—Blue, Green, and Red.
Byte 1 Brightness
0xE0 + 0x00:0x1F
Byte 2 Blue Intensity
0x00:0xFF
Byte 2 Green Intensity
0x00:0xFF
Byte 2 Red Intensity
0x00:0xFF
Note that the first byte, brightness, is a 5-bit value that affects all colors equally, without affecting the color mix, whereas each color has its own control, so that a color can be produced by mixing proportions of each primary color. The data cannot look like a start packet, but it can look like an end packet (maximum brightness white).
NUMBER 1
Now that you can light up analog and digital versions of these RGB LEDs, let’s get to the purpose of this column. I wanted to use LEDs to create a numerical digit or a seven segment display. Each segment will contain n LEDs. Since analog RGB LED strings must all be the same color and the same intensity, if we want to control these as separate segments we need to supply three control lines for each segment, for a total of three control outputs x seven segments or 21 control outputs for a digit. Since digital RGB LED strings can be controlled individually, only two controls outputs are needed for that same digit. Digital RGB LEDs are therefore the obvious choice for this project.
The cut seg ments of a DotStar strip are reassembled into the shape of a seven-segment display. Remember—each strip has an input and an output end. Segment size will determine digit dimensions. Four LED segments produce a digit slightly less than 8″ x 4″.
I chose to use four DotStar RGB LEDs for each segment, to make a digit about 7” high. (You can increase the number of LEDs per segment for larger digits.) I cut a 1m (60-LED) strip into seven strips of four LEDs each (Figure 6). To simplify the connection of all of these short strips, I laid out tiny PCB corners that have solder points along each edge. Wide traces connect adjacent connectors. There are two separate circuits on each PCB corner (Figure 7).
PCB corners allow segments to be easily reconnected. A center hole helps to secure the digit, or you can apply an adhesive. Flip the PCB around for the appropriate 90-degree connection.
Actually these corners can be used to put together a digit in four ways. You may begin in the center of either side and go up or down. You will end up in the center of the opposite side. Remember that these strips have an input side and an output side, so wherever you begin, make sure you connect these strips so they go from input side to output side. I went in on the right side and down, and ended coming down to the center on the left side. This means that the first data in will be pushed all the way to the end of the chain, down the lower right segment, through the bottom, up the lower left segment, across the middle segment, up the upper right, across the top, and down the upper left segment.
I used 1 byte (bits 1-7) to hold the seven segments worth of data. When a bit=1 the segment is ON, and when the bit=0 the segment is OFF. In my case, since each segment has four LEDs, each segment’s data must be repeated four times. I will use two separate arrays of 1 byte, digitArray[] to hold the digit of interest, 0-9 and segmentArray[]. I also use an array of 10-byte constants, segments[10], which holds the segment pattern for each digit. When a digit is chosen, for example, digitArray[0]=0, segmentArray[0] is set to equal segments[0], the bit pattern for digit “0” (0b11101110). Although I could use segmentArray[0] to determine the data dynamically at the time of outputing the data stream, I chose to create an array that holds the entire stream of data bytes. In this case, LEDArray[NUMPIXELS] will contain all the data necessary to stream in order to turn on the segments needed to display the numeral “0” (Listing 6).
For each LED in a segment, for each segment, for each digit (in this case, 1 digit), we load up LEDArray[] with the appropriate data based on the bits in segmentArray[] that will either turn an LED ON with a certain color and brightness or turn it OFF. Note that the brightness value must be 0xE0 or greater (Listing 7).
Listing 6
For my seven-segment digits, I need a few additional definitions to keep track of the number of Digits, Segments, and LEDsPerSegment so I can create the appropriate string of data to send to the LED digits.
#define Digits 1
#define Segments 7
#define LEDsPerSegment 4
#define NUMPIXELS Digits * Segments * LEDsPerSegment // Number of LEDs in strip
byte LEDArray[NUMPIXELS];
Listing 7
This routine handles filling the LEDArray[] with the fourbyte Data frames necessary for each LED. Each frame will be OFF or lit with a particular color based on the segment and the digit we are processing. See figure 6.
int i=0;
for(int D=0; D<Digits; D++)
{
for(int S=Segments; S>0; S--)
{
for(int L=LEDsPerSegment-1; L>=0; L--)
{
if(segmentArray[D] & 1<<S)
{
LEDArray[i] = 0xE0 + Brightness;
LEDArray[i+1] = BlueColor;
LEDArray[i+2] = GreenColor;
LEDArray[i+3] = RedColor;
}
else
{
LEDArray[i] = 0xE0;
LEDArray[i+1] = 0;
LEDArray[i+2] = 0;
LEDArray[i+3] = 0;
}
i=i+4;
}
}
}
Now that we have an array of the appropriate data, we can simply send it out to SPI pins. Remember—the data format is a start packet, data packets (LEDArray[NUMPIXELS]), and a stop packet. You’ll note that even though I could have used a different color for each LED, it made sense to use one color for all LEDs (segments). If you are looking for just white, you can purchase DotStar white LEDs or strips that use the same data format, except that the three LEDs in each device are white.
CONCLUSION
Even though I haven’t suggested multiple digits in this project, you’ll note that the reason for using an array of 1 instead of a single variable is that the application is set up to handle multiple digits. With four LEDs per segment and seven segments, each digit could draw a maximum of 28 x 60mA or 1.5A, with all colors ON full. This means you might need to connect each digit’s 5V and GND wires separately, to prevent too much current from traveling through the strip’s PCB traces.
I’ve always wanted a way to produce large seven-segment digits inexpensively. In the past, the PCB was always the costly issue, since you pay per square foot of PCB material. The tiny corners I had fabricated are small and thus inexpensive. At this segment size, a digit requires less than half of a 1m (60-LED) strip, at $30/strip. You can also get 100 individual DotStar RGB LEDs for the same price. Not bad.
I guess I can tick off another item on my bucket list. It seems like two items are added for each one removed. At this rate I’ll need to live a long time. Too much to do, so little time.
RESOURCES
Adafruit | www.adafruit.com
PUBLISHED IN CIRCUIT CELLAR MAGAZINE • NOVEMBER 2022 #388 – Get a PDF of the issue
Sponsor this ArticleJeff Bachiochi (pronounced BAH-key-AH-key) has been writing for Circuit Cellar since 1988. His background includes product design and manufacturing. You can reach him at: jeff.bachiochi@imaginethatnow.com or at: www.imaginethatnow.com.