Build notes and tooling for the SO-101 robot arm — two arms (leader + follower) using STS3215 servos and LeRobot.
- 13x STS3215 7.4V servos (7 follower, 6 leader)
- 2x Waveshare Motor Control Boards
- 2x USB-C cables + 5V power supplies
- 4x table clamps
pip install -e ".[feetech]"sudo chmod 666 /dev/ttyACM0Permanent fix (requires re-login):
sudo usermod -aG dialout $USERlerobot-find-portRun once per arm, one motor at a time:
# Follower (uses --robot.*)
lerobot-setup-motors --robot.type=so101_follower --robot.port=/dev/ttyACM0
# Leader (uses --teleop.*)
lerobot-setup-motors --teleop.type=so101_leader --teleop.port=/dev/ttyACM0Motor IDs assigned (same for both arms):
| Joint | ID |
|---|---|
| Shoulder Pan | 1 |
| Shoulder Lift | 2 |
| Elbow Flex | 3 |
| Wrist Flex | 4 |
| Wrist Roll | 5 |
| Gripper | 6 |
# Follower (uses --robot.*)
lerobot-calibrate --robot.type=so101_follower --robot.port=/dev/ttyACM0 --robot.id=my_follower_arm
# Leader (uses --teleop.*)
lerobot-calibrate --teleop.type=so101_leader --teleop.port=/dev/ttyACM0 --teleop.id=my_leader_armDuring calibration: move all joints through their full range except wrist_roll, which is a continuous rotation joint.
Run lerobot-find-port with each arm plugged in separately to confirm which port maps to which arm. Then:
lerobot-teleoperate \
--robot.type=so101_follower --robot.port=/dev/ttyACM1 --robot.id=my_follower_arm \
--teleop.type=so101_leader --teleop.port=/dev/ttyACM0 --teleop.id=my_leader_armIf you see a calibration mismatch prompt on startup, press Enter to load the saved calibration file.
Auto-detects the range of a single servo and sweeps min → center → max.
python3 test_servo.pyMove servo by hand (torque off) to set min/max, then replays the motion under power.
python3 calibrate_servo.pyESP32-S3 SuperMini + 2.0" ST7789V 240x320 SPI TFT. Receives serial commands from the LeRobot controller and shows animated retro ASCII Kaomoji expressions.
(Note: The face rendering utilizes a custom differential screen-wipe bypass directly addressing TFT_eSPI to stop the ESP32-S3 from crashing due to memory fragmentation.)
| Display | ESP32-S3 |
|---|---|
| VCC | 3.3V |
| GND | GND |
| CS | GPIO 10 |
| DC | GPIO 2 |
| RST | GPIO 3 |
| MOSI | GPIO 11 |
| SCK | GPIO 12 |
| BLK | 3.3V |
pip install platformio
cd face_display
# set upload_port in platformio.ini (e.g., /dev/ttyACM0)
pio run -t upload
pio device monitorSend over serial to trigger the color-coded ASCII expressions:
FACE:IDLE FACE:HAPPY FACE:SAD FACE:BLINK FACE:TALK
import serial
face = serial.Serial('/dev/ttyACM0', 115200, timeout=1)
face.write(b"FACE:HAPPY\n")