

Chopper body v2
Chopper body firmware — drive motors, dome rotation, servos, audio, encrypted ESP-NOW, PCB design
- Size
- 111.5 × 91.5 mm
- Copper layers
- 2
- Thickness
- 1.6 mm
- Parts
- 27
- Nets
- 24
- Pads
- 150
- Tracks, vias
- 265, 0
- Routed
- Yes
- KiCad
- KiCad 10.0 (file 20260206)
- Created
- 14 Aug 2026
- Updated
- 5 Sep 2026
Overview
Chopper body v2 is an open-source printed circuit board by andcoderes, from the andcoderes/Chopper-Body repository on GitHub. It is a 2-layer board of 111.5 × 91.5 mm with 27 parts on 24 nets, routed with 265 tracks and 0 vias.
Its parts include 16 resistors and 3 ICs. It belongs with the robots and motor control boards and the ESP32 and RISC-V boards of this library.
README
README.mdChopper Body V2
Acknowledgments
This firmware was strongly influenced by the Amidala firmware by thePunderWoman.
ESP32-based body controller for the Chopper robot. Controls drive motors, dome rotation, body servos, MP3 audio, and a bubble machine via ESP-NOW commands from the controller.
Features
- MDDS30 drive motors — PWM Independent mode, differential steering
- Cytron MD10C dome rotation — bidirectional speed control
- Pololu Maestro servos — scripted body animations via Serial1
- MP3 audio player — serial protocol playback via Serial2
- Bubbles relay — on/off control
- ESP-NOW — encrypted communication with the controller
- WiFi AP + OTA — web interface at
chopper.bodyfor firmware updates - Safety timeout — motors stop after 500ms without commands or on ESP-NOW disconnect
ESP32 Pin Assignments
| Function | GPIO | Type | Notes |
|---|---|---|---|
| MDDS30 IN1 (left dir) | 4 | Digital out | LOW = forward, HIGH = reverse. 10k pull-down on board |
| MDDS30 AN1 (left speed) | 17 | LEDC PWM | 5 kHz, 8-bit. 10k pull-down on board |
| MDDS30 IN2 (right dir) | 16 | Digital out | LOW = forward, HIGH = reverse. 10k pull-down on board |
| MDDS30 AN2 (right speed) | 5 | LEDC PWM | 5 kHz, 8-bit. 10k pull-down on board |
| Cytron MD10C DIR (dome dir) | 14 | Digital out | 10k pull-down on board |
| Cytron MD10C PWM (dome speed) | 27 | LEDC PWM | 5 kHz, 8-bit. 10k pull-down on board |
| Dome home sensor | 26 | Digital in | A3144 hall-effect, 10k+20k divider |
| Dome encoder A | 25 | Digital in | 10k+20k divider |
| Dome encoder B | 33 | Digital in | 10k+20k divider |
| Pololu Maestro TX | 32 | Serial1 TX | 9600 baud |
| Pololu Maestro RX | 35 | Serial1 RX | Input-only. 1k + 2k voltage divider (5V -> 3.3V) |
| MP3 Player TX | 19 | Serial2 TX | 9600 baud |
| MP3 Player RX | 18 | Serial2 RX | 1k + 2k voltage divider (5V -> 3.3V) |
| Bubbles relay | 21 | Digital out | Relay module, active HIGH |
| USB Debug TX | 1 | Serial0 TX | 115200 baud |
| USB Debug RX | 3 | Serial0 RX | 115200 baud |
GPIO12 and GPIO13 (previously used for MDDS30) are intentionally left unconnected — GPIO12 is an ESP32 boot-strapping pin (flash voltage select) and stray HIGH signals on it during power-up can put the board in a reset loop. GPIO22/23 (previously Maestro) and GPIO34 (previously dome encoder B) are also now unused.
MDDS30 DIP Switch Setting
0b10110100 — PWM Independent mode. All three hardware UARTs remain free.
Serial Port Allocation
| UART | GPIO TX | GPIO RX | Baud | Device |
|---|---|---|---|---|
| Serial0 | 1 | 3 | 115200 | USB debug console |
| Serial1 | 32 | 35 | 9600 | Pololu Maestro servo controller |
| Serial2 | 19 | 18 | 9600 | MP3 player module (Catalex-style, requires "select TF card" init) |
Build
Requires PlatformIO.
Before your first build, set up ESP-NOW encryption keys — see
Generating ESP-NOW keys below. The build will
fail with a clear error if .env is missing.
# One-time setup: copy the template and fill in real keys
cp .env.example .env
# Compile firmware
pio run
# Upload to ESP32
pio run --target upload
# Run unit tests (native)
pio test -e native
# Monitor serial output
pio device monitorProject Structure
pcb/ # KiCad project (schematic, board, gerbers)
firmware/
platformio.ini
.env.example # Template for ESP-NOW keys — copy to .env
partitions/ota_4MB.csv
scripts/load_secrets.py # Pre-build: .env -> include/secrets.h
scripts/embed_web.py # Pre-build: inlines CSS into HTML
web/
assets/common.css # Shared styles
index.html # Home page (info + links)
update.html # OTA firmware upload page
include/
web_pages.h # AUTO-GENERATED by embed_web.py
secrets.h # AUTO-GENERATED by load_secrets.py, gitignored
src/
config.h # Pin definitions, timeouts (keys come from secrets.h)
main.cpp # Setup/loop, WiFi, command dispatch
communication/
MessageTypes.h # BodyCommand / BodyTelemetry structs
EspNowController.h/.cpp # ESP-NOW encrypted communication
motor/
MotorController.h/.cpp # MDDS30 + L298N motor control
servo/
ServoController.h/.cpp # Pololu Maestro body servosESP-NOW Protocol
Controller -> Body (msgType = 3, 26 bytes)
| Field | Type | Description |
|---|---|---|
| msgType | int8 | Always 3 |
| status | int8 | 0 = buttons, 1 = movement, 2 = settings, -1 = connection |
| lx | int8 | Drive X axis: -100 to 100 |
| ly | int8 | Drive Y axis: -100 to 100 |
| domeSpeed | int8 | Dome rotation: -100 to 100 |
| audioTrack | uint8 | 0 = none, 1-255 = play track |
| volume | uint8 | 0-30 |
| bubbles | uint8 | 0 = off, 1 = on |
| button | char[8] | Servo button name (e.g. "y", "du") |
| macro | int16[4] | Servo macro IDs |
| connectionStatus | int8 | 0 = disconnected, 1 = connected |
| padding | uint8 | Alignment padding |
Body -> Controller (msgType = 4, 8 bytes)
| Field | Type | Description |
|---|---|---|
| msgType | int8 | Always 4 |
| connected | uint8 | 1 if ESP-NOW link active |
| motorsActive | uint8 | 1 if drive/dome motors running |
| audioPlaying | uint8 | 1 if audio track playing |
| uptimeMs | uint32 | Milliseconds since boot |
WiFi Access Point
- SSID: ChopperBody
- IP: 192.168.5.1
- mDNS: chopper-body.local
- Pages:
/(home),/update(OTA) - API:
/api/info(JSON: version, uptime, heap, MAC)
Generating ESP-NOW keys
This board talks to the other Chopper boards over encrypted ESP-NOW. Before
your first build, generate two 16-byte (128-bit) keys — a Primary Master Key
(PMK) and a Local Master Key (LMK) — and put them in a .env file
(gitignored, never commit it) at the project root:
cp .env.example .envGenerate two random 16-byte keys, each as 32 hex characters:
# Option 1: openssl
openssl rand -hex 16 # run twice — once for PMK_KEY, once for LMK_KEY
# Option 2: python
python3 -c "import secrets; print(secrets.token_hex(16))"Edit .env and paste the values in:
PMK_KEY=<first random hex string>
LMK_KEY=<second random hex string>These two keys must be byte-for-byte identical across chopper body,
chopper head, and chopper receiver — generate them once and copy the same
.env values into all three projects. Mismatched keys mean the boards can't
decrypt each other's ESP-NOW packets and the mesh will never connect.
.env also holds CONTROLLER_MAC — the controller's WiFi MAC address (as
hex, e.g. C0CDD6CA29E0), which must likewise be identical across all three
firmware projects — and WIFI_AP_PASSWORD, the password for this board's
local OTA-update access point (pick your own, at least 8 characters; unlike
the other values, this one doesn't need to match the other boards).
scripts/load_secrets.py runs automatically before every build (see
platformio.ini) and turns .env into include/secrets.h, which
config.h includes. Neither .env nor the generated secrets.h are
committed to git — the build fails with a clear error if .env is missing.
Parts
27| Qty | Value | Footprint | References |
|---|---|---|---|
| 9 | 10K | Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_Horizontal | R1, R3, R5, R7, R8, R9, R10, R11, R12 |
| 3 | 20K | Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_Horizontal | R2, R4, R4 |
| 2 | 1K | Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_Horizontal | R13, R15 |
| 2 | 2K | Resistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_Horizontal | R14, R16 |
| 1 | 5v_GND | TerminalBlock:TerminalBlock_Xinya_XY308-2.54-2P_1x02_P2.54mm_Horizontal | Power1 |
| 1 | AN1_IN1_GND | TerminalBlock:TerminalBlock_Xinya_XY308-2.54-3P_1x03_P2.54mm_Horizontal | foot_Motor_2 |
| 1 | DIR_PWM_GND | TerminalBlock:TerminalBlock_Xinya_XY308-2.54-3P_1x03_P2.54mm_Horizontal | Cytron_MD10C1 |
| 1 | IN1_IN2_GND | TerminalBlock:TerminalBlock_Xinya_XY308-2.54-3P_1x03_P2.54mm_Horizontal | foot_Motor_1 |
| 1 | XIAO-ESP32-C6-SMD | chopper_library:XIAO-ESP32-C6-DIP | XIAO-ESP32-C6-SMD1 |
| 1 | motor_head_enc_a_enc_b_5v_GND | TerminalBlock:TerminalBlock_Xinya_XY308-2.54-4P_1x04_P2.54mm_Horizontal | Encoder_motor1 |
| 1 | sensor_5v_GND | TerminalBlock:TerminalBlock_Xinya_XY308-2.54-3P_1x03_P2.54mm_Horizontal | home_sensor1 |
| 1 | ~ | chopper_library:Pololu_Maestro_12 | U3 |
| 1 | ~ | chopper_library:ESP32_30pin | U1 |
| 1 | ~ | chopper_library:audio_trigger | U4 |
The 26 most common of 27 parts.
Questions
How big is the Chopper body v2 PCB?
It measures 111.5 × 91.5 mm and has 2 copper layers, 1.6 mm thick.
What parts does the Chopper body v2 use?
27 parts. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.
Where can I download the Chopper body v2 KiCad files?
From the andcoderes/Chopper-Body repository on GitHub, at the commit this page was made from: the board is pcb/chopper_body_v2.kicad_pcb and its schematic pcb/chopper_body_v2.kicad_sch. PCBKit opens the board in the browser, with nothing to install.
Can I use the Chopper body v2 design in my own project?
Its repository states no licence, so its author keeps all rights: ask andcoderes before reusing the design.
Similar boards
- 2 layers47.6 × 27.3 mm

- 2 layers77.4 × 77.4 mm

CORAZON LEDS CON FADE
hackclub/OnBoard
💡 Join 1,000 teenagers and make your first PCB with a $100 grant!
27 parts915 - 2 layers44.2 × 29.3 mm

- 2 layers40 × 40 mm

- 2 layers22 × 42 mm

Nvme bm to e
CRImier/MyKiCad
My KiCad boards. Can't be bothered uploading individually anymore, so here you go.
27 parts253GPL-3.0 - 2 layers80 × 61 mm

PicoProducer
pjgpetecodes/pico-streamdeck
This project is an OBS Controller using a Raspberry Pi Pico and CircuitPython.
27 parts230MIT - 2 layers186 × 45 mm

AddOnD
freeDSP/freeDSP-aurora
freeDSP ADAU1452 with 8 analog input, 8 analog outputs, S/P-DIF I/O, ADAT I/O, USB Audio Class2, WiFi, Bluetooth
27 parts226CC-BY-SA-4.0 - 2 layers29.6 × 40.6 mm

Dbwifi revB
ugcs/ddc
Drone Show Software is software designed for drone shows, allowing flight path planning as well as control of dozens of drones simultaneously in a synchronized manner.
27 parts223