Introduction: ESP32-Touch, Hall, I2C, PWM, ADC, & DAC

The ESP32s, are a series of inexpensive microcontrollers introduced in the last quarter of 2016. They are powerful systems on a chip (SoC), they replace the ESP8266, and perhaps one might even say the Arduino UNO. Athough at the moment, they are somewhat more costly, and more complex to use than a UNO.

An ESP32 development board contains an ESP32-WROOM-32 module, made by Espressif Systems of Shangai, China.

The ‘W’ in the name is silent. So, ‘WROOM’ is pronounced as ‘room’.

Sadly, many on-line presenters mispronounce this chip's name, pronouncing the 'W', which should be silent.

I checked with Espressif to confirm that the 'W' is silent as in the English words wren, write, wrist, wrong, wrest, wrestling, wrinkle, wreck, etc. Espressif confirmed that "WROOM" is correctly pronounced with a silent 'W'.

The ESP32-WROOM-32 modules were on the boards that were the first to be made publicly available.

An ESP32-WROOM-32, at its core, contains a 32-bit Tensilica Xtensa LX6 chip, with both dual-core and single-core options available now, and perhaps more options available in the future. The chips are currently made using 40 nm fabrications. The ESP32-WROOM-32 modules also have an ULP (Ultra Low Power) coprocessor. This is used in some sleep modes that are not discussed in this tutorial.

There is an antenna at one end of the development board used here (i.e., the side opposite the USB connector). It is known as a MIFA (Meandered Inverted-F Antenna). Some other ESP32 boards have different antennas. However, most built-in antennas on development boards are MIFAs. This is because there is so little 'real-estate' that it is necessary to use an antenna that can fit on the boards. i.e., a Meandered antenna.

The development boards have PWM (Pulse Width Modulation), ADC (Analog to Digital Converter), and DAC (Digital to Analog Converter) capabilities, as well as I2C (Inter-Integrated Circuit), SPI (Serial Peripheral Interface), UART (Universal Asynchronous Receiver/Transmitter), CAN (Controller Area Network) 2.0, Standard Bluetooth, Bluetooth Low Energy, and 2.4 Ghz WiFi interfaces.

In general, the ESP32's development board GPIO pins 6 through 11 should normally be left unassigned in your project, as they are assigned to the ESP32's SPI flash memory.

One of the complexities of an ESP32 development module is that each pin, on a typical development board, has multiple functions. That is, each pin is multiplexed. This is unlike the Arduino UNO, were we have primarily two types of headers: digital and analog.

The ESP32 has a faster processor than the ESP8266 or the Arduino UNO. It runs at 160 Mhz, compared to a default speed of 80 Mhz for the ESP8266, and only 16 Mhz for the Arduino UNO.

Since we can use the same graphical user interface (GUI) for both an ESP32 development board and an Arduino UNO, the UNO can provide a a gentle entry point before turning to the ESP32 development module. Both capability, and (sadly) complexity, is increased when one uses a ESP32 development board, after using an Arduino UNO.

The ESP32 has some built-in sensors that we normally might find ourselves adding to an Arduino UNO. There are sensors on the ESP32 for touch, the Hall effect, and temperature (although this is for the chip’s internal temperature and not ambient temperature).

Typically an ESP32 development board has thirty (30), thirty-six (36), or thirty-eight (38) pins, spread evenly over two sides. These pins provide a transfer of functionality, with easy access, from the ESP32 module.

If one uses a DOIT development board on a breadboard, such as here, there is only room for free sockets on one side of the ESP32 development board. However, if one uses an ESP32 development board where the yellow tantalum capacitor is immediately below the ESP32 module, one finds these boards are usually slimmer in width and so these can accommodate holes on the breadboard on both sides of the development board. Of course, with the DOIT development boards, one can always leave one side of the development board hanging over one side of the breadboard, and so gain access to both sides of the development board's pins.

The width of the thinner-style ESP32 development board is usually somewhat greater than 25mm, while the width of the thicker-style ESP32 development board is typically over 27.5mm (see pictures).

We will look at two categories of sensors (touch and Hall effect) included on the chip, as well as the I2C, PWM, ADC, and DAC capabilities built-in to ESP32 modules, and consequently most ESP32 development boards.

In what follows we will use “DOIT” ESP32 development boards, as already noted; these boards are relatively inexpensive (compared to some others), although there are a multitude of ESP32 development boards to choose from. Fortunately, almost all ESP32 development boards can use the functionality discussed below.

The maximum current through any pin on an ESP32 chip should not exceed 12ma, with 6ma recommended, although there is some confusion on this, with some web pages suggesting 40ma.

A useful discovery I made is that when my development board is run from a five (5) volt USB source, I get five (5) volts from the VIN pin. This is the pin on the bottom left of the board (if the board is facing up, and the USB connector is at the bottom of the board). I use this 5 volt pin to power my devices that require 5 volts, noting the current limitations of an ESP32 pin.

I am able to use the 5 volt DC VIN pin, when my board is powered from USB, and will do so when I discuss my board's (and other ESP32 boards) built-in capabilities to use I2C.

We find that the metal clad ESP32-WROOM-32 module we see is not the entire picture. If we remove it's cover, as in the second (2nd) picture above, we can see that there are a number of components beneath this cover. These include an ESP32-DOWDQ6, really the heart of the ESP32-WROOM-32 module. We see that this particular ESP32-DOWDQ6 was made in 2018, as it's date code indicates. We can also see a memory IC, clock crystal, a variety of capacitors, and at least one resistor and one diode.

When you are uploading a sketch to the ESP32 it may be necessary for you to momentarily hold down the 'boot' button, on your development board, when you see 'Connecting........____' , in the bottom area of the Arduino IDE, in order to upload the code from the ARDUINO IDE to the development board.

The ESP32 is a super device and has a lot going for it. However, it is not without some problems. Most issues can, fortunately, be traced to unstable power, although not all. Power is something you should check, preferably with a storage oscilloscope, if you are having any difficulties.

It is easier to 'break' an ESP32 development board than it is to 'break' a UNO. That is, an ESP32 development board is somewhat less forgiving than an Arduino UNO. I can confirm this based on personal experience.

Supplies

The required items, for the material covered here, are:

- An ESP32 development board (in our examples we use a "DOIT" ESP32 development board, however, almost any ESP32 development board should work as well)

- Four (4) F-F Dupont Wires

- One (1) M-F Dupont Wire

- Two (2) M-M Dupont Wires

- A magnet (any kind will do, but one with a strong magnetic field is best)

- An I2C 1602 LCD display, a piggyback I2C adapter is used here to obtain an I2C LCD.

- An LED - a 10mm red LED is used here, but a 5mm or 3mm LED of any color will work as well

- A 330 Ohm resistor, or a resister greater than this to limit current to the LED

- An optional breadboard, which should prove useful when we look at PWM.

The two (2) M-M Dupont wires, the LED, the I2C 1602 display, the resistor, as well as the optional breadboard are really only useful when we come to the Step on PWM.

For the Step on ADCs one will need, in addition to the above, two 10K potentiometers, and some additional Dupont wires.

For the Step on DACs one will need, in addition to the above, a 220 ohm resistor.

Step 1: Touch Sensors

Let’s first consider touch sensors.

We will use a touch sensor's capabilities, i.e., to determine whether it is touched or not. This is, as a 'touch' sensor's name might imply.

In the example presented here, we use that capability to have a touch sensor act as a switch, i.e., turn something On/Off. We will see that we can use an if statement to check if a particular touch sensor was/is touched, and take action according to what we find.

Touch sensor capabilities are generally present on either 9 or 10 of the total pins available. The number of touch sensors present generally depends on the development board, and whether it has thirty (30), thirty-six (36), or thirty-eight (38) pins. The third picture in the introduction shows a development board with thirty-eight (38) pins. (As an aside, the AMS1117 3.3, shown in that picture as well as the picture of the board we use here, is the 3.3 DC volts regulator for the board, and the yellow component is a tantalum capacitor connected to the output of that voltage regulator to provide stable operation.)

Usually the lower the number of total pins, the lower the number of pins that have touch sensor capability. The development board we will use has thirty (30) pins, fifteen (15) in each of two (2) rows as shown. It has nine (9) pins with touch sensor capability.

The touch sensors pins are extensions of the ESP32 module. For the left-side touch sensors, sensor four (4) comes from the lower bottom of the module, sensors five (5), six(6), and seven (7) come from the lower left-side of the module, while sensors eight (8), and nine (9) come from the middle of the module's left-side. For the right-side touch sensors, sensors two (2), and three (3) come from the bottom right of the module, and touch sensor zero (0) comes from the lower right-side of the module.

If we used an Arduino UNO we would likely purchase, and add, a physical switch to our circuit to perform the same functionality, e.g. a tactile switch. The touch sensors, on the ESP32, are capacitive devices, and detect an electrical charge, such as on a human hand.

A touch sensor pin is read using the following instruction,

touchRead(touchSensorPinNumber).

Initially we can see the values that a touch sensor pin produces without being held, i.e., when the wire coming from a touch sensor pin is left free. As we see from the video that value varies between 74 and 75 (for the board we are using).

When I hold the male end of a M-F Dupont cable, the female end of which is connected to a touch sensor pin, the values I get change. They now vary between 11 and 13, i.e., considerably less than 74. The values you get depend on the particular ESP32 board you have. If you have a different board, these values may have some variation from what you see here.

I can easily test the value from a touch sensor using an if statement, and then, e.g., take actions depending on the results. Here we test whether the result is less than sixty (60) or not. The value, sixty (60), provides a clear demarcation, for the board I have, between the values when a touch sensor is held and when it is not.

Obviously, I could have use any value in the 20s, 30s, 40s, or 50s in the if statement, as any of these values would separate a "touched" touch pin from one not touched. You may want to experiment by changing the value in the if statement shown.

I used the ESP32 development board both with and without a breadboard in these experiments. A breadboard is helpful, but it is not necessary. It is useful when components other than the ESP32 development board are also used. The use of a breadboard can give these components a base of support.

I use the sketch below to test the touch sensor pin and read its value, both when the wire is free-standing and when it is being held.

/*
* Program by R. Jordan Kreindler

* for touch sensor pins

* Dupont wire M-F, F-side connected to pin D13 (T4)
* one of the touch sensor pins.

*/

int valueRead = 9999; // Here we set an integer and initialize it

#define touchSensorPinNumber T4

void setup() {

Serial.begin(9600);

}

void loop () {

valueRead = touchRead(touchSensorPinNumber);

Serial.print("Value Read = ");

Serial.println(valueRead);

}

We next use the ESP32 development board’s built-in LED. We detect if the touch sensor is touched, and if so we turn On the built-in LED.

These two examples should give you a good feel for the capabilities of a touch sensor.

/*
* Program by R. Jordan Kreindler

* for LED_BUILTIN and touch sensor

*

* Female connector on Dupont wire connected to pin D13 (T4)

* which is used for our touch sensor.

*

* Most ESP32 development boards have

* a built in LED

*/

int valueRead = 9999; // Establish and initialize the valueRead variable

#define touchSensorPinNumber T4


void setup() {

pinMode(LED_BUILTIN,OUTPUT); // Set mode of LED_BUILTIN as output

}


void loop () {

valueRead = touchRead(touchSensorPinNumber); // Read touch sensor

Serial.print("Value Read = ");

Serial.println(valueRead);


if(valueRead < 60) {

digitalWrite(LED_BUILTIN, HIGH);

} else {

digitalWrite(LED_BUILTIN, LOW);

}


}

Step 2: Hall Effect Sensor

The ESP32 chip, and consequently the ESP32 development board, has a Hall effect sensor. This sensor is located beneath the lid of the ESP32 module.

The Hall effect was found by Edwin Hall in 1879. On our ESP32 development board, the Hall effect sensor can be used to determine the intensity and direction of a magnetic field. By measuring magnetic field intensity, it can be used as a switch, as in the example below.

The result from the Hall effect sensor is read using the command,

hallRead().

The value that is read can be assigned to an integer variable. The greater a magnetic field the greater the value read, i.e., the greater the voltage reading.

Thus, this value can be used to determine the distance to a magnet, or employed as a switch. This later use is similar to how we used a touch sensor earlier.

Almost any magnet can be used for a switch. However, if this is done, whatever magnet is selected should be relatively strong. Keep in mind that a magnetic field's strength diminishes with distance.

We can see that when there is no close-by magnetic field the values read from the Hall sensor, on my ESP32 development board, range from 7 to 19. However, when a strong magnet is close-by, the values are 150 or greater.

The following sketch can be used to read values for the magnetic field detected.

/*
* Sketch by R. Jordan Kreindler

*

* This sketch reads and displays,

* on the Serial monitor, values

* read from the Hall sensor

*/

int value1;

int delay1 = 500;

void setup() {

Serial.begin(9600);

}

void loop() {

// Read value from the built-in

// Hall effect sensor

value1 = hallRead();

Serial.print("Hall value read: ");

Serial.println(value1);

delay(delay1);

}

In this sketch the value read from the Hall Sensor is assigned to the integer variable value1. This value can be tested in an if statement, and the results used to take any action felt appropriate. Here the action that is taken is to turn the built-in LED On and Off.

If the magnet is reversed, i.e., the magnetic field is strong but in the opposite direction, one will also see relatively large results, but these will usually be proceeded by a negative sign.

We can, as we did with a touch sensor, use the Hall sensor as a switch, as in the sketch below. This sketch is a slightly modified version of the one we used above with the touch sensor.

/*
* Program by R. Jordan Kreindler

* for LED_BUILTIN and Hall effect sensor

*

* Most ESP32 development boards have

* a built-in LED

*/

int valueRead = 9999; // Establish and initialize

// variable valueRead

void setup() {

pinMode(LED_BUILTIN, OUTPUT); // Set LED_BUILTIN

// for Output

Serial.begin(9600);

}

void loop () {

valueRead = hallRead();

Serial.print("Value Read = ");

Serial.println(valueRead);

if(valueRead > 60) {

digitalWrite(LED_BUILTIN, HIGH);

} else {

digitalWrite(LED_BUILTIN, LOW);

}

}

I also used the built-in plotter, available in the Arduino IDE, and took pictures without a magnet nearby and with the magnet close by. These can be seen here.

Step 3: I2C Built-in Capability

The ESP32 chips have a built-in temperature sensor. However, that sensor is only used to measure the internal temperature of the chip itself and is not used to measure ambient temperature. Thus, it will not be covered in this tutorial.

Instead we will cover the ESP32's built-in I2C capabilities. We noted in the introduction, that when my development board is run from a five (5) volt USB source, I get five (5) volts out of the VIN pin (the pin on the bottom left of the board if the USB connector is facing downward and the board is facing up). I will use this information to use an I2C 1602 display with my ESP32 development board.

The default I2C capabilities are available on GPIO pins 22 and 21, these are SCL and SDA respectively.

Here we will connect a 1602 to the ESP32 development board, remembering that VIN pin produces 5 volts if the development board is powered from a USB cable.

A sketch that uses the built-in I2C capabilities of the ESP32 development board is given below. It uses a I2C 1602 display and presents text on both lines of this display.

Here we use GPIO22 for I2C SCL and GPIO21 for I2C SDA. The VIN pin provides the necessary 5 volts, and the GND pin on the same side as VIN completes the circuit.

Here is the sketch,

/*
* Program by R. Jordan Kreindler

* showing the use of the I2C capabilities

* of the ESP32 Development board.

*/

#include

// Be sure and #include LiquidCrystal_I2C.h inside less than and

// greater than brackets, as Instructables often drops text between brackets

// Piggyback LCD address (Usually either 0x3F or 0x27)
// for my piggyback adapters. The display here is a 1602

void setup() {

lcd.init(); // Initialize the lcd

lcd.backlight(); // Turn the backlight On

lcd.clear(); // Clear the display, and set the cursor to home position

lcd.print(" Hello My World "); // Print " Hello My World " at this position

lcd.setCursor(0, 1); // Set the cursor at the starting position

// on the next row

lcd.print("How are you now?"); // Print "How are you now?" at this position

}

void loop() {

}

If you have any problems getting this program to run, and continue to get an error message from the Arduino IDE, try another "LiquidCrystal_I2C" zip file. The LiquidCrystal_I2C library you currently have may not work with an ESP32, even though it works with a UNO.

This is based on my personal experience in attempting to use my initial UNO LCD library.

Step 4: Pulse Width Modulation (PWM)

Almost every pin on the ESP32 development board used here accommodates PWM, except for the bottom two pins on both the left and right-side, and the top pin on the left side. This is with the development board face up, and the USB connector oriented down.

PWM allows us to simulate analog signals using digital means. It is often used to fade an LED up and down or control a motor.

In the example the follows we will use it in conjunction with an ESP32 development board to fade an LED up and down. We use more resolution for PWM than is available on the Arduino UNO which defaults to 8-bits, i.e., 0-255. Here we use 15-bits, so the range is from 0 to not quite 33,000.

/*
* Program by R. Jordan Kreindler

* showing the use of the PWM capabilities

* of an ESP32 Development board.

*/

#define ledChannel 0 // Channel 0 chosen, from 0-15 choices

#define resolution 15 // Resolution is chosen as 15 bits

#define frequency 5000 // 5,000 Hz frequency

#define LED 15

// LED connected via 330 ohm resistor and in turn to pin 15

// Other end of LED connected to GND

int delay1 = 20; // Delay in milliseconds

void setup() {

ledcSetup(ledChannel, frequency, resolution); // Set-up a channel

ledcAttachPin(LED, ledChannel); // Attach a channel to a pin

}

void loop() {

// In the for() loops, adding and subtracting

// 200 is necessary owing to the greater resolution

// provided by 15 bits, compared to the UNO

for (int i = 0; i < 32768; i = i + 200) { //loop to fade up

ledcWrite(ledChannel, i);

delay(delay1);

}

for (int i = 32767; i > -1; i = i - 200) { //loop to fade down

ledcWrite(ledChannel, i);

delay(delay1); }

}

}

Step 5: Analog to Digital Converters (ADCs)

An ESP32 development board provides 12-bit Analog to Digital Converters (ADCs). It provides this analog to digital conversion with 4096 (0-4095) discrete digital values. This compares to the 10-bit ADC capabilities of the Arduino UNO with 0-2^10th or 1024 (0-1023) discrete values. That is, 0 to 1023 versus 0 to 4095.

According to Espressif documentation "[t]he ADC driver API supports ADC1 (8 channels, attached to GPIOs 32 - 39), and ADC2 (10 channels, attached to GPIOs 0, 2, 4, 12 - 15 and 25 - 27)". However, ADC2 has some limitations due to a number of issues. such as the use of WiFi etc, and so these pins are used only in restricted circumstances for their ADC capabilities.

The DOIT development board, with thirty (30) pins) as used here, provides fifteen (15) different pins that offer ADC, with 12-bit (4096) discrete levels of resolution. Unfortunately, the ADC are not fully linear, particularly at their ends. Thus, it is difficult to distinguish between close very low or high voltages on these input pins, as these voltages merge together. Also, the “in between” values are not necessarily what might be expected If the system had a high level of uniformity, which the ESP32 ADCs, unfortunately, do not. The ADCs are generally linear, but unfortunately not precisely.

Typically, other than at the extremes, integer values tend to be slightly less than expected. You can program the ESP32 with a purpose-built lookup table to account for the results of the “off” ADCs. However, I might suggest that if one wants a totally linear ADC, it might be best to go with a dedicated off-board module.

The ADC integer values can be read with the command,

analogRead(ADC_GPIO);

where ADC_GPIO is one of the pins supporting ADC. GPIO pins 0, 2, 4, 12, 13, 14, 15, 25, 26, 27 should not be used if WiFi is being used, or you may have a problem getting readings from these pins.

There are additional functions that can be used with the ADC pins. They are not typically needed, and are not used in the example below.

In the example that follows, we use two (2) ten (10) K potentiometers and connect their wipers, outputs, to two (2) ADC pins: GPIO34 and GPIO35. These potentiometers are adjusted independently, as can be seen in the attached video. The results of these independent adjustments are shown on the attached LCD display.

Close-ups of the set-up were taken, and can be seen, in the four (4) attached still photographs. These can be used to see all connections.

The sketch that was used, with the two (2) potentiometers, is given below.

/*

Program by R. Jordan Kreindler

Sketch to read analog voltages from two potentiometers

*/

#include // Be sure to #include LiquidCrystal_I2C.h between greater than and less than

// signs as Instructables.com drops text between these signs and the signs

LiquidCrystal_I2C lcd(0x27, 16, 2); // Piggyback lcd(address one of 0x3F or 0x27,16,2);

#define potWiperPin1 34 // Set the pin where the wiper from potentiometer one connects

#define potWiperPin2 35 // Set the pin where the wiper from potentiometer two connects

int delay1 = 1000; // The delay between readings

int value1; // The output value of potentiometer one

int value2; // The output value of potentiometer two

void setup() {

lcd.init(); // Initialize the LCD display

lcd.backlight(); // Turn the LCD backlight on

}

void loop() {

value1 = analogRead(potWiperPin1); // Read the value of potentiometer one

value2 = analogRead(potWiperPin2); // Read the value of potentiometer one

lcd.clear(); // Clear the LCD display

lcd.setCursor(0, 0); // Place the cursor at position zero on line 0

lcd.print("Pot1 Value:"); // Display the value for potentiometer one

lcd.print(value1);

lcd.setCursor(0, 1); // Place the cursor at position zero on line 1

lcd.print("Pot2 Value:"); // Display the value for potentiometer two

lcd.print(value2);

delay(delay1); // Delay delay1 milliseconds

}

Step 6: Digital to Analog Converters (DACs)

An ESP32 provides two Digital to Analogue Converters (DACs). These allow us to output a true analog signal. Typically this voltage is between zero (0) and whatever is the native voltage of the board. In our case with a “DOIT” ESP32 that native voltage is 3.3 volts.

On the ESP32 the output of a DAC is eight (8)-bits, i.e, from 0 to 255.

That is, we do not need PWM to simulate a voltage, we can actually produce that voltage. Thus, we can directly control the brightness of an LED or the speed of a motor by controlling the 'real' voltage applied. There are many applications where a PWM signal will not do, and a 'real' voltage is required. So having a DAC available is an important capability.

In the example that follows we apply varying voltages to fade an LED up and down. Although this is similar to what we did using PWM, here we actually send varying voltages, rather than simulated voltages using PWM.

The sketch for this varying voltage example follows. A video showing the results is above and still photographs are attached so you can see how the circuit was wired.

/*

* Program by R. Jordan Kreindler

* to show how a DAC works

* A 10mm Red LED is used here (although a 3mm or 5mm LED

* of any color will do). One LED side is connected to a 220 ohm resistor.

* The other side of the resistor is connected to one of the

* DAC pins: GPIO26. The other side of the LED is connected to GND.

*/

#define DAC2 26 // Identify the digital to analog converter pin

int delay1 = 10; // The delay between successive writes

void setup() {

}

void loop() {

// Fade up

for (int i = 0; i < 256; i++) { // i = 255 = approx 3.3 volts, i = 0 = approx 0.0 volts

dacWrite(DAC2, i);

delay(delay1);

}

// Fade down

for (int i = 255; i > -1; i--) {

dacWrite(DAC2, i);

delay(delay1);

}

}

Step 7: Afterwards

If you have come to this point - congratulations. You should now have a basic understanding of some of the key built-in capabilities of the ESP32 development board covered in this tutorial. I hope you found this Instructable interesting and of value.

It should be obvious, that this tutorial, just "scratches the surface" of ESP32 development boards. Each of the capabilities covered here could probably have its own multi-part tutorial.

If you have any comments, suggestions, or questions related to this tutorial, please be kind enough to add your comments below. If you have any thoughts or questions, related to ESP32 development boards in general, but not covered in this tutorial, or any suggestions for how I could improve this, I would be pleased to hear from you.

You can contact me at transiintbox@gmail.com. (Please replace the second 'i' with an 'e' to contact me).

Sensors Contest

Participated in the
Sensors Contest