10  Electronics Class 2


10.1 Digital read

  • Goal: Read button press and take an action (flash external LED).

Use the following code.

Download code
const int buttonPin = A4;        // Pin where the button is connected
const int ledPin = D9;   // Pin where the LED is connected

// Variable to store the button state
int buttonState = 0;

void setup() {
  // Initialize the LED pin as an output
  pinMode(ledPin, OUTPUT);

  // Initialize the button pin as an input
  pinMode(buttonPin, INPUT_PULLUP);  // Use the internal pull-up resistor
  
  // Start serial communication for debugging (optional)
  Serial.begin(115200);
}

void loop() {
  // Read the state of the button
  buttonState = digitalRead(buttonPin);
  
  // Check if the button is pressed
  if (buttonState == LOW) {  // Assuming the button connects to ground when pressed
    Serial.println("Button pressed! Blinking LED...");
    
    // Blink the LED rapidly for 2 seconds (20 times per second)
    unsigned long startMillis = millis();
    while (millis() - startMillis < 2000) { // Run for 2000ms (2 seconds)
      digitalWrite(ledPin, HIGH);   // Turn the LED on
      delay(50);                    // Wait for 50ms
      digitalWrite(ledPin, LOW);    // Turn the LED off
      delay(50);                    // Wait for 50ms
    }
    
    // Optional: You can add a small delay here to debounce the button
    delay(200);  // Debounce the button for 200ms
  }

  // Add a small delay to avoid excessive readings
  delay(50);
}
see diagram
see wiring scheme

10.2 Reading an Analog Pin, convert to digital with built-in ADC

We will read the signal (middle) pin of the potentiometer, and use the 12-bit built-in Analog to Digital Converter.

Download code
const int analog_pin = A4; // Pin into which we connect the input voltage
int val ;  // variable to store the value read


void setup() {
  Serial.begin(115200); //
}

void loop() {
  val = analogRead(analog_pin);  // read the input pin
  Serial.println(val);          // print value
  delay(100);
}
see diagram
see wiring scheme

Why is the maximum number 4095? What does this have to do with 12-bits?

Try it · Analog to digital

How many values can we distinguish?

Move the voltage slowly, then change the number of bits. Watch when the digital reading jumps.

V
0 V3.3 V
2 bits12 bits
Possible values
8
Largest code
7
One step (LSB)
412.500 mV
Digital reading4100₂
ADC staircaseA staircase showing how a continuous input voltage becomes a discrete digital code.
The same code covers 1.6500 V ≤ input < 2.0625 V.

2³ = 8 possible values, numbered 0–7.

Why 4095, rather than 4096?

Twelve bits give 212 = 4096 possible codes. Counting starts at zero, so the last code is 4095. In this ideal model, each code covers a voltage interval of 3.3 / 4096 ≈ 0.806 mV. More bits make the intervals smaller; they do not remove sensor noise.

This is an ideal ADC with equal voltage intervals over 0–3.3 V. Real ESP32 measurements depend on attenuation, calibration, and noise; see the ADC documentation.

10.3 Reading an Analog Pin, convert to voltage

Use the same wiring as above. The ADC returns a code from 0 to 4095, rather than a number of bits. The example below uses the approximate endpoint conversion \(V = \text{code} \times 3.3 / 4095\). For calibrated ESP32 measurements, use analogReadMilliVolts().

Download code
const int analog_pin = A4; // Pin into which we connect the input voltage
int val ;  // variable to store the value read
float voltage;

void setup() {
  Serial.begin(115200); //
}

void loop() {
  val = analogRead(analog_pin);  // read the input pin
  voltage = (3.3/4095)*val;  // convert to voltage according to ADC
  Serial.print("Voltage: ");
  Serial.print(voltage);          // print value
  Serial.println(" V");
  delay(100);
}

10.4 PWM with ESP32

Pulse-width modulation (PWM) is a method of controlling the average power delivered by an electrical signal. The average value of voltage (and current) fed to the load is controlled by switching the supply between 0 and 100% at a rate faster than it takes the load to change significantly. PWM is a technique for getting analog results with digital means. Digital control is used to create a square wave, a signal switched between on and off. This on-off pattern can simulate voltages in between the full Vcc of the board and off by changing the portion of time the signal spends on versus the time that the signal spends off. PWM is used in many applications such as controlling motor speed, heat output of heaters and variable speed fan controllers.

Try it · Pulse-width modulation

Change the pulse, keep the voltage levels

Duty cycle changes the fraction of time the signal is HIGH. Frequency changes how often the cycle repeats.

%
Always LOWAlways HIGH
100 Hz5000 Hz
Time-average voltage
1.650 V
Period
2.000 ms
HIGH time per period
1.000 ms
PWM waveformA square wave switching between 0 and 3.3 volts. A dashed line marks the time-average voltage.
Solid: pin voltageDashed: time average

Average voltage = 0.50 × 3.3 V = 1.650 V

The pin switches between 0 V and 3.3 V. The dashed line is the time average, not a steady intermediate voltage at the pin.

What happens when only frequency changes?

At a fixed duty cycle, the average voltage stays the same. The period and HIGH time become shorter as frequency rises. In a fixed time window, you see more pulses. Choose “Two periods” to inspect a single pulse more closely.

Use the following code to activate LED pin with PWM, using the potentiometer as input.

Download code
const int ledPin = D9; // the number of the LED pin
int analog_pin = A4; // Pin into which we connect the middle leg of the potentiometer
int val ;  // variable to store the value read

// setting PWM properties
const int freq = 5000;        // 5000 Hz
const int ledChannel = 0;
const int resolution = 12;    // 12-bit resolution = between 0 and 4095
 
void setup(){
  Serial.begin(115200);
    
  // configure LED PWM functionalitites
  ledcSetup(ledChannel, freq, resolution);
  
  // attach the channel to the GPIO to be controlled
  ledcAttachPin(ledPin, ledChannel);
}
 
void loop(){
    val = analogRead(analog_pin);  // read the input pin
    Serial.println(val);          // print value
    ledcWrite(ledChannel, val);
    delay(15);  // update duty cycle parameters every 15 ms
  }
see diagram
see wiring scheme