ESP32-S3 Lab · Day 6 of 30

Ten lights
that flow

Ten outputs is where copy-paste stops scaling. So today you meet the fix you will use for every many-part circuit after this — put the pins in an array and let one loop walk it. Wire ten segments, upload, and watch a single lit segment sweep down the bar and back. The motion is nothing but the loop's counter stepping through the list.

About 25 minutesArduino firstMicroPython optionalNo electronics assumed
Agent assist code TSK-DAY06-LEDBAR

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, and ten of one of them. 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.

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 ten-segment LED bar graph module.
Manual photo

LED bar graph

Ten small LEDs in one strip, each its own segment.

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

10 × 220 Ω resistors

One per segment, in series, to set the current so each LED lights instead of burning out. Ten LEDs means ten resistors.

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 sketch to the board.

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. Ten segments look like a lot, but each one is the same small connection repeated.

Official Freenove circuit — C Tutorial, Chapter 3 (LED Bar), page 56.
10 bar segments (via 220 Ω each) GPIO 21 47 48 38 39 40 41 42 2 1 Each segment gets its own pin so code can address them one at a time. This exact order is the order the array uses. Its 220 Ω resistor sets the current that keeps that segment alive.
Bar common row GND Every segment returns through one shared row to zero volts

The bar can go in backwards. The LED bar's label direction is easy to reverse. If the whole bar stays dark once you upload, rotate it 180° in the breadboard and try again. 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 / 7 done
  1. Seat the ESP32-S3 on the GPIO extension board and keep USB unplugged while you wire.

  2. Place the LED bar across the breadboard's centre channel so each segment has its own row.

  3. Wire each segment through its own 220 Ω resistor to its GPIO pin, working along the list in order.

  4. Wire the bar's common row to ground so every segment shares the same return.

  5. Compare every wire to the chart before you plug in USB.

  6. Open Sketch_03.1_FlowingLight.ino in Arduino IDE and upload it.

  7. Watch a single lit segment sweep along the bar and back.

Ten pins, one motion.

A list of pins and a loop are now driving the whole bar. Head to Test & debug to confirm the flowing sweep.

04 Read just enough code

Read the code

The code for ten LEDs is the same length as the code for two. The pins live in one array and a loop does each job once per pass — one pinMode line prepares all ten, one digitalWrite line drives whichever the loop points at. Switch to MicroPython if you'd rather see the same idea in Python — the wiring never changes.

Sketch_03.1_FlowingLight.ino
byte ledPins[] = {21, 47, 48, 38, 39, 40, 41, 42, 2, 1};
int ledCounts;

void setup() {
  ledCounts = sizeof(ledPins);
  for (int i = 0; i < ledCounts; i++) {
    pinMode(ledPins[i], OUTPUT);
  }
}

void loop() {
  for (int i = 0; i < ledCounts; i++) {       // forward sweep
    digitalWrite(ledPins[i], HIGH);
    delay(100);
    digitalWrite(ledPins[i], LOW);
  }
  for (int i = ledCounts - 1; i > -1; i--) {  // reverse sweep
    digitalWrite(ledPins[i], HIGH);
    delay(100);
    digitalWrite(ledPins[i], LOW);
  }
}
byte ledPins[] = {21, 47, 48, 38, 39, 40, 41, 42, 2, 1}All ten pins in one array, in bar order. The array holds the pattern; position 0 is the first segment, position 9 the last.
ledCounts = sizeof(ledPins)Counts the array so the loop knows where to stop. Add or remove a pin and the loops adjust on their own, with no other edit.
pinMode(ledPins[i], OUTPUT)Runs once per pin in setup(), so a single line prepares all ten instead of ten copied lines.
digitalWrite(ledPins[i], HIGH)ledPins[i] reads the pin at position i, and the loop drives just that one HIGH; the next line sets it LOW before i moves on.
for (int i = ledCounts - 1; i > -1; i--)Same array, index counting down from 9 to 0, so the light steps back the other way.

05 Understand, don't memorise

An array of pins, walked by a loop

The instinct with ten LEDs is to write the blink code ten times over. That works, and it is a trap — it does not scale, and every change means editing ten places. The move that does scale is the whole lesson. Name the pins once, as a numbered list, and write the work once, as a loop that reads the list by position. The count of outputs stops mattering to the code.

1

Pins in an array

ledPins holds all ten pin numbers in bar order. An array is a numbered shelf- position 0 is the first segment, position 9 the last. The array is the pattern; the wiring just follows it.

2

An index selects one

The loop keeps a counter, i. ledPins[i] means read the pin number sitting at position i — so a single line can address any segment by changing one number.

3

Light, then step

The loop drives that one pin HIGH, waits, sets it LOW, then i++ moves the index one place along the array to the next pin.

4

Motion is the index over time

As i climbs 0 to 9, the lit pin marches down the bar. One counter, changing over time, is the whole of the motion. Reverse the count, 9 down to 0, and the light flows back.

The model pins in an array + a loop over the index = ten outputs from code you wrote once

Why an array beats ten variables

Ten named variables would mean ten copies of pinMode and two more of every blink line. The array collapses all of that into one loop, and it takes the count of pins from the list itself — so growing the bar is one more number, not another block of code.

Why each segment still needs its own resistor

A LED left to itself pulls far more current than it can survive and burns out in an instant. It has no way to limit itself. The 220 Ω resistor in series does the limiting — the voltage across it sets how much current flows, and that current is what a LED actually runs on. Ten LEDs on ten pins means ten resistors; one shared resistor would starve or overload them together.

06 Know it worked

Test & debug

Nothing prints to the screen today — the proof is the moving light on the bar.

What you should see
LED bar
  • One lit segment starts at one end of the bar.
  • It travels along the bar a segment at a time to the far end.
  • Then it flows back the other way, and repeats for as long as the board has power.

Only one segment is lit at any instant, so the flow you see is that single light stepping from one row to the next.

If it doesn't
  • Whole bar stays dark? The bar is likely in backwards — its direction is easy to reverse. Rotate it 180°, then re-check the common row to ground.
  • One segment never lights? That one segment is a broken circuit of its own — check its 220 Ω resistor and its jumper to the right GPIO pin. Its neighbours working tells you the code and ground are fine.
  • Segments light in a scrambled order, not a clean sweep? A jumper is on the wrong GPIO, so a pin sits out of sequence versus the array. Re-check each wire against the pin order 21 47 48 38 39 40 41 42 2 1.
  • Nothing happens at all? Re-check the common row to ground, then that the sketch actually uploaded.
  • Upload fails? Swap in a data-capable USB cable.

07 Make the idea yours

Try this: prove the array is the pattern

Two edits that touch only the array — no rewiring, and you never open the loops. If the motion follows what you type in the list, you have proven the pattern lives in the array and the loop just reads it.

Scramble the list

In byte ledPins[], swap two of the pin numbers — put 48 where 47 was, and 47 where 48 was — and upload. Nothing on the breadboard changed, yet the light now jumps between those two segments out of order. The sequence you see is the array's order, and the wiring only decides which physical LED each number reaches.

Make the bar shorter

Delete two numbers from the end of the array so it holds eight instead of ten, and upload. The sweep now covers eight segments and turns back early — even though you never edited a loop. The loops read their length from the array itself, so shortening the list shortens the pattern for free.

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-DAY06-LEDBAR

How the agent should behave: guide one segment at a time, wait for you to confirm, and always check wiring, board, port, and USB before changing code. Once the bar is sweeping, make the array-and-loop idea land- the pins are one list, the loop walks it by index, and the moving light is that index over time.

Keep your place

Finished Day 6?

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

Field note

Shortcut

Prompt copied