ESP32-S3 Lab · Day 28 of 30

Switch the light
from your phone

Today your words start moving hardware. You wire a single LED and upload a sketch that waits over Bluetooth for two exact commands — write led_on and it lights, led_off and it goes dark. Underneath, each word lands in a mailbox on the board, trips a callback, and drives the pin, so the board acts the instant your message arrives. Yesterday's pipe now carries orders.

About 25 minutesArduino firstMicroPython optionalSame one-LED circuit as Blink
Agent assist code TSK-DAY28-BTLED

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

Six things, most of them old friends from your first circuits. Tap Define on any part you haven't met — the answer opens as a field note you can read and dismiss without losing your place.

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

ESP32-S3 board

The brain that runs your uploaded sketch — its Bluetooth radio is built in.

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 light your phone will switch — it only works one way round.

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

220 Ω resistor

Sits in series with the LED to keep the current gentle.

Official manual image of a jumper wire.
Manual photo

2 jumper wires (M/M)

Two are enough today — the LED's feed and its return to ground.

Official manual screenshot of the Arduino IDE interface.
Manual screenshot

Arduino IDE

Uploads the sketch and opens Serial Monitor.

02 Make the physical circuit

Chart the circuit

The official Freenove diagram is your chart — schematic on top, the same circuit built on a breadboard below. Click it to enlarge. The circuit is the one-LED layout you know from Blink; the command now arrives by radio.

Official Freenove circuit — C Tutorial, Chapter 20 (Bluetooth), page 193.
LED long leg (+) GPIO 2 via 220 Ω The pin a matched command drives HIGH or LOW.
LED short leg (−) GND Completes the LED's path back to zero volts.

Mind the LED's legs. The LED only lights one way round — long leg toward GPIO 2 through the 220 Ω resistor, short leg to ground. Unplug USB before you move any wire.

03 One action at a time

Build it

This is the main path — you can finish the day without opening a single field note. Tap each step as you go to keep your place.

0 / 9 done
  1. Seat the ESP32-S3 on the GPIO extension board and keep USB unplugged while you wire.

  2. Place the LED so its long leg (+) is on the GPIO 2 side and its short leg (−) heads toward ground.

  3. Put the 220 Ω resistor in series between GPIO 2 and the LED's long leg.

  4. Compare every wire to the chart, then plug in USB.

  5. Open Sketch_20.2_BluetoothToLed.ino in Arduino IDE and upload it.

  6. Open Serial Monitor at 115200 and look for the "device started" line.

  7. On your phone, open LightBlue (Android) and connect to ESP32S3_Bluetooth, exactly as you did on Day 27.

  8. Find the write characteristic, set the format to utf-string, and write led_on — underscore, exact spelling.

  9. Watch the LED light, then write led_off to put it out.

The phone is the helm now.

A word typed in the air just moved a pin. Head to Test & debug to confirm both commands.

04 Read just enough code

Read the code

The BLE plumbing at the top of this sketch is Day 27's, line for line — the same service, the same name, and the same callback that copies each incoming write into rxload. Today's new idea lives in loop, where the board reads that buffer, matches it against two known commands, and drives the pin. Switch to MicroPython to see the same idea in Python — the wiring never changes.

Sketch_20.2_BluetoothToLed.ino
#define LED 2

void setup() {
  pinMode(LED, OUTPUT);
  setupBLE("ESP32S3_Bluetooth");
  Serial.begin(115200);
  Serial.println("\nThe device started, now you can pair it with Bluetooth!");
}

void loop() {
  long now = millis();
  if (now - lastMsg > 100) {
    if (deviceConnected && strlen(rxload) > 0) {
      if (strncmp(rxload, "led_on", 6) == 0) {
        digitalWrite(LED, HIGH);
      }
      if (strncmp(rxload, "led_off", 7) == 0) {
        digitalWrite(LED, LOW);
      }
      Serial.println(rxload);
      memset(rxload,0,sizeof(rxload));
    }
    lastMsg = now;
  }
}
setupBLE("ESP32S3_Bluetooth")Starts the same radio as Day 27 and registers the callback that fires on every write, so an arriving command lands in rxload without the loop asking for it.
strncmp(rxload, "led_on", 6) == 0Reads the mailbox and tests whether the first six characters spell led_on, underscore included — the match is what turns a received value into a command.
digitalWrite(LED, HIGH)The matched command drives GPIO 2 HIGH and the LED lights; led_off drives it LOW — the moment the phone's word becomes a voltage.
Serial.println(rxload)Echoes each received command to Serial Monitor, so you can watch the word arrive at the same instant the LED obeys.

05 Understand, don't memorise

How a command becomes an action

Day 27 opened a pipe: a value could travel from your phone into the board. Day 4 gave you digitalWrite, a value that moves a pin. Today the two ends meet. The write characteristic is a mailbox the phone drops a value into; a callback you registered is the doorbell that fires the instant it lands; and the bytes that arrive decide what the pin does. That is remote control, and the shape underneath is event-driven — the board stays ready and does work only when a message arrives.

Mailbox

The write characteristic

LightBlue writes your text into the board's RX characteristic, a named slot the board offers for exactly this. The phone drops a value in, and the board decides what that value means.

Doorbell

A callback fires

setupBLE handed the radio a standing instruction: when a write arrives, run this. The moment your phone sends, the radio runs it and copies the incoming bytes into rxload. The board waits, ready, and the arrival itself wakes it.

Match

Two known words

The loop reads rxload and compares it against led_on and led_off with strncmp. Whatever matches becomes a command, and the rest is text that scrolls past.

Act

The pin follows

A matched command calls digitalWrite on GPIO 2 — HIGH lights the LED, LOW puts it out. The value that left your phone is now a voltage on a wire.

The loop phone writes the characteristic → callback fires → match a known word → digitalWrite

Woken by the arrival

The radio holds your callback and fires it the instant a write lands, so the board spends its time ready and acts only when a message comes. That is event-driven control, and it scales — one board can hold many callbacks, each waiting for its own message.

Why spelling is the whole protocol

The board compares raw characters. led_on with an underscore is a command; any other spelling is text that arrives, gets echoed, and leaves the LED where it was. The meaning lives in the match, so both ends must agree on the words.

The radio only carries bytes

BLE moves the characters and nothing more. Which words count as commands, and what each one does, lives entirely in the sketch — the same bytes could dim a light, log a reading, or steer a motor.

06 Know it worked

Test & debug

The proof is the LED obeying your phone — Serial Monitor backs it up by echoing each command as it lands.

What you should see
LED
  • Write led_on in LightBlue (utf-string format) — the LED lights.
  • Write led_off — the LED goes dark.
  • Any other text leaves the LED exactly as it was — the sketch only acts on its two known commands.

Serial Monitor at 115200 shows "The device started, now you can pair it with Bluetooth!" on boot, then echoes every command the board receives.

If it doesn't
  • LED never reacts? Send the underscore spelling led_on exactly, in utf-string format.
  • LED wired but dead? Long leg to GPIO 2 through the 220 Ω resistor, short leg to GND.
  • Can't find the board? Repeat Day 27's LightBlue steps, and press reset before reconnecting.
  • Monitor blank? Set the baud rate to 115200.

07 Make the idea yours

Try this: map values to the light

Same working circuit, one new question — what should each value the phone sends mean? First watch which values the board acts on, then turn the light from a switch into a dial. Both fit inside today's 25 minutes.

Send values, watch the map

In LightBlue, write led_on and led_off, then near-misses like LED_ON, Led_on, and ledon. Serial Monitor echoes every one as it lands — proof the callback caught it — while the LED moves only for the two exact commands. You are watching the map from a received value to an action, where the board acts on the values it knows and lets the rest pass by.

Turn words into brightness

Attach GPIO 2 to a PWM channel once, then add a branch that reads the received text as a number from 0 to 255 and calls ledcWrite with it. Send 0, 64, and 255 from your phone and the light dims and brightens — the value you type maps straight onto brightness, so the phone becomes a dimmer.

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-DAY28-BTLED

How the agent should behave: guide one physical step at a time, wait for confirmation, and teach the control loop plainly — a value written into the characteristic trips a callback that drives the pin. Always check wiring, board, port, and USB before changing code, and use Serial Monitor to separate a delivery problem from a wiring one.

Keep your place

Finished Day 28?

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

Field note

Shortcut

Prompt copied