Tutorial 4 - Button Input

Introduction

In many electronic projects, buttons are a common way to provide user input.
In this tutorial, we’ll learn how to use a button with the ESP32-S3 Pico microcontroller board.

We’ll write a MicroPython program that reads the button’s state connected to one of the GPIO pins and prints a message when it is pressed.

YouTube Video


Components Needed

ComponentQuantity
ESP32-S3 Pico1
USB-C Cable1
Breadboard1
Jumper WiresSeveral
Push Button1

Fritzing Diagram - Active High

Button with pull-down resistor
Button with internal pull-down resistor (Active High)

Code (Active High)

from machine import Pin
import utime

button = Pin(40, Pin.IN, Pin.PULL_DOWN)

while True:
    if button.value() == 1:
        print("You pressed the button!")
        utime.sleep(1)

Fritzing Diagram - Active Low

Button with pull-up resistor
Button with internal pull-up resistor (Active Low)

Code (Active Low)

from machine import Pin
import utime

button = Pin(40, Pin.IN, Pin.PULL_UP)

while True:
    if button.value() == 0:
        print("You pressed the button!")
        utime.sleep(1)

Code Explanation

from machine import Pin
import utime

We import the Pin class from the machine module to control the GPIO pins on the ESP32-S3 Pico,
and the utime module to add delays in the program.

button = Pin(40, Pin.IN, Pin.PULL_UP or Pin.PULL_DOWN)

This sets up Pin 40 as an input.

  • Pin.IN means the pin is configured as an input.
  • Pin.PULL_DOWN means the pin defaults to 0 (LOW) until pressed.
  • Pin.PULL_UP means the pin defaults to 1 (HIGH) until pressed.

You can choose either setup depending on your circuit wiring.

while True:
    if button.value() == 1:
        print("You pressed the button!")
        utime.sleep(1)

This infinite loop checks the button state:

  • With PULL_DOWN, the button reads 1 when pressed.
  • With PULL_UP, the button reads 0 when pressed.

When pressed, it prints "You pressed the button!" and waits one second before checking again.

✅ The program will continuously monitor the button and react whenever you press it.

Knight Rider Code (Button Update)

In the previous tutorial, we built the Knight Rider / LiDAR Scanner effect using a row of LEDs that sweep left and right. Now, we’ll make it more interactive by adding two buttons that let us control the speed of the LED movement.

Knight Rider with Button Control
Buttons added to Knight Rider LEDs
from machine import Pin
import utime

# LED pins left → right
LED_PINS = [33, 34, 35, 36, 37, 38, 39, 40]

# Delay between steps (starting value)
STEP_DELAY = 0.05 

# Setup LEDs
leds = [Pin(p, Pin.OUT) for p in LED_PINS]
n = len(leds)

# Setup buttons (active-low with pull-ups)
btn_slower = Pin(17, Pin.IN, Pin.PULL_UP)   # Button makes STEP_DELAY larger
btn_faster = Pin(18, Pin.IN, Pin.PULL_UP)   # Button makes STEP_DELAY smaller

# Speed limits
MIN_DELAY = 0.01
MAX_DELAY = 0.3
STEP_CHANGE = 0.01   # amount to change per button press

def all_off():
    for led in leds:
        led.value(0)

def check_buttons():
    global STEP_DELAY
    if btn_slower.value() == 0:  # pressed
        STEP_DELAY = min(STEP_DELAY + STEP_CHANGE, MAX_DELAY)
        utime.sleep(0.2)  # debounce delay
    if btn_faster.value() == 0:  # pressed
        STEP_DELAY = max(STEP_DELAY - STEP_CHANGE, MIN_DELAY)
        utime.sleep(0.2)  # debounce delay

while True:
    # Left to right
    for i in range(n):
        check_buttons()  # check before each step
        all_off()
        leds[i].value(1)
        if i > 0:
            leds[i-1].value(1)
        utime.sleep(STEP_DELAY)

    # Right to left
    for i in range(n-1, -1, -1):
        check_buttons()
        all_off()
        leds[i].value(1)
        if i < n-1:
            leds[i+1].value(1)
        utime.sleep(STEP_DELAY)