Chapter 4 Analog & PWM

In previous study, we have known that one button has two states: pressed and released, and LED has light-on/off state, then how to enter a middle state? How to output an intermediate state to let LED “semi bright”? That’s what we’re going to learn.

First, let’s learn how to control the brightness of a LED.

Project 4.1 Breathing LED

Breathing light, that is, LED is turned from off to on gradually, and gradually from on to off, just like “breathing”. So, how to control the brightness of a LED? We will use PWM to achieve this target.

Component List

ESP32-WROVER x1

Chapter01_00

GPIO Extension Board x1

Chapter01_01

Breadboard x1

Chapter01_02

LED x1

Chapter01_03

Resistor 220Ω x1

Chapter01_04

Jumper M/M x2

Chapter01_05

Circuit

This circuit is the same as the one in engineering Blink.

Schematic diagram

Chapter04_02

Hardware connection

If you need any support, please feel free to contact us via: support@freenove.com

Chapter04_03

Sketch

This project is designed to make PWM output GPIO2 with pulse width increasing from 0% to 100%, and then reducing from 100% to 0% gradually.

Sketch_04.1_BreathingLight

../../../_images/Chapter04_04.png

Download the code to ESP32-WROVER, and you’ll see that LED is turned from on to off and then from off to on gradually like breathing.

../../../_images/Chapter04_05.png

The following is the program code:

 1/**********************************************************************
 2  Filename    : BreathingLight
 3  Description : Make led light fade in and out, just like breathing.
 4  Auther      : www.freenove.com
 5  Modification: 2024/06/18
 6**********************************************************************/
 7#define PIN_LED   2     //define the led pin
 8#define CHN       0     //define the pwm channel
 9#define FRQ       1000  //define the pwm frequency
10#define PWM_BIT   8     //define the pwm precision
11void setup() {
12  ledcAttachChannel(PIN_LED, FRQ, PWM_BIT, CHN);  //attach the led pin to pwm channel
13}
14
15void loop() {
16  for (int i = 0; i < 255; i++) { //make light fade in
17    ledcWrite(PIN_LED, i);
18    delay(10);
19  }
20  for (int i = 255; i > -1; i--) {  //make light fade out
21    ledcWrite(PIN_LED, i);
22    delay(10);
23  }
24}

The PWM pin output mode of ESP32 is not the same as the traditional controller. It controls each parameter of PWM by controlling the PWM channel. Any number of GPIO can be connected with the PWM channel to output PWM. In ledcAttachChannal(), you first configure a PWM channel and set the frequency and precision. Then the GPIO is associated with the PWM channel.

1ledcAttachChannel(PIN_LED, FRQ, PWM_BIT, CHN);  //attach the led pin to pwm channel

In the loop(), There are two “for” loops. The first makes the ledPin output PWM from 0% to 100% and the second makes the ledPin output PWM from 100% to 0%. This allows the LED to gradually light and extinguish.

1for (int i = 0; i < 255; i++) { //make light fade in
2  ledcWrite(PIN_LED, i);
3  delay(10);
4}
5for (int i = 255; i > -1; i--) {  //make light fade out
6  ledcWrite(PIN_LED, i);
7  delay(10);
8}

You can also adjust the rate of the state change of LED by changing the parameters of the delay() function in the “for” loop.

bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel)

Set the pin, frequency and accuracy of a PWM channel.

Parameters

pip : The pin of PWM.

freq : Frequency value of PWM.

resolution : Pwm precision control bit.

channal: channel index. Value range :0-15

void ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution);
void ledcDetach(uint8_t pin);

Bind/unbind a GPIO to a PWM channel.

void ledcWrite(uint8_t pin, uint32_t duty);

Writes the pulse width value to a PWM channel.

Project 4.2 Meteor Flowing Light

After learning about PWM, we can use it to control LED bar graph and realize a cooler flowing light.

The component list, circuit, and hardware are exactly consistent with the project Flowing Light.

Sketch

Meteor flowing light will be implemented with PWM.

Sketch_04.2_FlowingLight2

../../../_images/Chapter04_06.png

Download the code to ESP32-WROVER, and LED bar graph will gradually light up and out from left to right, then light up and out from right to left.

The following is the program code:

 1/**********************************************************************
 2  Filename    : FlowingLight2
 3  Description : More cool flowing light.
 4  Auther      : www.freenove.com
 5  Modification: 2024/06/18
 6**********************************************************************/
 7const byte ledPins[] = {15, 2, 0, 4, 5, 18, 19, 21, 22, 23};    //define led pins
 8const byte chns[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};             //define the pwm channels
 9const int dutys[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
10                     1023, 512, 256, 128, 64, 32, 16, 8, 4, 2,
11                     0, 0, 0, 0, 0, 0, 0, 0, 0, 0
12                    };      //define the pwm dutys
13int ledCounts;    
14int delayTimes = 50;  //flowing speed ,the smaller, the faster
15void setup() {
16  ledCounts = sizeof(ledPins);    //get the led counts
17  for (int i = 0; i < ledCounts; i++) {   //setup the pwm channels
18    ledcAttachChannel(ledPins[i], 1000, 10, chns[i]);
19  }
20}
21
22void loop() {
23  for (int i = 0; i < 20; i++) {        //flowing one side to other side
24    for (int j = 0; j < ledCounts; j++) {
25      ledcWrite(ledPins[j], dutys[i + j]);
26    }
27    delay(delayTimes);
28  }
29  for (int i = 0; i < 20; i++) {      //flowing one side to other side
30    for (int j = ledCounts - 1; j > -1; j--) {
31      ledcWrite(ledPins[j], dutys[i + (ledCounts - 1 - j)]);
32    }
33    delay(delayTimes);
34  }
35}

First we defined 10 GPIO, 10 PWM channels, and 30 pulse width values.

1const byte ledPins[] = {15, 2, 0, 4, 5, 18, 19, 21, 22, 23};    //define led pins
2const byte chns[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};             //define the pwm channels
3const int dutys[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
4                     1023, 512, 256, 128, 64, 32, 16, 8, 4, 2,
5                     0, 0, 0, 0, 0, 0, 0, 0, 0, 0
6                    };      //define the pwm dutys

In setup(), set the frequency of 10 PWM channels to 1000Hz, the accuracy to 10bits, and the maximum pulse width to 1023. Attach GPIO to these PWM channels.

1for (int j = 0; j < ledCounts; j++) {
2  ledcWrite(ledPins[j], dutys[i + j]);
3}

In loop(), a nested for loop is used to control the pulse width of the PWM, and LED bar graph moves one grid after each 1 is added in the first for loop, gradually changing according to the values in the array duties. As shown in the table below, the value of the second row is the value in the array duties, and the 10 green squares in each row below represent the 10 LEDs on the LED bar graph. Every 1 is added to I, the value of the LED bar graph will move to the right by one grid, and when it reaches the end, it will move from the end to the starting point, achieving the desired effect.

../../../_images/Chapter04_07.png

In the code, two nested for loops are used to achieve this effect.

 1for (int i = 0; i < 20; i++) {        //flowing one side to other side
 2  for (int j = 0; j < ledCounts; j++) {
 3    ledcWrite(ledPins[j], dutys[i + j]);
 4  }
 5  delay(delayTimes);
 6}
 7for (int i = 0; i < 20; i++) {      //flowing one side to other side
 8  for (int j = ledCounts - 1; j > -1; j--) {
 9    ledcWrite(ledPins[j], dutys[i + (ledCounts - 1 - j)]);
10  }
11  delay(delayTimes);
12}