EMB9A: Use of RGB LEDs

This scenario presents how to handle the brightness control of the tri-coloured LEDs. One is observable via camera, as presented in the figure (component 9A), while another is hidden inside the black enclosure and lights a colour sensor (component 9B). Both LEDs are electrically bound and cannot be controlled independently. Those LEDs have 3 colour channels, controlled independently: R (Red), G (Green) and B (Blue). Mixing of those colours creates other ones, such as pink and violet. Each R G B channel can be controlled with a separate GPIO to switch it on or off or control brightness using a PWM signal, as presented in this tutorial.


A good understanding of the PWM signal and duty cycle is necessary. We also use built-in timers to control the PWM hardware channels of the ESP32 chip. In this case, we do not use an external library; instead, we use built-in tools in the Arduino framework for ESP32 so that no additional libraries will be included in the project.

Suggested Readings and Knowledge Resources

Hands-on Lab Scenario

Task to be implemented

Implement a program that will light LEDs consecutively with R, G, and B. Use 50% of the maximum brightness. Use a PWM signal to control each GPIO for R, G and B, each colour separately to let you easily observe it.


Assuming you will use 8-bit PWM resolution, the minimum value is 0, and the max (full brightness) is 255. Note that full brightness may be too bright for the observation camera, so consider using a range between 0 and 60 (eventually up to 100).


To use PWM in ESP32, it is best to use built-in ledc* functions LEDC documentation on the ESP32 manufacturer's page. The leds use timers and have channels attached to the timer. We will use 1 channel per colour (R, G and B, so 3 in total). A PWM frequency is controlled with the timer to be shared for all 3 R, G and B channels. Channels control the PWM duty cycle.

Step 1

Define some parameters, including channel numbers, PWM resolution (here 8-bit) and PWM frequency (5000Hz):

#define RGBLED_B_PIN 26
#define RGBLED_G_PIN 21
#define RGBLED_R_PIN 33
#define PWM1_Ch    5
#define PWM2_Ch    6
#define PWM3_Ch    7
#define PWM_Res   8
#define PWM_Freq  5000

GPIO pins controlling LEDS are 33 (Red), 21 (Green) and 26 (Blue), respectively.

Step 2

Initialise 3 channels for PWM and make them dark:

    ledcSetup(PWM1_Ch, PWM_Freq, PWM_Res);
    ledcSetup(PWM2_Ch, PWM_Freq, PWM_Res);
    ledcSetup(PWM3_Ch, PWM_Freq, PWM_Res);
    ledcAttachPin(RGBLED_R_PIN, PWM1_Ch);
    ledcAttachPin(RGBLED_G_PIN, PWM2_Ch);
    ledcAttachPin(RGBLED_B_PIN, PWM3_Ch);

To control the LED (via PWM), use ledcWrite(PWM_Channel, duty_cycle_value);.

Step 3

Write a loop for each colour (R, G, then B) to light the colour from dark to max value (60 or 100, give it a test).

Full duty cycle (255) will be too bright for the remote access video camera to handle it. Use some reasonable range such as 0..60 or 0..100 is strongly advised.

Mind to compose code to increase and decrease each colour. A hint is below (PWM Channel 3 so that controls Blue):

  // Increase brightness
  for (int dutyCycle = 0; dutyCycle <= 100; dutyCycle++) {
    // Gradually increase duty cycle for Red LED
    ledcWrite(PWM3_Ch, dutyCycle);
    delay(20); // Delay for smooth transition
  // Decrease brightness
  for (int dutyCycle = 100; dutyCycle >= 0; dutyCycle--) {
    // Gradually decrease duty cycle for Red LED
    ledcWrite(PWM3_Ch, dutyCycle);
    delay(20); // Delay for smooth transition
There is a number of handy functions in the ledc library, including (among others):
  • analogWrite(pin,value); to keep compatibility with genuine Arduino,
  • ledcFade(pin, start_dutycycle, end_dutycycle, fade_time_ms); that you can use instead of the loop above,
  • ledcDetach(pin); to detach a pin from the channel (and thus release the PWM channel),
  • ledcWriteNote(pin, note, octave); where note is one of the values: NOTE_C, NOTE_Cs, … (and so on, music notes, up to NOTE_B) - it is useful when PWM controls a speaker, to generate a perfect musical note, but we do not use speakers here, sorry.

Result validation

You should be able to observe the pulsing colours of the RGB LED, increasing and decreasing brightness linearly.


What if I bind a PWM channel to more than one GPIO?: As you control the duty cycle writing to the channel rather than the GPIO, you will control those GPIOs simultaneously (in parallel) with the same signal. That can be handy to control parallelly separate devices that should behave the same way, i.e. servos.
What is the maximum number of channels?: the MCU we use here is ESP32-S3, so it has 8 PWM channels. You can use timers and software implementation of the PWM signal if you need more, but that is a bit tricky and may not be as precise as hardware PWM implementation.
What is the maximum bit resolution for PWM?: it is between 1 and 14 bits in this particular MCU.
What PWM frequency should I use?: there is no straightforward answer to this question: assuming you observe LED remotely with a camera, even 50Hz would be enough. But it would give a severe flickering experience to the live user, on the other hand. In the example above, we propose 5kHz, which this MCU can easily handle.

Project information

This Intellectual Output was implemented under the Erasmus+ KA2.
Project IOT-OPEN.EU Reloaded – Education-based strengthening of the European universities, companies and labour force in the global IoT market.
Project number: 2022-1-PL01-KA220-HED-000085090.

Erasmus+ Disclaimer
This project has been funded with support from the European Commission.
This publication reflects the views of only the author, and the Commission cannot be held responsible for any use that may be made of the information contained therein.

Copyright Notice
This content was created by the IOT-OPEN.EU Reloaded consortium, 2022,2024.
The content is Copyrighted and distributed under CC BY-NC Creative Commons Licence, free for Non-Commercial use.

en/iot-open/practical/hardware/sut/esp32/emb9a_1.txt · Last modified: 2024/03/22 10:00 by pczekalski
CC Attribution-Share Alike 4.0 International
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0