IoTSimulator

Blink an LED with an ESP32-C3

Connect a blue LED and a resistor to an ESP32-C3 DevKit to make it blink once per second while streaming live messages to the serial console. Learn how 3.3V digital outputs work on a modern 32-bit RISC-V microcontroller.
Muhammad Ichsanul Fadhil
IoTSim Editor
September 26, 2026
Blink an LED with an ESP32-C3

Live project track

Interactive hardware & logic preview

Every microcontroller project begins with the classic 'Hello World' of electronics: making an LED blink. It is the fastest, clearest way to verify that your development board is alive, your wiring is correct, and your firmware is running. On commercial IoT gadgets, this same blinking signal functions as a heartbeat indicator to show that the system is operating normally.

In this project, you will build a digital output circuit using an ESP32-C3 DevKit, a 220Ω resistor, and a blue 5mm LED. The main idea is that the ESP32-C3 controls GPIO 7 as a digital output, switching between 3.3 volts (HIGH) and 0 volts (LOW) every half second while printing live timestamps to the serial monitor.

How Digital Pins Control an LED

General Purpose Input/Output (GPIO) pins are the primary way a microcontroller interacts with external electronics. When configured as an output, a pin can be switched electronically between two voltages: 3.3V (HIGH) or 0V (LOW). Setting the pin to HIGH delivers current to turn the LED on, while setting it to LOW cuts off the current to turn the LED off.

ESP32-C3 digital output driving a resistor and blue LED
ESP32-C3 digital output driving a resistor and blue LED

The diagram above illustrates the complete current loop. When GPIO 7 is set to HIGH, current flows out of the pin, passes through the 220Ω resistor, enters the blue LED's anode (+), and returns through the cathode (−) into GND. The resistor is essential: without it, too much current would rush through the LED, risking permanent damage to both the LED and the microcontroller pin.

Pin stateOutput voltageCurrent flowLED appearance
HIGH3.3 VCurrent flows through resistor and LED to GNDIlluminated (ON)
LOW0.0 VNo voltage difference across the circuitDark (OFF)

Unlike older 5V boards such as the classic Arduino Uno, modern microcontrollers like the ESP32-C3 operate on 3.3V logic. Blue LEDs typically need around 3.0 to 3.1 volts to illuminate. With a 3.3V supply and a 220Ω resistor, the LED shines brightly while drawing less than 1 milliamp of current.

Understanding Blink Timing and Serial Logs

Blinking requires precise timing. By setting the pin HIGH for 500 milliseconds and LOW for 500 milliseconds, the circuit completes one full ON/OFF cycle every 1000 milliseconds (exactly 1.0 second). In physics terms, this repeating rhythm has a frequency of 1.0 Hertz.

Square wave output timing waveform and live serial monitor logs
Square wave output timing waveform and live serial monitor logs

The visual above shows the output voltage waveform alongside the serial monitor terminal. Every time the pin toggles, the firmware prints a timestamped message over the USB cable at 115200 baud. This allows you to verify that your code is executing on schedule, even when working on headless devices without an attached screen.

Cycle phaseDurationVoltage on GPIO 7Serial log message
Pulse ON500 ms3.3 V[T+Xms] LED Status: ON (GPIO 7 -> HIGH)
Pulse OFF500 ms0.0 V[T+Xms] LED Status: OFF (GPIO 7 -> LOW)
Total Period1000 ms (1.0 s)1.0 Hz CycleOne full blink repetition

Using millis() in the log statements lets you see the exact number of milliseconds since the microcontroller powered on, providing an accurate timestamp for every event.

Connecting the Circuit

Wiring an LED to a microcontroller is like connecting a water pipe to a delicate water wheel. You need a restriction in the pipe so the water does not rush through at full speed and destroy the wheel.

Intuitive water pipe analogy showing how a resistor narrows the electrical flow to protect the LED
Intuitive water pipe analogy showing how a resistor narrows the electrical flow to protect the LED

The 220Ω resistor acts as that narrow constriction, keeping the current safely at around 15 milliamps. Wire the LED and resistor to GPIO 7 as detailed in the connection matrix below:

Pin Connection Map
Blue LED & Resistor
220Ω Resistor Lead 1
→
ESP32-C3 DevKit
GPIO 7 (IO7)
Explanation
Switched 3.3V digital output from the microcontroller.
Blue LED & Resistor
220Ω Resistor Lead 2
→
ESP32-C3 DevKit
LED Anode (Long Leg +)
Explanation
Passes current safely through the series resistor into the LED.
Blue LED & Resistor
LED Cathode (Short Leg −)
→
ESP32-C3 DevKit
GND (Pin GND.2)
Explanation
Completes the ground return path to the ESP32-C3.

Remember that LEDs are polarized components: current can only pass through them in one direction. If you accidentally plug the LED in backwards, no current will flow and the light will remain completely dark.

Complete Code

Upload the following sketch to your ESP32-C3 DevKit. It configures the pin, initializes serial communication, and blinks the LED in a continuous loop.

C++ Source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// ============================================================================
// ESP32-C3 DevKit RISC-V: Digital Output & Status Indicator Blinking
// Target Pin: GPIO 7 (IO7 on right pin header)
// ============================================================================

#define LED_PIN 7
const unsigned long BLINK_INTERVAL_MS = 500;

void setup() {
  // Configure GPIO 7 as push-pull digital output
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
  
  // Initialize Serial Monitor at 115200 baud
  Serial.begin(115200);
  delay(200); // Brief settling delay for USB-CDC
  Serial.println(F("==========================================="));
  Serial.println(F("  ESP32-C3 RISC-V LED Blink Initialized    "));
  Serial.println(F("  Microcontroller Clock: 160 MHz           "));
  Serial.println(F("  Target Pin: GPIO 7 (Output)             "));
  Serial.println(F("==========================================="));
}

void loop() {
  // Drive GPIO high (3.3V)
  digitalWrite(LED_PIN, HIGH);
  Serial.print(F("[T+"));
  Serial.print(millis());
  Serial.println(F("ms] LED Status: ON (GPIO 7 -> HIGH)"));
  delay(BLINK_INTERVAL_MS);
  
  // Drive GPIO low (0V GND)
  digitalWrite(LED_PIN, LOW);
  Serial.print(F("[T+"));
  Serial.print(millis());
  Serial.println(F("ms] LED Status: OFF (GPIO 7 -> LOW)"));
  delay(BLINK_INTERVAL_MS);
}

How the Code Works, Part by Part

The program follows the standard Arduino structure with initialization in setup() and continuous execution in loop().

▸ Pin Declarations and Timing Constants

The sketch begins by declaring which pin to control and defining how long each blink phase should last.

C++ Source
1
2
#define LED_PIN 7
const unsigned long BLINK_INTERVAL_MS = 500;

Using #define assigns the pin number once at the top of the sketch. If you move your wire to a different GPIO pin later, you only have to change this single line.

▸ Initializing GPIO and Serial Monitor in setup()

Inside setup(), the microcontroller configures GPIO 7 as an active output and starts the serial communication channel.

C++ Source
1
2
3
4
5
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);

Serial.begin(115200);
delay(200);

The pinMode(LED_PIN, OUTPUT) command configures the internal transistors on GPIO 7 so they can supply power to external circuits. Initializing the pin to LOW ensures the LED starts in a known off state.

▸ Blinking and Timestamp Logging in loop()

The loop() function runs over and over for as long as the board has power.

C++ Source
1
2
3
4
5
6
7
8
9
10
11
digitalWrite(LED_PIN, HIGH);
Serial.print(F("[T+"));
Serial.print(millis());
Serial.println(F("ms] LED Status: ON (GPIO 7 -> HIGH)"));
delay(BLINK_INTERVAL_MS);

digitalWrite(LED_PIN, LOW);
Serial.print(F("[T+"));
Serial.print(millis());
Serial.println(F("ms] LED Status: OFF (GPIO 7 -> LOW)"));
delay(BLINK_INTERVAL_MS);

The delay() function halts execution for 500 milliseconds between state changes. The F() macro stores text strings directly in flash program memory, saving valuable RAM on the microcontroller.

Fixing Common Problems

If your circuit does not light up or the serial terminal stays blank, check the quick solutions below.

What you seeLikely causeWhat to try
LED stays completely offLED plugged in backwards or wrong pinVerify the long leg of the LED faces GPIO 7 through the resistor; confirm wire is in IO7
Serial monitor shows unreadable gibberishBaud rate mismatch in serial monitorChange the serial monitor baud rate dropdown to 115200 baud
LED is extremely dim, barely visibleResistor value too large (e.g., 220 kΩ instead of 220Ω)Check resistor color bands: Red-Red-Brown represents 220Ω
No serial output appears on board resetUSB CDC option disabled in board settingsIn Arduino IDE Tools menu, set USB CDC On Boot: Enabled

A common pitfall with the ESP32-C3 is the serial baud rate. Unlike traditional 9600 baud Arduino Uno projects, ESP32 boards run at 115200 baud by default to handle fast diagnostic messages.

Try It in the Simulator

Click the Start Simulation (▶) button in the top toolbar to begin. The blue LED will immediately start blinking once per second. Click on the Serial Monitor tab in the bottom panel to watch the live timestamped logs stream in as each blink occurs!

Keywords
#ESP32-C3 #LED #RISC-V #GPIO #Beginner #Digital Output #Hardware Basics
Total word count: 1068 words

Project discussion

Questions, feedback, and community insights
No discussions yet. Be the first to start!