ESP32-S3 Lab · Day 5 of 30

Turn a press
into a lamp

Yesterday the LED glowed only while you held the button. Today it stays. The loop reads that button thousands of times a second, so the leap to a lamp is teaching it to fire on the single moment a press begins — the edge — to remember whether the light is on, and to shrug off the milliseconds a switch bounces as it closes. That's the jump from a raw button to a product.

About 25 minutesArduino firstMicroPython optionalSame circuit as Day 4
Agent assist code TSK-DAY05-LAMP

Hand this to an agent so it can pull the lesson packet and coach you step by step.

01 First, know the pieces

What you need

Nothing new to buy — this is the exact circuit you built on Day 4. If it's still wired up, you only need to upload a new sketch. Tap Define on anything you'd like refreshed.

Official manual photo of the ESP32-S3 development board.
Manual photo

ESP32-S3 board

The brain that runs your uploaded sketch.

Official manual image of the ESP32-S3 GPIO extension board.
Manual photo

GPIO extension board

Spreads the pins into rows you can reach and label.

Official manual photo of a red LED with its longer positive leg and shorter negative leg labelled.
Manual photo

LED

The lamp — same LED, same one-way rule as Day 4.

Official manual photo of a resistor with coloured value bands.
Manual photo

220 Ω resistor

Still in series with the LED, keeping the current gentle.

Official manual photo of a resistor with coloured value bands.
Manual photo

2 × 10 kΩ resistors

Hold the button's pin steady between presses.

Official manual photo of a four-pin push button switch with its pin pairs labelled 1 and 2.
Manual photo

Push button

The same switch — now it toggles instead of holds.

Official manual image of a jumper wire.
Manual photo

Jumper wires

Temporary, solder-free connections.

Official manual screenshot of the Arduino IDE interface.
Manual screenshot

Arduino IDE

Uploads the new lamp sketch to the board.

02 Make the physical circuit

Chart the circuit

This is the Day 4 circuit, unchanged. Click the chart to enlarge, or skip straight to the sketch if your breadboard is still assembled — everything new today lives in the code.

Official Freenove circuit — C Tutorial, Chapter 2 (Button & LED), page 48. The table lamp reuses Project 2.1's circuit.
LED long leg (+) GPIO 2 via 220 Ω The pin the code drives HIGH or LOW to hold the lamp on or off.
LED short leg (−) GND Completes the LED's path back to zero volts.
Push button GPIO 13 The pin the loop watches for the HIGH-to-LOW fall that marks a press.
10 kΩ resistor 3.3V Holds GPIO 13 steady at HIGH until a press pulls it LOW — so the fall is a clean edge.

Same circuit, same care. Nothing moves on the breadboard today. If you do rewire, keep the LED's long leg toward GPIO 2 through the 220 Ω resistor, and unplug USB first.

03 One action at a time

Build it

Mostly upload-and-watch — the wiring is yesterday's. Tap each step as you go to keep your place.

0 / 6 done
  1. Confirm the Day 4 circuit is still wired, or rebuild it from the chart.

  2. Keep USB unplugged until every wire matches the chart.

  3. Open Sketch_02.2_TableLamp.ino in Arduino IDE.

  4. Upload it to the ESP32-S3.

  5. Press the button once and let go — the LED should latch on.

  6. Press once more — it should turn off.

You built a lamp.

A momentary button now behaves like a real switch — firing on the edge of a press and holding its state. Head to Test & debug to confirm the toggle and check for bounce double-presses.

04 Read just enough code

Read the code

The circuit is the same; the loop is what changed. It now catches the press at its edge, waits out the switch's bounce, and inverts the LED's remembered state on one clean press. Switch to MicroPython to see the same idea in Python.

Sketch_02.2_TableLamp.ino
void loop() {
  if (digitalRead(PIN_BUTTON) == LOW) {
    delay(20);                       // wait out the chatter
    if (digitalRead(PIN_BUTTON) == LOW) {
      reverseGPIO(PIN_LED);          // clean press -> flip the LED
    }
    while (digitalRead(PIN_BUTTON) == LOW);   // hold here until release
  }
}

void reverseGPIO(int pin) {
  digitalWrite(pin, !digitalRead(pin));
}
delay(20)Waits out the switch's physical bounce, so the recheck below reads a settled level rather than mid-bounce noise.
reverseGPIO(PIN_LED)Inverts the LED's own current level — on becomes off, off becomes on. That level is the state the lamp keeps between loop passes.
while (digitalRead(PIN_BUTTON) == LOW);Holds here for the whole press, so the long LOW of your finger is consumed once. The loop reacts to the falling edge, then waits for release.

05 Understand, don't memorise

Level, edge, and the bounce between them

A press feels instant, but to the board it is a long stretch of time the loop reads over and over. Making one press flip the lamp exactly once means seeing the difference between the pin's steady level and the single moment it changes, holding the light's on/off state between loop passes, and getting past the switch's mechanical bounce. Those three ideas are the whole lesson.

Level

What the loop sees

loop() reads GPIO 13 thousands of times a second. At rest it reads HIGH; through the entire press it reads LOW. That reading is the level — the pin's state right now.

The trap

A level flips forever

Flip the lamp whenever the pin reads LOW and it flips on every pass while you hold — hundreds of times before you let go. The level alone can't tell a tap from a long hold.

Edge

The moment of change

What you mean by a press is the edge: the single HIGH-to-LOW fall when the contact closes. One press has exactly one falling edge, so acting on the edge acts once.

Bounce

The edge arrives messy

The switch's metal contacts spring apart and back as they meet, so that one clean edge lands as a burst of edges over a few milliseconds. Left alone, one press reads as several.

Memory

Where on/off lives

The LED's own driven level is the remembered state. It stays put between loop passes, and each clean edge inverts it — so the lamp holds long after your finger is gone.

The model one clean falling edge → invert the remembered state → the lamp holds

Why a press reads LOW

The 10 kΩ resistor ties GPIO 13 to HIGH at rest, and a press pulls it to ground. So a press is a fall from HIGH to LOW — which is exactly the edge the sketch watches for.

How this sketch catches the edge

It acts the instant it first reads LOW, then the wait-until-release line holds through the rest of the press. The long LOW of your hold is spent there and never counted again — one press in, one flip out.

Why the contact bounces at all

The two metal contacts do not settle the instant they touch; they physically rebound a few times in a couple of milliseconds, like a dropped ball. The 20 ms pause lets them come to rest before the code rechecks.

06 Know it worked

Test & debug

Nothing prints today — the proof is whether the lamp stays without your finger.

What you should see
Lamp
  • Press once and release — the LED latches on and stays on.
  • Press once more — it turns off and stays off.
  • Each full press flips the light exactly once, cleanly.

A single press that flips twice is the switch's bounce slipping past — the 20 ms pause is what holds it back, which is exactly what the challenge lets you observe.

If it doesn't
  • One press flips it twice? A bounce edge got through — confirm the 20 ms debounce delay and the wait-until-release line are both present.
  • Nothing latches? Re-run Day 4's checks — LED direction, the resistors, GPIO 2 and 13.
  • LED flickers while held? The loop is following the LOW level all through the hold — you're likely on the Day 4 sketch, so upload Sketch_02.2_TableLamp.
  • Upload fails? Swap in a data-capable USB cable.

07 Make the idea yours

Try this: make the level and the bounce visible

The two ideas behind the lamp are easy to see once you take each guard away for a moment. Work on a copy of the sketch, run both experiments, then restore the original — it fits inside today's time.

Watch the loop read the level

Comment out the while (digitalRead(PIN_BUTTON) == LOW); line, upload, and hold the button down. The LED strobes — the loop now flips on every pass it reads LOW, hundreds of times through your hold. Uncomment the line and a hold gives one clean flip again, because the loop acts on the falling edge and then waits for release.

Catch the bounce

Put that line back, then change delay(20) to delay(0) so nothing waits out the bounce. Press slowly a dozen times — now and then one press flips the lamp twice, because the board caught the contact's rebound as a second edge. Restore delay(20) and the double-flips stop.

08 Learn it with a hand on the tiller

Coach me through it

Every lesson ships with a code and a machine-readable packet, so an agent can guide you with full context.

Lesson code

TSK-DAY05-LAMP

How the agent should behave: keep it to upload-and-watch on the existing circuit, and teach the real idea — the pin's steady level versus the edge of a press, the on/off state held across loops, and why a switch bounces. Check wiring, board, port, and USB before changing code.

Keep your place

Finished Day 5?

Mark it complete — it shows on your course map, and your place is saved on this device.

Field note

Shortcut

Prompt copied