LED on Button Press

Let's build a simple project that turns on an LED whenever the button is pressed. This combines everything we've learned about GPIO input and output.

Hardware Setup

ComponentPico PinNotes
Button (one side)GPIO 15Input pin
Button (other side)GNDGround connection
LED Anode (via 330Ω)GPIO 13Output pin
LED CathodeGNDGround connection
Button and LED circuit

Complete button + LED circuit

Button as Input

We configure GPIO 15 as an input with internal pull-up enabled. One side of the button connects to GPIO 15, the other to ground. When pressed, the pin is pulled LOW.

rust
let button = Input::new(p.PIN_15, Pull::Up);

// Pull-up means:
// - Button not pressed = HIGH (pulled up to 3.3V)
// - Button pressed = LOW (connected to GND)

LED as Output

We configure GPIO 13 as an output, starting in the LOW state (off). You can also use the onboard LED on the Pico 2 W, but that requires the cyw43 driver.

rust
// External LED on GPIO 13
let mut led = Output::new(p.PIN_13, Level::Low);

// For Pico 2 W onboard LED, you need cyw43 driver
// (covered in WiFi chapter)

Main Loop Logic

In the main loop, we constantly check if the button is pressed. When LOW (pressed), we turn on the LED for 3 seconds. Otherwise, the LED stays off. A small 5ms delay prevents overwhelming the system.

rust
loop {
    if button.is_low() {
        // Button is pressed (LOW with pull-up)
        defmt::info!("Button pressed");
        led.set_high();
        Timer::after_secs(3).await;
    } else {
        // Button not pressed
        led.set_low();
    }
    
    Timer::after_millis(5).await;
}
Note

Debouncing: If you reduce the delay, you might notice that a single button press triggers multiple detections. This is called "button bounce" - the metal contacts briefly bounce when pressed, creating multiple electrical signals.

In this example, the 3-second LED delay effectively masks bounce issues. For applications where you need to count individual presses accurately, you'll need debouncing logic (typically 10-50ms delay after detecting a press).

Complete Code

rust
#![no_std]
#![no_main]

use embassy_executor::Spawner;
use embassy_rp::gpio::{Input, Level, Output, Pull};
use embassy_rp::{self as hal, block::ImageDef};
use embassy_time::Timer;

use panic_probe as _;
use defmt_rtt as _;

#[unsafe(link_section = ".start_block")]
#[used]
pub static IMAGE_DEF: ImageDef = hal::block::ImageDef::secure_exe();

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    let p = embassy_rp::init(Default::default());
    
    // Button on GPIO 15 with pull-up
    let button = Input::new(p.PIN_15, Pull::Up);
    
    // LED on GPIO 13 (or use GPIO 25 for non-W Pico)
    let mut led = Output::new(p.PIN_13, Level::Low);
    
    loop {
        if button.is_low() {
            defmt::info!("Button pressed");
            led.set_high();
            Timer::after_secs(3).await;
        } else {
            led.set_low();
        }
        
        Timer::after_millis(5).await;
    }
}

Running the Program

bash
# Create new project
cargo generate --git https://github.com/ImplFerris/pico2-template.git

# Build and flash
cargo run --release

# With debug probe
cargo embed --release

Press the button and the LED should turn on for 3 seconds. This demonstrates basic interactive embedded programming where hardware input controls hardware output.