Arduino: Driving more advanced LEDs

In previous posts, we looked at driving an LED using an Arduino and Raspberry Pi. For a quick review, an LED is short for a light emitting diode. The light emitting part is obvious. When you put a voltage across an LED it will shine a light. If you put too much voltage the LED will melt and burn out. Typically a resistor is put in series with the LED to limit the voltage. The diode part of the LED only allows a voltage to be places in one direction. Typically, the anode or positive leg of the diode is attached to the positive voltage and the cathode is attached to ground.

There are two ways to limit the intensity of the LED. You can either increase/decrease the resistor or increase/decrease the voltage. To change the resistance a potentiometer or variable resistor is needed. Most potentiometers are mechanical and not programmable. Voltages usually can’t be changed for the Arduino or Raspberry Pi. The output pins are either 3 volts or 5 volts. What you can do is change how long the voltage is on in relation to how long it is off.

As long as the cycle is less than 60 cycles per second, the human eye typically can’t see the light turn on and off. If you use a 25% duty cycle, the light will be dimmed to a quarter of the maximum brightness. A duty cycle of 50% will dim the LED to half the maximum brightness.

More complex LEDs

More complex LEDs are available to provide additional functionality. Typical LEDs allow for single colors. The traditional colors are red, green, and blue. If you need something like yellow or orange you typically have to combine two colors together. This is difficult to do with LEDs that are one color. What you want is a single LED that has all three colors built into a single lens so that you can control the different colors at the same time and have them blended in the LED. Multicolored LEDs are available and have multiple leads for the different colors.

The key difference from our previous example with one LED is that we now need to drive three output pins from the Arduino or Raspberry Pi. We also need to know how to combine the colors of red, green, and blue to get the color that we want.

If we turn on the red and green at the same time, the light should be yellow. If we want orange, we need to have a little more red than we do green.

If we apply a 25% duty cycle or intensity on the green and a 75% duty cycle or intensity on the red we will have a color more orange than yellow. If we apply a 50% duty cycle or intensity on the green and a 50% duty cycle or intensity on the red we will have more yellow than orange. It is important to calibrate the resistors according to the voltage drop for the different color LEDs.

Note that green ranges from 2 volts to 3 volts while red ranges from 1.8 volts to 2 volts. What we should do is put a much larger resistor on the red LED to get more of a voltage drop on the resistor and less on the Red LED. If we put the same size resistor, 330 Ohms for example, the green LED will shine brighter than the red. TO correct for this we need to reduce the green intensity by half or two thirds to get yellow when both are turned on.

// Define Pins

#define BLUE 3

#define GREEN 5

#define RED 6

void setup()

{

pinMode(RED, OUTPUT);

pinMode(GREEN, OUTPUT);

pinMode(BLUE, OUTPUT);

digitalWrite(RED, HIGH);

digitalWrite(GREEN, LOW);

digitalWrite(BLUE, LOW);

}

// define variables

int redValue;

int greenValue;

int blueValue;

// main loop

void loop()

{

#define delayTime 100 // intensity time factor to change brightness

// yellow is half red and half green

redValue = 255  ; // choose a value between 1 and 255 to change the color.

greenValue = 255 / 3 ; // set to 2/3 brightness to compensate for using 330 Ohm resistor on both red and green

// to get orange change the greenValue = 255 / 9;

blueValue = 0;

#define delayTime 100

// this is unnecessary as we’ve either turned on RED in SETUP

for(int i = 0; i < 1000; i += 1) // fades out red bring green full when i=255 {

analogWrite(RED, redValue);

analogWrite(GREEN, greenValue);

delay(delayTime);

}

}

If we want an orange light we simply change the greenValue to 255/9 which adds more red than green. If we want to change the brightness of the light we add an analogWrite(RED, 0) and analogWrite(GREEN, 0) after the delay(delayTime). We will also need to add a delay(delayTime) after the write of zeros to create a duty cycle. In the code example we have a delayTime of 100 resulting in a 100 microseconds that the light is on and 100 microseconds that the light is off. Adding a multiplication factor changes the mix to 2 milliseconds on and 1 millisecond off.

analogWrite(RED, redValue);

analogWrite(GREEN, greenValue);

delayMicroseconds(delayTime * 20  );

analogWrite(RED,0);

analogWrite(GREEN,0);

delayMicroseconds(delayTime * 10 );

For this example we use delayMicroseconds() rather than delay() to reduce flicker but give us a duty cycle that we can change by changing the multiplication factor. This the example above we are on 2/3 of the time and off 1/3 of the time thus reducing the brightness of the yellow or orange light.

7 segment LED

A more complex LED is the seven segment LED to display numbers and letters. The key difference between the multi-color LED and the 7 segment LED is that the 7 segment is arranged to light up bars that can combine to make a letter. The multi-color LED has three cathode leads. The 7 segment LED has seven cathode leads.

To display the number “1” segments b and c need to be turned on. To display the number “2” segments a, b, g, e, and d need to be turned on. A table can be created correlating which segments need to be turned on for what number and letter you want to display.

If we connect the Arduino directly to the 7 segment LED it requires resistors to limit the voltage across the 7 segment LED.

The code to drive the 7 segment LED is a little more complex in that it introduces something called arrays. For each number that can be represented, the table of which segment needs to be turned on can be defined in an array.

// make an array to save Sev Seg pin configuration of numbers

int num_array[10][7] = { { 1,1,1,1,1,1,0 }, // 0
{ 0,1,1,0,0,0,0 }, // 1
{ 1,1,0,1,1,0,1 }, // 2
{ 1,1,1,1,0,0,1 }, // 3
{ 0,1,1,0,0,1,1 }, // 4
{ 1,0,1,1,0,1,1 }, // 5
{ 1,0,1,1,1,1,1 }, // 6
{ 1,1,1,0,0,0,0 }, // 7
{ 1,1,1,1,1,1,1 }, // 8
{ 1,1,1,0,0,1,1 }}; // 9

//function header
void Num_Write(int);

void setup()
{
// set pin modes
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);


}

void loop()
{

//counter loop

for (int counter = 10; counter > 0; –counter)
{
delay(1000);
Num_Write(counter-1);
}
delay(3000);
}

// this functions writes values to the sev seg pins
void Num_Write(int number)
{
int pin= 2;
for (int j=0; j < 7; j++) {
digitalWrite(pin, num_array[number][j]);
pin++;
}
}

In the code we define num_array as a two dimensional array. The second part of the array defines the segments that need to be turned on or off. Note that there are seven values for the second part of the array. The first part of the array defines the number to be displayed. Note that there are ten values for the first part of the array. Arrays start indexing with a zero rather than a one. If we talk about num_array[0][0] we are talking about the first row of the array that contains all “1”‘s and one zero and specifically the first 1 in the second part of the array. The subroutine Num_Write takes an input of a number that should range from 0 to 9 (although it is not checked in the subroutine which can cause errors) and walks through the each segment value and outputs it to the output pin to drive the 7 segment LED. If the input number is a 0 then num_array[0][0] through num_array[0][6] are all “1”‘s. The num_array[0][7] value is a “0” and gets written to the output pin. Note that we start with pin 2 which correlates to the “a” segment and walks through all seven segments that correlate to pins 2-8 on the Arduino.

4 digit 7 segment LED

To make this more complex, we can attach to a 4 digit 7 segment LED by adding four additional control lines to select which of the 7 segment LEDs we are talking to.

Wiring for this example looks like

TM1637 4 digit 7 segment LED

Alternatively we can use a chip between the Arduino and the LEDs to reduce the number of output lines that are needed. The TM1637 4 digit 7 segment LED is a combination of LEDs and a controller chip that know how to “clock” in data rather than driving the LEDs with 12 lines. With this chip we only need two output lines along with power and ground. One output line, CLK, is a clock that strobes the values into the LEDs. The second line, DIO, contains commands that sequence through the segments on the LEDs and which digit is being addressed.

For an Arduino there is a header file called “TM1637Display.h” that defines how to talk to the libraries and TM1637 component.

Some examples of what can be done with a TM1637 component include a countdown timer, a clock showing the time of day, a temperature or humidity gauge, or anything that can be represented with numbers and some limited characters (“C” or “F” for temperature or a “:” in the middle to differentiate hours and minutes). We won’t go into a detailed example on how to use this chip but will save an example when we look at building a more complex system with inputs to create a real world example.

Raspberry Pi: IDE, Java, and GPIO

It is not often that I throw my hands up and stop trying to get something to work. This is one of those cases. The basic problem is that the GPIO chip changed between the Raspberry Pi 4 and Raspberry Pi 5. Given that my background was working for a large computer companies, Sun Microsystems/Oracle/IBM, and I have a Masters degree in Computer Engineering and another in Electrical Engineering, I am very unsettled that a simple chip change causes software to break between hardware releases. What should happen is that the operating system should hide these changes. A new device driver should be written to keep the user interfaces, both at the command line and libraries for various languages, the same and functional between chip changes.

Just to set this in time, it is the first of August in 2024 and I can not get Java libraries to work on the Raspberry Pi because pieces and components don’t exist to allow for a simple download. I can get both Eclipse and Microsoft Visual Studio working on a Raspberry Pi 5 but no Java libraries that work with the GPIO exist. I am struggling to get Studio to work properly on a Raspberry Pi 4 because there is not enough memory on the 2MB model that I have. The IDE loads and starts to run. When I try to install Java or C/C++ as a language extension the operating system “wedges” and stops working. The net result is that I can’t get an IDE, Java, and the GPIO libraries to work on a Raspberry Pi 4 or a Raspberry Pi 5.

The https://www.pi4j.com/ website says that the Java libraries on the site support the Raspberry Pi 5 but the libraries are dependent upon the http://wiringpi.com/ WiringPi interfaces. When you go to the WiringPi website it says that

“As of the end of 2023, you’ll not find anything here anymore. It’s gone.

Email if you want – if you know my email address. Work it out.

-Gordon

This is unfortunate and breaks the only Java GPIO library that states that it works with the Raspberry Pi 5.

In theory, you can download a fully baked operating system that contains an older version of the Pi4J library as well as the WiringPi interfaces but I am hesitant to download any code that no longer has support.

The fundamental problem is that not only has the chip changed but the numbering of the GPIO pins has changed. According to http://git.munts.com/muntsos/doc/AppNote11-link-gpiochip.pdf the basic problem is how the devices are presented from the operating system.

The Raspberry Pi 5 introduced an unfortunate breaking API change for manipulating GPIO pins. For all previous Raspberry Pi boards, you needed to use /dev/gpiochip0 to manipulate the expansion header GPIO pins. On the Raspberry Pi 5, the GPIO controllers enumerated differently, and you must now use /dev/gpiochip4. Furthermore, the
Raspberry Pi engineering staff have indicated that the enumeration order may change in the future as well. This change has broken many Raspberry Pi GPIO libraries…

Given that the way that the operating system presents the definition of the GPIO pins has changed and will probably change again, my recommendation is to use something else. If you have to use the GPIO pins, expect to change your code again in a year or two and know that at some point it will break again. Unfortunately, driving motors, LEDS, and meters are done easily using the GPIO pins. These changes not only effect the GPIO pins but the IC2 and other pins that can be used to drive input and output for the Raspberry Pi. I will continue to go through tutorials using Python, C/C++, and Java where it makes sense. Realize at some point these tutorials will break and the libraries that are used to show functionality will probably break and an alternate solution might be needed.

What we have that is working is as follows

LanguageIDEGPIO
Command LineNoPi4 – gpio, raspi-gpio
Pi5 – pinctl
PythonEclipse, Visual StudioPi5 with IDE
Pi4 with no IDE
C/C++Eclipse, Visual StudioPi5 with IDE
Pi4 with no IDE
JavaEclipse, Visual StudioPi4 with no IDE

Raspberry Pi: working with GPIO from C/C++

There are a variety of libraries available for the C and C++ compilers that allow you to talk to the GPIO on a Raspberry Pi. Unfortunately, most do not support the Raspberry Pi 5. The Raspberry Pi 5 uses a different chip than the Raspberry Pi 4 to control the GPIO pins. This means that most libraries that worked on a Pi4 do not work on the Pi5. Again, unfortunately, there are similar problems with the Pi3 and Pi2 because the chips that control the GPIO pins are different between all of the boards. Fortunately, the older boards have been around a while and most libraries support upto the Pi4.

GPIO differences

If we look at the history of the GPIO controller chip and pinouts, function and operation have changes with each board release

Type 1 – Model B (original model)

  • 26 pin header (P1).
  • Hardware revision numbers of 2 and 3.
  • User GPIO 0-1, 4, 7-11, 14-15, 17-18, 21-25.
GPIOpinpinGPIO
3V3125V
SDA0345V
SCL156Ground
47814TXD
Ground91015RXD
ce117111218ce0
211314Ground
22151623
3V3171824
MOSI101920Ground
MISO9212225
SCLK1123248CE0
Ground25267CE1

Type 2 – Model A, B (revision 2)

  • 26 pin header (P1) and an additional 8 pin header (P5).
  • Hardware revision numbers of 4, 5, 6 (B), 7, 8, 9 (A), and 13, 14, 15 (B).
  • User GPIO 2-4, 7-11, 14-15, 17-18, 22-25, 27-31.
    GPIOpinpinGPIO
    3V3125V
    SDA2345V
    SCL356Ground
    47814TXD
    Ground91015RXD
    ce117111218ce0
    271314Ground
    22151623
    3V3171824
    MOSI101920Ground
    MISO9212225
    SCLK1123248CE0
    Ground25267CE1
    GPIOpinpinGPIO
    5V123V3
    SDA283429SCL
    305631
    Ground78Ground

    Type 3 – Model A+, B+, Pi Zero, Pi Zero W, Pi2B, Pi3B, Pi4B

    • 40 pin expansion header (J8).
    • Hardware revision numbers of 16 or greater.
    • User GPIO 2-27 (0 and 1 are reserved).
    GPIOpinpinGPIO
    3V3125V
    SDA2345V
    SCL356Ground
    47814TXD
    Ground91015RXD
    ce117111218ce0
    271314Ground
    22151623
    3V3171824
    MOSI101920Ground
    MISO9212225
    SCLK1123248CE0
    Ground25267CE1
    ID_SD027281ID_SC
    52930Ground
    6313212
    133334Ground
    miso19353616ce2
    26373820mosi
    Ground394021sclk

    Type 4 – Pi5

    • 40 pin expansion header (J8).
    • Hardware revision numbers of 16 or greater.
    • User GPIO 2-27 (0 and 1 are reserved).
    GPIOpinpinGPIO
    3V3125V
    SDA2345V
    SCL356Ground
    47814TXD
    Ground91015RXD
    ce117111218ce0
    271314Ground
    22151623
    3V3171824
    MOSI101920Ground
    MISO9212225
    SCLK1123248CE0
    Ground25267CE1
    ID_SD027281ID_SC
    52930Ground
    6313212
    133334Ground
    miso19353616ce2
    26373820mosi
    Ground394021sclk

    Even though the pinouts remain the same between the Raspberry Pi 4 and Raspberry Pi 5, the functionality changes between the two and how you program them internally is different. If you are using the command line on a Raspberry Pi 4 the raspi-gpio is the command that has traditionally been used. The operating systems takes care of talking to the controller chip and everything works as expected. When the Raspberry Pi 5 came out, the raspi-gpio command was no longer supported.

    $ raspi-gpio help

    raspi-gpio is not supported on Pi 5 – use pinctrl

    The pinctrl command replaces raspi-gpio and uses a different kernel call and process to talk to the GPIO. Fortunately pinctrl accepts the same arguments as raspi-gpio so little if any changes will be needed for command line programs.

    Pigpio

    The pigpio library, https://abyz.me.uk/rpi/pigpio/index.html , is a library that supports Python, C/C++, and Java with some extensions. Unfortunately, this library only works with the older versions and has not been ported to the Raspberry Pi 5. The authors of the library have stated that the port is not insignificant and will require some work to get things working as with previous versions.

    Libgpiod

    An alternate is to have a common user call given that the command lines have changed from hardware version to hardware version. The libgpiod, https://github.com/brgl/libgpiod , is designed to be a common user interface that hides the hardware dependencies between hardware and operating system flavors. On a grander scale, this would also work on an Arduino using the same or similar code that runs on the Raspberry Pi.

    RPi.GPIO

    The RPi.GPIO library, https://pypi.org/project/RPi.GPIO/ , is a Python only library and does not work with C/C++ or Java.

    WiringPi

    The WiringPi, https://github.com/WiringPi/WiringPi/ , library appears to be one of the first GPIO libraries that support the Raspberry Pi. To install WiringPi, use git to download the source and build to build the library

    $ sudo git clone https://github.com/WiringPi/WiringPi.git
    $ cd WiringPi
    $ sudo ./build

    If we look in the examples folder there is a blink.c file that writes to an LED on GPIO pin 17.

    // LED Pin – wiringPi pin 0 is BCM_GPIO 17.

    #include <stdio.h>

    #include <wiringPi.h>

    #define LED 0

    int main (void) {
    printf (“Raspberry Pi blink\n”) ;

    wiringPiSetup () ;
    pinMode (LED, OUTPUT) ;

    for (;;) {
    digitalWrite (LED, HIGH) ; // On
    delay (500) ; // mS

    digitalWrite (LED, LOW) ; // Off
    delay (500) ;

    }
    return 0 ;
    }

    The Makefile has one significant change with the compile in that it pulls in the libwiringPi library that gets installed in /usr/local/lib

    LDLIBS = -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt

    $ gcc -o blink blink.c -L/usr/local/lib -lwiringPi

    The -L/usr/local/lib command tells the compiler to look in the /usr/local/lib folder the the libraries. The -lwiringPi tells the compiler to look for the libwiringPi.so or libwiringPi.a library. These libraries contain the routines wiringPiSetup(), pinMode(), and digitalWrite(). The wiringPiSetup() routine initializes the GPIO chip and linkages that are needed for the connection. The pinMode(<pinNumber>,<mode>) routine programs the pin number as an input or an output. It is important to note that the GPIO pins do not necessarily match with the library pin numbers. For example, pin 0 with the library correlates to pin 17 on the GPIO pins.

    If you type gpio readall it will show the pin mappings

    If you look at the BCM column, that corresponds to the GPIO label. Looking at the right of the table, physical pin 12 corresponds to the name GPIO.1, GPIO pin 18, or wPi library number 1. All of this is confusing but works. My suggestion would be to define your own labels. For example, I would define gpio_pin_17 as 0 and gpio_pin_18 as 1 rather than using the wPi library names GPIO.0 and GPIO.1.

    In summary, we do have one solution that allows us to use C/C++ to read and write GPIO pins. The code is not optimal but it does function. Mapping of the GPIO pin labels to the wPi library labels is a little more complex than needed. If you have code previously developed on the Raspberry Pi 4 or older versions the same code might or might not work on a Raspberry Pi 5 based on what library was selected and if the library is available on the new board. In time, the other libraries should be ported to the Raspberry Pi 5 and less complex solutions will be available.

    Raspberry Pi: Python LED Blink

    In this post we will look at what it takes to blink an LED on the Raspberry Pi using the GPIO pins and Visual Studio. We will use Visual Studio to show the process of software development. We will discuss the electrical components needed to attach an LED and resistor to the Raspberry Pi. We will also dive into the code required to turn the LED on and off.

    Start connecting to your Raspberry Pi using a keyboard/mouse/computer screen or through VNC from your desktop computer. From the top left of the screen select the Visual Studio IDE.

    Visual Studio restarts where we left off when we last worked in the environment.

    From here we click on File -> New File

    Note that we now have an option to start a Python file since we have developed at least one Python file in the IDE.

    We are now ready to start development. Note that the file is Untitled. We can save it as a file with the “.py” extension of just start typing code.

    If we start typing the IDE will suggest what we might want to finish. In this instance we want to include the gpiozero module so that we can talk to the GPIO pins from our program.

    You do need to know a little about the module and interfaces/routines that are available in the module. In this example, we are pulling in the gpiozero module and talking to an LED method.

    The import command says pull in the LED method from the gpiozero module and make it available to use. We want to pull in the sleep command as well so that we can loop and delay between turning on and off the LED. When we call the LED() method the IDE tells us what parameters are needed to call the method.

    In this instance we are using pin 17 as an output to drive the LED. We need to assign the LED to a variable. We select “led” as the variable and call then turn on (with the led.on() call) or off (with the led.off() call). The sleep function allows us to sleep for a number of seconds. The while True statement says anything that is indented below that command will be executed in a loop.

    Now that we have the code ready to run, we can save the file with File -> Save File

    We call the file gpio_led.py and can now debug the code as desired using the IDE. Before we can debug we need to attach the external LED and resistor to pin 17 and ground.

    We connect a wire from GPIO pin 17 to the long side of the LED. The short side of the LED is attached to a resistor. The other side of the resistor is attached to ground on the Raspberry Pi.

    When selecting a resistor it is important to note that different color LEDs need different size resistors. Given that the resistor and LED are in series it creates a voltage division between the two elements. The GPIO pin will output 5 volts across both elements.

    Putting a small resistor in series with the LED will cause the LED to be brighter. A larger resistor will take more voltage from the LED can cause it to not be as bright.

    You need to make sure that the resistor is not too small because it will burn out the LED. If the resistor is too large the LED will never turn on. To calculate the resistor size, we need to know the voltage drop across the LED (which we can get from the chart above) and subtract that number from 5 volts.

    In this example we have a 1.8 volt drop from the LED and a 3.2 volt drop needed across the resistor. If we assume 20 milliAmps of current we can either calculate the resistor needed

    or look up in a table what minimum resistor value is needed.

    In this example we would use a 150 Ohm resistor to get a 2 volt drop across the LED. Note that this would work for most LEDs but not the blue or white LEDs since it would need a 2 volt Vf drop. These LEDs would need a smaller resistor since there would be less voltage across the resistor given that the blue or white LEDs create a 3 volt drop rather than a 2 volt drop. Calculating the resistor size is a bit complex but starting with an LED in the 200 to 330 Ohm range and going up to 1K to 2K are good starting points to keep the LED from burning out. If you use too small of a resistor you risk driving too much voltage across the LED and melting it.

    It is also important to note that the LED has two leads, one long and on short.

    The long lead connects to the GPIO pin in our example and the short lead connects to the resistor. The other side of the resistor connects to ground. The gpiozero module assumes that you are driving the GPIO pin high to turn on the LED. This assumes that you are driving the anode (long side) and not the cathode (short side). If you put 5 volts on the cathode and ground the anode nothing will happen. The LED will stop current from flowing and will not light up. If you put voltage on the anode and ground the cathode the LED will turn on. By calling the led.on() routine, 5 volts is driven through GPIO pin 17 to go across the LED and resistor.

    Running the code from the IDE will start driving pin 17 high and low with a second delay between turning on and turning off.

    Note that there is no output in the console since we don’t print anything to the console. To stop the program click on the red outlined box at the top of the code.

    Note that the console shows that we are ready to execute more code and is no longer running our Python code. If we want to run the code with a breakpoint we can set a breakpoint by clicking to the left of the line number.

    When we try to run again we can only run with Debugger since we have a breakpoint. When we run the code stops at the breakpoint.

    At this point we should have the LED shining brightly. Clicking on the Step Over button will execute the led.off() method.

    This should turn off the LED and get ready to sleep again. Clicking on the Continue button will run the program until we hit the breakpoint again.

    Once we understand how the program works to this point we might want to clear the breakpoint. To do this, right click on the breakpoint and either clear, disable, or edit the breakpoint.

    If we had variables defined in the code we could look at the values stored in the code as it runs. Setting a breakpoint at a critical point can help with unknown values or logic associated with code. This is one of the critical improvements that an IDE brings over just developing at the command line. Unfortunately, the IDE does not help with timing issues in code because the IDE introduces a delay into the code as it steps through everything. The code runs much faster without a debugger. The IDE collects data as it debugs so that you can look at variables and state inside your code.

    In summary, we can use Visual Studio to create, develop, and debug code on the Raspberry Pi. Using Python we can pull in the gpiozero module and control things like an LED attached to the GPIO pins. We can use the time modules to create delays so that we can see the lights blink on and off. We can change the delay with this code and in future posts we will look at changing the brightness of the LED using different parts of the gpiozero code.