Space IO is a two-player pygame game with haptic steering/throttle controls. The Python game runs on the laptop, a Teensy 4.1 reads encoder values and forwards motor commands, and the Hapkit board drives the motors.
game_engine/- Python game, haptics, input, rendering, and configuration.teensy_controller/- Teensy 4.1 firmware for encoders and force forwarding.hapkit_controller/- Hapkit motor receiver firmware.
-
Clone the repo.
git clone https://github.com/timbercarey/space-io.git cd space-io -
Create and activate a Python virtual environment.
python3 -m venv .venv source .venv/bin/activate -
Install the Python requirements.
pip install -r game_engine/requirements.txt
-
Check the serial port in
game_engine/config.py.SERIAL_PORT = '/dev/cu.usbmodem199646501' BAUD_RATE = 1000000 SIMULATION_MODE = False
Update
SERIAL_PORTif your Teensy appears under a different port. On Windows this will usually look likeCOM3or similar. -
Make sure the Arduino IDE can build the firmware.
- Install Teensyduino / Teensy board support.
- Install the Teensy
QuadEncoderlibrary. - Confirm Arduino IDE can see the Teensy serial port.
Before running the game, make sure the code on the Teensy and Hapkit board matches the sketches in this repo. If either board is out of date, upload the current sketch from the repo.
- Open
teensy_controller/teensy_controller.inoin Arduino IDE. - Select
Tools -> Board -> Teensy 4.1. - Select
Tools -> USB Type -> Serial. - Select the Teensy serial port.
- Click Upload.
The Teensy reads encoder counts, calculates encoder velocities, and sends both
to the laptop at 1000000 baud.
- Open
hapkit_controller/hapkit_controller.inoin Arduino IDE. - Select the Hapkit board target.
- Select the correct serial port.
- Click Upload.
The Hapkit board receives motor commands from the Teensy and drives the two motors. If motor direction is wrong, fix the wiring or firmware direction mapping before increasing force gains.
Only one USB cable is needed between the laptop and Teensy. The Teensy reads both players' encoders and forwards motor packets to both Hapkit modules.
- Upload current firmware to the Teensy and Hapkit board if needed.
- Plug in power to the Hapkit board or boards.
- Connect Teensy GND to both Hapkit GND pins.
- Connect Teensy
Serial4TX, pin 17, to module A Hapkit serial RX. - Connect Teensy
Serial7TX, pin 29, to module B Hapkit serial RX. - Plug the Teensy into the computer over USB.
- Confirm
game_engine/config.pypoints at the Teensy serial port. - Close Arduino Serial Monitor or any other program using the Teensy serial port before starting the game.
From the repo root:
cd game_engine
python3 main.pyThe game opens a menu where you can select one or two players and start.
For keyboard-only testing, set this in game_engine/config.py:
SIMULATION_MODE = TrueSet it back to False before using haptic hardware.
The game prints the current controls when a round starts.
Keyboard simulation:
- Player 1:
W/Sthrottle,A/Dsteering. - Player 2: arrow keys.
General controls:
ESC: quit current game.R: restart the current game without returning to the menu.H: toggle haptic visualization.B: toggle hitbox display.Shift+S: toggle the live audio mixer.M: toggle music.N: toggle sound effects.T: switch background music track.Z: zero steering and throttle, hardware mode only.- Round over:
SPACEBARstarts the next round. - Game over:
SPACEBARreturns to the menu.
Background music and generated sound effects live under
game_engine/assets/audio/. Music and asset licensing notes are tracked in
game_engine/assets/audio/CREDITS.md.
You can use Arduino IDE Serial Monitor to verify encoder values and manually send force commands.
-
Upload
teensy_controller/teensy_controller.ino. -
Open Arduino IDE Serial Monitor.
-
Set the baud rate to
1000000. -
Rotate each encoder and watch for lines like:
P,P1_STEER_COUNTS,P1_THROTTLE_COUNTS,P2_STEER_COUNTS,P2_THROTTLE_COUNTS,P1_STEER_VEL,P1_THROTTLE_VEL,P2_STEER_VEL,P2_THROTTLE_VELExample:
P,1500,-300,0,0,1240.50,-210.00,0.00,0.00 -
To send a force command from Serial Monitor, send:
F,P1_STEER_FORCE,P1_THROTTLE_FORCE,P2_STEER_FORCE,P2_THROTTLE_FORCEExample:
F,100,-100,50,-50Start with small values and verify motor direction before increasing force. Force values are clamped to
-1000..1000. -
Close Serial Monitor before running the Python game. The game cannot open the Teensy serial port while Arduino IDE has it open.
SerialExceptionor port busy: close Arduino Serial Monitor and any other serial tools, then restart the game.- No hardware input: check
SERIAL_PORT, USB connection, Teensy firmware, and thatSIMULATION_MODE = False. - Motors do not move: check Hapkit power, Teensy-to-Hapkit serial wiring, common ground, and Hapkit firmware.
- Motor force pushes the wrong way: stop testing at low force and fix motor
wiring, firmware direction mapping, or force direction signs in
game_engine/config.py.