Skip to content
jespernPublic

About

An ESP32 device that reads button events from Wahoo Kickr Bike shifter units and emulates a Zwift Click v2 BLE controller, enabling STI-feel shift paddles for Zwift virtual shifting with a Kickr Move + Zwift Cog setup.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

Zwahoo

An ESP32 device that reads button events from Wahoo Kickr Bike shifter units and emulates a Zwift Click v2 BLE controller, enabling STI-feel shift paddles for Zwift virtual shifting with a Kickr Move + Zwift Cog setup.

Why?

Zwift's virtual shifting requires their Click v2 controllers — small rubber-band-mounted buttons that lack the tactile feel of real STI shifters. The Wahoo Kickr Bike has excellent shift paddles, but they only work with the Kickr Bike's own HCU. This bridge connects the two: plug in spare Kickr Bike shifters, and Zwift sees a Click v2 controller.

Hardware

Bill of Materials

Component Qty Notes
ESP32-S3 DevKitC (or similar) 1 Must support 2x I2C peripherals
2.5mm TRRS panel-mount socket 2 For left and right shifters
USB-C breakout or direct power 1 Powers the ESP32
Enclosure 1 3D printed or off-the-shelf project box

Wiring

Each 2.5mm TRRS jack from the Kickr Bike shifter carries I2C:

Jack Pin Signal ESP32 (Left) ESP32 (Right)
Tip 3.3V 3V3 3V3
Ring 1 SCL GPIO 9 GPIO 11
Ring 2 SDA GPIO 8 GPIO 10
Sleeve GND GND GND

Important: The shifters use 2.5mm TRRS jacks, not 3.5mm!

Each shifter contains a TCA8418 I2C keypad scanner at address 0x34. Since both share the same address, they must be on separate I2C buses (the ESP32 has two hardware I2C peripherals).

Button Mapping

Kickr Bike Button Zwift Function
Right Gear Outer Shift Up (+) — harder gear
Left Gear Outer Shift Down (-) — easier gear
Right Inner Steer Right / D-pad Right
Left Inner Steer Left / D-pad Left
Right Forward D-pad Up
Left Forward D-pad Down
Right Gear Inner A button (PowerUp / Ride On)
Left Gear Inner B button

The mapping is defined in buildZwiftBitmap() and can be changed to your preference.

Building

  1. Install Arduino IDE 2.x
  2. Add ESP32 board support (Espressif Systems)
  3. Install the NimBLE-Arduino library via Library Manager
  4. Open zwahoo.ino
  5. Adjust the I2C pin definitions at the top if your board uses different GPIOs
  6. Select your ESP32-S3 board and upload

Usage

  1. Plug Kickr Bike shifters into the two 2.5mm sockets
  2. Power the ESP32 via USB-C
  3. Serial monitor will show detected shifters and button events
  4. In Zwift, go to the pairing screen → Controls → look for "Zwift SF2"
  5. Pair it — Zwift will perform the "RideOn" handshake
  6. Start riding with virtual shifting!

How It Works

  1. Input: The ESP32 polls two TCA8418 I2C keypad scanners (one per shifter) for button press/release events via the key event FIFO
  2. Processing: Button events are mapped to the Zwift Click v2 button bitmap format
  3. Output: The ESP32 advertises as a Zwift-compatible BLE controller using the proprietary Zwift service UUID (0xFC82), responds to the "RideOn" handshake, and sends protobuf-encoded button state notifications at 10-50 Hz

Known Limitations & TODOs

  • Untested — this is a v0.1 based on reverse-engineered protocols; it will likely need tuning
  • No analog steering — the Click v2 protocol supports analog paddle values; this firmware only sends digital button states. Steering via D-pad arrows should still work.
  • Handshake may need refinement — the exact bytes Zwift sends/expects during connection may vary between Zwift versions. Monitor the serial output and adjust if needed.
  • Button mapping is guesswork — the exact Kickr Bike button-to-row mapping may need verification with your specific shifter hardware. Connect one shifter at a time and press each button while watching serial output.
  • Service UUID versioning — if Zwift doesn't see the device, try the old 128-bit UUID instead of 0xFC82

Credits

  • StephenDone/kickr_shifters — TCA8418 reverse engineering, pinout, cabling, and Python test script for Kickr Bike shifters
  • Makinolo — Zwift Ride/Click v2 BLE protocol reverse engineering
  • JuergenLeber/SHIFTR — ESP32 Zwift BLE protocol implementation reference

License

MIT

About

An ESP32 device that reads button events from Wahoo Kickr Bike shifter units and emulates a Zwift Click v2 BLE controller, enabling STI-feel shift paddles for Zwift virtual shifting with a Kickr Move + Zwift Cog setup.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages