IoTSimulator

Control a Servo Positioner with a Raspberry Pi Pico

Build a servo angle positioning dial with the Raspberry Pi Pico. Map analog potentiometer readings into 50Hz PWM pulse widths to command an SG90 micro servo from 0° to 180° with endstop boundary alerts.
Muhammad Ichsanul Fadhil
IoTSim Editor
October 9, 2026
Control a Servo Positioner with a Raspberry Pi Pico

Live project track

Interactive hardware & logic preview

From steering wheels in remote-controlled cars to precision joints on robotic arms, standard DC motors that spin continuously cannot deliver the exact pointing angle you need. To point a camera, open a mechanical valve, or tilt an aircraft rudder, you need a motor that turns to a precise degree, stops on a dime, and holds its position against outside forces.

In this project, you will build a servo angle positioner using a Raspberry Pi Pico. By turning a rotary potentiometer knob, you will command an SG90 micro servomotor to rotate smoothly between 0° and 180°. You will also wire an indicator LED that illuminates whenever the motor reaches its mechanical limits, alerting you that the arm cannot turn any further.

How Servomotors Understand Angular Position

How 50Hz PWM Pulses Control Servo Motor Angles
Figure 1: Pulse duration determines servo angle: 1.0ms for 0°, 1.5ms for 90°, and 2.0ms for 180°.

Unlike simple electric motors that change speed based on voltage, a servomotor listens to electrical pulses repeated 50 times every second (a 50 Hz signal with a 20 millisecond period). The width of the high pulse tells an internal controller chip exactly where to hold the motor shaft.

Pulse DurationAngular PositionPhysical Horn OrientationApplication Example
1.0 millisecond (1000 µs)0°Far left mechanical stopFully closed air vent or left steering limit
1.5 milliseconds (1500 µs)90°Center neutral pointStraight-ahead steering or level camera view
2.0 milliseconds (2000 µs)180°Far right mechanical stopFully open air vent or right steering limit

Inside the servo casing, a small DC motor connects to reduction gears and an internal potentiometer attached to the output shaft. If external weight attempts to nudge the arm out of place, the internal circuitry detects the error and powers the motor to resist the movement, keeping your commanded angle rock solid.

Separating Sensitive Sensors from Motor Power

Dual Power Rails: 3.3V Logic vs 5V Motor Power
Figure 2: Powering the servo motor from 5V VBUS protects the Pico from sudden voltage drops and restarts.

A common mistake when working with motors on the Raspberry Pi Pico is connecting the motor power wire directly to the 3.3V pin. Servomotors contain wire coils that draw sudden bursts of electrical current when starting to move. Drawing that heavy current from the Pico's sensitive 3.3V logic regulator can cause the board to brown out and restart unexpectedly.

Power RailPico Pin LocationOperating VoltageConnected ComponentPurpose
3.3V Logic RailPin 36 (3V3)3.3V Regulated10k PotentiometerProvides a steady, noise-free reference for accurate angle reading
5.0V USB RailPin 40 (VBUS)5.0V Raw USB PowerSG90 Micro Servo (Red Wire)Supplies the heavy current surges needed to move motor gears
Common GroundPins 13, 18, 38 (GND)0V Ground ReferenceAll ComponentsShared return line connecting logic and motor signals

Connecting motor power directly to VBUS (Pin 40) taps straight into the incoming 5V USB power line, giving the motor plenty of power while keeping the Pico's central processor running smoothly.

Everyday Magic: How Dashboard Needles Work

Dashboard gauge needle pointing smoothly across 0 to 180 degrees
Figure 3: Dashboard needle indicator: turning the knob smoothly swings the servo pointer from 0 degrees (empty) up to 180 degrees (full).

Think of this project like the speedometer needle or fuel gauge on a car dashboard. Instead of a mechanical cable, modern vehicle dials use miniature servo motors. When the engine sensor or fuel float reports a value, the microcontroller calculates the exact angle and sweeps the pointer across the dial face. When it hits the maximum 180-degree mark, an alert LED lights up. The interactive connection tables below show how each module connects to the Raspberry Pi Pico.

Pin Connection Map
Potentiometer Pin
SIG (Center Wiper)
→
Raspberry Pi Pico Pin
GP26 (Physical Pin 31 / ADC0)
Explanation
Carries variable position voltage (0V to 3.3V)
Potentiometer Pin
VCC (Outer Leg)
→
Raspberry Pi Pico Pin
3V3 (Physical Pin 36)
Explanation
Clean 3.3V power reference for potentiometer
Potentiometer Pin
GND (Opposite Leg)
→
Raspberry Pi Pico Pin
GND (Physical Pin 38)
Explanation
Sensor ground return connection

Turning the potentiometer knob slides the internal wiper along a resistive track, outputting an analog voltage between 0V and 3.3V that pin GP26 converts into a digital number.

Pin Connection Map
SG90 Servo Wire
PWM Signal (Orange Wire)
→
Raspberry Pi Pico Pin
GP15 (Physical Pin 20)
Explanation
50 Hz pulse timing control line
SG90 Servo Wire
Power V+ (Red Wire)
→
Raspberry Pi Pico Pin
VBUS (Physical Pin 40 / 5V)
Explanation
High-current 5V power from USB supply
SG90 Servo Wire
Ground GND (Brown Wire)
→
Raspberry Pi Pico Pin
GND (Physical Pin 18)
Explanation
Motor ground return connection

Standard servo ribbon cables follow a color convention: orange is the signal line, red is positive power, and brown (or black) is ground. Confirm each wire aligns with the correct Pico pin before powering on.

Pin Connection Map
Limit Warning LED
Anode (+)
→
Raspberry Pi Pico Pin
GP14 (Physical Pin 19)
Explanation
Digital output driving boundary alert
Limit Warning LED
Cathode (-)
→
Raspberry Pi Pico Pin
GND (Physical Pin 13)
Explanation
LED ground return line

The warning LED turns on whenever the commanded angle is within 10 degrees of either mechanical endstop (below 10° or above 170°), visually showing that the motor has reached its physical limit.

Complete Code

Upload the following complete sketch to your Raspberry Pi Pico. Open the Serial Monitor at 115200 baud to view real-time angle updates:

C++ Source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// ============================================================================
// Raspberry Pi Pico Precision Servo Angle Gauge
// Potentiometer Analog Input on GP26 (ADC0)
// Hardware PWM Servo Output on GP15 | Boundary Limit LED on GP14
// ============================================================================

#include <Servo.h>

Servo myServo;
const int POT_PIN   = 26; // Analog ADC0 (Pin 31)
const int SERVO_PIN = 15; // PWM Servo Signal (Pin 20)
const int LED_PIN   = 14; // Limit Alert Indicator (Pin 19)

void setup() {
  Serial.begin(115200);
  delay(200); // USB settling delay
  
  myServo.attach(SERVO_PIN);
  pinMode(LED_PIN, OUTPUT);
  
  Serial.println(F("Raspberry Pi Pico Precision Servo Angle Gauge Initialized!"));
}

void loop() {
  // 1. Read 10-bit analog voltage (0 to 1023)
  int potVal = analogRead(POT_PIN);
  
  // 2. Map analog dial rotation directly to servo angular degrees (0 to 180°)
  int angle = map(potVal, 0, 1023, 0, 180);
  angle = constrain(angle, 0, 180);
  
  // 3. Command the hardware PWM servo position
  myServo.write(angle);
  
  // 4. Illuminate warning LED at mechanical endstop boundaries (< 10° or > 170°)
  if (angle <= 10 || angle >= 170) {
    digitalWrite(LED_PIN, HIGH);
  } else {
    digitalWrite(LED_PIN, LOW);
  }
  
  // 5. Stream telemetry over Serial
  Serial.print(F("[SERVO] ADC: "));
  Serial.print(potVal);
  Serial.print(F(" -> Target Angle: "));
  Serial.print(angle);
  Serial.println(F("°"));
  
  delay(25); // ~40Hz update rate for smooth physical tracking
}

How the Code Works, Part by Part

The program runs a continuous 40 Hz loop that translates potentiometer movement into motor motion with boundary protection.

Reading the Potentiometer and Mapping to Degrees

The loop begins by reading the potentiometer voltage and scaling it directly into degrees:

C++ Source
1
2
3
int potVal = analogRead(POT_PIN);
int angle = map(potVal, 0, 1023, 0, 180);
angle = constrain(angle, 0, 180);

The map() function translates the 0-1023 analog range into 0-180 degrees. The constrain() call guarantees that electrical noise never tries to push the servo past 180° or below 0°, protecting the mechanical plastic gears inside.

Commanding the Servo Angle

With the target angle calculated, the Servo library sends the appropriate pulse train to pin GP15:

C++ Source
1
myServo.write(angle);

The write() function converts the degree value into the exact microsecond pulse duration needed, pulsing pin GP15 every 20 milliseconds in the background without tying up processor time.

Detecting Mechanical Limits

To let the user know when the dial is near its limits, an if statement checks the angle:

C++ Source
1
2
3
4
5
if (angle <= 10 || angle >= 170) {
  digitalWrite(LED_PIN, HIGH);
} else {
  digitalWrite(LED_PIN, LOW);
}

When the motor approaches within 10 degrees of either extreme (0° to 10° or 170° to 180°), pin GP14 lights up the LED. As soon as the knob returns to the safe middle zone, the LED turns off.

Fixing Common Problems

If your servo motor is not responding or behaving erratically, review the troubleshooting table below:

Observed ProblemLikely CauseHow to Fix
Pico resets or disconnects when servo movesServo power connected to 3.3V instead of VBUSMove the servo red wire to VBUS (Pin 40), which supplies direct 5V USB power.
Servo turns the opposite way when rotating knobOuter potentiometer legs reversedSwap the VCC and GND connections on the potentiometer, or change map(potVal, 0, 1023, 180, 0).
Servo twitches or vibrates slightly at restSlight electrical noise on analog wireAdd a tiny 2-degree deadband check in code so minor sensor flickers are ignored.
Servo hums loudly but will not turnMechanical arm jammed or bad wireCheck that the servo horn is not physically blocked and verify orange wire is in GP15.

Remember that standard SG90 servos have physical internal stops preventing 360-degree rotation. Never force the horn with your hands when power is applied.

Try It in the Simulator

Click the Start Simulation button in the top toolbar to begin. Click and drag the potentiometer knob with your mouse: watch the virtual servo horn follow your dial smoothly. Move the dial all the way to the left or right to see the yellow boundary LED illuminate as the angle enters the limit zone.

Keywords
#Raspberry Pi Pico #RP2040 #Potentiometer #Servo #SG90 #PWM #LED #Robotics #Beginner
Total word count: 1121 words

Project discussion

Questions, feedback, and community insights
No discussions yet. Be the first to start!