Shimano Di2 to Bluetooth Keyboard remote control mod

Modifying a Shimano Di2 road shifter to control virtual shifting in Rouvy and MyWhoosh and a smart trainer using Seeed Studio XIAO nRF52840 module as a Bluetooth Keyboard

Date 29 November 2025
Views 257
Time to read 13 minutes read

Earlier this month, I modified my right side Shimano Di2 shifter and Zwift Click v2 remote controls to use with Zwift on my Tacx Neo 2T smart bike trainer.

There are other online smart training apps which I sometimes use including Rouvy and MyWhoosh which now support virtual shifting via keyboard controls.

As I had not used the left Shimano Di2 shifter I decided to modify this to act as a Bluetooth keyboard and map the buttons to the keyboard commands required for each application.

My original plan was to have this completely self-contained within the shifter body using a small button cell to power the circuit, but after disassembling the Shimano Di2 shifter, I found the space available was too small for the Bluetooth module and a battery holder. Software issues with power management also ruled out this idea.

Please note, these modifications will remove all original functionality from your Shimano Di2 shifters.

Seeed Studio XIAO nRF52840 module

After looking at various microcontroller options, I decided to use the Seeed Studio XIAO nRF52840 module, which has built-in Bluetooth Low Energy (BLE) support and is small enough to fit inside the Di2 shifter body.

Features:

  • Bluetooth 5.0 with onboard antenna
  • Nordic nRF52840, ARM® Cortex®-M4 32-bit processor with FPU, 64 MHz
  • Ultra-Low Power: Standby power consumption is less than 5μA
  • Battery charging chip: Supports lithium battery charge and discharge management
  • Onboard 2 MB flash
  • Ultra Small Size: 21 x 17.8mm
  • 11 digital I/O
  • Single-sided components, surface mounting design
Cable extension

Parts Used:

Part Price Each Link
Shimano Di2 road shifter    
Seeed XIAO nRF52840 £9.50 The Pi Hut
Heat Shrink Tubing to cover the wire connections and cable join inside the shifter. A suitable kit is available from Amazon 560 Pcs Heat Shrink Tubing Kit £6.43 Amazon
USB power supply    
Thin multicore core cable such as alarm cable    
Small Cable Tie    
USB C to USB A cable 100cm    

Tools required

  • Soldering Iron and solder.
  • Wire cutters.
  • An electric drill and 4mm drill bit.

Accessing the Switches in the Shimano Di2 Shifter

These modifications are easier to do with the shifter removed from the handlebars. This is based on the Shimano Ultegra ST-6870 Di2 model shifters, but other models should be similar to modify.

In my first Shimano Di2 shifter mod, I removed the switch body and lever from the shifter. In this version, I decided to leave these in place and cut the wire where it enters the Di2 module.

First, remove the rubber shifter hood by pulling it forward over the brake and gear lever. These can be very tight, so gently warming them with a hair dryer can make it easier to remove.

Modifying the Shifter Switches and extending the cable

The Di2 controller box needs to be removed from the cable using wire cutters unclip the Di2 controller and cut the cable where it enters the controller box. This cable needs to be extended by approx 8cm to solder to the Bluetooth module.

I pulled the trimmed cable back through the shifter body to allow more room to work on the cable.

Carefully remove the outer insulation on the switch wire which reveals three wires and a strainer cord.

On my Di2 switch, the wires are connected as follows:

  • Grey - Common
  • Purple - Rear button
  • Yellow - Front Button

Using thin multicore wire, cut three pieces approximately 8cm long and solder these to the existing switch wires. I used some spare alarm cable which had cores with matching colours, but any thin multicore cable will be suitable.

Slide the smaller heat shrink over the solder joints and gently heat to shrink to fit. Then slide the outer cover heat shrink over the joints and shrink to fit to protect everything.

The wires can now be routed back through the shifter body.

Cable extension
Completed cable extension

Shifter body modifications

The Di2 shifter body has three slots designed to support the Di2 cables; these need to be removed to allow the USB connector and cable to be installed flush with the shifter cover.

Using a small file, carefully remove the cable guides so the USB connector can be fitted.

Using a 4mm drill, create two holes as shown in the photo to allow the installation of a cable tie to secure the USB connector.

Shifter body changes
Shifter body modifications

Connecting the XIAO nRF52840 module

The three wires need to be soldered to the pads on the XIAO nRF52840 module. Solder the wires as follows:

  • Grey - GND
  • Purple - D1
  • Yellow - D0

Using a thin cable tie, feed the tie through one of the holes you drilled into the shifter body and back through the second hole.

Connect the USB cable to the XIAO nRF52840 module and carefully install it into the space where the Di2 module was installed. Using the cable tie, secure the connector so it is central in the space.

The shifter modifications are now complete and the Bluetooth module is ready to be programmed.

Bluetooth module installed
Bluetooth module installed

Programming the XIAO nRF52840 module

There are various applications available for programming the XIAO nRF52840 module. I initially tried to use the nRF Connect SDK from Nordic Semiconductor , but had numerous issues with compiling the code and connecting to the module.

I then decided to use the Arduino IDE and Adafruit libraries for the board, as I am familiar with the IDE and its ease of use.

To install and setup the software required to program the XIAO nRF52840 module, follow the instructions on wiki.seeedstudio.com/XIAO_BLE/ to install the Arduino IDE and install the necessary Arduino Libraries.

You also need to install Python to use the XIAO nRF52840, see this forum post for more information. Download and install from python.org/downloads/.

Code

The software is based on the Adafruit blehid_keyboard example and modified to include button inputs using interrupts and additional code to debounce the button presses.

Open the Arduino IDE, select the Seeed XIAO nRF52840 board and Com port and start a new Sketch.

First we add the bluefruit include and define the Bluetooth objects.

#include <bluefruit.h>
// Define bluetooth objects
BLEDis bledis;
BLEHidAdafruit blehid;

Create buttons assigned to inputs D0 and D1.

// Buttons
const uint8_t button1Pin = D1;
const uint8_t button2Pin = D0;

Define button key press character.

// MyWhoosh Settings
// K = Shift Up
// I = Shift Down
char button1Char = 'K';
char button2Char = 'I';

// Rouvy Settings
// + = Shift Up
// - = Shift Down
//char button1Char = '+';
//char button2Char = '-';

Add variables and functions for debounce code.

// ISR flags
volatile bool button1Flag = false;
volatile bool button2Flag = false;

// Button pressed state for state-machine debounce
bool button1Pressed = false;
bool button2Pressed = false;


// Time from last button press
volatile uint64_t lastPressTime = 0;
const uint64_t minimumDelay = 100;

// ----------------------------
// Minimal ISR —  set flags to true
// ----------------------------
void button1ISR() { button1Flag = true; }
void button2ISR() { button2Flag = true; }

Track if BLE has been started for this power cycle.

bool bleStarted = false;

Function to start BLE advertising & HID

void startBLE()
{
  if(bleStarted) return;
  bleStarted = true;

  Bluefruit.begin();
  Bluefruit.setTxPower(4);

  Bluefruit.setName("Di2Keyboard");
  bledis.setManufacturer("Bluetooth Di2");
  bledis.setModel("Di2 Keyboard");

  Bluefruit.autoConnLed(true);
  // Start services
  bledis.begin();
  blehid.begin();

  // Advertising
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addAppearance(BLE_APPEARANCE_HID_KEYBOARD);
  Bluefruit.Advertising.addService(blehid);
  Bluefruit.Advertising.addName();

  // Automatic reconnect
  Bluefruit.Advertising.restartOnDisconnect(true);

  // Advertising interval ~100–200ms
  Bluefruit.Advertising.setInterval(160, 320);
  Bluefruit.Advertising.start(0);
}

Function to send the keycommand via Bluetooth.

void sendKey(char key)
{
  if (!Bluefruit.connected()) return;

  blehid.keyPress(key);
  delay(10);
  blehid.keyRelease();
}

Setup function to assign pins and add interupts.

void setup()
{
  pinMode(button1Pin, INPUT_PULLUP);
  pinMode(button2Pin, INPUT_PULLUP);

  // Attach interrupts on falling edge
  attachInterrupt(button1Pin, button1ISR, FALLING);
  attachInterrupt(button2Pin, button2ISR, FALLING);

  startBLE();
}

Main loop where we check if Bluetooth is connected and handle button presses.

void loop()
{
  if(!Bluefruit.connected())
  {
    // Start BLE if not connected
    if((button1Flag || button2Flag) && !bleStarted)
    {
      startBLE();
    }
  }
  
  // --------------------------
  // Handle button 1 press (state-machine debounce)
  // --------------------------
  if(button1Flag) {
    button1Flag = false;  // clear ISR flag
    if(!button1Pressed && digitalRead(button1Pin) == LOW) {
      if (millis() - lastPressTime > minimumDelay){
        sendKey(button1Char);
        button1Pressed = true;  // mark as pressed
        lastPressTime = millis();
      }
    }
  }

  // --------------------------
  // Handle button 2 press (state-machine debounce)
  // --------------------------
  if(button2Flag) {
    button2Flag = false;
    if(!button2Pressed && digitalRead(button2Pin) == LOW) {
      if (millis() - lastPressTime > minimumDelay){
        sendKey(button2Char);
        button2Pressed = true;
        lastPressTime = millis();
      }
    }
  }

  // --------------------------
  // Reset pressed state when button released
  // --------------------------
  if(button1Pressed && digitalRead(button1Pin) == HIGH) button1Pressed = false;
  if(button2Pressed && digitalRead(button2Pin) == HIGH) button2Pressed = false;
}

You can use the Arduino IDE to program the module via the USB cable.

The full code for this project can be downloaded from GitHub at github.com/briandorey/xiao-nrf52840-to-shimano-di2-keyboard-controller.

The Di2BluetoothKeyboard folder contains the code in Di2BluetoothKeyboard.ino and needs to be compiled and programmed to the XIAO nRF52840 module to use it as a Bluetooth keyboard.

To configure the controller to use Rouvy, remove the "//" from the start of lines 25 and 26 and add "//" to lines 19 and 20.

To configure the controller to use MyWhoosh, remove the "//" from the start of lines 19 and 20 and add "//" to lines 25 and 26.

Future upgrades could include adding a button to switch between keymaps for different applications.

Paring the controller

Power on the XIAO nRF52840 module by connecting it to a USB power source. The module will enter pairing mode for 60 seconds, during which time you can pair it with your computer or tablet.

On your computer or tablet, search for Bluetooth devices and select "Di2Keyboard" from the list of available devices. Once paired, the controller will be ready to use.

You can check the keyboard commands are being sent by opening Notepad or a text editor and pressing the shifter buttons which should send the keyboard presses to the computer.

Installation onto the bike

Remove any bar tape on the side of the handlebar where the shifter is to be installed and mount the Di2 shifter onto your handlebar.

The wire needs to be routed under the bar tape to the middle of the handlebar, where it will be connected to USB power bank or other USB power supply.

Refit the shifter's rubber hood.

Using the controller

When using Rouvy or MyWhoosh, press the front button on the Di2 shifter to shift up and the rear button to shift down.

Enjoy your modified Shimano Di2 shifter with Bluetooth keyboard functionality!

Comments


Leave a comment

Your email address will not be published.

Please enter the text from the image below to prove you are a human
Verify you are human

Table of Contents


AB Electronics

Expand your Raspberry Pi

IO Zero 32

IO Zero 32

32 channel digital I/O development board, for the Raspberry Pi.

Order IO Zero 32

Please support the blog and our projects by buying your Raspberry Pi development boards, connectors and accessories from our online store at AB Electronics UK.

Share

Share to Bluesky

Share to X / Twitter

Share to Facebook

Share to linkedin

Share to Pinterest

Copy Link