ESP32 ADC with Arduino IDE - Measuring voltage example (original) (raw)

In this tutorial, we will learn how how to use ADC (analog to digital converter) module of ESP32 development board with Arduino IDE. Furthermore, We will learn to measure analog voltage with ESP32 ADC channels.

ESP32 ADC Introduction

ESP32 board has two 12 bit analog to digital converters. The type of ADCs used in this development board is SAR-based which is also known as a successive approximation ADC. To know more about types of ADC’s, check this analog to digital converter tutorial.

Both these ADCs support up to 18 analog channels which means we can connect eighteen analog sensors at a time with this board. But ADC2 is shared between other resources of this development board. So we have two ADCs in ESP32 ADC1 and ADC2. Pin mapping of ADC channels for both ADCs is shown below.

ESP32 ADC1 Pin Mapping

Following is a pin mapping of with ESP32 devkit DOIT board. Although ESP32 chip ADC1 has eight channels, the Devkit DOIT board supports only 6 channels. But still, we have enough analog channels for analog sensor interfacing.

ESP32 ADC2 Pin Mapping

Pin mapping for ADC2 channel is given below. Devkit DOIT board ADC2 supports 10 analog channels and all these analog channels are available on Devkit do it board.

Although we have 10 analog channels available in ADC2, all these channels are shared among other resources. For example, ADC2 is shared with WiFi drivers, therefore you can only use it after WiFi drivers are not started. You have to make your program smart enough to switch between two resources. The easy way is to turn off the WiFi driver when you want to use ADC2 and read the analog value and after that turn on the WiFi driver when you want to update value to the server etc. I will explain more about it in later parts of this series of tutorials. The figure below shows the pins of analog channels on Devkit DOIT:

ESP32 ADC channels pinout

How to use Analog to digital converter channels of ESP32

Now, let’s see how to write code or program for reading ADC values of with any of these 15 channels available on this board. After that, we will see an example, where we connect a variable resistor with the analog channel and measure voltage and display it on the serial monitor of Arduino IDE. ESP32 analog channels are of 12 bit which means the minimum step of each voltage level is between 0 and 4095. Analog channel produces a digital value between 0 and 4095 according to the voltage at the input of the analog channel. For example

You may like to check our previous project on high voltage measurement with other microcontrollers. Although a different microcontroller is used in these projects, you can apply the same concepts to ESP32 ADC for measurement of current, voltage, power factor and ac power.

One main disadvantage of ESP32 ADC is that it has a non linear behavior. Graph below shows its non-linear curve. you can find more information about it on this link.

ESP32 adc curve non linear

Program for ESP32 Analog to digital converter

So we are using Arduino IDE in these tutorials. Arduino IDE provides a built-in function to read analog values that are analogRead function.

ESP32 ADC Useful Arduino Functions

These functions are used to set various parameters of ESP32 ADC pins such as resolution, attenuation, setting reference voltage, etc.

void analogReadResolution(uint8_t bits)

This function sets the resolution of analogRead return values. Default is 12 bits (range from 0 to 4096). If between 9 and 12, it will equal the set hardware resolution, else the value will be shifted. The range is between 1 – 16.

void analogSetWidth(uint8_t bits)

The analogSetWidth function sets the sample bits and analog read resolution. By default, it is 12bit (0 – 4095) and the range is between 9 – 12.

void analogSetClockDiv(uint8_t clockDiv)

This method selects the divider for the ADC clock. By default, it is 1 and the range is between 1 – 255.

void analogSetAttenuation(adc_attenuation_t attenuation)

The analogSetAttenuation() function sets the attenuation for all ADC channels. By default, it is 11db. Also, the attenuation can be one of these values:

typedef enum {
    ADC_0db,
    ADC_2_5db,
    ADC_6db,
    ADC_11db
} adc_attenuation_t;
Attenuation Measurable input voltage range
ADC_ATTEN_DB_0 100 mV ~ 950 mV
ADC_ATTEN_DB_2_5 100 mV ~ 1250 mV
ADC_ATTEN_DB_6 150 mV ~ 1750 mV
ADC_ATTEN_DB_11 150 mV ~ 2450 mV

int hallRead()

The hallRead() function is used to get value for the HALL sensor (without LNA). Hall Sensor output is connected to pins 36(SVP) and 39(SVN)

void analogSetVRefPin(uint8_t pin)

The analogSetVRefPin() function sets the pin to use for ADC calibration or to set a reference voltage for ADC if the esp is not already calibrated (25, 26, or 27). You can use reference voltage on 25, 26, and 27 pins.

If you are just getting started with ESP32 programming, check these earlier tutorials:

Now make this circuit diagram on your bread board and after that, we will write a code to measure voltage using a variable resistor.

measure analog voltage ESP32

In the above circuit diagram, a variable resistor is used. one terminal of the variable resistor is connected with the ground, and another terminal with 3.3 volts. The Center terminal of the variable resistor is connected with GPIO15 which is ADC2 channel three or ADC2_CH3.

Code for analog voltage measurement using ESP32 ADC

Code for analog voltage measurement is shown below. All the functions used in this code have already explained in the previous tutorial and in this tutorial except serial.begin() and serial.println() functions.

const int Analog_channel_pin= 15;
int ADC_VALUE = 0;
int voltage_value = 0; 
void setup() 
{
Serial.begin(115200);
}
void loop() 
{
ADC_VALUE = analogRead(Analog_channel_pin);
Serial.print("ADC VALUE = ");
Serial.println(ADC_VALUE);
delay(1000);
voltage_value = (ADC_VALUE * 3.3 ) / (4095);
Serial.print("Voltage = ");
Serial.print(voltage_value);
Serial.print("volts");
delay(1000);
}

Now I will explain the working of code line by line. In these lines, first we give a name to ADC2_CH3 as Analog_channel_pin and after that, we initialized two integer variables to zero. One variable is used to store digital value and other variable is used to store voltage.

const int Analog_channel_pin= 15;
int ADC_VALUE = 0;
int voltage_value = 0;

In the setup() function, we have serial.begin function is used to initialize serial communication of esp32 and serial communication rate is defined by baud rate. So we have initialized the baud rate of 115200. Serial communication will be used to send data from ESP32 board to serial monitor of Arduino IDE.

Serial.begin(115200);

Now the main function of this code is a loop part where we are taking an analog input and displaying it on serial monitor of Arduino IDE with the help of these lines. serial.print function is used to transmit data without a new line at the end and serial.println() function is also used to transmit data but it also includes new line character at the end of the string. After that delay function is used to add a delay of one second. It is not necessary to add delay but we did it check to receive value after every one second.

ADC_VALUE = analogRead(Analog_channel_pin);
Serial.print("ADC VALUE = ");
Serial.println(ADC_VALUE);
delay(1000);

Now in the last part, the first line will convert the digital value of ADC_VALUE into voltage. ADC_VALUE is multiplied with a resolution of ESP32 ADC which is 3.3 / 4095. Resolution is also know as a minimum step of adc for each corresponding value of voltage. For example, if ADC_VALUE counts 10, its mean it has taken 10 steps or value at the input pin is 10 times the resolution of adc. So to convert ADC_VALUE back into voltage we simply multiply it with resolution as we done in the first line. After that Serial.print function sends a string of “voltage = ” and then voltage value and after that unit of voltage is transmitted to the serial monitor.

voltage_value = (ADC_VALUE * 3.3 ) / (4095);
Serial.print("Voltage = ");
Serial.print(voltage_value);
Serial.print("volts");
delay(1000);

So when you run this code on ESP32 board, you will get the value of adc and voltage value after every one second on serial monitor of Arduino IDE.

ADC ESP32 values on serial monitor

Display ADC and Voltage on OLED with ESP32

In the previous section, we have seen how to convert an analog signal into a digital value using ESP32 in Arduino IDE. Similarly, we can also convert the measured digital value back into voltage by multiplying digital value with the resolution of ADC which is 3.3/4095 for 12-bit resolution of ESP32. Now let’s see an example to measure analog voltage signal with ESP32.

In this section, we will see how to display analog voltage values on a 0.96 SSD1306 OLED display using ESP32 and Arduino IDE.

You can read this in-depth guide on OLED interfacing with ESP32:

OLED Display Interfacing with ESP32 – Display Text, Draw shapes and Images

Installing OLED Libraries in Arduino IDE

To use the OLED display in our project, we have to install the Adafruit SSD1306 library and Adafruit GFX library in Arduino IDE. Follow the steps below to successfully install them.

Open Arduino IDE and click on Sketch > Library > Manage Libraries. Type ‘SSD1306’ in the search tab and install the Adafruit SSD1306 OLED library.

Install OLED SSD1306 Library Arduino IDE

We will also require the Adafruit GFX library which is a dependency for SSD1306. Type ‘Adafruit GFX’ in the search tab and install it as well.

install gfx library adafruit

After installing the libraries, restart your IDE.

ESP32 I2C Pins

The I2C pins stated above are set in default. If we want to change the GPIO pins we have to set them in code. The diagram below shows the pinout for the ESP32.

ESP32 I2C Pins

ESP32 I2C Pins

Note: If we use other GPIO pins of ESP32 for I2C communication, we can define other pins inside our Arduino sketch. We don’t have to use default I2C pins.

You can learn more about I2C communication here:

I2C Communication Introduction

Connection Diagram– OLED with ESP32 and Potentiometer

Required Components:

  1. ESP32 board
  2. Potentiometer
  3. 0.96-inch SSD 1306 OLED Display
  4. Breadboard
  5. Connecting Wires

As you can see above, we have connected all the VCC terminal of OLED with a Vin pin of ESP32. The SCL terminal of the OLED is connected with GPIO22 and the SDA terminal of the OLED is connected with the GPIO21 pin of ESP32. The grounds of all three devices are common.

ESP32 board SSD1306 OLED Display
Vin VCC
GPIO21(I2C SDA) SDA
GPIO22(I2C SCL) SCL
GND GND

The I2C pins stated above are set in default. If you want to use any GPIO pins for I2C, you will have to set it in code using SoftI2C().

Assemble the circuit as shown in the schematic diagram below:

esp32 adc value on OLED Arduino IDE

As you can see above, the middle pin of the potentiometer is connected with D15 pin of ESP32 board. Other terminals such as left and right are connected to 3.3V and GND pins, respectively.

Arduino Sketch (Displaying ADC and Voltage Readings on OLED Display)

Open your Arduino IDE and go to File > New. A new file will open. Copy the code given below in that file and save it.

This sketch will display the ADC values and the corresponding analog voltage on the Serial Monitor as well as on the OLED.

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

const int ADC_pin = 15;  
int sensor_reading = 0;  
float analog_voltage;

Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire, -1);

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

   if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
  Serial.println(F("SSD1306 allocation failed"));
  for(;;);}
  delay(2000);
display.clearDisplay();
display.setTextColor(WHITE);
}

void loop() {
  display.setCursor(0,0);
  display.setTextSize(1);
  display.clearDisplay();
  
  sensor_reading = analogRead(ADC_pin);
  Serial.print("ADC reading = ");
  Serial.println(sensor_reading);
  analog_voltage = sensor_reading * 3.3 / 4095;
  Serial.print("Volatge reading = ");
  Serial.println(analog_voltage);
  

  display.setTextSize(1);
  display.setCursor(0,0);
  display.print("ADC reading: ");
  display.setTextSize(2);
  display.setCursor(0,10);
  display.print(sensor_reading);
 
  display.setTextSize(1);
  display.setCursor(0,35);
  display.print("Voltage: ");
  display.setTextSize(2);
  display.setCursor(0,45);
  display.print(analog_voltage);
  display.print(" ");
  display.setTextSize(2);
  display.setCursor(60,45);
  display.print("V");
  
  Serial.println();
  display.display();
  delay(1000);
  
}

How the Code Works?

Firstly, we will include the OLED libraries that we previously installed for the proper functionality of the OLED display.

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Secondly, we will define three variables. One will hold the ADC pin GPIO15 that we will connect with the potentiometer. The second one will be called ‘sensor_reading’ and will save the ADC values. It is initially set to 0. For the third variable, we will define it as a floating type to save the values for the analog voltage.

const int ADC_pin = 15;  
int sensor_reading = 0;  
float analog_voltage;

Now, we will create an object named display which will be handling the OLED display and specifying the width, height, I2C instance (&Wire), and -1 as parameters inside it.’ -1′ specifies that the OLED display which we are using does not have a RESET pin. If you are using the RESET pin then specify the GPIO through which you are connecting it with your development board.

Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire, -1);

Inside the setup() function, we will open the serial communication at a baud rate of 115200.

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

Moreover, we will also initialize the OLED display by using display.begin(). Make sure you specify the correct address of your display. In our case, it is 0X3C.

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
  Serial.println(F("SSD1306 allocation failed"));
  for(;;);}

Then, we will clear the buffer by using clearDisplay() on the Adafruit_SSD1306 object. Additionally, we will set the colour of the text as white.


display.clearDisplay();
display.setTextColor(WHITE);

Now, inside the infinite loop() function, we will set the position of the cursor of the OLED and also set the text size. The clearDisplay() function will wipe off the previous readings from the display after each loop() so that new ones can be printed.

  display.setCursor(0,0);
  display.setTextSize(1);
  display.clearDisplay();

Obtaining ADC and Voltage Readings

Then we will first find out the ADC value according to the varying voltage using analogRead(). We will pass the ADC pin connected to the potentiometer as an argument inside it. This will be stored in the integer variable we defined previously ‘sensor_reading.’

sensor_reading = analogRead(ADC_pin);

Next, we will print these readings in the serial monitor after every 1 second.

Serial.print("ADC reading = ");
Serial.print(sensor_reading);

Next we will calculate the analog voltage we multiplying the ADC digital value with a resolution of ESP32 ADC which is 3.3/4095. It will provide output in the form of voltage.

 analog_voltage = sensor_reading * 3.3 / 4095;
  Serial.print("Volatge reading = ");
  Serial.println(analog_voltage);

Displaying Text on OLED

Now, we will display these two readings on the OLED display. First, we will set the size of the text using setTextSize() and pass the size as a parameter inside it. 1 denotes the smallest size and it increases relatively after incrementing it. We have set the font size as ‘1’ for the headings and ‘2’ for the actual values. Whenever we increase the size by +1, the pixel resolution of the text increases by 10 in height.

We will use the setCursor() function to denote the x and the y axis position from where the text should start. We have passed (0,0) as the parameter hence the text starts from the upper left corner. The first parameter is the x-axis position that (+x) increases towards the right. The second parameter is the y-axis position that (+y) increases downwards. We will set the cursor to new positions for each text.

Then by using println() we will pass the text which we want to display on the OLED. Just specify it like you do when printing strings/variables of serial objects.

Finally, we will call the display() function on the display object so that the text displays on the OLED.

display.setTextSize(1);
  display.setCursor(0,0);
  display.print("ADC reading: ");
  display.setTextSize(2);
  display.setCursor(0,10);
  display.print(sensor_reading);

display.setTextSize(1);
  display.setCursor(0,35);
  display.print("Voltage: ");
  display.setTextSize(2);
  display.setCursor(0,45);
  display.print(analog_voltage);
  display.print(" ");
  display.setTextSize(2);
  display.setCursor(60,45);
  display.print("V");
  
  Serial.println();
  display.display();
  delay(1000);
  

Demonstration

Choose the correct board and COM port before uploading your code to the ESP32 board. Go to Tools > Board and select ESP32 Dev Module.

ESP32 dev module selection for webserver

Next, go to Tools > Port and select the appropriate port through which your board is connected.

Selecting COM PORT ESP32

Now click on the upload button to upload code to ESP32. After that open the Arduino serial monitor and press the enable button on ESP32:

ESP32 enable reset button

The OLED display will start showing values for ADC and its corresponding voltage in volts. Rotate the knob of the potentiometer and see the values changing from 0-1023 for ADC and 0-3.3V for voltage.

Analog Voltage Measurement ESP32 ADC OLED Arduino IDE

Open the serial monitor and set the baud rate to 115200. You will be able to see all the different ADC and voltage readings as you rotate the knob of the potentiometer.

ESP32 ADC Values on Arduino serial monitor

Serial Monitor

Conclusion

In conclusion, we have learned about ESP32 ADC and how to get readings of both ADC and analog voltage using Arduino IDE. We used a potentiometer to supply varying voltage to the ESP32 ADC GPIO15 pin and then read the signal obtained. Likewise, we were also able to display the readings on an OLED display.

You can also check ESP32 Webserver tutorial.

We have similar guide with MicroPython here: