Simple Zwift game BLE Remote Control with only 2 buttons
- ESP32 with H2Zero's NimBLE library
- nRF52840 with Adafruit's Bluefruit library
The code turns an ESP32/nRF52 board into a 2 button (with 3 states) Bluetooth LE controller. Optimized for use with Zwift game to remotely initiate specific game actions that are mapped (by the Zwift app) to different keys of the regular keyboard.
In short: mimics some keys of your keyboard!
- Some functionality of Keith Wakeham's Kommander
Review by DCRainmain: Zwift Control For Your Handlebars - Some functionality of Zwift Play Controller (NO steering!)
The code has implemented a limited set of the official mapping of keyboard shortcuts during the Zwift game: see keyboard shortcuts
Default Zwift keyboard mapping is one key to one action! The present code allows for 3 actions mapped to one key, since it can detect the difference between a single button click, a double button click and a "long" button click (longer than 0.5 second). Each of these will initiate a different game action.
In addition the Zwift game knows two different modes: Freeride/Race and Workout mode. Some of the mappings will have only effect if in the "right" mode. The code will handle the different modes without your intervention, as detailed hereafter:
Action | Button #1 | Button #2 |
---|---|---|
One Click | Turn left at intersection | Turn right at intersection |
Double Click | Power Up | Make an U-turn |
Long | Change your Camera View |
Action | Button #1 | Button #2 |
---|---|---|
One Click | Increase FTP | Decrease FTP |
Double Click | ||
Long | Change your Camera View | Skip a Workout Segment |
Connecting two push buttons to the board is quite easy but should be done correctly. Any push button will do but a membrane push button has the advantage that it is not very sensitive to fluids and it has a sticky tape at the back for easy mounting...
Wiring correctly
Wire the buttons to the GPIO pins of the respective development boards in accordance with the settings in the code, or change the settings!
// Two Keys/Buttons settings connected to
#define PIN_BUTTON1 A0 // GPIO26 -> ESP32 Feather V2
#define PIN_BUTTON2 A1 // GPIO25 -> ESP32 Feather V2
PullUP
The most simple setup is to activate and use the internal pullup resistor that these SOC's offer: connect a button at one side to the GPIO pin of your choice and the other button side to ground. When the button is pushed/closed the GPIO pin will go to logical LOW.
#include <OneButton.h>
/* https://www.arduino.cc/reference/en/libraries/onebutton/
* Initialize the OneButton library.
* OneButton::OneButton(const int pin, const boolean activeLow, const bool pullupActive)
* @param pin -> The pin to be used for input from a momentary button.
* @param activeLow -> Set to true when the input level is LOW when the button is pressed, Default is true.
* @param pullupActive -> Activate the internal pullup when available. Default is true.
*/
// Setup button pushed input level is LOW and use internal PullUp resistor
OneButton zwiftButton1(PIN_BUTTON1, true, true);
OneButton zwiftButton2(PIN_BUTTON2, true, true);
PullDOWN
Connect a button at one side to the GPIO pin of your choice and to ground with a 10kOhm resistor. The other button side is connected to Vcc. When the button is pushed/closed the GPIO pin will go to logical HIGH.
The 3 states of the 2 buttons are mapped in the Zwift Control code to max 6 Zwift actions (see above) that are most relevant to select remotely during a ride or workout. The selection can be changed at your own appreciation...
/* Key list value Name Zwift Action
KEY_ESC; // Escape Show End Ride Screen
KEY_RETURN; // Return/Enter Confirm choice/Select
KEY_PAGE_UP; // Page Up Increase FTP
KEY_PAGE_DOWN; // Page Down Decrease FTP
KEY_UP_ARROW; // Up arrow Display Action Bar
*/
const uint8_t singleClickButton1Value = KEY_LEFT_ARROW; // Left arrow Turn left at intersection
const uint8_t singleClickButton2Value = KEY_RIGHT_ARROW; // Right arrow Turn right at intersection
const uint8_t doubleClickButton1Value = KEY_SPACE; // Space Power Up
const uint8_t doubleClickButton2Value = KEY_DOWN_ARROW; // Down arrow Make an U-turn
const uint8_t longPressStopButton1Value = 49; // 49-57 1-9 Change your Camera View 1 - 9 views
const uint8_t longPressStopButton2Value = KEY_TAB; // Tab Skip Workout Step
Use your inventiveness to mount the electronics enclosure (a.k.a. pod) that houses the board and a (LiPo) battery near the handlebars. It is critical that you can reach the buttons easily during an intense ride or workout. Two obvious options:
- Mount the buttons on top of the pod or
- Wire the buttons detached of the pod and tie or stick the buttons to your handlebars.
Notice how in this ergonomic setup a modified cable binder (type Velcro strap) is holding the (face down!) pushbutton in place. Fore finger is most capable of doing the clicking in a natural way while still holding the handlebar.
When you first power on your ESP32/nRF52 board with the Zwift-Control code loaded, it advertises itself as a standard Bluetooth keyboard (officially per the spec called a HID – Human Interface Device). That means you’ll see it show up on your Bluetooth settings on a Mac or PC. Notice: Zwift itself doesn’t support the Bluetooth HID devices. Pairing is between the computer and the Zwift Control!
You’ll simply have to go into your Bluetooth devices on Mac or PC, and pair it up just like you’d pair up a new Bluetooth keyboard, mouse or headphones. Only after you have successfully paired it with your computer that runs the Zwift app, it will be active during a Zwift ride. Next time when you start your computer and ESP32/nRF52 Zwift Control, pairing will be fully automatic without your intervention! As long as you do not remove the Zwift Control from the list of bonded BLE devices it will pair when both devices are powered on!