Arduino Nano firmware and a Windows desktop app to read a custom-wired joystick over serial and map its buttons and analog sticks to keyboard and mouse input.
This project bridges a custom joystick wired to an Arduino Nano with a Windows 11 desktop application.
The Arduino reads:
- 4 digital buttons
- 2 analog sticks (4 analog axes total)
It then sends the raw state to the PC over USB serial.
The Python desktop application provides:
- a live joystick test/calibration interface inspired by the Windows joystick properties panel
- calibration tools for each axis
- configurable button and axis mapping
- keyboard and mouse input emulation
- JSON-based profiles for different games
- background operation while minimized to the system tray
- a panic stop to disable input mapping immediately
- Reads 4 digital buttons
- Reads 4 analog channels
- Sends joystick state over serial
- Lightweight and easy to customize
- Keeps hardware logic simple and pushes calibration/mapping to the desktop app
- Windows-style joystick monitoring interface
- Live display of:
- Button 1-4
- Stick 1 X/Y
- Stick 2 X/Y
- Per-axis calibration:
- min
- center
- max
- deadzone
- invert
- sensitivity
- curve/expo
- Mapping modes:
- button -> keyboard
- button -> mouse click
- axis -> keyboard thresholds
- axis -> mouse movement
- JSON profile save/load
- Run while minimized to tray
- Global enable/disable mapping
- Panic stop
serial-joystick-keymapper/
ββ README.md
ββ LICENSE
ββ .gitignore
ββ firmware/
β ββ nano_serial_joystick/
β ββ nano_serial_joystick.ino
ββ desktop_app/
β ββ pyproject.toml
β ββ requirements.txt
β ββ main.py
β ββ app/
β β ββ ui/
β β β ββ main_window.py
β β β ββ widgets/
β β β β ββ stick_widget.py
β β β β ββ button_indicator.py
β β β β ββ calibration_panel.py
β β ββ serial/
β β β ββ serial_reader.py
β β β ββ protocol.py
β β ββ mapping/
β β β ββ actions.py
β β β ββ keyboard_mouse.py
β β β ββ axis_processing.py
β β ββ profiles/
β β β ββ profile_manager.py
β β β ββ schema.py
β β ββ core/
β β ββ models.py
β β ββ state_store.py
β β ββ controller.py
β ββ profiles/
β ββ example_profile.json
ββ docs/
ββ protocol.md
ββ calibration.md
ββ roadmap.md
- Arduino Nano
const uint8_t BTN1_PIN = 2;
const uint8_t BTN2_PIN = 3;
const uint8_t BTN3_PIN = 4;
const uint8_t BTN4_PIN = 5;
const uint8_t STICK1_X_PIN = A0;
const uint8_t STICK1_Y_PIN = A1;
const uint8_t STICK2_X_PIN = A2;
const uint8_t STICK2_Y_PIN = A3;Adjust these pins to match your wiring.
The firmware sends newline-delimited frames in text format:
T,<b1>,<b2>,<b3>,<b4>,<s1x>,<s1y>,<s2x>,<s2y>\n
Example:
T,0,1,0,0,512,498,1023,14
Where:
b1..b4are button states (0or1)s1x, s1y, s2x, s2yare analog readings (0..1023)
- Windows 11
- Python 3.11+
- Arduino Nano
Upload the sketch in:
firmware/nano_serial_joystick/nano_serial_joystick.ino
cd desktop_app
python -m venv .venv
.venv\Scripts\activatepip install -r requirements.txtpython main.pyThis application can generate real keyboard and mouse events on the system.
Recommended safeguards:
- keep the visible global enable/disable toggle in mind
- use the panic stop in the toolbar or tray menu
- start with harmless test bindings first
- avoid binding movement keys or mouse actions until calibration is correct
- Designed for Windows 11
- Depends on stable serial communication
- Not presented as a USB HID joystick device
- Requires the desktop application to be running for mappings to work
- Some games may react differently to synthetic keyboard/mouse events depending on anti-cheat or input backend
This project is released under the MIT License. See the LICENSE file for details.