diff --git a/website/versioned_docs/version-acs_cc/project/2026/mihai.topa/index.md b/website/versioned_docs/version-acs_cc/project/2026/mihai.topa/index.md
index 36f1828afe4..7156331385a 100644
--- a/website/versioned_docs/version-acs_cc/project/2026/mihai.topa/index.md
+++ b/website/versioned_docs/version-acs_cc/project/2026/mihai.topa/index.md
@@ -21,9 +21,9 @@ The controller features:
- **4 face buttons** (A, B, X, Y)
- **D-Pad** (4 directions)
- **Shoulder buttons** (L1, R1)
-- **System buttons** (Start, Select, Home)
- **Vibration motor** for haptic feedback, PWM-controlled
- USB-C powered — no batteries needed
+- Fully recognized by **Steam** (Steam Input) — all axes, buttons, and triggers work correctly
I initially planned a two-microcontroller architecture (STM32 + ESP32-C3) communicating over Bluetooth LE, but decided against it — coordinating BLE HID between two microcontrollers turned out to be quite complex. The final design uses a **single STM32 Nucleo-U545RE-Q** that reads all inputs and connects directly to the host as a USB HID gamepad.
@@ -41,14 +41,14 @@ As an avid gamer, I wanted to understand how a controller works from the ground
┌─────────────────────────────────────────────────────────────┐
│ INPUT LAYER │
│ [Joystick L] [Joystick R] [Trigger L] [Trigger R] │
-│ [Buttons x18] [Vibration Motor] │
+│ [Buttons x12] [Vibration Motor] │
└───────────────────────┬─────────────────────────────────────┘
│ ADC / GPIO / PWM
▼
┌─────────────────────────────────────────────────────────────┐
│ STM32 Nucleo-U545RE-Q │
│ - Reads ADC: 4 joystick axes + 2 triggers │
-│ - Reads GPIO: 18 buttons │
+│ - Reads GPIO: 12 buttons │
│ - Controls vibration motor via PWM │
│ - Acts as USB HID Gamepad │
└───────────────────────┬─────────────────────────────────────┘
@@ -77,6 +77,7 @@ As an avid gamer, I wanted to understand how a controller works from the ground
- Wired the potentiometers for the analog triggers.
- Wired the vibration motor and tested PWM control.
- Full integration test.
+- Added the controller to Steam and configured it using Steam Input. Steam correctly detects all axes, buttons, and triggers. The controller is fully functional in Steam.
---
@@ -88,9 +89,9 @@ As an avid gamer, I wanted to understand how a controller works from the ground
The only microcontroller in the project. Reads all inputs, controls the vibration motor, and communicates with the host via USB HID.
- 6 ADC channels: Joystick L (X, Y), Joystick R (X, Y), Trigger L, Trigger R
-- 18 GPIO pins for buttons
-- 1 PWM output for the vibration motor
-- USB Full Speed via USB-C (CN3) to the host
+- 12 GPIO pins for buttons
+- 1 PWM output for the vibration motor (TIM3 CH1)
+- USB Full Speed via USB-C (PA11/PA12) to the host
- Powered via USB-C; provides 3.3V on Morpho pins (CN7) for peripherals
#### 2. Analog Joysticks PS2 Module (×2)
@@ -99,8 +100,8 @@ Standard XY joystick modules with integrated push-button (L3/R3). Each module: V
- VRx/VRy → 2 ADC channels per joystick
- SW → GPIO with internal pull-up
-#### 3. Tactile Buttons 12×12mm (×20)
-A, B, X, Y, L1, R1, Start, Select, Home, D-Pad ×4, L3, R3 + 2 spare.
+#### 3. Tactile Buttons 12×12mm (×12)
+A, B, X, Y, L1, R1, D-Pad ×4, L3, R3.
- Wired between GPIO and GND, internal pull-up enabled in firmware
@@ -135,32 +136,45 @@ The schematic covers the following connections:
**Joysticks (×2)**
| Signal | STM32 Pin |
|--------|-----------|
-| Joystick L — VRx | ADC channel (TBD) |
-| Joystick L — VRy | ADC channel (TBD) |
-| Joystick L — SW | GPIO (TBD) |
-| Joystick R — VRx | ADC channel (TBD) |
-| Joystick R — VRy | ADC channel (TBD) |
-| Joystick R — SW | GPIO (TBD) |
+| Joystick L — VRx | PA0 (ADC1) |
+| Joystick L — VRy | PA1 (ADC1) |
+| Joystick L — SW (L3) | PB13 |
+| Joystick R — VRx | PA4 (ADC1) |
+| Joystick R — VRy | PB0 (ADC1) |
+| Joystick R — SW (R3) | PB14 |
**Triggers (×2)**
| Signal | STM32 Pin |
|--------|-----------|
-| Trigger L — wiper | ADC channel (TBD) |
-| Trigger R — wiper | ADC channel (TBD) |
+| Trigger L2 — wiper | PC1 (ADC1) |
+| Trigger R2 — wiper | PC0 (ADC1) |
-**Buttons (×18)**
+**Buttons (×12)**
| Button | STM32 Pin |
|--------|-----------|
-| A, B, X, Y | GPIO (TBD) |
-| L1, R1 | GPIO (TBD) |
-| Start, Select, Home | GPIO (TBD) |
-| D-Pad ×4 | GPIO (TBD) |
-| L3, R3 | GPIO (TBD) |
+| A | PA3 |
+| B | PA2 |
+| X | PC6 |
+| Y | PB10 |
+| L1 | PC9 |
+| R1 | PC10 |
+| D-Up | PB8 |
+| D-Down | PA8 |
+| D-Left | PC8 |
+| D-Right | PC7 |
+| L3 | PB13 |
+| R3 | PB14 |
**Vibration Motor**
| Signal | STM32 Pin |
|--------|-----------|
-| PWM control | TIM channel (TBD) |
+| PWM control | PB4 (TIM3 CH1) |
+
+**USB**
+| Signal | STM32 Pin |
+|--------|-----------|
+| USB D- | PA11 |
+| USB D+ | PA12 |
---
@@ -170,26 +184,47 @@ The schematic covers the following connections:
|--------|-------|-------|
| STM32 Nucleo-U545RE-Q | Main microcontroller + USB HID | ~110 RON *(owned)* |
| Joystick Module 2-Axis XY ×2 | Left + right joystick with L3/R3 | 5.45 RON × 2 |
-| Tactile Button PCB 12×12×7.3mm ×20 | A, B, X, Y, D-Pad, L1, R1, Start, Select, Home | 1.33 RON × 20 |
+| Tactile Button PCB 12×12×7.3mm ×12 | A, B, X, Y, D-Pad ×4, L1, R1, L3, R3 | 1.33 RON × 12 |
| Linear Potentiometer Module 10K ×2 | Analog triggers L2 and R2 | 12.52 RON × 2 |
| Vibration Motor Module DC PWM 3–5V | Haptic feedback | 5.36 RON |
-| Breadboard 400p ×3 | Prototyping base | 6.62 RON × 3 |
+| Breadboard 800p ×1 | Prototyping base | ~14 RON |
| Dupont Wire 30cm M-M ×2 (40pcs/set) | Breadboard connections | 8.39 RON × 2 |
| Dupont Wire 30cm M-F ×1 (40pcs/set) | Module connections | 7.59 RON |
| Ceramic Capacitor Set 300pcs | VCC / ADC decoupling | 12.55 RON |
| Resistor Kit 30 values 600pcs | Pull-up / protection | 15.16 RON |
-| | Subtotal (components) | 139.84 RON |
+| | Subtotal (components) | 123.34 RON |
| | Delivery | 15.15 RON |
-| | **Total (cart incl. VAT)** | **154.99 RON** |
-| | **Grand Total (incl. Nucleo)** | **~265 RON** |
+| | **Total (cart incl. VAT)** | **138.49 RON** |
+| | **Grand Total (incl. Nucleo)** | **~248 RON** |
---
## Software
-*(To be developed)*
+Firmware is written in **Rust** using the **Embassy** async framework for STM32.
+
+### Key components
+
+- **USB HID** — `embassy-usb` with a custom 8-byte gamepad report descriptor (12 buttons + 4 axes + 2 triggers). The device presents itself as a Game Pad (Usage Page 0x01, Usage 0x05) for maximum OS compatibility.
+- **ADC** — `embassy-stm32` ADC1, blocking reads at ~19.5 cycles sample time. Raw 14-bit values mapped to signed 8-bit axis values via `adc_to_axis()`.
+- **GPIO** — 12 buttons, active-low with internal pull-up. Press events detected on falling edge (bit was 0, now 1).
+- **PWM** — TIM3 CH1 on PB4 drives the vibration motor. Duty cycle: 100% when R2 trigger is maxed out, 70% for 500 ms every 10 seconds otherwise.
+- **Clocks** — HSI @ 16 MHz for the CPU; HSI48 (synced from USB SOF) for the USB peripheral; SYS clock routed to ADC.
+
+### HID report layout (8 bytes)
+
+| Byte | Content |
+|------|---------|
+| 0 | Buttons 0–7 (A, B, X, Y, L1, R1, D-Up, D-Down) |
+| 1 | Buttons 8–11 (D-Left, D-Right, L3, R3) + 4-bit padding |
+| 2 | LX axis (i8) |
+| 3 | LY axis (i8) |
+| 4 | RX axis (i8) |
+| 5 | RY axis (i8) |
+| 6 | L2 trigger (u8, 0–255) |
+| 7 | R2 trigger (u8, 0–255) |
-Firmware will be written in **Rust** using the **Embassy** async framework for STM32. Implementation details will be added as development progresses.
+The main loop runs at ~200 Hz (5 ms `Timer::after`). USB HID and USB stack are joined as two concurrent async tasks via `embassy-futures::join`.
---
diff --git a/website/versioned_docs/version-acs_cc/project/2026/mihai.topa/schematic.svg b/website/versioned_docs/version-acs_cc/project/2026/mihai.topa/schematic.svg
index 0b62b75f25c..ac749a1cedc 100644
--- a/website/versioned_docs/version-acs_cc/project/2026/mihai.topa/schematic.svg
+++ b/website/versioned_docs/version-acs_cc/project/2026/mihai.topa/schematic.svg
@@ -1 +1 @@
-
\ No newline at end of file
+