# ERTOtech Refleks Lite Arcade Stick

This directory contains the hardware and firmware files for the ERTOtech
Refleks Lite arcade stick project.

```text
codespid/
├── ArcadeStick_hardware/     PCB and hardware design files
└── arcadestick_software/     STM32 firmware, bootloader, and USB code
```

## Architecture

The board uses an STM32F103RBT6. Its 128 KB flash is divided into three
regions:

| Region | Start address | Size | USB VID:PID |
| --- | ---: | ---: | --- |
| Bootloader / DFU | `0x08000000` | 24 KB | `1209:E667` |
| App1 / XInput | `0x08006000` | 52 KB | `1209:E668` |
| App2 / DInput | `0x08013000` | 52 KB | `1209:E669` |

The USB manufacturer string is `ERTOtech`.

## Boot and firmware selection

The bootloader runs after every reset, reads the slide switch, and jumps to
the selected application's vector table.

The current electrical switch logic is:

- `PA4 LOW`, `PA5 HIGH`: run XInput App1.
- `PA4 HIGH`, `PA5 LOW`: run DInput App2.
- If both inputs have the same level, the selection is invalid and DFU starts.
- Holding `START` during reset starts DFU regardless of the switch position.
- If the selected application's stack pointer or reset vector is invalid, the
  bootloader remains in DFU mode.

When the switch is moved while an application is running, the new state must
remain stable for 50 ms. USB is then stopped, the firmware waits 100 ms, and
calls `NVIC_SystemReset()`. After reset, the bootloader starts the application
selected by the new switch position.

## DInput application

DInput runs as a standard USB HID game pad. Its input report is 9 bytes:

```text
Bytes 0..1: 11 buttons plus padding
Byte 2:      4-bit hat switch plus padding
Bytes 3..8: X, Y, Z, Rx, Ry, and Rz (unsigned 8-bit axes)
```

The neutral hat value is `8`; directions use HID hat-switch values `0..7`.
The six neutral axis values are `0x80`. Physical buttons are active LOW and
use GPIO pull-ups. Button 11 is present in the descriptor but is not connected
to a physical input.

The DInput USB configuration contains three interfaces:

| Interface | Purpose | Endpoints | Behavior |
| --- | --- | --- | --- |
| IF0 | Gamepad HID | `0x81` IN | Functional 9-byte input reports |
| IF1 | Setup HID | `0x82` IN, `0x02` OUT | Compatibility stub |
| IF2 | Raw HID | `0x83` IN | Compatibility stub |

The Setup and Raw report descriptors match the original device layout, but
the original configuration protocol is not implemented. Unsupported control
requests are stalled, unused IN endpoints remain at NAK, and Setup OUT data is
accepted and discarded.

## XInput application

XInput uses a vendor-specific Xbox 360 wired-controller interface:

```text
Interface class    : 0xFF
Interface subclass : 0x5D
Interface protocol : 0x01
IN endpoint         : 0x81, interrupt, 64 bytes
OUT endpoint        : 0x02, interrupt, 64 bytes
Input report        : 20 bytes
```

The 20-byte input report carries the D-pad, Start, Back, A/B/X/Y, LB/RB, and
digital LT/RT values. LT and RT send `0xFF` while pressed and `0x00` while
released. Eight-byte rumble packets received through the OUT endpoint are
parsed and the left/right motor values are stored. Unless motor outputs are
connected, those values remain software-only.

The XInput configuration also exposes Setup HID on IF1 (`0x83` IN and `0x03`
OUT) and Raw HID on IF2 (`0x84` IN). These are compatibility stubs with the
same behavior described for DInput.

There are no Microsoft OS, compatible-ID, `MSFT100`, or WinUSB descriptors in
the XInput application. Windows currently binds IF0 to `xusb22.inf` by its
class/subclass/protocol match. Xbox console authentication is not implemented;
the target is Windows/PC.

## GPIO mapping

All arcade inputs are active LOW and configured with pull-ups.

| Function | Pin |
| --- | --- |
| Up | PC0 |
| Down | PC1 |
| Right | PC2 |
| Left | PC3 |
| A | PC4 |
| B | PC5 |
| Y | PC6 |
| X | PC7 |
| RB | PC8 |
| RT | PC9 |
| LB | PC10 |
| LT | PC11 |
| Start | PC14 |
| Select / Back | PA2 |
| Switch input 1 | PA4 |
| Switch input 2 | PA5 |
| USB D- | PA11 |
| USB D+ | PA12 |

## Building

The project uses the VS Code STM32Cube and CMake tools. In VS Code, press
`Ctrl+Shift+B` and run `CMake: build` to build the bootloader, both
applications, and the combined package.

The CMake presets can also be used from a terminal:

```powershell
cube-cmake --preset Debug
cube-cmake --build --preset Debug

cube-cmake --preset Release
cube-cmake --build --preset Release
```

Main CMake targets:

| Target | Description |
| --- | --- |
| `arcadestick_bootloader` | USB DFU bootloader |
| `arcadestick_xinput` | App1 XInput firmware |
| `arcadestick` | App2 DInput firmware |
| `arcadestick_package` | Combines all three firmware images into one BIN/HEX |

Each firmware target produces `.elf`, `.hex`, and `.bin` files.

## Initial programming

For a blank MCU, program the following combined image with an ST-Link:

```text
arcadestick_software/build/Release/arcadestick_full.hex
```

This HEX contains the bootloader, XInput App1, and DInput App2 at their correct
flash addresses. The combined BIN is exactly 128 KB:

```text
arcadestick_software/build/Release/arcadestick_full.bin
```

## Updating with USB DFU

To enter DFU, hold `START` while resetting the board. The USB device appears as
`ERTOtech Refleks Lite DFU` with VID:PID `1209:E667`.

The bootloader uses standard DFU 1.1 and does not use DfuSe address commands.
Select the destination firmware with the alternate interface number:

```powershell
# Update XInput App1
dfu-util -d 1209:e667 -a 0 `
  -D arcadestick_software/build/Release/arcadestick_xinput.bin

# Update DInput App2
dfu-util -d 1209:e667 -a 1 `
  -D arcadestick_software/build/Release/arcadestick.bin
```

- `-a 0` writes only the XInput slot.
- `-a 1` writes only the DInput slot.
- The bootloader region cannot be written through DFU.
- Data is received in 1024-byte blocks. The corresponding 1 KB flash page is
  erased and programmed as half-words.
- A zero-length DFU packet at the end of the download completes the update.
  The bootloader resets after approximately 100 ms.
- The slide switch's current position, not the downloaded file, determines
  which application runs after reset.

### Windows driver setup

This DFU bootloader does not expose Microsoft OS or automatic WinUSB
descriptors. On each Windows computer, WinUSB must therefore be installed once
for `Refleks Lite DFU (1209:E667)` before `dfu-util` can access the device.

Use Zadig as follows:

1. Start the board in DFU mode and open Zadig.
2. Select `Options > List All Devices`.
3. Select only `Refleks Lite DFU (1209:E667)`.
4. Select `WinUSB` as the target driver and click `Install Driver`.

Do not replace the drivers of the XInput (`1209:E668`), DInput (`1209:E669`),
ST-Link, or any other USB device. The Zadig operation is required only once per
Windows computer unless the driver is later removed. `dfu-util` must also be
installed and available in `PATH`.

If an update is interrupted, hold `START` during reset to re-enter DFU. This
simple bootloader does not provide atomic or power-fail-safe updates; do not
remove power while an update is in progress.

## Debugging with VS Code

The Run and Debug list contains:

- `STM32: Debug Arcade Stick (ST-Link)`: DInput App2
- `STM32: Debug Arcade Stick XInput (ST-Link)`: XInput App1
- `STM32: Debug Arcade Stick Bootloader (ST-Link)`: bootloader

The applications no longer begin at the start of flash. The bootloader must
already be programmed before debugging App1 or App2. While debugging the
bootloader, hold `START` to prevent it from jumping immediately to an app and
to keep execution in `main()`.

Place XInput breakpoints in `XInput_AppProcess()` in
`Core/Src/xinput_app.c`. Place DInput breakpoints in `DInput_AppProcess()` in
`Core/Src/dinput_app.c`.

## CubeMX code generation

The main CubeMX project file is:

```text
arcadestick_software/arcadestick.ioc
```

The custom DInput, XInput, and DFU classes are stored in project-owned source
files. The root `CMakeLists.txt` replaces CubeMX's single-interface HID
application source list with the custom composite DInput sources. No
post-generation repair script is required.

## Important source files

| File | Purpose |
| --- | --- |
| `Core/Src/bootloader_main.c` | Boot selection, app validation, and jump |
| `Core/Src/main.c` | Shared app startup and switch reset control |
| `Core/Src/xinput_app.c` | XInput button/report mapping |
| `Core/Src/dinput_app.c` | DInput button/report mapping |
| `USB_DEVICE/App/usbd_xinput.c` | Composite XInput USB class and rumble OUT handling |
| `USB_DEVICE/App/usbd_dinput.c` | Composite DInput USB class |
| `USB_DEVICE/App/usbd_dfu_bootloader.c` | DFU state machine and flash programming |
| `STM32F103xx_FLASH.ld` | Parameterized flash/RAM linker layout |
| `CMakeLists.txt` | Bootloader, App1, App2, and package targets |
| `tools/package_firmware.ps1` | Creates the combined 128 KB firmware package |

## Known limitations

- Do not distribute products with these PID values until they are formally
  assigned by pid.codes.
- Xbox console authentication/security is not implemented.
- The Setup and Raw HID interfaces are compatibility stubs; the original
  device configuration protocol is not implemented.
- The XInput application does not expose Microsoft OS or compatible-ID
  descriptors.
- DFU has no redundant image or rollback protection against power loss.
- Do not confuse the switch signal names with the observed LOW pins: on the
  current board, XInput is `PA4 LOW` and DInput is `PA5 LOW`.

## Licensing

- Project-authored firmware: MIT License
- Hardware design sources: CERN-OHL-P-2.0
- STM32 HAL, CMSIS, and other third-party components: their respective
  upstream licenses

See the root `LICENSE` file and the component-specific license files for the
complete terms.
