TinySkiff ESP32-S3 Lab · Day 24 of 30

Steer the horn
with a knob

Today the board gets a helm. You wire the knob from Day 13 and the servo from Day 23 into one circuit, and a two-line conversion joins them — turn the knob and the horn swings with your hand, live. Serial Monitor prints the raw reading and the finished angle side by side so you can watch the mapping happen.

About 20 minutesArduino firstMicroPython optionalNo electronics assumed
Agent assist code TSK-DAY24-SERVOKNOB

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 you've met them all. Tap Define on any part you'd like a refresher on — 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 hobby servo with its three-wire lead and a set of horns.
Manual photo

Servo

The motor that holds whatever angle you command.

Official manual illustration of a rotary potentiometer with rotation arrows and its three pins labelled 1, 3, and 2.
Manual illustration

Rotary potentiometer

The knob — a resistor track with a sliding tap.

Official manual image of a jumper wire.
Manual photo

6 jumper wires (M/M)

Temporary, solder-free connections.

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. Two familiar circuits share the board today: the knob on one side, the servo on the other, joined through the code.

Official Freenove circuit — C Tutorial, Chapter 17 (Servo), page 159.
Pot outer pin 1 3.3V Feeds one end of the knob's track with full voltage.
Pot outer pin 2 GND Holds the other end of the track at zero volts.
Pot middle pin 3 GPIO 14 The wiper delivers the in-between voltage for the ADC to read.
Servo signal (orange) GPIO 21 Carries the timing pulse that names the horn's angle.
Servo VCC (red) 5V Powers the servo's motor and gears.
Servo GND (brown) GND Gives board and servo the same zero point.

Two power rails, each to its own device. The servo's motor drinks from the 5V pin; the potentiometer stays on 3.3V so GPIO 14 reads within its limit. Keep the two supplies straight, never twist the horn by hand while it's powered, and 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. Push the potentiometer into the breadboard so each of its three pins has its own row.

  3. Wire the pot's outer pins to 3.3V and GND, then run its middle pin to GPIO 14.

  4. Connect the servo's lead — orange signal to GPIO 21, red to 5V, brown to GND.

  5. Press a horn onto the servo's output shaft so the motion is easy to see.

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

  7. Open Sketch_17.2_Control_Servo_by_Potentiometer.ino in Arduino IDE and upload it.

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

  9. Turn the knob slowly end to end and watch the horn follow your hand.

The helm answers.

The horn tracks the knob, live. Head to Test & debug to read the number pairs behind the motion.

04 Read just enough code

Read the code

The loop is five working lines — read the knob, print, map, steer, print again. Switch to MicroPython if you'd rather see the same idea in Python — the wiring never changes.

Sketch_17.2_Control_Servo_by_Potentiometer.ino
#define SERVO_PIN 21  //define the pwm pin
#define SERVO_CHN 0   //define the pwm channel
#define SERVO_FRQ 50  //define the pwm frequency
#define SERVO_BIT 12  //define the pwm precision
#define ADC_PIN   14  //define the adc pin

void setup() {
  servo_set_pin(SERVO_PIN);
  Serial.begin(115200);
}

void loop() {
  // read the value of the potentiometer (value between 0 and 4095)
  int potVal = analogRead(ADC_PIN);
  Serial.printf("potVal_1: %d\t",potVal);
  // scale it to use it with the servo (value between 0 and 180)
  potVal = map(potVal, 0, 4095, 0, 180);
  // set the servo position according to the scaled value
  servo_set_angle(potVal);
  Serial.printf("potVal_2: %d\r\n",potVal);
  delay(15);// wait for the servo to get there
}

void servo_set_pin(int pin) {
  ledcAttachChannel(pin, SERVO_FRQ, SERVO_BIT, SERVO_CHN);
}

void servo_set_angle(int angle) {
  if (angle > 180 || angle < 0)
    return;
  long pwm_value = map(angle, 0, 180, 103, 512);
  ledcWrite(SERVO_PIN, pwm_value);
}
map(potVal, 0, 4095, 0, 180)Rescales the knob's raw reading onto the servo's range — 0 stays 0, 4095 becomes 180, and everything between lands in proportion.
map(angle, 0, 180, 103, 512)The second map turns the angle into a duty value — the width of the pulse that tells the servo where to hold.
ledcAttachChannel(pin, SERVO_FRQ, SERVO_BIT, SERVO_CHN)Sets GPIO 21 up as 50 Hz PWM with 12-bit precision — the timing a hobby servo expects.
Serial.printf("potVal_1: %d\t",potVal)Prints the raw reading before the mapping, so each line pairs the input with its finished angle.

05 Understand, don't memorise

Reading → angle → pulse

Everything in today's loop is a rescale. The knob speaks in 0 to 4095, the servo listens in 0 to 180, and the PWM hardware wants a duty value — map() translates at each border.

Turn

Knob dials voltage

The wiper hands GPIO 14 a voltage that tracks the knob's position.

Sample

ADC measures

analogRead turns that voltage into a number from 0 to 4095.

Map

Reading becomes angle

map() rescales 0-4095 onto 0-180 so every knob position names an angle.

Steer

Angle becomes pulse

A second map() converts the angle to a duty value from 103 to 512 and ledcWrite sends it.

The chain angle = map(reading, 0, 4095, 0, 180) → pulse = map(angle, 0, 180, 103, 512)

Why two map() calls

Each call bridges one border — sensor range to human range, then human range to hardware range. Keeping the angle in the middle lets you think in degrees.

The pulse under the angle

At 50 Hz with 12-bit precision, duty 103 is roughly a 0.5 ms pulse and 512 is roughly 2.5 ms — the width, repeated every 20 ms, is the position command the servo holds.

06 Know it worked

Test & debug

The horn tracking your hand is the payoff; the serial pairs are the proof. 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
potVal_1: 0 potVal_2: 0potVal_1: 2048 potVal_2: 90potVal_1: 4095 potVal_2: 180

Each line pairs the raw reading with its mapped angle — one end of the knob prints near 0 and 0, the centre near 2048 and 90, the far end near 4095 and 180.

If it doesn't
  • Horn ignores the knob? Check the pot's middle wire on GPIO 14, then its outer pins on 3.3V and GND.
  • Angles print but nothing moves? Check the servo's signal wire on GPIO 21 and its power pair on 5V and GND.
  • Horn jitters at rest? Small twitches are normal; a steadier 5V supply calms them.
  • Garbled or random characters? Set Serial Monitor to 115200 baud to match the sketch.

07 Make the idea yours

Try this: tiller stops

Same working circuit, three small rewrites of one line. Each teaches you something about owning the mapping. It fits inside today's 20 minutes.

Fit tiller stops

Change the first map() so the angle runs 67 to 112 — a 45° window around centre, like the stops on a boat's tiller. The knob still sweeps end to end; the horn stays inside its lane.

Steer the other way

Swap the output ends — map to 180 down to 0 — and the horn runs against the knob. One line reverses the helm.

Mark the helm

With the original sketch restored, find the knob positions that print 0, 90, and 180, and mark each on the knob with a sliver of tape. You've calibrated a control.

Logbook

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-DAY24-SERVOKNOB

How the agent should behave: guide one wire at a time, wait for you to confirm, explain terms on request, and always check wiring, board, port, and baud rate before changing code.

Field note

Shortcut

Prompt copied