Skip to the content
PCBKitNew board
2 layers111.5 × 91.5 mmChopper body v2, the printed circuit board in 3D

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.md

Chopper 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.body for firmware updates
  • Safety timeout — motors stop after 500ms without commands or on ESP-NOW disconnect

ESP32 Pin Assignments

FunctionGPIOTypeNotes
MDDS30 IN1 (left dir)4Digital outLOW = forward, HIGH = reverse. 10k pull-down on board
MDDS30 AN1 (left speed)17LEDC PWM5 kHz, 8-bit. 10k pull-down on board
MDDS30 IN2 (right dir)16Digital outLOW = forward, HIGH = reverse. 10k pull-down on board
MDDS30 AN2 (right speed)5LEDC PWM5 kHz, 8-bit. 10k pull-down on board
Cytron MD10C DIR (dome dir)14Digital out10k pull-down on board
Cytron MD10C PWM (dome speed)27LEDC PWM5 kHz, 8-bit. 10k pull-down on board
Dome home sensor26Digital inA3144 hall-effect, 10k+20k divider
Dome encoder A25Digital in10k+20k divider
Dome encoder B33Digital in10k+20k divider
Pololu Maestro TX32Serial1 TX9600 baud
Pololu Maestro RX35Serial1 RXInput-only. 1k + 2k voltage divider (5V -> 3.3V)
MP3 Player TX19Serial2 TX9600 baud
MP3 Player RX18Serial2 RX1k + 2k voltage divider (5V -> 3.3V)
Bubbles relay21Digital outRelay module, active HIGH
USB Debug TX1Serial0 TX115200 baud
USB Debug RX3Serial0 RX115200 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

UARTGPIO TXGPIO RXBaudDevice
Serial013115200USB debug console
Serial132359600Pololu Maestro servo controller
Serial219189600MP3 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.

bash
# 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 monitor

Project Structure

text
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 servos

ESP-NOW Protocol

Controller -> Body (msgType = 3, 26 bytes)

FieldTypeDescription
msgTypeint8Always 3
statusint80 = buttons, 1 = movement, 2 = settings, -1 = connection
lxint8Drive X axis: -100 to 100
lyint8Drive Y axis: -100 to 100
domeSpeedint8Dome rotation: -100 to 100
audioTrackuint80 = none, 1-255 = play track
volumeuint80-30
bubblesuint80 = off, 1 = on
buttonchar[8]Servo button name (e.g. "y", "du")
macroint16[4]Servo macro IDs
connectionStatusint80 = disconnected, 1 = connected
paddinguint8Alignment padding

Body -> Controller (msgType = 4, 8 bytes)

FieldTypeDescription
msgTypeint8Always 4
connecteduint81 if ESP-NOW link active
motorsActiveuint81 if drive/dome motors running
audioPlayinguint81 if audio track playing
uptimeMsuint32Milliseconds 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:

bash
cp .env.example .env

Generate two random 16-byte keys, each as 32 hex characters:

bash
# 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:

text
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
QtyValueFootprintReferences
910KResistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_HorizontalR1, R3, R5, R7, R8, R9, R10, R11, R12
320KResistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_HorizontalR2, R4, R4
21KResistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_HorizontalR13, R15
22KResistor_THT:R_Axial_DIN0204_L3.6mm_D1.6mm_P7.62mm_HorizontalR14, R16
15v_GNDTerminalBlock:TerminalBlock_Xinya_XY308-2.54-2P_1x02_P2.54mm_HorizontalPower1
1AN1_IN1_GNDTerminalBlock:TerminalBlock_Xinya_XY308-2.54-3P_1x03_P2.54mm_Horizontalfoot_Motor_2
1DIR_PWM_GNDTerminalBlock:TerminalBlock_Xinya_XY308-2.54-3P_1x03_P2.54mm_HorizontalCytron_MD10C1
1IN1_IN2_GNDTerminalBlock:TerminalBlock_Xinya_XY308-2.54-3P_1x03_P2.54mm_Horizontalfoot_Motor_1
1XIAO-ESP32-C6-SMDchopper_library:XIAO-ESP32-C6-DIPXIAO-ESP32-C6-SMD1
1motor_head_enc_a_enc_b_5v_GNDTerminalBlock:TerminalBlock_Xinya_XY308-2.54-4P_1x04_P2.54mm_HorizontalEncoder_motor1
1sensor_5v_GNDTerminalBlock:TerminalBlock_Xinya_XY308-2.54-3P_1x03_P2.54mm_Horizontalhome_sensor1
1~chopper_library:Pololu_Maestro_12U3
1~chopper_library:ESP32_30pinU1
1~chopper_library:audio_triggerU4

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 mmMmc usb 1, the printed circuit board in 3D

    Mmc usb 1

    rusefi/rusefi

    rusefi - GPL internal combustion engine control unit

    27 parts1.2k
  • 2 layers77.4 × 77.4 mmCORAZON LEDS CON FADE, the printed circuit board in 3D

    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 mmI2c tiny usb, the printed circuit board in 3D

    I2c tiny usb

    harbaum/I2C-Tiny-USB

    Cheap and simple I²C to USB interface

    27 parts454
  • 2 layers22 × 42 mmNvme bm to e, the printed circuit board in 3D

    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 mmPicoProducer, the printed circuit board in 3D

    PicoProducer

    pjgpetecodes/pico-streamdeck

    This project is an OBS Controller using a Raspberry Pi Pico and CircuitPython.

    27 parts230MIT
  • 2 layers186 × 45 mmAddOnD, the printed circuit board in 3D

    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 mmDbwifi revB, the printed circuit board in 3D

    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