The "blink" sketch is the "Hello World" of microcontroller programming. You will make an LED turn on and off repeatedly, and along the way, learn how PlatformIO's build-and-upload workflow compares to the Arduino IDE. If you have already read the Arduino IDE version of this tutorial, the code is identical -- the only differences are the #include <Arduino.h> header, the file location (src/main.cpp), and the buttons you click.
🔗How an Arduino Sketch Works
Every Arduino sketch has two required functions:
#include <Arduino.h> // Required in PlatformIO
void setup() {
// Runs once when the board powers on or resets
}
void loop() {
// Runs over and over, forever
}setup()is where you configure things: set pin modes, start serial communication, initialize sensors. It runs once.loop()is where your main program logic goes. It runs continuously aftersetup()finishes -- thousands of times per second if your code is short.
This two-function structure is the same for every Arduino-compatible board, including the ESP32. The only PlatformIO-specific requirement is the #include <Arduino.h> line at the top. The Arduino IDE inserts this automatically behind the scenes; PlatformIO does not.
🔗Blink the Built-in LED
Most ESP32 development boards have a small LED soldered onto the board that is connected to a GPIO pin. On the majority of ESP32-WROOM-32 DevKit boards, this is GPIO 2.
Important: The built-in LED pin varies between boards. GPIO 2 is the most common, but some boards use GPIO 5, GPIO 22, or have no built-in LED at all. Check your specific board's documentation if GPIO 2 does not work. You can always connect an external LED to any available GPIO pin instead.
🔗The Blink Sketch
If you do not already have a PlatformIO project, create one now (see PlatformIO Setup). Open src/main.cpp and replace its contents with:
#include <Arduino.h>
#define LED_PIN 2 // Built-in LED on most ESP32 DevKit boards
void setup() {
pinMode(LED_PIN, OUTPUT); // Set the LED pin as an output
}
void loop() {
digitalWrite(LED_PIN, HIGH); // Turn LED on
delay(1000); // Wait 1 second (1000 milliseconds)
digitalWrite(LED_PIN, LOW); // Turn LED off
delay(1000); // Wait 1 second
}🔗Building and Uploading
With the file saved, look at the PlatformIO toolbar at the bottom of the VS Code window:
- Build -- Click the checkmark icon (or press
Ctrl+Alt+B). PlatformIO compiles your code and reports any errors in the terminal panel. - Upload -- Click the right arrow icon (or press
Ctrl+Alt+U). PlatformIO compiles (if needed) and uploads the firmware to your ESP32.
The first time you build a project, PlatformIO downloads the ESP32 toolchain automatically. You will see output like:
Resolving esp32dev dependencies...
Platform Manager: Installing espressif32
Downloading [####################################] 100%
...
Compiling .pio/build/esp32dev/src/main.cpp.o
Linking .pio/build/esp32dev/firmware.elf
Building .pio/build/esp32dev/firmware.bin
========================= [SUCCESS] Took 45.67s =========================Subsequent builds are much faster (typically a few seconds) because the toolchain is cached.
After a successful upload, the LED on your board should blink on and off once per second.
🔗Understanding the Code
#include <Arduino.h> -- Brings in the Arduino framework: pinMode, digitalWrite, delay, Serial, and all other Arduino functions. Required in PlatformIO; implicit in Arduino IDE.
#define LED_PIN 2 -- Creates a constant called LED_PIN with the value 2. Using a named constant instead of writing 2 everywhere makes the code easier to read and change. If your board uses a different pin, you only need to change this one line.
pinMode(LED_PIN, OUTPUT) -- Tells the ESP32 that GPIO 2 should be an output pin. This means we will send signals out of this pin to control the LED.
digitalWrite(LED_PIN, HIGH) -- Sets the pin to HIGH (3.3 V). Current flows through the LED, and it lights up.
digitalWrite(LED_PIN, LOW) -- Sets the pin to LOW (0 V). No current flows, and the LED turns off.
delay(1000) -- Pauses the program for 1000 milliseconds (1 second). During this time, the ESP32 does nothing.
🔗Experiment: Change the Blink Rate
Now try modifying the sketch. Change the delay values, save the file, and upload again:
void loop() {
digitalWrite(LED_PIN, HIGH);
delay(100); // On for 0.1 seconds
digitalWrite(LED_PIN, LOW);
delay(100); // Off for 0.1 seconds
}The LED blinks much faster. Try different values:
| On delay | Off delay | Effect |
|---|---|---|
| 1000 ms | 1000 ms | Slow, steady blink |
| 100 ms | 100 ms | Fast blink |
| 50 ms | 950 ms | Quick flash, long pause |
| 500 ms | 100 ms | Long on, short off |
This kind of experimentation is one of the best ways to learn. Change values, upload, and observe what happens. In PlatformIO, the upload shortcut Ctrl+Alt+U makes this cycle quick.
🔗Add Serial Output: "Hello World"
Blinking an LED is great for visual feedback, but you often need to see text output from your program -- sensor readings, status messages, or debug information. The Serial Monitor is how you do that.
Modify your src/main.cpp to include serial communication:
#include <Arduino.h>
#define LED_PIN 2
void setup() {
pinMode(LED_PIN, OUTPUT);
Serial.begin(115200); // Start serial communication at 115200 baud
delay(1000); // Give the serial connection a moment to initialize
Serial.println("ESP32 Blink Sketch Started");
}
void loop() {
digitalWrite(LED_PIN, HIGH);
Serial.println("LED is ON");
delay(1000);
digitalWrite(LED_PIN, LOW);
Serial.println("LED is OFF");
delay(1000);
}🔗Understanding the Serial Code
Serial.begin(115200) -- Initializes serial communication at 115200 bits per second (baud). This speed must match the Serial Monitor setting. 115200 is the standard baud rate for ESP32.
Serial.println("text") -- Sends a line of text over the serial connection. println adds a newline at the end; print does not.
delay(1000) after Serial.begin() -- The ESP32's USB-serial connection can take a moment to stabilize after reset. Without this delay, the first few messages might be lost.
🔗Using the PlatformIO Serial Monitor
- Upload the sketch (
Ctrl+Alt+U) - Open the Serial Monitor by clicking the plug icon in the bottom toolbar (or press
Ctrl+Alt+S) - Make sure
monitor_speed = 115200is set in yourplatformio.ini(this is the PlatformIO equivalent of selecting the baud rate dropdown in Arduino IDE) - You should see:
ESP32 Blink Sketch Started
LED is ON
LED is OFF
LED is ON
LED is OFFIf you see garbled characters, check that monitor_speed in platformio.ini matches the value in your Serial.begin() call. If you see nothing at all, press the EN (reset) button on the ESP32 to restart the sketch from the beginning.
Tip: You can also open the Serial Monitor from the terminal with
pio device monitor. This is useful if you want to pass extra flags like--filter timeto add timestamps.
🔗Printing Variables
Serial.println() can also print numbers and variables, which is essential for debugging:
#include <Arduino.h>
int counter = 0;
void setup() {
Serial.begin(115200);
delay(1000);
}
void loop() {
counter++;
Serial.print("Loop count: ");
Serial.println(counter);
delay(1000);
}Output:
Loop count: 1
Loop count: 2
Loop count: 3
...Notice the difference between Serial.print() (no newline) and Serial.println() (adds a newline). Using print for the label and println for the value puts them on the same line.
🔗Blink an External LED
If your board does not have a built-in LED, or you want to control a separate one, you can connect an external LED to any available GPIO pin.
🔗What You Need
| Component | Quantity |
|---|---|
| LED (any color) | 1 |
| 220 $\Omega$ resistor | 1 |
| Jumper wires | 2 |
| Breadboard | 1 |
🔗Wiring
| ESP32 pin | Connects to |
|---|---|
| GPIO 13 | 220 $\Omega$ resistor, then to LED anode (long leg) |
| GND | LED cathode (short leg) |
The circuit is: GPIO 13 --> 220 $\Omega$ resistor --> LED anode (+) --> LED cathode (-) --> GND
Why a resistor? Without a current-limiting resistor, the LED would draw too much current from the GPIO pin. This could damage the LED, the ESP32, or both. A 220 $\Omega$ resistor limits the current to about $\frac{3.3 - 2.0}{220} \approx 6 \, \text{mA}$, which is safe and bright enough.
🔗Code for the External LED
#include <Arduino.h>
#define LED_PIN 13 // External LED on GPIO 13
void setup() {
pinMode(LED_PIN, OUTPUT);
Serial.begin(115200);
delay(1000);
Serial.println("External LED blink started");
}
void loop() {
digitalWrite(LED_PIN, HIGH);
Serial.println("LED ON");
delay(500);
digitalWrite(LED_PIN, LOW);
Serial.println("LED OFF");
delay(500);
}Change LED_PIN to whichever GPIO pin you connected your LED to.
🔗Common Issues
| Problem | Likely Cause | Solution |
|---|---|---|
| LED does not blink | Wrong pin number | Check your board's pinout; try GPIO 2 or your board's documented LED pin |
| LED stays on or off | LED in backwards | Flip the LED (long leg to resistor side) |
| Serial shows garbled text | Baud rate mismatch | Set monitor_speed = 115200 in platformio.ini |
| Nothing in Serial Monitor | Serial not initialized | Check Serial.begin(115200) is in setup() |
| Upload fails | Board in wrong state | Hold BOOT button during upload |
| Red squiggles in editor but code compiles | IntelliSense index stale | Build once, then run "PlatformIO: Rebuild IntelliSense Index" |
| "Port busy" on upload | Serial Monitor still open | Close the Serial Monitor (Ctrl+C) before uploading |
🔗Arduino IDE vs. PlatformIO: What Changed?
The code in this article is identical to the Arduino IDE version. The differences are entirely in the workflow:
| Aspect | Arduino IDE | PlatformIO |
|---|---|---|
| File location | .ino sketch file | src/main.cpp |
| Arduino header | Added automatically | Must write #include <Arduino.h> |
| Build | Upload button or Ctrl+U | Checkmark icon or Ctrl+Alt+B |
| Upload | Upload button or Ctrl+U | Arrow icon or Ctrl+Alt+U |
| Serial Monitor | Magnifying glass icon | Plug icon or Ctrl+Alt+S |
| Baud rate setting | Dropdown in Serial Monitor | monitor_speed in platformio.ini |
The C++ code inside setup() and loop() does not change at all.
🔗What You Learned
In this article you:
- Learned how the
setup()/loop()structure works in PlatformIO (same as Arduino IDE, plus#include <Arduino.h>) - Used
pinMode(),digitalWrite(), anddelay()to blink an LED - Built and uploaded code using the PlatformIO toolbar
- Used
Serial.begin(),Serial.print(), andSerial.println()to send debug text - Wired an external LED with a current-limiting resistor
These are building blocks you will use in every project from here on.
🔗Next Steps
Now that you can control a digital output (on/off), the Using the Serial Monitor article goes deeper into serial debugging features specific to PlatformIO -- including monitor filters, timestamps, and logging to files.