ESP32-S3 Lab · Day 19 of 30

Read two axes
and one button

Today you wire the control that steers every gamepad, and take it apart in your head first. Under the stick sit two potentiometers at right angles and a push button beneath them, so the same analogRead from the potentiometer day runs twice, once per axis, and the same digitalRead catches the press. Two numbers together name a point on a plane; the third is a plain button. Watch your hand become a coordinate on screen.

About 20 minutesArduino firstMicroPython optionalNo electronics assumed
Agent assist code TSK-DAY19-JOYSTICK

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

Five things, one of them brand new. 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 joystick module with X and Y axis arrows and its five pins labelled 1 to 5.
Manual photo

Joystick module

A thumb stick over two dials and a hidden push button.

Official manual image of a jumper wire.
Manual photo

5 jumper wires (F/M)

Female ends grip the module's header pins; male ends reach the board.

Official manual screenshot of the Arduino IDE interface.
Manual screenshot

Arduino IDE

Uploads code 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. Each connection tells you where the wire goes and why.

Official Freenove circuit — C Tutorial, Chapter 13 (Joystick), page 128.
VRX GPIO 14 Carries the X potentiometer's voltage for the board to measure.
VRY GPIO 13 Carries the Y potentiometer's voltage the same way.
SW GPIO 12 The button under the stick pulls this pin LOW when you press.
+5V 3.3V Powers the module — the official diagram feeds this pin from the 3.3V rail.
GND GND Gives board and module the same zero point.

Power from 3.3V. The module's pin is printed +5V, and the official diagram feeds it from the 3.3V rail — follow the diagram. 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 / 8 done
  1. Seat the ESP32-S3 on the GPIO extension board and keep USB unplugged while you wire.

  2. Push the five female jumper ends onto the module's header pins — the labels VRX, VRY, SW, +5V, and GND are printed beside them.

  3. Connect VRX → GPIO 14, VRY → GPIO 13, and SW → GPIO 12.

  4. Connect the module's +5V pin to the 3.3V rail and GND → GND, just as the chart shows.

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

  6. Open Sketch_13.1_Joystick.ino in Arduino IDE and upload it.

  7. Open Serial Monitor and set the baud rate to 115200.

  8. Glide the stick to its edges, then press it straight down like a button.

Three readings, one stick.

Your hand's position is now three numbers on screen. Head to Test & debug to learn what healthy values look like.

04 Read just enough code

Read the code

The loop is three reads and one print — two axes with analogRead, the button with digitalRead, the two skills from earlier days sitting side by side. A printf line sends all three up at once. Switch to MicroPython if you'd rather see the same idea in Python; the wiring never changes.

Sketch_13.1_Joystick.ino
int xyzPins[] = {14, 13, 12};   //x,y,z pins
void setup() {
  Serial.begin(115200);
  pinMode(xyzPins[2], INPUT_PULLUP);  //z axis is a button.
}

void loop() {
  int xVal = analogRead(xyzPins[0]);
  int yVal = analogRead(xyzPins[1]);
  int zVal = digitalRead(xyzPins[2]);
  Serial.printf("X,Y,Z: %d,\t%d,\t%d\n", xVal, yVal, zVal);
  delay(500);
}
int xyzPins[] = {14, 13, 12}One array holds all three pins — X on GPIO 14, Y on GPIO 13, Z on GPIO 12.
pinMode(xyzPins[2], INPUT_PULLUP)Sets the button pin so it rests at 1 and a press pulls it to 0.
analogRead(xyzPins[0])Measures the X potentiometer's voltage as a number from 0 to 4095.
Serial.printf("X,Y,Z: %d,\t%d,\t%d\n", …)Prints all three values on one tab-separated line, twice a second.

05 Understand, don't memorise

A point on a plane, plus a press

Nothing here is new. Reading a potentiometer with analogRead was the potentiometer day; reading a button that rests HIGH and drops on a press was the button day. A joystick stacks two of those pots at right angles and hides a button beneath them, so one grip hands the board a position and a press. Seeing it as a composite, three familiar things in one part, is the whole lesson.

Two dials

Right angles

Under the stick sit two potentiometers mounted at right angles — one follows left-and-right, the other forward-and-back. Each is the same dial from the potentiometer day.

Two numbers

analogRead, twice

analogRead measures each pot's voltage as a number from 0 to 4095. Two axes means two of the reads you already know, one for X and one for Y.

One point

X and Y together

On their own, X and Y are two loose dials. Together they name a point on a plane — X across, Y up and down — which is how a stick's position becomes a coordinate the code can steer with.

One press

digitalRead, once

Push straight down and a hidden button closes to ground. INPUT_PULLUP holds GPIO 12 at 1 until the press pulls it to 0 — the same button-and-pull-up from earlier, riding on a third pin.

The model one joystick = two ADC axes naming a point (X, Y) on a plane + one button (Z)

Why the centre is near 2048, never exactly

At rest each pot sits near the middle of its travel, so each axis reads near the middle of the 0-to-4095 range — about 2048. Real pots and the ADC never land dead centre, so expect resting numbers anywhere from about 1800 to 2200, each board its own. The centre is a neighbourhood, not a single value.

Why the axes move independently

The two pots share nothing but the stick. Tilt purely left-to-right and X swings while Y barely moves; tilt forward and Y swings while X holds. Because each axis is its own dial, the pair can point anywhere inside the square between the corners — that independence is what makes a plane rather than a line.

Why Z rests at 1

The internal pull-up holds the button pin HIGH with nothing else connected in the module. Pressing the stick closes the switch to ground, so the reading drops to 0 — a plain digital button carried alongside the two analog axes.

06 Know it worked

Test & debug

Success and recovery sit side by side, so you never have to go hunting when something looks off.

What you should see
Serial Monitor115200 baud
X,Y,Z: 1917, 1846, 1X,Y,Z: 4095, 1852, 1X,Y,Z: 1921, 1839, 0

A new line lands twice a second. At rest both axes sit near mid-scale — around 2048, though real pots land anywhere from about 1800 to 2200, and your exact numbers will differ. A full push drives one axis toward 0 or 4095, and a straight-down press flips Z from 1 to 0.

If it doesn't
  • One axis stuck at 0 or 4095? Reseat that axis's wire — VRX belongs on GPIO 14, VRY on GPIO 13.
  • Z always 1? Check the SW wire on GPIO 12, then press straight down firmly — a sideways tilt won't click it.
  • Both axes dead? Check the module's +5V and GND pins first.
  • Blank monitor? Confirm 115200 baud, then the board and port.
  • Upload fails? Swap in a data-capable USB cable.

07 Make the idea yours

Try this: read your stick as coordinates

Same working circuit, one new habit — reading the stick as a point rather than two loose numbers. It fits inside today's 20 minutes.

Find your true centre

Let the stick rest and write down the X and Y numbers. Neither will be exactly 2048 — note how far each sits from it, then watch a few more lines to see the resting values drift by a handful even while nothing touches the stick. That is the centre-is-a-neighbourhood idea in your own numbers.

Reach both extremes

Push X fully one way, then fully the other, and record its lowest and highest — near 0 and near 4095. Do the same for Y. Those four numbers are your two axis spans: the edges of the square the stick can point inside.

Read a tilt as a point

Now hold the stick partway toward a corner and read the pair as one coordinate — X across, Y up. Nudge it and watch both numbers change together. That moving (X, Y) pair is exactly the position a game would steer with, and why two dials beat one.

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-DAY19-JOYSTICK

How the agent should behave: guide one physical connection at a time and wait for confirmation, but teach the joystick as a composite — two analog axes that together name a point on a plane, plus one digital button, all skills the learner already has. Explain terms on request, and always check wiring, board, port, and USB before changing code.

Keep your place

Finished Day 19?

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

Field note

Shortcut

Prompt copied