Open-source hexapod robot
Build the Hexapod Macaroon
A complete four-phase guide — from 3D printing through electronics, firmware, and calibration. Powered by stronger 25kg servos.
Critical: Do NOT tighten servo horn screws during assembly. Leave them loose — you’ll tighten them properly during calibration after setting each servo to its neutral position.
What you’ll build
Degrees of freedom18 (6 legs × 3)
Build time8–12 hours
Skill levelBeginner–Intermediate
ToolsHex key set, small screwdriver
No soldering required with the pre-assembled Macaroon controller board ↗ — the servos, switch and battery holder plug straight in. You only need a soldering iron and wire strippers if you build your own wiring harness.
Specifications
| Degrees of freedom | 18 — 6 legs × 3 joints (coxa, femur, tibia) |
| Actuators | 18 × 25 kg digital servos, 180° travel, 8.4 V capable |
| Controller | ESP32 with dual PCA9685 PWM drivers (I²C 0x40 / 0x41) |
| Power | 4 × 18650 Li-ion in 2S2P — 7.4 V nominal, 8.4 V fully charged |
| Connectivity | 2.4 GHz WiFi access point — the robot hosts its own network |
| Control | Binary UDP protocol on 192.168.4.1:1234, plus a web UI on http://192.168.4.1 |
| Real-time streaming | All 18 joints at 50 Hz, with per-joint slew limiting and a 1 s failsafe |
| Firmware updates | USB or over-the-air (OTA) over WiFi |
| Resolution | ~0.44° per servo tick (410 ticks over 180°) |
Safety notes
- Check battery polarity twice before the first power-up — reversed polarity will destroy the controller board
- Keep fingers clear of the joints when powered. Eighteen 25 kg servos have enough torque to pinch hard
- Support the robot on first power-on. It runs its stand-up sequence automatically as soon as a client joins its WiFi, so hold it or stand it on a box with the legs free
- Treat the Li-ion pack with respect — protected cells only, a proper 2S charger, and never charge unattended
- If a leg moves somewhere unexpected, cut power with the toggle switch rather than trying to hold the leg back
Print settings
Layer height0.2 mm
Infill20–30%
MaterialPLA / PETG — TPU for foot tips
SupportsSome parts
All STL files — plus ready-to-print Bambu Studio projects (
bambu_studio_*.3mf) — are a free download ↗. Check each STL’s orientation for supports, and print one leg’s worth of parts first to test-fit before committing to all six.
Body components — ×1 set
Install servos before closing the body. Route wires neatly to avoid pinching. Test-fit all parts before final assembly.
body_base×1
body_side×6
body_top×1
body_head×1
body_battery×1
body_servo_side×12
fence Optional×1Joint components — 3 standard + 3 mirrored
Left side legs use mirrored joints. Right side legs use standard joints. Refer to assembled robot images for correct orientations.
joint_bottom×12
joint_cross×6
joint_top×12Leg components — ×6
Ensure bearings are properly seated. Pins should slide in smoothly without forcing. Verify smooth joint rotation before proceeding.
leg_bottom×6
leg_side×12
leg_top×6Foot components — 3 standard + 3 mirrored
Match foot orientation with joint — left = mirrored, right = standard.
foot_bottom×6
foot_top×6
foot_ground×6
foot_tip TPU×6Hardware
Organize all hardware into labeled containers before assembly. Standard metric parts — available from Amazon, AliExpress, or local hardware stores.
| Item | Spec | Qty | Use |
|---|---|---|---|
| Screw | M2 × 6mm hex socket | 36 | Servo mounting |
| Screw | M2 × 12mm countersunk | 180 | General assembly |
| Nut | M2 hex nut | 216 | Securing screws |
| Pin | M4 × 6mm stainless steel (304) | 18 | Joint pivots |
| Bearing | MR74-2RS — 4mm ID, 7mm OD, 2.5mm bore | 18 | Smooth joint rotation |
Assembly order
1
Install servos in body before closing it — route wires neatly to avoid pinching


2
Assemble joints: bottom → cross → top. Pay close attention to left vs right orientation


3
Build each leg — seat bearings, insert pins (should slide smoothly without forcing)


4
Attach feet — verify orientation matches the joint (standard or mirrored)


5
Attach all 6 completed legs to the body


6
Connect servos and controller — then proceed to Electronics and Software before final tightening
Components
| Image | Component | Spec | Qty | Note |
|---|---|---|---|---|
![]() | Controller board | Macaroon ESP32 board — servos driven directly by the batteries | 1 | Purchase ↗ |
![]() | Servo | 25kg — 180° rotation, must support 8.4V supply | 18 | Same batch |
![]() | Toggle switch | SPST, 12mm diameter | 1 | Available with the controller board ↗ |
![]() | 18650 battery | 3.7V Li-ion, 2000mAh+ | 4 | Protected cells only |
![]() | Battery holder | 4-cell (2S2P) with wire leads | 1 | Printable holder ↗, or with the controller board ↗ |
Use protected 18650 batteries. Unprotected cells can be hazardous — the protection circuit guards against overcharge, over-discharge, and short circuits.
Wiring
1
Connect each 25kg servo to the numbered ports on the controller board — 18 servos total (3 per leg)
2
Install the toggle switch for main power
3
Connect the 4-cell (2S2P) 18650 battery holder to the board’s power input (7.4V nominal)
4
Refer to the wiring diagram below for exact pin assignments




The custom controller board simplifies wiring significantly — all servo connectors and power management are built in. Available from the RookiDroid shop.
Dependencies
| Library | How to install |
|---|---|
arduino-esp32 | Add board support URL in Arduino IDE → Boards Manager |
Adafruit PWM Servo Driver | Arduino IDE → Library Manager → search and install |
AsyncUDP, ArduinoOTA, EEPROM, WebServer | Included with arduino-esp32 — nothing extra to install |
Upload steps
1
Install Arduino IDE (2.x recommended) from arduino.cc ↗
2
Install
arduino-esp32 board support following the Espressif instructions ↗, or add https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json to Preferences → Additional Board Manager URLs3
Install Adafruit PWM Servo Driver through Arduino Library Manager
4
Download the firmware from Hexapod Arduino ESP32 ↗ and open
hexapod_esp32.ino5
Select board
ESP32 Dev Module — Upload Speed 115200, Flash Frequency 80MHz, Partition Scheme Default 4MB with spiffs6
Select the COM port and upload. Open Serial Monitor at 115200 baud to see the AP address — the robot stands up when a client connects to its WiFi
Default WiFi config
SSID
hexapod_macaroon
Password
hexapod_1234
UDP port
1234
AP address
192.168.4.1
Change the credentials in
config.h (APSSID / APPSK). The hexapod creates its own 2.4 GHz access point — connect to it directly rather than looking for it on your router.
Source files
📄
hexapod_esp32.ino — main sketch with setup() / loop() and shared system state📄
motion_control.ino — PWM drivers and motion look-up table playback📄
realtime.ino — real-time pose streaming and its slew limiter📄
network.ino — WiFi AP, OTA, UDP endpoint and packet parsing📄
calibration.ino — servo offsets loaded from / saved to EEPROM📄
web_ui.ino / web_page.h — web calibration interface📄
hexapod.h / protocol.h — shared state and UDP packet layouts📄
config.h — WiFi credentials, servo pin mappings, and hardware settings📄
motion.h — pre-generated motion look-up tables for smooth walking📄
README.md ↗ — ESP32-specific documentation, including the full UDP command referenceRegenerating the motion tables
The gaits shipped in
motion.h are generated, not hand-written — they come from the path tool ↗, a Python inverse-kinematics generator.
1
Install Python with
numpy and Jupyter, then open lut_generator.ipynb from the path_tool folder2
Adjust the parameters (or add a generator in
path_tool.py) and run all cells — the last cell writes a new motion.h next to the notebook3
Copy that
motion.h into software/hexapod_esp32/ and re-upload the firmware4
A brand-new table also needs an entry in
motion_config[] in motion_control.ino — see Adding Custom Motions ↗
If you change the frame or leg dimensions, update
config.json first — every look-up table is derived from it.
Control protocol
The robot listens on UDP port
1234. Three binary packet types share that port — the first byte selects the protocol, so the firmware dispatches on the magic number before it checks the length. All packets are little-endian and unpadded.
| Magic | Packet | Size | Purpose |
|---|---|---|---|
0xA5 | Motion command | 6 B | Play one of the built-in gait look-up tables |
0xA6 | Real-time pose | 44 B | Stream raw servo positions for all 18 joints |
0xA7 | Session control | 6 B | Enter/leave real-time mode, relax, keep-alive |
Motion commands — 0xA5
Byte 0 is the magic number, byte 1 the command ID, and bytes 2–5 a 32-bit sequence number.
| ID | Action | Legacy text command |
|---|---|---|
0 | Stop and hold position | standby |
1 | Walk forward | walk0 |
2 | Walk backward | walk180 |
3 | Walk right at 45° | walkr45 |
4 | Walk right at 90° | walkr90 |
5 | Walk right at 135° | walkr135 |
6 | Walk left at 45° | walkl45 |
7 | Walk left at 90° | walkl90 |
8 | Walk left at 135° | walkl135 |
9 | Fast walk forward | fastforward |
10 | Fast walk backward | fastbackward |
11 | Turn left in place | turnleft |
12 | Turn right in place | turnright |
13 | Climbing gait forward | climbforward |
14 | Climbing gait backward | climbbackward |
15 | Body rotation — pitch | rotatex |
16 | Body rotation — roll | rotatey |
17 | Body rotation — yaw | rotatez |
18 | Body twist motion | twist |
Session control — 0xA7
| Action | Name | Effect |
|---|---|---|
0 | Exit | Leave real-time mode, resume look-up table playback from standby |
1 | Enter | Enter real-time mode, holding the standby posture |
2 | Relax | Disable both PWM drivers so the servos go limp |
3 | Ping | Keep-alive; resets the failsafe timer |
Real-time pose streaming (
0xA6). A 44-byte packet carries raw servo ticks for all 18 joints, bypassing the motion look-up tables — receiving one implicitly enters real-time mode, and each joint slews toward its target by at most max_step ticks per 20 ms cycle. If no packet arrives for 1 second the robot eases back to standby and returns to look-up table control, so send a ping while idle. Sending a motion command (0xA5) also leaves real-time mode, so the two control styles cannot fight over the servos. Hexapod Link ↗ is a desktop app that uses this to drive the robot live.
Prefer not to build packets yourself? Use the Android app ↗. The firmware also still falls back to plain text commands (the legacy column above) when a packet matches none of the binary layouts — handy for testing from a terminal, but new clients should use the binary protocol. Full field layouts are in the UDP command reference ↗.
OTA updates supported. Connect to the hexapod WiFi network, pick the robot under Arduino IDE → Tools → Port → Network Ports, and upload without USB. OTA is disabled once the robot enters real-time mode or receives a legacy text command — binary motion commands (
0xA5) leave it enabled. Reboot the robot to re-enable it.
Troubleshooting
- Upload fails: use a data-capable USB cable, try another port, confirm board and port, and hold BOOT during upload if needed
- Servos not responding: check battery voltage (7.4V nominal), servo connections, PCA9685 addresses (0x40 left, 0x41 right) and enable pins (GPIO 19 left, GPIO 26 right). Confirm the servo pin mappings in
config.hmatch your wiring, and test individual servos withposture_calibration() - WiFi issues: the hexapod creates an access point — connect to it directly instead of looking for it on your router. Verify SSID/password in
config.hand make sure your device supports 2.4 GHz; the IP is always192.168.4.1 - OTA not working: real-time mode and legacy text commands turn OTA off — reboot before uploading, stay connected to the hexapod AP, and check your computer’s firewall
The ESP32 firmware includes a web-based calibration interface — no need to edit code or re-upload firmware. All adjustments happen live through your browser.
Target neutral positions (all servos at 90°)
Left legs 1, 2, 3
Joint 1 — Coxa (hip)⊥ body
Joint 2 — Femur (thigh)horizontal
Joint 3 — Tibia (shin)90° to femur
Right legs 1, 2, 3
Joint 1 — Coxa (hip)⊥ body
Joint 2 — Femur (thigh)horizontal
Joint 3 — Tibia (shin)90° to femur
Calibration procedure
1
Flash firmware with default offsets, insert batteries, turn on power switch
2
Connect your device to the
hexapod_macaroon WiFi network (password: hexapod_1234)3
Open a browser and navigate to
http://192.168.4.14
Click “Enter Calibration Mode” — motion stops, current offsets load, and a grid of all 18 servos appears (6 legs × 3 joints)


5
For each misaligned servo, use +/− buttons (1 tick ≈ 0.44°) or type a value — changes apply immediately
6
Compare each leg against the reference images: coxa ⊥ body, femur horizontal, tibia at 90° to femur
7
Click “Save Offsets” — values are written to EEPROM and survive power cycles. Also printed to Serial Monitor as backup
8
Now tighten servo horn screws — with each servo in its correct neutral position, firmly tighten all horn screws
9
Click “Exit Calibration Mode” and send a walk command to verify smooth motion
Tips for good calibration
- Calibrate one leg completely before moving to the next
- Use +/− buttons for fine control — avoid jumping to large values
- If offsets exceed ±25 ticks, physically reposition the servo horn instead of relying on software offset
- Loose servo horns throw off calibration — make sure the horn is seated firmly on the spline before adjusting
- Servos respond immediately as you adjust, so you get real-time visual feedback
- Save your offsets — values persist in EEPROM but always note them from Serial Monitor as backup
- Re-enter calibration mode any time to fine-tune if walking looks uneven




