In this tutorial, we will see how we can use ADCs in MicroPython to perform an analog input with which to read sensors and other devices.
Unlike digital inputs, which can only detect two states (HIGH or LOW) analog inputs allow us to measure continuous values, within a range of voltages and with a specified resolution.
The measurement is performed using an ADC (Analog-to-Digital Converter), which transforms the analog voltage into a digital value that can be processed by the microcontroller.
We can use analog inputs to read sensors that operate through analog signals, such as potentiometers, temperature sensors, light sensors, pressure sensors (or any other quantity that varies gradually).
If you want to learn more, check out,
Configuration of Analog Inputs
In MicroPython, analog inputs are configured using the ADC
class from the machine
module (which by now we know well).
# Configure the analog pin (GPIO 34 on ESP32)
pot = ADC(Pin(34))
Once the ADC instance is defined, we can use some of its methods. Some of the common ones are,
Command | Description |
---|---|
adc.read() | Reads the analog value on a scale of 0 to 4095 (ESP32) or 0 to 1023 (ESP8266) |
adc.read_u16() | Reads the analog value with 16-bit resolution (0 to 65535) |
adc.atten(attenuation) | Sets the attenuation to adjust the input voltage range |
adc.width(width) | Sets the reading resolution (only on ESP32) |
We can use the atten(attenuation)
function to adapt the analog input range. On the ESP32, the available values are:
ADC.ATTN_0DB
: 0 to 1.1V.ADC.ATTN_2_5DB
: 0 to 1.5V.ADC.ATTN_6DB
: 0 to 2.0V.ADC.ATTN_11DB
: 0 to 3.3V.
MicroPython allows you to set the ADC resolution using the width()
method. On the ESP32, the available options are:
WIDTH_9BIT
: Range of 0 to 511.WIDTH_10BIT
: Range of 0 to 1023.WIDTH_11BIT
: Range of 0 to 2047.WIDTH_12BIT
: Range of 0 to 4095.
Not all functions and values will be available on all board models. Check the information related to your specific board.
Practical Example
Let’s see it in a simple example. The following code shows how we could read an analog voltage value we have on a GPIO36 pin (corresponds to an ADC1 on the ESP32).
from machine import ADC, Pin
# ADC pin configuration
adc = ADC(Pin(36)) # GPIO36 or VP
adc.width(ADC.WIDTH_12BIT) # Set resolution to 12 bits (default value)
adc.atten(ADC.ATTN_11DB) # Set input range to 0-3.3V
# Read the analog value
value = adc.read() # Read a value between 0 and 4095
print(f"Analog value read: {value}")
Analog signals often have noise. Use hardware filters (like capacitors) or software (averaging readings) to reduce noise. We will see this in the sensor reading tutorial.
Conversion of Analog Values to Physical Magnitudes
In general, the digital value obtained from the ADC is not useful by itself. We don’t care about its value, in ADC units 😜.
What we need is to convert it into a physical magnitude (such as voltage, temperature, light, etc). To do this, a conversion formula needs to be applied.
For example, let’s say we want to convert the digital value obtained from the ADC into a real voltage. The conversion formula is as follows:
In a 12-bit ADC with a reference voltage of 3.3V, the formula would be:
In code, this would look like this,
from machine import ADC, Pin
from time import sleep
# Configure the analog pin (GPIO 34 on ESP32)
pot = ADC(Pin(34))
# Configure the reading range (0-4095 for a 12-bit ADC)
pot.atten(ADC.ATTN_11DB) # Full range of 0 to 3.3V
while True:
value = pot.read() # Read the analog value
voltage = (value * 3.3) / 4095 # Convert to voltage
print(f"Voltage: {voltage:.2f} V")
sleep(0.5) # Wait 0.5 seconds
Which would give this output (for example), which is what we need
Voltage: 1.65 V
Voltage: 0.82 V
Voltage: 2.47 V
...
To convert it to another magnitude, such as temperature or light, you will need the characteristic equation of the sensor, which converts voltage values into the measured magnitude.
Practical Examples
Mapping Values
In some cases, it is useful to map the input range (0-4095) to a more meaningful range (for example, 0-100%):
def map(value, in_min, in_max, out_min, out_max):
return (value - in_min) * (out_max - out_min) // (in_max - in_min) + out_min
# Map analog value to percentage
percentage = map(value, 0, 4095, 0, 100)
print(f"Percentage: {percentage}%")
Reading a Potentiometer
A potentiometer is an electronic component that varies its resistance depending on the position of a slider. By connecting it to an analog input, we can measure its position and convert it into a digital value.
- Connect one end of the potentiometer to 3.3V (or 5V, depending on the microcontroller).
- Connect the other end to GND.
- Connect the slider (middle pin) to an analog pin, such as GPIO 34 on the ESP32.
from machine import ADC, Pin
from time import sleep
# Configure the analog pin (GPIO 34 on ESP32)
pot = ADC(Pin(34))
# Configure the reading range (0-4095 for a 12-bit ADC)
pot.atten(ADC.ATTN_11DB) # Full range of 0 to 3.3V
while True:
value = pot.read() # Read the analog value
print(f"Potentiometer value: {value}")
sleep(0.5) # Wait 0.5 seconds
In this example, the value read will vary according to the position of the potentiometer. If you turn the potentiometer, you will see that the value changes between 0 and 4095.