{"id":19892210,"url":"https://github.com/berg0162/zwift-control","last_synced_at":"2026-05-11T08:46:17.575Z","repository":{"id":218047407,"uuid":"743875958","full_name":"Berg0162/Zwift-Control","owner":"Berg0162","description":"Simple Zwift BLE Remote Control with only 2 buttons ","archived":false,"fork":false,"pushed_at":"2024-01-19T10:21:36.000Z","size":2836,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-11T19:43:49.861Z","etag":null,"topics":["2-button","ble","control","esp32","keyboard","nrf52","remote","zwift"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Berg0162.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2024-01-16T07:13:00.000Z","updated_at":"2024-10-10T13:00:00.000Z","dependencies_parsed_at":"2024-01-19T12:41:58.830Z","dependency_job_id":"64cd2296-3fd1-473c-b7ef-84670507fd59","html_url":"https://github.com/Berg0162/Zwift-Control","commit_stats":null,"previous_names":["berg0162/zwift-control"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Berg0162%2FZwift-Control","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Berg0162%2FZwift-Control/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Berg0162%2FZwift-Control/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Berg0162%2FZwift-Control/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Berg0162","download_url":"https://codeload.github.com/Berg0162/Zwift-Control/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241319925,"owners_count":19943637,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["2-button","ble","control","esp32","keyboard","nrf52","remote","zwift"],"created_at":"2024-11-12T18:22:29.251Z","updated_at":"2026-05-11T08:46:17.570Z","avatar_url":"https://github.com/Berg0162.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Zwift-Control\nSimple Zwift game BLE Remote Control with only 2 buttons\n\n## Versions for 2 different SOC's:\n- ESP32 with H2Zero's NimBLE-Arduino library (\u003cb\u003eesp32_Zwift_Control_v02\u003c/b\u003e complies the latest version 2.x)\n- nRF52840 with Adafruit's Bluefruit library\n\n## What it does:\nThe 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.\u003cbr\u003e \nIn short: mimics some keys of your keyboard!\n- ESP32 users\u003cbr\u003e\nSee also: [modified support library for NimBLE-Arduino version 2.x](https://github.com/Berg0162/ESP32-NIMBLE-Keyboard)\n\n## Low cost DIY alternative for:\n- Some functionality of Keith Wakeham's [Kommander](https://titanlab.co/715-2/) \u003cbr\u003e\nReview by DCRainmain: [Zwift Control For Your Handlebars](https://www.dcrainmaker.com/2021/02/kommander-review-zwift-control-for-your-handlebars.html)\n- Some functionality of Zwift Play Controller (\u003cb\u003eNO\u003c/b\u003e steering!)\n\n## Mapping of 2 buttons to game actions\nThe code has implemented a limited set of the official mapping of keyboard shortcuts during the Zwift game: [see keyboard shortcuts](https://support.zwift.com/en_us/keyboard-shortcuts-rkGrgwd4B)\u003cbr\u003e\nDefault 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.\u003cbr\u003e\nIn 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:\n\n### Freeride/Race mode\n|Action|Button #1|Button #2|\n|---------------|--------------------------|--------------------------|\n|One Click|Turn left at intersection|Turn right at intersection|\n|Double Click|Power Up|Make an U-turn|\n|Long|Change your Camera View| |\n\n### Workout mode\n|Action|Button #1|Button #2|\n|---------------|--------------------------|--------------------------|\n|One Click|Increase FTP|Decrease FTP|\n|Double Click| | |\n|Long|Change your Camera View|Skip a Workout Segment  |\n\n## Wiring the buttons to the board\n\n\u003cimg src=\"./images/membrane-keypad-red-button-600x600w.jpg\" width=\"200\" height=\"200\" align=\"left\" alt=\"Membrane Button\"\u003e\u003cbr\u003e\n\nConnecting 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...\u003cbr\u003e\n\n\u003cb\u003eWiring correctly\u003c/b\u003e\u003cbr\u003e\nWire the buttons to the GPIO pins of the respective development boards in accordance with the settings in the code, or change the settings!\n\u003cbr clear=\"left\"\u003e\n```C++\n// Two Keys/Buttons settings connected to\n#define PIN_BUTTON1    A0   // GPIO26 -\u003e ESP32 Feather V2 \n#define PIN_BUTTON2    A1   // GPIO25 -\u003e ESP32 Feather V2\n```\n\u003cb\u003ePullUP\u003c/b\u003e\u003cbr\u003e\nThe 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.\n```C++\n#include \u003cOneButton.h\u003e\n/* https://www.arduino.cc/reference/en/libraries/onebutton/\n * Initialize the OneButton library.\n * OneButton::OneButton(const int pin, const boolean activeLow, const bool pullupActive)\n * @param pin           -\u003e The pin to be used for input from a momentary button.\n * @param activeLow     -\u003e Set to true when the input level is LOW when the button is pressed, Default is true.\n * @param pullupActive  -\u003e Activate the internal pullup when available. Default is true.\n */\n// Setup button pushed input level is LOW and use internal PullUp resistor \nOneButton zwiftButton1(PIN_BUTTON1, true, true);\nOneButton zwiftButton2(PIN_BUTTON2, true, true);\n```\n\u003cbr\u003e\n\u003cp align=left\u003e\n\u003cimg src=\"./images/button.png\" width=\"796\" height=\"336\" alt=\"wiring\"\u003e\n\u003c/p\u003e\n\u003cbr clear=\"left\"\u003e\n\u003cb\u003ePullDOWN\u003c/b\u003e\u003cbr\u003e\nConnect a button at one side to the GPIO pin of your choice \u003cb\u003eand\u003c/b\u003e 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.\u003cbr\u003e\n\n## Key to Zwift Action mapping\nThe 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...\n```C++\n/*                                      Key list value       Name         Zwift Action       \n                                        KEY_ESC;          // Escape       Show End Ride Screen\n                                        KEY_RETURN;       // Return/Enter Confirm choice/Select\n                                        KEY_PAGE_UP;      // Page Up      Increase FTP\n                                        KEY_PAGE_DOWN;    // Page Down    Decrease FTP\n                                        KEY_UP_ARROW;     // Up arrow     Display Action Bar\n*/\nconst uint8_t singleClickButton1Value = KEY_LEFT_ARROW;   // Left arrow   Turn left at intersection\nconst uint8_t singleClickButton2Value = KEY_RIGHT_ARROW;  // Right arrow  Turn right at intersection\n\nconst uint8_t doubleClickButton1Value = KEY_SPACE;        // Space        Power Up\nconst uint8_t doubleClickButton2Value = KEY_DOWN_ARROW;   // Down arrow   Make an U-turn\n\nconst uint8_t longPressStopButton1Value = 49;             // 49-57 1-9    Change your Camera View 1 - 9 views\nconst uint8_t longPressStopButton2Value = KEY_TAB;        // Tab          Skip Workout Step\n\n```\n## Setup around the handlebars\nUse your inventiveness to mount the electronics enclosure (a.k.a. pod) that houses the board and a (LiPo) battery \u003cb\u003enear the handlebars\u003c/b\u003e. It is critical that you can reach the buttons easily during an intense ride or workout. Two obvious options:\n- Mount the buttons on top of the pod or\n- Wire the buttons detached of the pod and tie or stick the buttons to your handlebars.\n\nNotice 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.\n \n\u003cimg src=\"./images/Zwift_Control_Button_mount_small.png\" width=\"796\" height=\"336\" alt=\"Velcro strap\"\u003e\n\n### How to connect to your computer?\nWhen 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!\u003cbr\u003e\n\nYou’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!\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fberg0162%2Fzwift-control","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fberg0162%2Fzwift-control","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fberg0162%2Fzwift-control/lists"}