For Sistemas Digitales II (SDG2) at ETSIT UPM, my classmate Alicia and I built one of the most fun projects of my bachelor’s degree: a robotic combat game for the Raspberry Pi. Players drive a laser-equipped tricycle tank with an Xbox 360 controller — left stick to drive, right stick to aim the turret, trigger to fire an IR “laser” at your opponent. Each tank has limited lives, and the last one standing wins.Para Sistemas Digitales II (SDG2) en la ETSIT UPM, mi compañera Alicia y yo construimos uno de los proyectos más divertidos del grado: un juego de combate robótico para Raspberry Pi. Se conduce un triciclo tanque con láser con un mando Xbox 360 — stick izquierdo para conducir, derecho para la torreta, gatillo para disparar un “láser” IR al rival. Cada tanque tiene vidas limitadas; gana el último en pie.
The whole thing is written in C (gnu99) on top of wiringPi, with a small Python bridge that reads the controller through evdev and hands the state to the C program over a file polled every 10 ms. The full source is open on GitHub:Todo está escrito en C (gnu99) sobre wiringPi, con un pequeño puente en Python que lee el mando vía evdev y pasa el estado al programa en C mediante un archivo sondeado cada 10 ms. El código completo es open source en GitHub:
github.com/alejp1998/tricycle-tank
📺 Demo video from the SDG2 DIE-UPM course channel, showing the physical tank in action.
🔧 The hardware🔧 El hardware
- Raspberry Pi (40-pin GPIO, wiringPi) as the brainRaspberry Pi (GPIO de 40 pines, wiringPi) como cerebro
- Xbox 360 controller — wired or wireless, via the Python bridgeXbox 360 controller — con o sin cables, vía el puente en Python
- L298N dual H-bridge driving the two rear wheels (tricycle steering via hardware PWM)L298N dual H-bridge moviendo las dos ruedas traseras (dirección de triciclo vía hardware PWM)
- 2× SG90-class servos on a pan/tilt turret (software PWM)2× SG90-class servos en una torreta pan/tilt (software PWM)
- IR emitter + receiver for the “laser” and hit detection (pin 9 out, pin 11 in as a rising-edge interrupt)IR emitter + receiver para el “láser” y la detección de impactos (pin 9 out, pin 11 in como rising-edge interrupt)
- Buzzer for sound effects via
softTone(pin 23)Buzzer para efectos de sonido víasoftTone(pin 23)
⚙️ Four state machines on one 10 ms clock⚙️ Cuatro FSMs en un reloj de 10 ms
The architecture is the part I am proudest of: four parallel finite-state machines running on a shared 10 ms clock — the xbox360 FSM (input/debounce/joysticks), the player FSM (sound effects), the ruedas FSM (wheels/steering) and the torreta FSM (turret + laser shooting) — all built on a tiny reusable FSM engine (fsm.c) with the hit detection handled by a wiringPi interrupt service routine. The IR hit logic includes debounce and shoot-timeout so a single shot can never double-count.La arquitectura es la parte de la que estoy más orgulloso: cuatro máquinas de estado finitas en paralelo sobre un reloj compartido de 10 ms — la FSM xbox360 (input/debounce/joysticks), la player (efectos de sonido), la ruedas (ruedas/dirección) y la torreta (torreta + disparo láser) — con un mini motor de FSM reutilizable (fsm.c) y la detección de impactos en una ISR de wiringPi. La lógica de impacto IR incluye debounce y shoot-timeout para que un disparo nunca cuente doble.
The theory, in plain words: a finite-state machine is a flowchart the program actually lives inside — exactly one state active at a time, and every arrow between states has a condition. Formally, \[ \delta : S \times I \rightarrow S \] is a transition function mapping (current state, input event) to the next state. And PWM — how you control a motor with 0s and 1s — fakes “in between” by switching very fast: \[ \text{duty} = \frac{t_{\text{on}}}{T} \times 100\% \] The duty cycle sets the effective voltage, i.e. the motor speed; the wheels use hardware PWM while the turret servos use software PWM, where the pulse width itself encodes the angle (1 ms ≈ 0°, 1.5 ms ≈ 90°, 2 ms ≈ 180°).
Sound effects were a highlight: the buzzer plays Despacito, Game of Thrones, Tetris and Star Wars note tables — selectable from the controller’s D-Pad and played with the A button. Because the FSM engine and timer modules are pure C, the project ships with hardware-free tests: make test runs the FSM unit tests and make check syntax-checks every source on any host using wiringPi stubs — no Raspberry Pi required.Los efectos de sonido fueron un punto fuerte: el buzzer toca tablas de notas de Despacito, Game of Thrones, Tetris y Star Wars — seleccionables desde el D-Pad y con el botón A. Como el motor de FSM y los timers son C puro, el proyecto trae tests sin hardware: make test ejecuta los unit tests de las FSM y make check valida la sintaxis con stubs de wiringPi — sin Raspberry Pi.
🎮 Play it in the browser🎮 Juega en el navegador
Years later I ported the game to the browser: a PixiJS web edition deployed on GitHub Pages with the same rules as the real tank — 10-bullet magazine (auto-reloads), 10 hits to win, 5 hull points to lose. Drive with WASD/arrows, aim with the mouse, fire with click/Space and reload with R, while AI tanks hunt you across three difficulty levels — 🟢 Training Grounds (2 target tanks), 🟡 Industrial Zone (3 hunters) and 🔴 Fortress Siege (4 relentless tanks, 9 walls) — and crates absorb laser fire. It even plays the original Tetris and Star Wars buzzer tables through WebAudio:Años después porté el juego al navegador: una edición web con PixiJS en GitHub Pages con las mismas reglas que el tanque real — cargador de 10 balas (recarga automática), 10 impactos para ganar, 5 de casco para perder. Conduce con WASD/flechas, apunta con el ratón, dispara con clic/Espacio, recarga con R, mientras tanques AI te cazan en tres niveles — 🟢 Training Grounds (2 objetivos), 🟡 Industrial Zone (3 cazadores) y 🔴 Fortress Siege (4 tanques, 9 muros) — y los cajones absorben el láser. Toca incluso las tablas originales de Tetris y Star Wars vía WebAudio:
alejp1998.github.io/tricycle-tank
The in-game Guide modal explains the whole project — hardware, FSMs and controller mapping — and the web core ships its own test suite verifying the browser port against the original game logic.El modal Guide del juego explica todo el proyecto — hardware, FSMs y mapeo del mando — y el core web trae su propia suite de tests que verifica el port al navegador contra la lógica original.
At the other end of the scale, Training Grounds (easy) is where you learn the ropes — two target tanks, an open grid and a relaxed pace:En el otro extremo, Training Grounds (fácil) es donde aprendes los básicos — dos tanques objetivo, rejilla abierta y ritmo relajado:
GitHub RepositoryGitHub Repository Play the GamePlay the Game VideoVideo Raspberry Pi · wiringPi