# CMDR Joystick

USB HID joystick firmware + hardware: 2 hall‑effect gimbals, 2 physical hat
switches, and a 5x5 button matrix (plus 2 extra buttons). The firmware exposes
7 HID axes (X, Y, Z, Rx, Ry, Rz, Slider), 32 buttons and one 8‑way HAT.

## Layout

```cpp
HW Button index map:
---------------------------------------------------------------
|  0 L|  1 U| 25 U |        |  2  |        | 26 U |  4 U|  3 L|
---------------------------------------------------------------
|     |  5  |  6  |  7  |             | 12  | 11  | 10  |     |
|                                                             |
|                 |  8  |             | 13  |                 |
|                 |  9  |             | 14  |                 |
|      X1/Y1                                       X2/Y2      |
|                 | 16  |             | 21  |                 |
|           | 19  | 15  | 17  | | 24  | 20  | 22  |           |
|                 | 18  |             | 23  |                 |
---------------------------------------------------------------

USB HID joystick map :
---------------------------------------------------------------
| Fn L| B19 U| B7 U|        | B32 |         | B8 U| B1 U| B2 L|
---------------------------------------------------------------
|     | B5 |  B14 | B9  |             | B10 | B15 | B6 |      |
|                                                             |
|                 | B13 |             | B17 |                 |
|                 | B16 |             | B18 |                 |
|      X1/Y1                                       X2/Y2      |
|                 | H1U |             | H2U |                 |
|           | H1L | B11 | H1R | | H2L | B12 | H2R |           |
|                 | H1D |             | H2D |                 |
---------------------------------------------------------------

USB HID joystick map (Fn):
---------------------------------------------------------------
| Fn L| B21 U| B27 U|       | B32 |         | B8 U| B3 U| B4 L|
---------------------------------------------------------------
|     | TH |  B22 | B26 |             | B28 | B24 | B23 |     |
|                                                             |
|                 | B29 |             | B31 |                 |
|                 | B16 |             | B18 |                 |
|      X1/Y1                                       X2/Y2      |
|                 | H3U |             | H4U |                 |
|           | H3L | B20 | H3R | | H4L | B25 | H4R |           |
|                 | H3D |             | H4D |                 |
---------------------------------------------------------------

Config Layer (holding CONFIG button)
---------------------------------------------------------------
|BOOT L| CAL U|           | CONFIG |              |  -  |  -  |
---------------------------------------------------------------
|     |  -  |  -  |  -  |             |  -  |  -  |  -  |     |
|                                                             |
|                 |  -  |             |  -  |                 |
|                 |  -  |             |  -  |                 |
|        -/-                                        -/-       |
|                 |  -  |             |  -  |                 |
|           |  -  |  -  |  -  | |  -  |  -  |  -  |           |
|                 |  -  |             |  -  |                 |
---------------------------------------------------------------

```

## Features

- Ergonomic design (low profile)
- Hall-effect gimbals (FrSky M7/M10)
- USB HID joystick device
  - 7 axes: X, Y, Z, Rx, Ry, Rz, Slider
  - 32 buttons
  - 1× 8‑way HAT
- Advanced input pipeline
  - Digital smoothing for stable axes
  - Per-axis calibration (min/center/max) with EEPROM persistence
  - Optional exponential response curves (LUT based)
  - Throttle hold (capture + remap around center)
  - Virtual throttle mode (map right-X to Slider; disable Z)
  - 1 ms USB HID poll rate with immediate post-scan processing for minimal latency
- Status LED (WS2812 via PIO) for mode/health indication
  - Power-on heartbeat (green) before USB enumeration
  - Activity colors: green (active), blue (virtual throttle/calibration), orange (holds)
  - Warning/error tones (red) and bootloader purple
  - Idle heartbeat flashes at half speed once inputs settle
  - LED turns off during USB suspend for power savings
- Power management for USB suspend/resume
  - Automatic power saving when USB host suspends device
  - Reduced input scanning frequency (10x slower) during suspend
  - Wake-on-input detection for gimbals and buttons
  - Immediate resume response when inputs detected

## Low-latency firmware path

- USB interrupt endpoint configured for 1 ms poll interval (1 kHz reports)
- Input scan, smoothing, processing, and mapping now execute back-to-back
- Enhanced button debounce: 15-scan threshold (3ms) with anti-bounce protection to prevent double presses
- Smart HAT switch filtering: disables all HAT buttons when multiple directions are detected to prevent spurious inputs
- First activity after idle forces immediate USB packet without waiting for the next tick
- Existing idle timeout preserved (5 s) to avoid unnecessary host wake-ups

## Hardware

- 2x FrSky M7 or M10 gimbals [M7 datasheet](https://www.frsky-rc.com/product/m7/)
- 7x Kailh choc low profile switches [Brown](http://www.kailh.com/en/Products/Ks/CS/)
- 8x Cherry MX switches [Brown](https://www.cherrymx.de/en/cherry-mx/mx-original/mx-brown.html)
- 2x Miniature Toggle Switch (M6 shaft, 7-8mm wide body. Ex Apem 5636) [Apem 5000 series](https://www.farnell.com/datasheets/2626614.pdf?_ga=2.22934718.461231604.1570510103-1672862477.1542183430)
- 2x Alpine RKJXM1015004 hat switches [pdf](https://www.mouser.se/datasheet/2/15/RKJXM-1662398.pdf)
- 1x Bottom case (3D printed)
- 1x Top plate (3D printed)
- 2x Hat swith top (3D printed) [stl](/mCAD/Hat_Castle_Short_scale_99_99_130.stl)
- 1x Custom PCB (CMDR Joystick rev A)
  - ![pcb_top](/eCAD/cmdr-joystick/cmdr-joystick_rev_a_board_top.png)
  - ![pcb_bottom](/eCAD/cmdr-joystick/cmdr-joystick_rev_a_board_bottom.png)
  - Gerber files: [zip](/eCAD/cmdr-joystick/cmdr-joystick_rev_a_gerber.zip)
  - Schematics: [pdf](/eCAD/cmdr-joystick/cmdr-joystick_rev_a_schematics.pdf)
    - rp2040zero pinout: [jpg](https://www.waveshare.com/w/upload/2/2b/RP2040-Zero-details-7.jpg)
    - rp2040zero schematic: [pdf](https://www.waveshare.com/w/upload/4/4c/RP2040_Zero.pdf)
    - eeprom 24C32LV: [pdf](https://www.mouser.se/datasheet/2/308/1/NV24C32LV_D-2319484.pdf)
    - P-Fet si2372eds: [pdf](https://www.vishay.com/docs/63924/si2371eds.pdf)
    - N-Fet 2N7002: [pdf](https://www.mouser.se/datasheet/2/408/T2N7002AK_datasheet_en_20150401-1916411.pdf)
    - Small signal diod 1N4148W: [pdf](https://www.diodes.com/assets/Datasheets/BAV16W_1N4148W.pdf)

## Build, test, and flash (install.sh)

Use the top‑level `install.sh` to run tests, build, and flash. It handles
prerequisites, target setup, UF2 conversion, and optional SSH deployment.

Common commands

```
# Run comprehensive tests (host + embedded checks + clippy + release build)
./install.sh test

# Build and flash locally (copies UF2 to RPI-RP2 mass storage)
./install.sh flash --local

# Build and transfer UF2 via SSH to a remote machine
./install.sh flash --ssh --target user@host --mount /Volumes/RPI-RP2

# Clean build artifacts and temporary files
./install.sh clean
```

Entering bootloader

- Hardware: hold BOOTSEL while plugging USB
- Firmware shortcut (at power‑on): hold the front‑left‑lower button
- In‑firmware combo: Front‑left‑lower + Top‑left‑mode + Top‑right‑mode

## References

- rp2040 datasheet: [pdf](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf)

## Calibration

1. Center both gimbals.
2. Start calibration (LED will flash): press Front‑left‑upper + Top‑left‑mode + Top‑right‑mode.
3. Move both gimbals through full travel (all corners).
4. Optional: select gimbal mode while calibrating
   - M10: press Top‑left‑UP
   - M7: press Top‑left‑DOWN
5. Save calibration to EEPROM: press the right hat center button.
6. Exit calibration: repeat step 2 combination (toggles off).

Notes

- During calibration, min/max are tracked from the smoothed ADC values.
- On mode change (M10/M7) centers are reset from the current position.

## Status LED behaviour

| Condition                         | Colour  | Pattern                  |
|-----------------------------------|---------|--------------------------|
| Power (USB not enumerated yet)    | Green   | Fast heartbeat           |
| Normal active operation           | Green   | Solid                    |
| Idle (no input activity)          | Green   | Slow heartbeat (½ speed) |
| Virtual throttle / calibration    | Blue    | Solid / heartbeat        |
| Throttle hold / other overlays    | Orange  | Solid / heartbeat        |
| Warning / Error                   | Red     | Blink / heartbeat        |
| Bootloader                        | Purple  | Solid                    |

## Runtime controls

- Throttle hold: press the Top‑left‑mode button to capture the current throttle value
  - Press again at center to clear hold
- Virtual throttle mode: press the Top‑right‑mode button to toggle
  - Disables Z axis and maps right‑X to the Slider (symmetric around center)

## Development and testing

Run unit tests on host (uses `std` for test modules):

```
cd rp2040
cargo test --features std
```

Repo structure

- `rp2040/src/*.rs`: firmware modules (axis, buttons, matrix, calibration, storage, HID)
- `rp2040/memory.x`: linker script
- `rp2040/uf2conv.py`: UF2 converter (alternative to elf2uf2‑rs)
- `install.sh`: unified helper to test, build, and flash (local or via SSH)
