A smartphone-controlled robot platform built on the ESP32-S3 and Dynamixel XL330 servo motors. The robot hosts its own Wi-Fi network and serves a web interface that lets you control it from your phone's browser — no app install required.
This repo contains architectural improvements building on the incredible Rei Lee's Conebot
Rei's Original ConeBot A tiltyBot in gyro control mode A tiltyBot in drive control modeThe ESP32-S3 runs an HTTPS server over a local Wi-Fi access point. You connect your phone to that network, open the control page in a browser, and send commands to the motors over WebSocket. All three control modes are available from a single firmware — pick from the index page:
- Drive — Differential drive with a virtual joystick. Two wheels plus a caster ball.
- Tilty — A pan/tilt head that responds to your phone's gyroscope orientation, or manual sliders.
- 2-Motor — Direct position control of two motors (0–360°). Useful as a starting point for custom builds.
There's also a sound recorder page for capturing and playing back audio clips through a Bluetooth speaker.
- Waveshare ESP32-S3-Zero (ESP32-S3, 4MB Flash, 2MB PSRAM)
- 2× Dynamixel XL330 servo motors
- XL330 Hinge
- USB-C cable
- Wheels, caster wheels
- Cardboard, tape, and whatever else you want to build with
The motors daisy-chain together and connect to the ESP32-S3-Zero via 3 wires:
| Dynamixel XL330 Pin | Signal | ESP32-S3-Zero |
|---|---|---|
| Pin 1 — GND | Ground | GND |
| Pin 2 — VDD | Power (5V) | 5V |
| Pin 3 — Data | Half-duplex serial | GPIO1 + GPIO2 (tied together) |
GPIO1 (TX) and GPIO2 (RX) are next to each other on the left side of the board. Both connect to the single Dynamixel Data wire for half-duplex communication.
You can use either VS Code with PlatformIO, or the PlatformIO CLI directly.
- Visual Studio Code
- PlatformIO extension for VS Code
Install PlatformIO Core:
# macOS / Linux
curl -fsSL -o get-platformio.py https://raw.githubusercontent.com/platformio/platformio-core-installer/master/get-platformio.py
python3 get-platformio.py
# Or via pip
pip install platformioVerify the installation:
pio --versionYou'll also need Git.
git clone https://github.com/imandel/tiltybot.git
cd tiltybotEach motor needs a unique ID. Flash the motor setup tool and open a serial connection:
pio run -e motor_setup -t upload
pio device monitor -b 115200You'll see:
========================================
TILTYBOT MOTOR SETUP
========================================
Options:
1 - Configure this motor as Motor 1
2 - Configure this motor as Motor 2
t - Test both motors (daisy-chained)
Connect one motor at a time:
- Connect Motor 1, type
1→ configures it as ID 1 at 115200 baud (LED blinks, motor moves to confirm) - Disconnect Motor 1, connect Motor 2, reset the board, type
2→ configures it as ID 2 - Daisy-chain both motors, reset the board, type
t→ runs full test (LEDs, position mode, drive mode)
Edit the WiFi credentials near the top of src/tiltybot/main.cpp:
const char *ssid = "my-robot";
const char *password = "something"; // must be 8+ charactersUpload the SSL certificates to LittleFS (first time only, or after changing certs):
pio run -e tiltybot -t uploadfsUpload the firmware:
pio run -e tiltybot -t upload- On your phone, join the Wi-Fi network you configured.
- Open
https://192.168.4.1in your browser. - Accept the self-signed certificate warning.
- Pick a control mode from the menu.
All commands are run from the project root.
pio run -e tiltybot # build firmware
pio run -e motor_setup # build motor setup toolpio run -e tiltybot -t upload # flash firmware
pio run -e tiltybot -t uploadfs # flash SSL certs to LittleFS
pio run -e motor_setup -t upload # flash motor setup toolIf the port isn't auto-detected, specify it:
pio run -e tiltybot -t upload --upload-port /dev/cu.usbmodem101pio device monitor -b 115200Or find the port and use any serial tool:
pio device list # list available ports
screen /dev/cu.usbmodem101 115200 # macOS/Linuxpio run -e tiltybot -t clean # remove build artifactsIf the board gets into a bad state:
pio run -e tiltybot -t erase # erase entire flashYou'll need to re-upload both firmware and LittleFS after erasing.
src/
tiltybot/main.cpp — Unified firmware (all control modes + sound recorder)
motor_setup/main.cpp — Motor configuration tool
data/
server.crt / server.key — Self-signed SSL certificate
partitions.csv — Custom partition table (4MB flash)
platformio.ini — PlatformIO configuration
| Environment | Description |
|---|---|
tiltybot |
Main firmware — all control modes |
motor_setup |
Motor ID/baud configuration tool |
Managed automatically by PlatformIO (see platformio.ini):
- PsychicHttp — HTTPS server with WebSocket support
- Dynamixel_XL330_Servo_Library — Motor control
- ArduinoJson — JSON parsing
Platform: pioarduino (Arduino Core 3.x / ESP-IDF 5.x)
- Password must be 8+ characters — shorter passwords will prevent devices from joining the WiFi network.
- HTTPS is required for gyroscope access — browsers only allow
DeviceOrientationEventin secure contexts. - Turn off cellular data on your phone so it doesn't drop the robot's network.
- Disconnect from any VPN before connecting.
- If you're building the tilty robot, set both motors to position 0 in 2-motor mode before assembling. Don't rotate the motors by hand after that.
- Corporate/managed phones may not work due to network restrictions.
- The SSL certificate in
data/is self-signed. To regenerate:openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -keyout data/server.key -out data/server.crt -days 3650 -nodes \ -subj "/CN=tiltybot.local"


