Your First Arduino Program: Blink an LED

Every embedded project starts the same way: configure a pin as an output, drive it HIGH, drive it LOW, repeat. The Arduino Blink program does exactly that with one LED, one resistor, and a few lines of code. It also confirms that your board, USB cable, driver, and IDE all work together. This guide covers the built-in LED method, an external LED circuit, the resistor math, a non-blocking version, and a register-level version for readers who want to see what digitalWrite() hides.

Quick Takeaways

  • Blink needs only two functions: pinMode() to set direction and digitalWrite() to set the pin level.
  • Always put a 220Ω–330Ω resistor in series with an external LED on a 5V Arduino pin.
  • The Uno’s onboard LED sits on pin 13, which the LED_BUILTIN constant maps for you.
  • Replace delay() with millis() timing as soon as your sketch needs to do more than one thing.
Spec Value (Arduino Uno R3)
Microcontroller ATmega328P
Clock speed 16MHz
Logic level 5V
Max per-pin current (recommended) 20mA (40mA absolute max)
Onboard LED pin D13 (PB5)
Programming interface USB (UART bridge)

What You Need

Component Quantity Notes
Arduino Uno (or compatible) 1 Nano and Mega work with the same code
USB cable (Type-B for Uno) 1 Must carry data, not just power
5mm LED (red) 1 Forward voltage about 2.0V
220Ω resistor 1 Red-red-brown bands
Breadboard 1 Half-size is enough
Jumper wires 2 Male-to-male

Install the free Arduino IDE (2.x) from arduino.cc before you start. Plug the board in, then pick your board and port under Tools.

Step 1: Blink the Built-In LED

The Uno has a small LED soldered to D13, already wired through its own resistor. You need no extra parts to test it.

  1. Connect the board over USB.
  2. Open File → Examples → 01.Basics → Blink.
  3. Select Tools → Board → Arduino Uno and choose the correct Port.
  4. Click Upload.

The L LED near pin 13 should flash once per second. If it does, your toolchain works.

The Blink Code, Line by Line

// Blink: toggles the onboard LED once per second.

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);   // Configure pin 13 as an output (writes the DDR register bit)
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH); // Drive pin to 5V: LED turns on
  delay(1000);                     // Hold for 1000 ms
  digitalWrite(LED_BUILTIN, LOW);  // Drive pin to 0V: LED turns off
  delay(1000);                     // Hold for 1000 ms
}
  • setup() runs once after power-up or reset.
  • loop() runs forever after setup() finishes.
  • LED_BUILTIN is a board-defined constant. It equals 13 on the Uno, so the same sketch ports to other boards.
  • delay(1000) halts the CPU’s sketch logic for 1000 milliseconds.

Step 2: Wire an External LED

The onboard LED proves the board works. An external circuit teaches you the real skill: switching a load from a GPIO pin.

Understand LED Polarity

An LED is a diode. Current flows in one direction only.

  • Anode (+): the longer leg. Connect it toward the resistor and the signal.
  • Cathode (−): the shorter leg, next to the flat edge of the plastic lens. Connect it to GND.

Reverse the LED and nothing lights, but nothing breaks either at 5V.

Calculate the Resistor

An LED has almost no resistance once it conducts. Without a resistor, the pin sources current until something fails. Use Ohm’s law:

R = (Vsupply − Vf) / I

For a red LED at 5V with a target of 15mA:

R = (5V − 2.0V) / 0.015A = 200Ω

The nearest standard value is 220Ω, which gives about 13.6mA. That is bright and safely under the 20mA pin rating.

LED Color Typical Vf Resistor for ~10mA at 5V Nearest Standard Value
Red 2.0V 300Ω 330Ω
Yellow 2.1V 290Ω 330Ω
Green 2.2V 280Ω 270Ω
Blue 3.2V 180Ω 180Ω
White 3.2V 180Ω 180Ω

Forward voltage varies by manufacturer. Check the datasheet when you can. A higher resistor value only dims the LED, so rounding up is always safe.

Build the Circuit

Power the board off (unplug USB) before wiring.

  1. Insert the LED into the breadboard with legs in separate rows.
  2. Connect a 220Ω resistor from the LED’s anode row to an empty row.
  3. Run a jumper from that empty row to D8.
  4. Run a jumper from the LED’s cathode row to a GND pin on the Arduino.

The signal path is: D8 → 220Ω resistor → LED anode → LED cathode → GND.

The resistor can sit on either side of the LED. The current is the same everywhere in a series loop.

Upload the External LED Sketch

// Blink an external LED on digital pin 8.

const uint8_t LED_PIN = 8;       // Pin connected to the resistor/LED chain
const uint16_t BLINK_MS = 500;   // Half-period in milliseconds

void setup() {
  pinMode(LED_PIN, OUTPUT);      // D8 becomes a push-pull output
}

void loop() {
  digitalWrite(LED_PIN, HIGH);   // ~5V on the pin: current flows through the LED
  delay(BLINK_MS);
  digitalWrite(LED_PIN, LOW);    // 0V on the pin: current stops
  delay(BLINK_MS);
}

Using named constants instead of bare numbers makes the sketch easier to change later. Change BLINK_MS to 100 for a fast flicker, or 2000 for a slow pulse.

Step 3: Stop Blocking the CPU with millis()

delay() is simple, but it blocks. While it runs, your sketch cannot read a button, poll a sensor, or handle serial data. The fix is to compare timestamps from millis(), which returns milliseconds since power-up.

// Non-blocking blink using millis().

const uint8_t LED_PIN = 8;
const unsigned long INTERVAL_MS = 500;

unsigned long previousMillis = 0;  // Timestamp of the last toggle
bool ledState = LOW;               // Current LED level

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  unsigned long now = millis();    // Read the timer once per pass

  // Subtraction stays correct even when millis() rolls over (~49.7 days)
  if (now - previousMillis >= INTERVAL_MS) {
    previousMillis = now;          // Save the time of this toggle
    ledState = !ledState;          // Flip the state
    digitalWrite(LED_PIN, ledState);
  }

  // Other tasks can run here without waiting on the LED
}
Method CPU Blocked? Code Complexity Best For
delay() Yes Lowest First sketches, quick tests
millis() No Low Multitasking, button + LED sketches
Timer interrupt No Medium Precise timing, low-power designs

Declare timestamp variables as unsigned long. Using int overflows after about 32 seconds and breaks the timing.

Step 4: See What digitalWrite() Does (Register-Level Blink)

On the ATmega328P, pin 13 is port PB5. Three registers control a port:

  • DDRB: data direction. A 1 makes the pin an output.
  • PORTB: output latch. A 1 drives the pin high.
  • PINB: input state. Writing a 1 here toggles the matching PORTB bit on this chip.
// Direct port manipulation: blinks pin 13 (PB5) without digitalWrite().

void setup() {
  DDRB |= (1 << DDB5);     // Set bit 5 of DDRB: PB5 (pin 13) becomes an output
}

void loop() {
  PORTB |= (1 << PORTB5);  // Set bit 5 of PORTB: pin 13 goes HIGH
  delay(500);
  PORTB &= ~(1 << PORTB5); // Clear bit 5 of PORTB: pin 13 goes LOW
  delay(500);
}

Direct register writes finish in a couple of clock cycles at 16MHz. A digitalWrite() call takes several microseconds because it looks up the pin, checks for PWM, and disables interrupts briefly. For a 1Hz blink the difference is invisible. For bit-banged protocols it matters.

Built-In LED Pins Across Common Boards

LED_BUILTIN hides these differences, but the table shows why you should use it.

Board Onboard LED Pin Logic Level Notes
Arduino Uno R3 D13 5V Shares SPI SCK
Arduino Nano D13 5V Same as the Uno
Arduino Mega 2560 D13 5V Same as the Uno
ESP32 DevKit (typical) GPIO2 3.3V Varies by board vendor
Raspberry Pi Pico GP25 3.3V Pico W routes its LED through the wireless chip

On 3.3V boards, recalculate the resistor. A red LED at 3.3V with 10mA needs about 130Ω, so use 150Ω.

Real-World Project: A Heartbeat Status Indicator

A blinking LED is more useful than it looks. Professional firmware often uses one as a heartbeat to show the main loop is alive. A frozen LED means a hung program.

Extend the millis() sketch with a fault pattern:

// Heartbeat LED: slow blink = normal, fast blink = fault.

const uint8_t LED_PIN = 8;
const uint8_t FAULT_INPUT = 2;     // Pull to GND to simulate a fault

unsigned long lastToggle = 0;
bool ledState = LOW;

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(FAULT_INPUT, INPUT_PULLUP); // Internal ~20-50k pull-up: HIGH when open
}

void loop() {
  // LOW on the input means a fault: blink every 100 ms, else every 1000 ms
  unsigned long interval = (digitalRead(FAULT_INPUT) == LOW) ? 100 : 1000;

  if (millis() - lastToggle >= interval) {
    lastToggle = millis();
    ledState = !ledState;
    digitalWrite(LED_PIN, ledState);
  }
}

Wire a jumper from D2 to GND to trigger the fast blink. The INPUT_PULLUP mode removes the need for an external pull-up resistor.

Troubleshooting: LED Won’t Blink

Symptom Likely Cause Fix
Upload fails with “port not found” Charge-only USB cable or missing driver Swap the cable and reinstall the CH340/FTDI driver on clone boards
Upload works, external LED dark LED reversed Flip the anode and cathode
LED dark, wiring looks correct Wrong pin number in code Match LED_PIN to the jumper location
LED very dim Resistor too large (e.g., 10kΩ) Check the color bands, use 220Ω–330Ω
Onboard LED blinks, external does not Open breadboard connection Press the parts in and check rail splits
Blink timing looks wrong Wrong board selected in the IDE Re-select the board under Tools

Measure with a multimeter if you have one. With the sketch running at HIGH, you should read about 5V at D8 relative to GND, and roughly 2V across a red LED.

Safe Practices

  • Never connect an LED directly to a pin or to 5V without a series resistor.
  • Do not draw more than 20mA from any one pin or about 200mA total from the chip.
  • To drive larger loads such as LED strips or relays, use a MOSFET or BJT switch instead of the pin itself.
  • Disconnect USB before changing wiring.

FAQ

Why does my Arduino LED need a resistor?

An LED limits nothing on its own. Once the supply passes its forward voltage (about 2V for red), current rises sharply. A series resistor sets the current, typically 10–15mA, which protects both the LED and the pin’s 20mA rating.

What does pin 13 do on an Arduino Uno?

Pin 13 (port PB5) is a normal digital I/O pin that also carries the SPI SCK signal. The board connects the onboard L LED to it, so any sketch that toggles pin 13 flashes that LED.

How do I make the LED blink faster or slower?

Change the value passed to delay() or the INTERVAL_MS constant. The value is in milliseconds, so 250 gives a 2Hz blink (250 ms on, 250 ms off) and 1000 gives 0.5Hz.

Can I blink an LED without using delay()?

Yes. Compare millis() against a stored timestamp and toggle the pin when the interval has passed. This keeps the CPU free to read sensors and buttons while the LED blinks.

Hot this week

Logic Analyzers Explained: See Digital Signals Clearly

Learn how a logic analyzer works, how to choose one, and how to decode I2C, SPI, and UART. Read the guide and debug your circuits faster.

Oscilloscopes for Beginners: What to Buy and How to Use One

New to oscilloscopes? Compare the best beginner scopes, learn the controls, and take your first measurement in 10 minutes. Start debugging today.

Raspberry Pi vs Arduino vs ESP32: Choosing the Right Platform

Compare Raspberry Pi, Arduino, and ESP32 on specs, power draw, price, and use cases. Find the right board for your build and start today.

Build a Weather Station: Sensors, Logging, and Dashboards

Build an ESP32 weather station with a BME280, MQTT logging to InfluxDB, and a Grafana dashboard. Wiring, code, and calibration tips. Start building.

Arduino IDE 2 Setup: Install, Configure, and Upload Your First Sketch

Install Arduino IDE 2, add board cores, select the port, and upload Blink in minutes. Follow this step-by-step setup guide and flash your first board.

Topics

Logic Analyzers Explained: See Digital Signals Clearly

Learn how a logic analyzer works, how to choose one, and how to decode I2C, SPI, and UART. Read the guide and debug your circuits faster.

Oscilloscopes for Beginners: What to Buy and How to Use One

New to oscilloscopes? Compare the best beginner scopes, learn the controls, and take your first measurement in 10 minutes. Start debugging today.

Raspberry Pi vs Arduino vs ESP32: Choosing the Right Platform

Compare Raspberry Pi, Arduino, and ESP32 on specs, power draw, price, and use cases. Find the right board for your build and start today.

Build a Weather Station: Sensors, Logging, and Dashboards

Build an ESP32 weather station with a BME280, MQTT logging to InfluxDB, and a Grafana dashboard. Wiring, code, and calibration tips. Start building.

Arduino IDE 2 Setup: Install, Configure, and Upload Your First Sketch

Install Arduino IDE 2, add board cores, select the port, and upload Blink in minutes. Follow this step-by-step setup guide and flash your first board.

Desoldering Done Right: Braid, Pumps, and Hot Air

This guide covers solder braid, solder suckers, and hot air rework, with temperatures, technique, and a decision table. Read now.

Subqueries vs CTEs: Writing Cleaner SQL

Compare SQL CTEs and subqueries with code, performance notes, and a decision table. Learn when each wins, then refactor your queries today.

Cohort Analysis: Understand Retention Over Time

Learn cohort analysis in Python with Pandas. Build retention tables, heatmaps, and churn insights step by step. Copy the code and start analyzing today.

Related Articles

Popular Categories