LabWired The easy way to build hardware

Learn / Track 02

Getting started with the ESP32

“ESP32” is the family of cheap and fast microcontrollers with WiFi and Bluetooth onboard. This lesson is about getting started fast with any ESP32 using LabWired!

Contents


Which chip is on your board

Read the metal shield on the module. It gives the chip name. The name sets everything else.

ChipCoreRadioGPIOsUSB
ESP32Two Xtensa LX6, up to 240 MHzWi-Fi 4, Bluetooth Classic and LE34External converter chip
ESP32-S3Two Xtensa LX7, up to 240 MHzWi-Fi 4, Bluetooth LE45In the chip
ESP32-C3One RISC-V, up to 160 MHzWi-Fi 4, Bluetooth LE22In the chip
ESP32-C6One RISC-VWi-Fi 6, Bluetooth LE, 802.15.4In the chip
ESP32-H2One RISC-VBluetooth LE, 802.15.4. No Wi-FiIn the chip

Four differences change what you write and how you wire:

Two USB paths. On the classic ESP32: USB connector, then a CP2102 or CH340 converter chip, then UART0 on GPIO1 and GPIO3, then the chip. On the C3, S3, C6 and H2: USB connector straight into a USB Serial/JTAG controller inside the chip.
The converter chip is why one board needs a driver and the other does not, and why the port on the newer chips disappears each time the chip resets.

Which board to buy

A development board is a module, a voltage regulator, a USB connector, and a row of pins. Boards cost between 5 and 15 US dollars. Three boards cover almost all first projects:

Buy two boards. A second board tells you in one minute if a failure is in the hardware or in your code. The second board costs less than the hour that you spend without it.

You can see full pinout in chip properties in Labwired.

Start now

The lab below is an ESP32-C3 SuperMini with an LED and a 330 Ω resistor on GPIO4. It operates in your browser. There is no toolchain to install, no driver, and no cable. You can build your virtual circuit. write and validate you r code and than flash the board.

Open the full lab →

The firmware is the same Arduino sketch that The first program gives below. The chip is not a video of a chip. The LED on the canvas changes state each 500 ms because the program in the model wrote to the GPIO4 output register.

This is the header of that board. and from the ESP32-C3 datasheet:

ESP32-C3 SuperMini pinout. Left header from the top: 5V, GND, 3V3, GPIO4, GPIO3, GPIO2, GPIO1, GPIO0. Right header from the top: GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO20, GPIO21. Each pin carries its alternate functions, with the strapping pins and the serial console pins marked.
Thirteen of the 22 GPIOs reach the header. GPIO8 has the on-board LED and is also a strapping pin, which is why the lab uses GPIO4 for its external LED.

Two properties of this board catch people:

Two conditions make this more than a demonstration:

Use the lab to learn the program structure and the pin rules. Use the real board to learn the parts that a simulation cannot teach you: a loose wire, a weak power supply, and a cable that does not carry data.

Install the toolchain

Three environments are used. Select one and do not mix them at the start.

Arduino IDE is the correct first choice. It has the largest quantity of libraries, and almost all beginner material uses it.

  1. Install the Arduino IDE, version 2.x.
  2. Open File > Preferences. In Additional boards manager URLs, add: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
  3. Open Tools > Board > Boards Manager. Search for esp32. Install esp32 by Espressif Systems, version 3.x. The download is approximately 1 GB and it takes some minutes.
  4. Select your board in Tools > Board. Select the port in Tools > Port.

ESP-IDF is the official Espressif framework. It gives access to each peripheral, the real-time operating system (FreeRTOS), and the partition table. Move to it when Arduino hides something that you must control. Do not start with it.

PlatformIO, as an extension in VS Code, builds Arduino and ESP-IDF projects. It records the exact framework version in a platformio.ini file in the project, so a project builds again after two years. Use it when a project becomes larger than one file.

The first program

#include <Arduino.h>

const int LED_PIN = 4;
uint32_t count = 0;

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  Serial.println("ESP32-C3 up. First blink.");
}

void loop() {
  digitalWrite(LED_PIN, HIGH);
  Serial.print("blink ");
  Serial.println(++count);
  delay(500);
  digitalWrite(LED_PIN, LOW);
  delay(500);
}

setup() operates one time after each reset. loop() then operates again and again until the power stops. There is no main() that you write, and there is no exit.

Serial.begin(115200) starts the serial port at 115200 bits each second. Set the same speed in the Serial Monitor. An incorrect speed does not give an error. It gives characters that have no meaning, which is the same symptom as damaged hardware and sends many beginners to the wrong conclusion.

delay(500) stops the program for 500 milliseconds. It is correct in a first program and incorrect in a real one: the processor does no other work during that time. Use millis() to compare times when the program must do more than one operation.

Use the LED pin of your board. GPIO4 is the pin in this lab. The DevKit V1 uses GPIO2. The C3 SuperMini has a user LED on GPIO8 that is active-low, so LOW makes it operate. If the LED does not change, this is the first item to examine.

Five failures that stop the first upload

These five conditions cause almost all failed first uploads. Examine them in this sequence.

1. The cable carries power and not data. Many USB cables that are supplied with a device have two conductors, not four. The board receives power, the LED on the board operates, and no port appears on the computer. Use a different cable before you examine anything else. This is the most frequent failure and the least expected.

2. The driver is not installed. A board that uses a converter chip needs the driver for that chip: CP210x for a Silicon Labs converter, CH340 for a WCH converter. Without the driver, no port appears. Boards with a USB port directly to the chip (C3, S3, C6, H2) do not need a driver.

3. The chip is not in download mode. The board resets the chip into download mode with the data terminal ready (DTR) and request to send (RTS) lines. Some boards do not have the transistor circuit that this needs. The upload then stops at Connecting........_____. Do it manually, in this order:

Four steps: hold BOOT, tap EN or RESET while BOOT is still held, release BOOT, then the upload starts writing. Below, the esptool output that means the chip never entered download mode.
The sequence holds GPIO0 low across the reset, which is what selects download boot mode.

4. The port is incorrect. More than one serial port can be present, in particular on a computer with Bluetooth. Disconnect the board, look at the port list, connect the board, and look again. The port that appears is your board.

5. The port disappears after the upload. This occurs on the C3, S3, C6, and H2, and not on the classic ESP32. Their USB port is made by the chip. When the chip resets to start your program, the port disappears from the computer and then appears again. The Serial Monitor loses the connection and shows nothing.

There are two results of this condition. First, in the Arduino IDE, set Tools > USB CDC On Boot > Enabled, or Serial sends to the hardware UART pins and not to the USB port. Second, open the Serial Monitor after the board completes the restart, and expect to lose the first lines that the program prints. To read the first lines, put a delay(2000) at the start of setup().

The last condition looks the same as a chip that has stopped: no output. An empty buffer is not a stopped chip. Change one condition at a time.

The pins that you must not use

The ESP32 family has a GPIO matrix. Almost any peripheral can operate on almost any pin. This is a real advantage, and it is also the reason that a tutorial pin number can damage your project without an error message.

Some pins have another function that operates before your program does.

The manufacturer diagram gives each pin and each function. These are the two Espressif boards. The C3 SuperMini is in Start now, with no board above.

Official Espressif pin layout for the ESP32-DevKitC V4, with each pin labelled with its GPIO number and alternate functions.
ESP32-DevKitC V4. GPIO6 to GPIO11 carry the flash bus, and GPIO34 to GPIO39 are input only. Diagram © Espressif Systems, from esp-dev-kits, Apache-2.0.
Official Espressif pin layout for the ESP32-S3-DevKitC-1 v1.1, with each pin labelled with its GPIO number and alternate functions.
ESP32-S3-DevKitC-1 v1.1. Diagram © Espressif Systems, from esp-dev-kits, Apache-2.0.

Those diagrams give each function of each pin. They do not answer the question that you have while you select a pin, which is shorter: may I use this one? The figure below answers that question, for the three chips, from the same datasheets.

Every GPIO on the ESP32, ESP32-C3 and ESP32-S3, coloured by what else uses the pin: free, strapping, flash or PSRAM, USB, input only, or the UART0 console. ADC1 and ADC2 channels are tagged.
The same pins, reduced to one question. Green is yours. Everything else has a second function that operates before your program does.

The paragraphs below give the reason behind each colour.

Strapping pins are read by the chip at reset to select the boot mode. If your circuit holds one at the incorrect level, the chip does not start your program. It goes into download mode, or it does not start at all. On the classic ESP32 there are five: GPIO0, GPIO2, GPIO5, GPIO12, and GPIO15. On the C3 there are three: GPIO2, GPIO8, and GPIO9. On the S3 there are four: GPIO0, GPIO3, GPIO45, and GPIO46.

GPIO12 on the classic ESP32 is the condition that is most difficult to find. It selects the voltage of the internal regulator that supplies the flash memory. It has an internal pull-down resistor. If you connect a device that pulls GPIO12 up at reset, the chip sets the flash supply to 1.8 V, the flash does not operate, and the board goes into a boot loop. The wiring is correct, the code is correct, and the board does not start.

Flash pins connect the chip to its own program memory. If you use them, the chip cannot read the program that it is executing. On the classic ESP32 these are GPIO6 to GPIO11. On the C3 these are GPIO12 to GPIO17. On the S3 these are GPIO26 to GPIO32, and also GPIO33 to GPIO37 on modules that have octal PSRAM. Many boards do not bring these pins to the header. Do not use them if they are present.

Input-only pins are on the classic ESP32: GPIO34 to GPIO39. They have no output driver and no internal pull-up or pull-down resistor. digitalWrite on one of these pins does nothing and reports no error. A button on one of these pins needs an external resistor.

USB pins carry the USB signals on the chips that have a USB controller: GPIO18 and GPIO19 on the C3, and GPIO19 and GPIO20 on the S3. Use them for another function and you lose the port that you program the board through.

Console pins carry UART0, which is where the boot messages and Serial.print go: GPIO1 and GPIO3 on the classic ESP32, GPIO20 and GPIO21 on the C3, GPIO43 and GPIO44 on the S3. The circuit operates if you use them, and you lose the serial monitor while it does. Connect an LED to GPIO1 on a DevKit V1 and it blinks with each character that the board prints.

The second analog-to-digital converter (ADC2) and Wi-Fi are in conflict on the classic ESP32. The Wi-Fi driver uses ADC2. A read on an ADC2 pin while Wi-Fi is active can fail instead of giving a value. If a sensor reading becomes incorrect at the moment that the network connects, this is the cause. Use an ADC1 pin (GPIO32 to GPIO39) for each analog input in a project that uses Wi-Fi.

A short rule for the classic ESP32: use GPIO4, GPIO16 to GPIO23, GPIO25 to GPIO27, and GPIO32 or GPIO33 for outputs. Keep GPIO34 to GPIO39 for inputs.

What to build after the blink

Add one item at a time, and make sure that each item operates before you add the next.

  1. Read a sensor over I²C. A BME280 gives temperature, humidity, and pressure on two wires. This teaches you buses, addresses, and pull-up resistors — and it is where most beginners meet their first failure with no error message. Debugging I²C gives the four failures and the check that finds each one.
  2. Connect to Wi-Fi and print your internet protocol (IP) address. This is 10 lines. It is also the point at which power supply failures start: the radio takes current in pulses, and a weak USB port causes a brownout reset the moment that the radio transmits.
  3. Send the sensor reading somewhere. A message queuing telemetry transport (MQTT) broker, an HTTP endpoint, or a display.
  4. Make it operate on a battery. Deep sleep changes each decision that you made before it. It is the correct point to move from Arduino to ESP-IDF.

Each of these steps is available in the browser before your board arrives, and each of them stays available after: a lab that repeats exactly is the fastest way to separate a code failure from a hardware failure.

Open the Playground to wire your own ESP32 circuit, or point your coding agent at the LabWired MCP server and have it run the firmware that it writes, on a chip model, before you flash a board.

Do you have an ESP32 failure that took you a day to find? Tell us on r/labwired. We add the best examples to this lesson.

Andrii Shylenko
Andrii Shylenko

Founder, LabWired.