How to make a linear color transition with an RGB LED

Posted in raspberrypi, led (photo made by Sfecles Petru)

Why another post about RGB LEDs when are thousands already? I searched everywhere, but I couldn't find something about color transitions for an RGB LED.

Usually, when changing the color of an RGB LED, everything is happening in an instant (e.g., RED to BLUE). Being unpleasant for the eye may be irritating. Depending on the situations, you need a beautiful and calm change in color that is more appealing to the eye (why not, to the brain ultimately).

The pin connections.

I use a Raspberry Pi Zero and an RGB LED connected to PWM. Don't be afraid to use any other Rasp version, or to adapt to any other board type you are using (e.g., Arduino). The common node for the RGB LED I'm using it is the anode. I connected it to the 5V pin and the other pins to PWM. If the common node for your RGB LED is the cathode, attach it to GND.

In the below table are the pin associations that I made.

 RGB LED Signal Raspberry Pi Zero 1 RED GPIO 12 (PWM) 2 GREEN GPIO 13 (PWM) 3 BLUE GPIO 19 (PWM) 4 Anode 2 (5V VCC)

The code.

During a transition I have multiple intermediary colors between the current color and the end color: [currentColor, color1, color2, ... , colorN, targetColor]. I'm using the color HEX codes to represent the colors. It can be replaced very easily with RGB color model if that fits your project better.

The number of intermediary colors is given by the duration of a transition and the number of frames per second (fps). As an example, for a duration of 2s and 30 fps, it will result in a ~60 intermediary colors (steps).

Each step will increment/decrement the current color in order to reach the desired color. This is very similar to a linear function and we can say that this is a linear color transition.

The first thing is to find the distance from the current color to the new color. The distance is computed for each color from the LED (Red, Green, and Blue). The distance is used to compute the incrementing value at each step. The incrementing value is applied individually per color. This way the LED will make a linear transition to the new color.

In order to control the each LED intensity, I transform the hex color value in percent. This way, it can be easily passed to the duty cycle function.

The RGB LEDs that I have work with a maximum frequency of 100Hz.

The code works for both type of RGB LEDs with common node cathode or anode. By default is using the cathode, but you can change this by modifying the constant value COMMON_NODE to 0.

I added a list of colors that demonstrate the behavior, you can change this with the colors that you want.

``````import RPi.GPIO as GPIO
import time

# use channel numbers on the Broadcom SOC
GPIO.setmode(GPIO.BCM)

# defining the pins
GPIO_RED = 12
GPIO_GREEN = 13
GPIO_BLUE = 19

# defining the pins as output
GPIO.setup(GPIO_RED, GPIO.OUT)
GPIO.setup(GPIO_GREEN, GPIO.OUT)
GPIO.setup(GPIO_BLUE, GPIO.OUT)

# choosing a frequency for pwm
PWM_FREQUENCY = 50

# configure to use PWM
COLOR_PWM = [
GPIO.PWM(GPIO_RED, PWM_FREQUENCY),
GPIO.PWM(GPIO_GREEN, PWM_FREQUENCY),
GPIO.PWM(GPIO_BLUE, PWM_FREQUENCY)
]

# For CATHODE this must be 0
# For ANODE this must be 1
COMMON_NODE = 1

def transition(currentColor, targetColor, duration, fps):
distance = colorDistance(currentColor, targetColor)
increment = calculateIncrement(distance, fps, duration)

for i in range(0, int(fps)):
transitionStep(currentColor, targetColor, increment)
time.sleep(duration/fps)

def colorDistance(currentColor, targetColor):
distance = [0, 0, 0]

for i in range(len(currentColor)):
distance[i] = abs(currentColor[i] - targetColor[i])

return distance

def calculateIncrement(distance, fps, duration):
increment = [0, 0, 0]

for i in range(len(distance)):
inc = abs(distance[i] / fps)
increment[i] = inc
return increment

def transitionStep(currentColor, targetColor, increment):
for i in range(len(currentColor)):
if currentColor[i] > targetColor[i]:
currentColor[i] -= increment[i]
if currentColor[i] <= targetColor[i]:
increment[i] = 0
else:
currentColor[i] += increment[i]
if currentColor[i] >= targetColor[i]:
increment[i] = 0
setColor(currentColor)

def setColor(color):
for i in range(len(COLOR_PWM)):
percent = hexPercent(color[i])
if COMMON_NODE:
percent = 100 - percent
COLOR_PWM[i].ChangeDutyCycle(percent)

def hexPercent(color):
percent = (color / float(0xFF)) * 100
return percent

if __name__ == '__main__':
try:
for i in range(len(COLOR_PWM)):
COLOR_PWM[i].start(1)

duration = 2.0
fps = 90.0

i = 0
while 1:
colors = [
[0xE8, 0x0B, 0x0B], # red
[0x0b, 0x12, 0xe8], # blue
[0xe8, 0xd5, 0x0b], # yellow
[0xdd, 0x0b, 0xe8], # purple
[0x1A, 0xe8, 0x0B], # green
[0x0b, 0xd5, 0xe8]  # teal
]

currentColor = colors[i % len(colors)]

i = (i + 1) % len(colors)
nextColor = colors[i]

transition(currentColor, nextColor, duration, fps)

# close execution by pressing CTRL + C
except KeyboardInterrupt:
print("Intrerrupted by user")
pass
finally:
print("Program stopped")
for colorPwm in COLOR_PWM:
colorPwm.stop()
GPIO.cleanup()``````

Run the code using the following command on the terminal and press CTRL + C to cancel anytime.

``python rgb_transition.py``

Conclusions In the above gif, I made the led to make a transition from RED to BLUE. I covered with a square opaque glass in order to highlight the transition.

This project can be expanded to work with a matrix/band of RGB LEDs and to create other types of transition. Feel free to share in the comments what you achieved.

Always aim for the best and never give up.