Skip to content
Open-source robot remote

Build the Arcade Remote

A real arcade joystick, five snap-in buttons and an ESP32-C6 in a 3D-printed cabinet — a WiFi controller for every RookiDroid hexapod, in four phases from printing to your first walk command.

The finished Arcade Remote — 3D-printed cabinet with an arcade joystick and five snap-in buttons

What you’ll build

Inputs9 switches — joystick + 5 buttons
Build time2–3 hours after printing
Skill levelBeginner
ToolsHex key set, small screwdriver
Arcade remote — top view Arcade remote — back with the magnetic battery cover
Works with all three hexapods. The same remote drives Nougat ↗, Mochi ↗ and Macaroon ↗ — they all speak the same binary UDP protocol. Point the firmware at the robot’s access point and you’re done; see Phase 03.
No soldering required with the arcade controller board ↗ — the joystick harness plugs into its 5-pin header and the button wires push onto the 2×7 header. A bare ESP32-C6 SuperMini works too, if you wire the nine switches straight to the GPIOs in the pin map and feed it 5 V.

Specifications

ControllerESP32-C6 SuperMini on the RookiDroid remote carrier board
Inputs4-way microswitch joystick + 5 arcade push buttons — all digital, no analog stick and no deadzone
Feedback1 × WS2812 (NeoPixel) RGB status LED on GP8, visible through the window in the top panel
Power9 V battery → on-board Mini360 buck converter → 5 V, with a slide switch on the board
Connectivity2.4 GHz WiFi — the remote joins the robot’s own access point as a station, so no router is involved
Control6-byte binary UDP packets to 192.168.4.1:1234
Update rate20 Hz (50 ms loop), sent continuously including standby
Enclosure3D-printed body, cover, bottom plate and magnetically-latched battery cover

Safety notes

  • Check the 9 V polarity twice before the first power-up — the snap connector screws into a plain terminal block, and reversed leads can destroy the buck converter and the ESP32-C6
  • Power the robot up first, then the remote. The firmware waits for the robot’s access point before it does anything else
  • Give the robot room before you test. The remote starts sending commands the moment the LED turns green
  • Keep the small magnets clear of the battery leads while you press them in — they will happily jump to anything ferrous nearby

Print settings

Layer height0.2 mm
Nozzle0.4 mm
MaterialPLA
Printer profileBambu Lab H2S
The whole cabinet is one Bambu Studio projectarcade.3mf is a free download ↗, and the Fusion 360 source is arcade.f3d on GitHub. The cover is multi-material so the logo and the button outlines print in a second color — print it in a single color if you don’t have an AMS.

Printed parts

PartQtyNote
arcade body1Holds the controller board and the battery bay
arcade cover1Top panel with the joystick, button and LED cut-outs Multi-color
bottom1Closes the underside
battery cover1Magnetic latch — no screws

Hardware

All standard metric parts — available from Amazon, AliExpress, or local hardware stores. Sort them into labeled containers before you start.
ItemSpecQtyUse
ScrewM3 × 10 mm8Cover to body
NutM3 hex nut8Cover to body
ScrewM4 × 10 mm4Joystick mounting plate
NutM4 hex nut4Joystick mounting plate
ScrewM2 × 4 mm4Controller board to body
Magnet6 mm dia. × 2 mm4Hold the battery cover — half in the bay, half in the cover

Assembly order

1
Clean up the cut-outs. The buttons and the joystick shaft need a snug snap fit — deburr the openings and test-fit one button before pressing all five home
2
Mount the inputs in the cover. Snap the five 30 mm buttons in, then bolt the joystick plate down with the M4 screws and nuts
Buttons and joystick mounted in the cover
3
Mount the controller board in the body with the four M2 × 4 mm screws, keeping the USB-C port reachable for flashing
4
Wire it up — joystick harness into the 5-pin header, one wire from each button terminal to the 2×7 header. Full details in Phase 02 — Electronics
5
Fit the battery hardware. Screw the 9 V snap connector leads into the terminal block — minding polarity — and press the magnets into the battery bay and its cover so the pairs attract rather than repel
6
Close it up with the eight M3 screws and nuts, fit the bottom plate, then flash the firmware and power it on
Flash and test the board before you screw the cover down — a swapped button wire is much easier to fix with the cabinet still open.

Components

ImageComponentSpecQtyNote
Controller boardController boardESP32-C6 SuperMini on a carrier board — Mini360 buck converter, power switch, 2-pin battery terminal, 5-pin joystick header and a 2×7 button header1Purchase ↗ — or use a bare ESP32-C6 SuperMini and wire the switches straight to the GPIOs below
Arcade joystickArcade joystickMicroswitch joystick, 97 × 65 mm mounting plate, ~119 mm overall height, 5-pin harness1Ball top; the harness plugs into the joystick header
Arcade push buttonArcade push button30 mm snap-in button with a 2-terminal microswitch5Any five colors — four for the direction cluster, one for the special button
9 V battery connector9 V battery connectorSnap connector with flying leads1Screws into the 2-pin terminal block Mind polarity
9 V battery9 V batteryAlkaline or rechargeable1Sits in the bay behind the magnetic cover
Jumper wires2.8 mm spade or Dupont, 150–200 mm~11One per button terminal — the joystick comes with its own harness

Connection diagram

Arcade remote connection diagram
Every input is a plain switch to ground. The firmware enables the internal pull-ups (INPUT_PULLUP), so one terminal of each microswitch goes to its signal pin and the other to any GND pin — no 5 V wire runs to a button, and there is no debounce logic, because the command is re-sent every 50 ms anyway.

Pin map

InputSignalGPIOWhere it lands on the board
Joystick upJS_UPGP35-pin joystick header
Joystick downJS_DOWNGP25-pin joystick header
Joystick leftJS_LEFTGP05-pin joystick header
Joystick rightJS_RIGHTGP15-pin joystick header
Button — upBT_UPGP142×7 button header
Button — downBT_DOWNGP152×7 button header
Button — leftBT_LEFTGP182×7 button header
Button — rightBT_RIGHTGP192×7 button header
Button — specialBT_SPECIALGP202×7 button header
Status LEDPIN_RGBGP8On-board WS2812 of the SuperMini
The four direction buttons are the cluster outlined on the top panel; the button sitting on its own is the special (modifier) button.

Wiring

1
Plug the joystick harness into the 5-pin header — that covers all four directions and their ground in one connector
2
Run one wire from each button’s signal terminal to its GPIO on the 2×7 header, following the pin map above
3
Run the second terminal of each button to any GND pin — daisy-chaining the grounds is fine
4
Screw the 9 V connector leads into the 2-pin terminal block, red to +, black to −, and leave the slide switch off until you have double-checked it
5
Dress the wires so nothing is pinched between the cover and the body when you close the cabinet

Prerequisites

RequirementHow to install
Arduino IDE 2.xDownload from arduino.cc ↗
esp32 board packageVersion 3.x or newer (Espressif Systems) — required for ESP32-C6 support. Boards Manager → search esp32
Adafruit NeoPixelArduino IDE → Library Manager → search and install
WiFi, WiFiUdpIncluded with the esp32 board package — nothing extra to install

Upload steps

1
Download the firmware and open software/arcade/arcade.ino
2
Select board ESP32C6 Dev Module, and the port that appears when you plug in the USB-C cable
3
Check the WiFi settings near the top of the sketch match your robot — see the configuration below
4
Upload. If the port never enumerates, hold BOOT, tap RESET, release BOOT, and try again
5
Open the Serial Monitor at 115200 baud to watch the WiFi connection — it prints a dot every 500 ms until the robot’s access point answers, then the IP address it was given

Default configuration

As the sketch ships — set up for a Nougat. The table below has the values for the other hexapods.

ssid
hexapod_nougat
password
hexapod_1234
udpAddress
192.168.4.1
udpPort
1234
setup() blocks until the robot’s network is reachable, so power the robot up first. Until the remote connects it does nothing and the LED stays off — that is normal, not a fault.

Which hexapod are you driving?

The remote works with all three hexapods — only the ssid line changes. Every robot hosts its own access point at 192.168.4.1 and listens on port 1234, so password, udpAddress and udpPort stay as they ship.
RobotSet ssid toPasswordBuild guide
Hexapod Nougathexapod_nougathexapod_1234Build Nougat ↗
Hexapod Mochihexapodhexapod_1234Build Mochi ↗
Hexapod Macaroonhexapod_macaroonhexapod_1234Build Macaroon ↗
Changed your robot’s credentials in its own config.h? Match them here. Driving something else entirely works too — any robot that accepts the 6-byte packet on a known IP and port can be flown from this remote.

Status LED

ColorMeaning
OffStill connecting — or no power
GreenConnected — commands are being sent
RedWiFi lost. The sketch keeps running and recovers by itself when the robot comes back

How the sketch works

1
setup() puts all nine switch pins in INPUT_PULLUP, starts the NeoPixel, then blocks in connectToWiFi() until the robot’s AP accepts it
2
A WiFi event handler opens the UDP socket and turns the LED green on GOT_IP, or red on disconnect
3
loop() reads all nine pins, resolves exactly one command from them, sends it as a 6-byte packet, and waits 50 ms — a 20 Hz heartbeat
4
Nothing pressed resolves to CMD_STANDBY, which is why the robot stops the instant you let go — there is no separate stop button

Troubleshooting the upload

  • Board doesn’t enumerate over USB: hold BOOT, tap RESET, release BOOT, then upload
  • No ESP32C6 Dev Module in the board list: the esp32 board package is older than 3.x — update it in Boards Manager
  • Compile fails on Adafruit_NeoPixel.h: the library isn’t installed, or the IDE is still pointed at a non-ESP32 board
  • Nothing on the Serial Monitor: check the baud rate is 115200, and that the cable carries data rather than power only
Every 50 ms the firmware reads all nine switches and sends exactly one command. The joystick takes priority over the buttons, and nothing pressed sends CMD_STANDBY.

Control map

Joystick — walking
Upwalk forward
Downwalk backward
Leftsidestep left
Rightsidestep right
Up + Leftforward-left
Up + Rightforward-right
Down + Leftbackward-left
Down + Rightbackward-right
Buttons — joystick centered
Upwalk forward
Downwalk backward
Leftturn left
Rightturn right
Special + Upbody pitch
Special + Leftbody roll
Special + Rightbody yaw
Special + Downbody twist
Turbo. Hold the up button while pushing the joystick up for CMD_FAST_FORWARD, or the down button while pulling the joystick down for CMD_FAST_BACKWARD.

Commands sent

IDCommandControl
0CMD_STANDBYNothing pressed
1CMD_WALK_0Joystick up, or the up button
2CMD_WALK_180Joystick down, or the down button
3CMD_WALK_R45Joystick up + right
4CMD_WALK_R90Joystick right
5CMD_WALK_R135Joystick down + right
6CMD_WALK_L45Joystick up + left
7CMD_WALK_L90Joystick left
8CMD_WALK_L135Joystick down + left
9CMD_FAST_FORWARDJoystick up + up button Turbo
10CMD_FAST_BACKWARDJoystick down + down button Turbo
11CMD_TURN_LEFTLeft button
12CMD_TURN_RIGHTRight button
13CMD_CLIMB_FORWARDNot mapped to a control
14CMD_CLIMB_BACKWARDNot mapped to a control
15CMD_ROTATE_XSpecial + up — body pitch
16CMD_ROTATE_YSpecial + left — body roll
17CMD_ROTATE_ZSpecial + right — body yaw
18CMD_TWISTSpecial + down — body twist

UDP protocol

Each packet is 6 bytes, little-endian and unpadded, sent to 192.168.4.1:1234. The layout matches UdpControlPacket in the hexapod firmware — see the UDP command reference ↗ for the other packet types the robot understands.
OffsetFieldTypeValue
0magicuint80xA5 — motion command
1cmduint8Command ID from the table above
2–5seq_numuint32Increments on every packet

First test

1
Power the robot up first and give it clear space — it starts moving as soon as a command arrives
2
Slide the remote’s power switch on. The LED stays off while it hunts for the access point, then turns green
3
Nudge the joystick up — the robot should walk forward, and stop the moment you release it
4
Work around every input once: four joystick directions, four diagonals, four direction buttons, and the four special combinations
5
Happy with it? Close the cabinet with the M3 hardware and drop the 9 V battery in behind the magnetic cover

Troubleshooting

SymptomThings to check
LED stays off, Serial prints dots foreverThe robot isn’t powered up, or the SSID/password don’t match — setup() waits for the AP
LED is green but the robot ignores the remoteudpAddress and udpPort must match the robot, and the robot must be in its normal (non-calibration) mode
One direction never triggersCheck that switch’s signal wire and its GND — an unconnected INPUT_PULLUP pin just reads high forever
Diagonals don’t workThe joystick has a 4-way restrictor plate fitted — swap in the 8-way gate
Robot keeps moving after you let goStandby packets aren’t arriving — check the WiFi link; the robot should also have its own failsafe
The wrong action firesOnly one command is sent per cycle and the joystick wins — if a button acts like a joystick direction, check it isn’t wired to a joystick GPIO
Board doesn’t enumerate over USBHold BOOT, tap RESET, release BOOT, then upload
Full sources, print files and issue tracker: github.com/rookidroid/remote-arcade ↗ — MIT licensed, and build questions are welcome in Issues.