Mixing with PWM
Each colour pin can do PWM, so each colour can be anywhere from off to full. Three duty cycles make one mixed colour: the diffuser blends the three chips and your eye adds the light. What it adds up to is not what the numbers suggest.
Three duty cycles
Each slider is one analogWrite value. A pin is still only ever fully on or
fully off. The value sets what fraction of each period it spends on, and the
LED flashes faster than an eye can follow, so you see the average. Brightness
is a duty cycle covers
that for one LED, and nothing about it changes with three.
What is new is the mix. The three chips sit a millimetre or two apart under a white diffuser, which spreads each one's light across the whole face of the package. Look at the LED from a little way off and the eye cannot separate them, so it sees one colour: the sum of the three lights.
What the sketch does
setColour(r, g, b) sends three values at once. The loop steps through each
pair of colours, one mostly-red mix, and then all three at full, printing the
numbers as it goes.
Red and green make a yellow to orange; green and blue a cyan; blue and red a purple to pink. Exactly which shade depends on the balance of light, and that is the point of the figure's percentages: they are each colour's share of the light, not its share of the numbers.
The numbers are not the light
Slide all three to 255. Each pin is on all the time, and each colour's share of the light is set only by how much light that LED gives at its current. From 5 V, according to the arithmetic in this book, green gives about half the light, red about two fifths and blue about an eighth.
So analogWrite sets a fraction of each LED's own maximum, and the three
maximums are not the same. That is why the last step of the sketch, the one
everybody expects to be white, is not.
The code
setColour sends three analogWrite values at once, 0 to 255 each. The loop steps through the three pairs and then all three. Change the three pin numbers to your PWM pins.
/*
RGB LED - mixing with PWM TK02 / /p/tk02
Wiring. Count from the square pad on the TinkerBlock board, LED side
up, header at the bottom:
GND -> GND
NC -> nothing (both NC pins are unconnected on the board)
NC -> nothing
RED -> a PWM pin: D9 on an Uno, GPIO 25 on an ESP32,
GPIO 4 on an ESP32-S3, GP13 on a Raspberry Pi Pico
GREEN -> D10, GPIO 26, GPIO 5, GP14
BLUE -> D11, GPIO 27, GPIO 6, GP15
Arduino IDE
Tools > Board your board, e.g. ESP32S3 Dev Module
Tools > Port the one that appears when you plug in
Tools > USB CDC On Boot Enabled (ESP32-S3 only)
No library needed. ESP32 boards need board package 2.0 or later.
*/
// Uno: 9, 10, 11. ESP32: 25, 26, 27. ESP32-S3: 4, 5, 6. Pico: 13, 14, 15.
const int RED_PIN = 4;
const int GREEN_PIN = 5;
const int BLUE_PIN = 6;
// 0 is off, 255 is full. Common cathode: no "255 -" anywhere.
void setColour(int r, int g, int b) {
analogWrite(RED_PIN, r);
analogWrite(GREEN_PIN, g);
analogWrite(BLUE_PIN, b);
}
void show(int r, int g, int b) {
setColour(r, g, b);
Serial.print(r);
Serial.print(", ");
Serial.print(g);
Serial.print(", ");
Serial.println(b);
delay(1500);
}
void setup() {
Serial.begin(115200);
pinMode(RED_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
pinMode(BLUE_PIN, OUTPUT);
}
void loop() {
show(255, 255, 0); // red and green
show(0, 255, 255); // green and blue
show(255, 0, 255); // blue and red
show(255, 60, 0); // mostly red, a little green
show(255, 255, 255); // all three full: look closely
}Watch the last step. 255, 255, 255 is every channel fully on, and it is not white. The next article works out what it is instead, and what to send for white.
The same mixes in MicroPython. machine.PWM runs each pin at 1 kHz, and duty_u16 takes 0 to 65535, so set_colour multiplies each 0 to 255 value by 257.
"""
RGB LED - mixing with PWM, MicroPython TK02 / /p/tk02
Wiring. Count from the square pad on the TinkerBlock board, LED side
up, header at the bottom:
GND -> GND
NC -> nothing (both NC pins are unconnected on the board)
NC -> nothing
RED -> GPIO 25 on an ESP32, GPIO 4 on an ESP32-S3,
GP13 on a Raspberry Pi Pico
GREEN -> GPIO 26, GPIO 5, GP14
BLUE -> GPIO 27, GPIO 6, GP15
Thonny
Run > Configure interpreter MicroPython (ESP32) or
MicroPython (Raspberry Pi Pico)
Save it to the board as main.py to run it on every power-up.
Nothing to install: machine and time are built in.
"""
from machine import Pin, PWM
import time
# ESP32: 25, 26, 27. ESP32-S3: 4, 5, 6. Pico: 13, 14, 15.
red = PWM(Pin(4), freq=1000)
green = PWM(Pin(5), freq=1000)
blue = PWM(Pin(6), freq=1000)
def set_colour(r, g, b):
# 0 is off, 255 is full. Common cathode: no "255 -" anywhere.
red.duty_u16(r * 257)
green.duty_u16(g * 257)
blue.duty_u16(b * 257)
def show(r, g, b):
set_colour(r, g, b)
print(r, g, b)
time.sleep_ms(1500)
while True:
show(255, 255, 0) # red and green
show(0, 255, 255) # green and blue
show(255, 0, 255) # blue and red
show(255, 60, 0) # mostly red, a little green
show(255, 255, 255) # all three full: look closelyOn a Pico, GP14 and GP15 share one PWM slice and so share a frequency. The sketch sets all three to 1 kHz, so it never notices. Stop it with Ctrl-C in Thonny's shell.
When it does not work
Its pin is not doing PWM. On an Uno only pins 3, 5, 6, 9, 10 and 11 can, and a library can take a pin's timer away: the Servo library stops PWM on 9 and 10, and tone() interferes with 3 and 11. On the ESP32 boards and the Pico every output pin can do PWM.
Update the ESP32 board package. analogWrite arrived in the ESP32 core's 2.0 releases and is standard in 3.x. On an old core, use the ledc functions instead; the ESP32 PWM article linked below shows both.
That is the PWM itself. A phone camera catches the off parts of each cycle, and with three channels switching it can show bands of different colours. It is not visible in the room and not a fault.
Equal numbers are not equal light: green gives more light per step than red, and the eye weighs them differently again. Turn one channel down until it looks right to you. The next article works out why the numbers and the colour disagree.
What 255, 255, 255 really is, and what to send instead.
Why equal numbers are not white →Edit this page — content/books/rgb-led/mixing-with-pwm.mdx
Questions about this product
See what other owners have asked, and read their solutions.
RGB LED
Loading discussions…
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.