Analog output: Here we would like to output a voltage between 0 and supply. The problem is that is not easy. Instead microprocessors use a PWM output. PWM is Pulse Width Modulation. Essentially we are going to switch the output ON and OFF very rapidly. The "Pulse Width" is the ratio between the amount of time the output is ON to OFF. Check out this video. A pseudo analog output is achieved using this method. The analogWrite(pin,value) command is used. The 2 parameters are which pin and what value? The value is usually 8 bit or 0 to 255 (0000 0000 to 1111 1111 in binary). It turns out that PWM signals are handy for doing other tasks like controlling servo motors.
We are going to look at analog output next because we already have an LED connected to our ESP32. We can see how to control the brightness of the LED.
A PWM signal switches on and off very quickly. We know that the human eye cannot see this if the rate of switching - the frequency, climbs above 25 cycles per second, 25Hz (Hertz). If we applied a PWM signal to an LED at a frequency of 1000Hz, 1kHz, we will not notice the rapid switching on and off. Now the voltage does not change. When it is in the ON part of the cycle the PWM signal will be Vss, the supply voltage for that device. The ESP32 Vss is 3.3V. When it is in the off part of the cycle the PWM signal is at 0V or ground potential. But if the duty cycle of the PWM signal is 50% then current is flowing through the LED only half of the time. That is the effective or average current will be half of fully on. The LED will be dimmer. It is using less power.
Recall that LEDs are diodes and all diodes have a forward bias threshold voltage. Below this voltage the diode will not conduct. But we can fade an LED right to 1% and still notice it glowing because the PWM signal is still applying full voltage. If we simply tried to reduce the voltage to dim the LED we would notice that at the threshold voltage, somewhere between 1.5 and 3 volts, the LED would just shut off.
Later we will see other uses for PWM outputs but for now, let's see if we can fade the LED connected to our ESP32.
The ESP32, as it turns out, has some built-in commands for LEDs.
ledcSetup(c, f, r) creates an internal virtual structure to control the LED. The 3 parameters are
c: the LED channel - an internal virtual structure for controlling LEDs that can communicate with the pinsf: the frequency of the PWM signalr: the resolution or number of bits to be used
r = 8 means we will use 8 bits and control the signal with values from 0 to 255r = 10 means we will use 10 bits and control the signal with values from 0 to 1023
ledcAttachPin(p,c) attaches the internal virtual structure to the GPIO pin used to connect to the physical LED.p: ESP32 GPIO pinc: the LED channel
ledcWrite(c,b) is used to send the appropriate PWM output to the LED.c: the LED channelb: the brightness value - if the resolution is 8 bits then this will be a value between 0 and 255.
Now all we need is a loop that counts up from 5 to 255 and then back down from 255 to 5. There are 2 ways to do this. We can use a for-next loop to count up and then another one to count down.
or
We can count using a positive increment until we get to 255 and then change to a negative increment to force the count to go backwards. Notice that in both examples we "declare" all of our parameters right at the start of the program. This way we can easily change the way the program works by changing these values.
Method 1 For-next loops
int freq = 1000; //frequency
int ledChannel = 0; //LED channel
int resolution = 8; //number of bits to use
int brightness = 5; //variable to set LED brightness
int fadeAmount = 10;//increment amount
int ledPin = 13; //GPIO pin connected to LED
void setup() {
ledcSetup(ledChannel, freq, resolution);
ledcAttachPin(ledPin, ledChannel);
}
void loop(){
for (brightness=5; brightness<=255; brightness+=10){
ledcWrite(ledChannel, brightness);
delay(30); //30ms between changes in brightness
} //loop to count up from 5 to 255
delay(500); //pause at the top and bottom
for (brightness=250; brightness>=5; brightness-=5){
ledcWrite(ledChannel, brightness);
delay(30); //30ms between changes in brightness
} //loop to count down from 250 to 5
delay(500); //pause at the top and bottom
}
Method 2 (reverse increment)
int freq = 1000; //frequency
int ledChannel = 0; //LED channel
int resolution = 8; //number of bits to use
int brightness = 5; //variable to set LED brightness
int fadeAmount = 10;//increment amount
int ledPin = 13; //GPIO pin connected to LED
void setup() {
ledcSetup(ledChannel, freq, resolution);
ledcAttachPin(ledPin, ledChannel);
}
void loop(){
ledcWrite(ledChannel, brightness);
brightness = brightness + fadeAmount;
/* If fadeAmount is positive brightness will increment
* If fadeAmount becomes greater than 255 or less than 5
* the sign before fadeAmount changes, + to - or - to +
* || means "or"
*/
if (brightness <= 5 || brightness >= 255 ) {
fadeAmount = -fadeAmount;
delay(500); //pause at the top and bottom
}
delay(30); //30ms between changes in brightness
}
SAFETY GLASSES ON!
Plug in the USB cable.
Try both programs out. You will see there is no difference in the behavior of the LED. If you take the comments out of the program on the right you will see that it uses fewer commands and therefore uses less memory. Memory is not a problem right now but if we are writing really big programs we need to pay attention to memory usage.
Unplug the USB cable.