Building Custom Fishing Rod Controller For Chaotic Party Game
Dango Dynamics dove into the process of building a physical alternative controller for Chop Chop Together's gamescom showcase, exploring the design challenges, technical decisions, and lessons learned along the way.
Introduction
At its core, Chop Chop Together is a fast-paced, chaotic co-op party game for 1 to 4 players built in Unreal Engine 5. Players are thrown into shared, high-pressure workspaces divided into three distinct professions: Fishing, Warehouse Management, and Blacksmithing. While each job features completely different core mechanics and internal systems, the fundamental goal remains the same: complete incoming orders under brutal time constraints to progress through increasingly unforgiving levels.
The comedy and the chaos come from four people trying to execute multi-step processes at once without colliding, all while surviving dynamic map hazards like slippery ice floors, order-stealing seagulls, player-pulling tornadoes, stray cars, and passing streetcars.
Chop Chop Together
Chop Chop Together
Chop Chop Together
From Childhood Nostalgia to a Gamescom Vision
Our obsession with physical alternative controllers runs deep. It stems from a very old, foundational love for tactile gaming the kind of pure joy that started with hunting ducks with the NES Zapper in the arcade era and evolved into snapping plastic attachments onto Wii Remotes. We've always believed that physically touching the game world transforms the player's connection to the experience.
The custom fishing rod didn't start in a boardroom as a calculated marketing hook; it started with a late-night video share and a sudden spark of inspiration. Our co-founder and game developer, Melike, was scrolling online when she stumbled upon footage from an unconventional digital interactive showcase. Unlike standard gaming expos, this exhibition featured bespoke, oversized physical interfaces: a massive pair of mechanical scissors that triggered digital cuts on screen with every snip, and a giant trash can that registered physical paper tosses as software inputs.
She immediately dropped the video into our team chat with a single sentence: "I found how we're going to steal the show at Gamescom."
In massive, crowded convention halls, pulling attendees away from AAA booths is notoriously difficult. But deep down, we knew an irresistible physical hook was the answer. (Spoiler alert: it worked better than we ever imagined).
The moment the idea landed, our other co-founder and Game Developer, Kerem, a die-hard hardware enthusiast with a fleet of 3D printers and a history of building custom tech, immediately set out to turn the visual concept into functional hardware.
He dove into hardware forums, microcontroller datasheets, and robotics subreddits to map out the exact sensor array and signal path needed. Before a single component arrived, Kerem pulled up our in-game fishing rod model in Blender, completely dismantling its internal geometry to engineer custom mounting points, battery bays, and cable channels. Once the blueprints were locked in and the parts were ordered, we moved straight from software design to physical manufacturing.
In-Game Fishing Mechanic & Physical Translation
In-game, reeling is a continuous rotary gesture on the right stick. The important thing, and it drove every decision that followed, is what the game actually measures. It doesn't care where the stick is. It cares how fast the stick's angle is changing, and in which direction.
Concretely: the input vector becomes an angle via Atan2, the difference against the previous frame's angle is computed in a wrap-safe way so that crossing 360° doesn't register as an enormous jump, the sign of that difference gives clockwise versus counter-clockwise, and the magnitude divided by elapsed time gives rotation speed. Speed and direction are what the minigame consumes.
Why that detail decides the hardware: Because the game reads angular velocity rather than position, the controller never has to reproduce a thumbstick. It has to produce a believable rotating vector. That's what let us drive the game from a rotary encoder on a crank: the firmware doesn't report where a stick is being pushed, it synthesizes a point orbiting a circle at whatever rate the crank is turning.
From there, the physical mapping picked itself:
- The reel crank: Rotation. The one-to-one translation, and the main reason the project exists.
- Casting: An actual swing. A gesture your arm already knows, detected by an accelerometer and gyroscope rather than a button.
- Walking: A thumbstick on the grip. Unglamorous but necessary; you still have to move around the boat between catches, and handing the player back a second controller for that would break the illusion.
- Picking things up and using them: One button under the thumb, with a short press and a long press as separate actions, so one button covers two verbs.
First Prototype & Early Experiments
The first prototype was a bench assembly: no enclosure, no rod, just the signal path. A Seeed XIAO ESP32-S3, later a Heltec V3 board whose onboard OLED let us watch live values without a PC attached, and three sensors:
- An AS5600 12-bit magnetic rotary encoder for the reel. A diametrically magnetized magnet sits on the crank shaft, and the chip reads its angle contactlessly. We chose it over a potentiometer, which wears out and has a dead zone where it wraps, and over an optical encoder, which needs a slotted disc and more assembly.
- An LSM6DS3TR-C six-axis accelerometer and gyroscope for the cast gesture.
- A KY-023 thumbstick module for movement.
Both sensors are I²C, and both sat on a plug-together connector chain, which meant the sensor rig could be rearranged in seconds while we were still deciding what the rod's internals looked like.
The Bluetooth version and why it was thrown away: The first transport was BLE: the microcontroller advertised itself as a Bluetooth HID gamepad, so there was no dongle and no cable. It worked. Windows saw a gamepad, the axes moved, the buttons registered.
Then it ran into a wall that had nothing to do with our code. A Bluetooth gamepad on Windows is always a HID/DirectInput device, and it can never be an XInput device. XInput is Microsoft-proprietary, and there is no wireless XInput protocol a third party can implement. A great many games and tools only look at XInput. Our rod appeared in the Windows controller panel, moved its axes there, and did nothing in the game.
There was a workaround, and we ran it: a virtual-pad driver plus a mapper application, translating DirectInput to a synthetic XInput device on the PC. Functional, tested, fine for a developer's own machine. Useless as a product. It means the streamer opens the box and, before catching a single fish, installs a kernel-level driver and configures a mapper on stream. That's not a marketing kit, that's a support ticket.
What the rebuild had to be: The requirement became: the PC must recognize it with zero installs. Two consequences fell out immediately:
- First, the radio link and the USB link had to be separated. The rod keeps its battery and its radio; a small USB dongle becomes the thing the PC actually sees, and the dongle is a plain USB gamepad that any operating system already understands.
- Second, chip selection stopped being a preference and became a constraint, because only some parts can act as a USB device at all. We looked at the ESP32-C5 and found it has no USB-OTG device support Espressif's own tracker marks it "Won't Do." The S2, S3 and C6 can. That chose the dongle for us: a LILYGO T-Dongle-S3, an ESP32-S3 in a USB-A stick form factor that happens to carry a small color LCD.
For the radio, we moved from BLE to ESP-NOW, Espressif's connectionless 2.4 GHz protocol. It broadcasts, so there is no pairing handshake and, more to the point, no pairing UI for the person unboxing it.
The Final Physical Build
It settled into two objects:
The Rod (Battery powered):
- An Adafruit ESP32 Feather V2 as the brain;
- The AS5600 encoder reading the reel shaft, and the IMU for cast detection, both on the I²C chain;
- The thumbstick and its push switch;
- Two 18650 cells wired in parallel for 6400 mAh, a slide switch in series on the positive lead, charging through the board's own USB port;
- A 3D-printed body split into segments joined by bayonet fittings, so it can be packed down and assembled.
The Dongle (Bus-powered):
- The T-Dongle-S3, plugged straight into a USB-A port;
- Its 80×160 LCD runs a live diagnostic view: link state, packet rate, signal strength, rod battery, a dot for the thumbstick, a dot orbiting a ring for the reel with live RPM in the middle, and indicators for the three buttons.
Three decisions that came from datasheets, not taste:
- Cells in parallel, never in series. Two 18650s in series are 8.4 V and destroy the board. The pair is voltage-matched before being joined;
- Analog inputs live only on the first ADC bank. On the classic ESP32, the second ADC bank is shared with the WiFi radio; read a pin there while the radio is up and the value is garbage. That one line of the datasheet dictated the thumbstick's wiring;
- The I²C sensor rail on that board is power-gated behind a GPIO, which must be driven high at boot or the sensors simply aren't present. It's a two-line fix that costs an afternoon if you don't know it.
Software, Tools & Telemetry
- Blender for the rod body and the bayonet segment joints, printed on FDM;
- Arduino IDE with the ESP32 core for both firmwares, plus Adafruit's sensor and display libraries;
- Platform inspection tools for verification: Windows controller panel, Linux input device list (`evtest`), and browser gamepad testers;
- Unreal's own input diagnostics: the raw input plugin's verbose logging and the live action-value debug overlay.
The Tuning Interface:
The rod serves as its own tuning interface. It brings up a WiFi access point and hosts a live page: connect a phone to it, and while the rod is in your hand, you can watch live acceleration and rotation magnitudes, peak-hold trackers, reel RPM, raw thumbstick values, and magnet status, and move every threshold with a slider. Changes apply instantly; a save button writes them to flash so they survive a power cycle.
Calibrating a swing from a laptop with a USB cable tethering the thing you need to swing is miserable and produces bad numbers. With a phone in your pocket it takes minutes, and you're calibrating the real gesture rather than an approximation of it performed while leaning over a desk. Constraint worth knowing: The access point is pinned to the same radio channel as the ESP-NOW link. WiFi and ESP-NOW share the radio, so if the board ever joined a normal network, it would follow that network's channel and the rod-to-dongle link would silently stop working.
Telemetry as a deliberate feature: The dongle prints a status line once a second: link state, packet rate, signal strength, battery, and the exact bytes it is about to hand the operating system. Almost every difficult bug in this project was some version of "one of these layers is lying to me," and having each layer state plainly what it just did collapsed that question every single time.
Communication & Unreal Integration
Communication occurs over three hops, each hiding its complexity from the next:
Rod (Encoder + IMU + Thumbstick) → Dongle (ESP-NOW 50 Hz, 20-byte packet) → Unreal Engine (USB HID, 4 axes/8 buttons)
- Rod to Dongle: An ESP-NOW broadcast 50 times a second carrying a compact 20-byte packet: device ID, counter, 4 stick axes, button bitfield, reel RPM telemetry, battery percentage, and flag bytes. Broadcasting eliminates pairing handshakes. Device IDs allow multiple rods and dongles to operate in the same room without cross-talk.
- Dongle to PC: The dongle presents a standard USB gamepad with a hand-written report descriptor (4 unsigned 8-bit axes, centered at midpoint, 8 buttons). Requires no external drivers.
- PC to Unreal: Unreal Engine's raw input plugin is configured to recognize our specific vendor and product IDs. We emit standard gamepad keys directly, allowing the rod to arrive looking like an Xbox pad (Reel = Right Stick, Thumbstick = Left Stick, Actions = Face Buttons).
Because the rod resolves to ordinary gamepad keys, the game's input mappings needed no rod-specific entries at all. Nothing in the project knows the rod exists. If the dongle hears nothing for 300 milliseconds, it automatically centers the axes and releases every button to prevent runaway character movement.
Design Challenges, Refinements & Bug Fixes
Making rotation feel continuous: When cranking stops, returning the virtual stick to center produced a severe instantaneous angle delta spike, causing in-game micro-stutters (the "tak-tak"). The fix was to freeze the virtual stick vector in place on the circle when cranking stops, maintaining an angle delta of zero.
Camera spin edge case: Freezing the stick position caused the third-person camera to spin endlessly (since the stick was held at 95% deflection). We added a 500ms timeout: keep freezing through sub-second cranking gaps, but if inactive for over half a second, treat as idle and reset the stick vector to center.
Gesture calibration: Cast detection triggers when linear acceleration and angular rate both exceed defined thresholds. Peak-hold trackers were built into the firmware to log real arm swings, setting thresholds at 60-70% of peak values, followed by a cooldown period to prevent double-casting from a single swing.
Switch debouncing: Tactile switch micro-bounces reset the hold timer, breaking long-press functionality. Software debouncing resolved this, updating logic so short presses fire once on release (only if long-press thresholds were never crossed).
Hardware diagnostics: To catch broken hardware or offset pin headers without a multimeter, firmware boot checks probe pins with internal pull-ups and pull-downs to detect open circuits or shorts automatically.
USB Charging compatibility: The USB-C pass-through extension lacked configuration resistor pins (CC pins), preventing modern C-to-C smart chargers from delivering power. The rod charges reliably via legacy USB-A to USB-C cables.
Playtesting Insights & Iteration
Every refinement was driven by real human observations rather than code inspection:
- The "tak-tak" issue was originally reported by playtesters not as a math anomaly, but as "the reel feeling like it's catching or sticking."
- The infinite camera rotation was discovered instantly once playtesting moved from a fixed camera test scene to a dynamic third-person setup;
- Desktop calculations for swing acceleration were completely inaccurate compared to natural player motion, prompting the creation of live telemetry trackers.
Advice for Indie Developers Building Custom Controllers
- Prototype the signal path before modeling the enclosure: A crank, encoder, breadboard, and live screen values yield more answers in an afternoon than a 3D shell.
- Decide early if it must be plug-and-play: Requiring driver installation instantly limits adoption.
- Understand hardware & platform constraints: Wireless XInput cannot be custom-implemented, and not all microcontrollers support USB HID.
- Build a wireless tuning interface: Calibration parameters will change constantly during physical testing.
- Provide clear telemetry across all layers: Instrument each boundary to isolate bugs quickly.
- Test on external machines with other peripherals attached: Uncover hidden driver or controller mapping conflicts.
- Design so the game logic remains agnostic: Map custom hardware to standard game controller inputs to avoid long-term maintenance overhead.
- Ensure the physical action serves an existing mechanic: Custom hardware should improve how an existing input feels, rather than inventing novel mechanics just to justify the hardware.