├── .github ├── dependabot.yml └── workflows │ └── main.yml ├── BleConnectionStatus.cpp ├── BleConnectionStatus.h ├── BleGamepad.cpp ├── BleGamepad.h ├── BleGamepadConfiguration.cpp ├── BleGamepadConfiguration.h ├── BleNUS.cpp ├── BleNUS.h ├── BleOutputReceiver.cpp ├── BleOutputReceiver.h ├── README.md ├── TroubleshootingGuide.md ├── examples ├── CharacteristicsConfiguration │ └── CharacteristicsConfiguration.ino ├── DrivingControllerTest │ └── DrivingControllerTest.ino ├── Fightstick │ └── Fightstick.ino ├── FlightControllerTest │ └── FlightControllerTest.ino ├── ForcePairingMode │ └── ForcePairingMode.ino ├── Gamepad │ └── Gamepad.ino ├── GetPeerInfo │ └── GetPeerInfo.ino ├── IndividualAxes │ └── IndividualAxes.ino ├── Keypad4x4 │ └── Keypad4x4.ino ├── MotionController │ └── MotionController.ino ├── MultipleButtons │ └── MultipleButtons.ino ├── MultipleButtonsAndHats │ └── MultipleButtonsAndHats.ino ├── MultipleButtonsDebounce │ └── MultipleButtonsDebounce.ino ├── PotAsAxis │ └── PotAsAxis.ino ├── SetBatteryLevel │ └── SetBatteryLevel.ino ├── SetBatteryPowerState │ └── SetBatteryPowerState.ino ├── SingleButton │ └── SingleButton.ino ├── SingleButtonDebounce │ └── SingleButtonDebounce.ino ├── SpecialButtons │ └── SpecialButtons.ino ├── TestAll │ └── TestAll.ino └── TestReceivingOutputReport │ └── TestReceivingOutputReport.ino ├── keywords.txt ├── library.properties └── license.txt /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | # Maintain dependencies for GitHub Actions 5 | - package-ecosystem: "github-actions" 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Compile Sketches 3 | 4 | 'on': 5 | push: 6 | tags: 7 | - "v[0-9]+.[0-9]+.[0-9]*" 8 | branches: 9 | - "master" 10 | - "ci-builds-*" 11 | pull_request: 12 | workflow_dispatch: 13 | 14 | permissions: {} 15 | 16 | jobs: 17 | compile-sketches: 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@v4 23 | 24 | # See: https://github.com/arduino/compile-sketches#readme 25 | - name: Compile sketches 26 | uses: arduino/compile-sketches@v1 27 | with: 28 | fqbn: esp32:esp32:esp32 29 | sketch-paths: | 30 | - examples/CharacteristicsConfiguration/CharacteristicsConfiguration.ino 31 | - examples/DrivingControllerTest/DrivingControllerTest.ino 32 | - examples/Fightstick/Fightstick.ino 33 | - examples/FlightControllerTest/FlightControllerTest.ino 34 | - examples/ForcePairingMode/ForcePairingMode.ino 35 | - examples/Gamepad/Gamepad.ino 36 | - examples/GetPeerInfo/GetPeerInfo.ino 37 | - examples/IndividualAxes/IndividualAxes.ino 38 | - examples/Keypad4x4/Keypad4x4.ino 39 | - examples/MultipleButtons/MultipleButtons.ino 40 | - examples/MultipleButtonsAndHats/MultipleButtonsAndHats.ino 41 | - examples/MultipleButtonsDebounce/MultipleButtonsDebounce.ino 42 | - examples/PotAsAxis/PotAsAxis.ino 43 | - examples/SetBatteryLevel/SetBatteryLevel.ino 44 | - examples/SingleButton/SingleButton.ino 45 | - examples/SingleButtonDebounce/SingleButtonDebounce.ino 46 | - examples/SpecialButtons/SpecialButtons.ino 47 | - examples/TestAll/TestAll.ino 48 | - examples/TestReceivingOutputReport/TestReceivingOutputReport.ino 49 | libraries: | 50 | - name: NimBLE-Arduino 51 | - name: Keypad 52 | - name: Bounce2 53 | - source-path: . 54 | -------------------------------------------------------------------------------- /BleConnectionStatus.cpp: -------------------------------------------------------------------------------- 1 | #include "BleConnectionStatus.h" 2 | #include "NimBLELog.h" 3 | 4 | static const char* LOG_TAG = "BleConnectionStatus"; 5 | 6 | BleConnectionStatus::BleConnectionStatus(void) 7 | { 8 | } 9 | 10 | void BleConnectionStatus::onConnect(NimBLEServer *pServer, NimBLEConnInfo& connInfo) 11 | { 12 | NIMBLE_LOGD(LOG_TAG, "onConnect - Connected Address: %s", std::string(connInfo.getAddress()).c_str()); 13 | pServer->updateConnParams(connInfo.getConnHandle(), 6, 7, 0, 600); 14 | } 15 | 16 | void BleConnectionStatus::onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) 17 | { 18 | NIMBLE_LOGD(LOG_TAG, "onDisconnectConnect - Disconnected Address: %s", std::string(connInfo.getAddress()).c_str()); 19 | this->connected = false; 20 | } 21 | 22 | void BleConnectionStatus::onAuthenticationComplete(NimBLEConnInfo& connInfo) 23 | { 24 | NIMBLE_LOGD(LOG_TAG, "onAuthenticationComplete - Authenticated Address: %s", std::string(connInfo.getAddress()).c_str()); 25 | this->connected = true; 26 | } 27 | -------------------------------------------------------------------------------- /BleConnectionStatus.h: -------------------------------------------------------------------------------- 1 | #ifndef ESP32_BLE_CONNECTION_STATUS_H 2 | #define ESP32_BLE_CONNECTION_STATUS_H 3 | #include "sdkconfig.h" 4 | #if defined(CONFIG_BT_ENABLED) 5 | 6 | #include "nimconfig.h" 7 | #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) 8 | 9 | #include 10 | #include "NimBLECharacteristic.h" 11 | #include "NimBLEConnInfo.h" 12 | 13 | class BleConnectionStatus : public NimBLEServerCallbacks 14 | { 15 | public: 16 | BleConnectionStatus(void); 17 | bool connected = false; 18 | void onConnect(NimBLEServer *pServer, NimBLEConnInfo& connInfo) override; 19 | void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo& connInfo, int reason) override; 20 | void onAuthenticationComplete(NimBLEConnInfo& connInfo) override; 21 | NimBLECharacteristic *inputGamepad; 22 | }; 23 | 24 | #endif // CONFIG_BT_NIMBLE_ROLE_PERIPHERAL 25 | #endif // CONFIG_BT_ENABLED 26 | #endif // ESP32_BLE_CONNECTION_STATUS_H 27 | -------------------------------------------------------------------------------- /BleGamepad.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "NimBLEHIDDevice.h" 5 | #include "HIDTypes.h" 6 | #include "HIDKeyboardTypes.h" 7 | #include "sdkconfig.h" 8 | #include "BleConnectionStatus.h" 9 | #include "BleGamepad.h" 10 | #include "NimBLELog.h" 11 | #include "BleGamepadConfiguration.h" 12 | 13 | #include 14 | 15 | #if defined(CONFIG_ARDUHAL_ESP_LOG) 16 | #include "esp32-hal-log.h" 17 | #define LOG_TAG "BLEGamepad" 18 | #else 19 | #include "esp_log.h" 20 | static const char *LOG_TAG = "BLEGamepad"; 21 | #endif 22 | 23 | #define SERVICE_UUID_DEVICE_INFORMATION "180A" // Service - Device information 24 | 25 | #define CHARACTERISTIC_UUID_MODEL_NUMBER "2A24" // Characteristic - Model Number String - 0x2A24 26 | #define CHARACTERISTIC_UUID_SOFTWARE_REVISION "2A28" // Characteristic - Software Revision String - 0x2A28 27 | #define CHARACTERISTIC_UUID_SERIAL_NUMBER "2A25" // Characteristic - Serial Number String - 0x2A25 28 | #define CHARACTERISTIC_UUID_FIRMWARE_REVISION "2A26" // Characteristic - Firmware Revision String - 0x2A26 29 | #define CHARACTERISTIC_UUID_HARDWARE_REVISION "2A27" // Characteristic - Hardware Revision String - 0x2A27 30 | #define CHARACTERISTIC_UUID_BATTERY_POWER_STATE "2A1A" // Characteristic - Battery Power State - 0x2A1A 31 | 32 | #define POWER_STATE_UNKNOWN 0 // 0b00 33 | #define POWER_STATE_NOT_SUPPORTED 1 // 0b01 34 | #define POWER_STATE_NOT_PRESENT 2 // 0b10 35 | #define POWER_STATE_NOT_DISCHARGING 2 // 0b10 36 | #define POWER_STATE_NOT_CHARGING 2 // 0b10 37 | #define POWER_STATE_GOOD 2 // 0b10 38 | #define POWER_STATE_PRESENT 3 // 0b11 39 | #define POWER_STATE_DISCHARGING 3 // 0b11 40 | #define POWER_STATE_CHARGING 3 // 0b11 41 | #define POWER_STATE_CRITICAL 3 // 0b11 42 | 43 | BleGamepad::BleGamepad(std::string deviceName, std::string deviceManufacturer, uint8_t batteryLevel, bool delayAdvertising) : _buttons(), 44 | _specialButtons(0), 45 | _x(0), 46 | _y(0), 47 | _z(0), 48 | _rX(0), 49 | _rY(0), 50 | _rZ(0), 51 | _slider1(0), 52 | _slider2(0), 53 | _rudder(0), 54 | _throttle(0), 55 | _accelerator(0), 56 | _brake(0), 57 | _steering(0), 58 | _hat1(0), 59 | _hat2(0), 60 | _hat3(0), 61 | _hat4(0), 62 | _gX(0), 63 | _gY(0), 64 | _gZ(0), 65 | _aX(0), 66 | _aY(0), 67 | _aZ(0), 68 | _batteryPowerInformation(0), 69 | _dischargingState(0), 70 | _chargingState(0), 71 | _powerLevel(0), 72 | hid(0), 73 | pCharacteristic_Power_State(0), 74 | configuration(), 75 | pServer(nullptr), 76 | nus(nullptr) 77 | { 78 | this->resetButtons(); 79 | this->deviceName = deviceName; 80 | this->deviceManufacturer = deviceManufacturer; 81 | this->batteryLevel = batteryLevel; 82 | this->delayAdvertising = delayAdvertising; 83 | this->connectionStatus = new BleConnectionStatus(); 84 | 85 | hidReportDescriptorSize = 0; 86 | hidReportSize = 0; 87 | numOfButtonBytes = 0; 88 | enableOutputReport = false; 89 | outputReportLength = 64; 90 | nusInitialized = false; 91 | } 92 | 93 | void BleGamepad::resetButtons() 94 | { 95 | memset(&_buttons, 0, sizeof(_buttons)); 96 | } 97 | 98 | void BleGamepad::begin(BleGamepadConfiguration *config) 99 | { 100 | configuration = *config; // we make a copy, so the user can't change actual values midway through operation, without calling the begin function again 101 | 102 | enableOutputReport = configuration.getEnableOutputReport(); 103 | outputReportLength = configuration.getOutputReportLength(); 104 | 105 | uint8_t buttonPaddingBits = 8 - (configuration.getButtonCount() % 8); 106 | if (buttonPaddingBits == 8) 107 | { 108 | buttonPaddingBits = 0; 109 | } 110 | uint8_t specialButtonPaddingBits = 8 - (configuration.getTotalSpecialButtonCount() % 8); 111 | if (specialButtonPaddingBits == 8) 112 | { 113 | specialButtonPaddingBits = 0; 114 | } 115 | uint8_t numOfAxisBytes = configuration.getAxisCount() * 2; 116 | uint8_t numOfSimulationBytes = configuration.getSimulationCount() * 2; 117 | 118 | numOfButtonBytes = configuration.getButtonCount() / 8; 119 | if (buttonPaddingBits > 0) 120 | { 121 | numOfButtonBytes++; 122 | } 123 | 124 | uint8_t numOfSpecialButtonBytes = configuration.getTotalSpecialButtonCount() / 8; 125 | if (specialButtonPaddingBits > 0) 126 | { 127 | numOfSpecialButtonBytes++; 128 | } 129 | 130 | uint8_t numOfMotionBytes = 0; 131 | if (configuration.getIncludeAccelerometer()) 132 | { 133 | numOfMotionBytes += 6; 134 | } 135 | 136 | if (configuration.getIncludeGyroscope()) 137 | { 138 | numOfMotionBytes += 6; 139 | } 140 | 141 | hidReportSize = numOfButtonBytes + numOfSpecialButtonBytes + numOfAxisBytes + numOfSimulationBytes + numOfMotionBytes + configuration.getHatSwitchCount(); 142 | 143 | // USAGE_PAGE (Generic Desktop) 144 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 145 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 146 | 147 | // USAGE (Joystick - 0x04; Gamepad - 0x05; Multi-axis Controller - 0x08) 148 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 149 | tempHidReportDescriptor[hidReportDescriptorSize++] = configuration.getControllerType(); 150 | 151 | // COLLECTION (Application) 152 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xa1; 153 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 154 | 155 | // REPORT_ID (Default: 3) 156 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x85; 157 | tempHidReportDescriptor[hidReportDescriptorSize++] = configuration.getHidReportId(); 158 | 159 | if (configuration.getButtonCount() > 0) 160 | { 161 | // USAGE_PAGE (Button) 162 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 163 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 164 | 165 | // LOGICAL_MINIMUM (0) 166 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; 167 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 168 | 169 | // LOGICAL_MAXIMUM (1) 170 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x25; 171 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 172 | 173 | // REPORT_SIZE (1) 174 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 175 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 176 | 177 | // USAGE_MINIMUM (Button 1) 178 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x19; 179 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 180 | 181 | // USAGE_MAXIMUM (Up to 128 buttons possible) 182 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x29; 183 | tempHidReportDescriptor[hidReportDescriptorSize++] = configuration.getButtonCount(); 184 | 185 | // REPORT_COUNT (# of buttons) 186 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 187 | tempHidReportDescriptor[hidReportDescriptorSize++] = configuration.getButtonCount(); 188 | 189 | // INPUT (Data,Var,Abs) 190 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 191 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 192 | 193 | if (buttonPaddingBits > 0) 194 | { 195 | 196 | // REPORT_SIZE (1) 197 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 198 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 199 | 200 | // REPORT_COUNT (# of padding bits) 201 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 202 | tempHidReportDescriptor[hidReportDescriptorSize++] = buttonPaddingBits; 203 | 204 | // INPUT (Const,Var,Abs) 205 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 206 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x03; 207 | 208 | } // Padding Bits Needed 209 | 210 | } // Buttons 211 | 212 | if (configuration.getTotalSpecialButtonCount() > 0) 213 | { 214 | // LOGICAL_MINIMUM (0) 215 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; 216 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 217 | 218 | // LOGICAL_MAXIMUM (1) 219 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x25; 220 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 221 | 222 | // REPORT_SIZE (1) 223 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 224 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 225 | 226 | if (configuration.getDesktopSpecialButtonCount() > 0) 227 | { 228 | 229 | // USAGE_PAGE (Generic Desktop) 230 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 231 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 232 | 233 | // REPORT_COUNT 234 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 235 | tempHidReportDescriptor[hidReportDescriptorSize++] = configuration.getDesktopSpecialButtonCount(); 236 | 237 | if (configuration.getIncludeStart()) 238 | { 239 | // USAGE (Start) 240 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 241 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x3D; 242 | } 243 | 244 | if (configuration.getIncludeSelect()) 245 | { 246 | // USAGE (Select) 247 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 248 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x3E; 249 | } 250 | 251 | if (configuration.getIncludeMenu()) 252 | { 253 | // USAGE (App Menu) 254 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 255 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x86; 256 | } 257 | 258 | // INPUT (Data,Var,Abs) 259 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 260 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 261 | } 262 | 263 | if (configuration.getConsumerSpecialButtonCount() > 0) 264 | { 265 | 266 | // USAGE_PAGE (Consumer Page) 267 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 268 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x0C; 269 | 270 | // REPORT_COUNT 271 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 272 | tempHidReportDescriptor[hidReportDescriptorSize++] = configuration.getConsumerSpecialButtonCount(); 273 | 274 | if (configuration.getIncludeHome()) 275 | { 276 | // USAGE (Home) 277 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x0A; 278 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x23; 279 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 280 | } 281 | 282 | if (configuration.getIncludeBack()) 283 | { 284 | // USAGE (Back) 285 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x0A; 286 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x24; 287 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 288 | } 289 | 290 | if (configuration.getIncludeVolumeInc()) 291 | { 292 | // USAGE (Volume Increment) 293 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 294 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xE9; 295 | } 296 | 297 | if (configuration.getIncludeVolumeDec()) 298 | { 299 | // USAGE (Volume Decrement) 300 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 301 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xEA; 302 | } 303 | 304 | if (configuration.getIncludeVolumeMute()) 305 | { 306 | // USAGE (Mute) 307 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 308 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xE2; 309 | } 310 | 311 | // INPUT (Data,Var,Abs) 312 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 313 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 314 | } 315 | 316 | if (specialButtonPaddingBits > 0) 317 | { 318 | 319 | // REPORT_SIZE (1) 320 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 321 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 322 | 323 | // REPORT_COUNT (# of padding bits) 324 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 325 | tempHidReportDescriptor[hidReportDescriptorSize++] = specialButtonPaddingBits; 326 | 327 | // INPUT (Const,Var,Abs) 328 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 329 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x03; 330 | 331 | } // Padding Bits Needed 332 | 333 | } // Special Buttons 334 | 335 | if (configuration.getAxisCount() > 0) 336 | { 337 | // USAGE_PAGE (Generic Desktop) 338 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 339 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 340 | 341 | // USAGE (Pointer) 342 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 343 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 344 | 345 | // LOGICAL_MINIMUM (-32767) 346 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x16; 347 | tempHidReportDescriptor[hidReportDescriptorSize++] = lowByte(configuration.getAxesMin()); 348 | tempHidReportDescriptor[hidReportDescriptorSize++] = highByte(configuration.getAxesMin()); 349 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; // Use these two lines for 0 min 350 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 351 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; // Use these two lines for -32767 min 352 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x80; 353 | 354 | // LOGICAL_MAXIMUM (+32767) 355 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x26; 356 | tempHidReportDescriptor[hidReportDescriptorSize++] = lowByte(configuration.getAxesMax()); 357 | tempHidReportDescriptor[hidReportDescriptorSize++] = highByte(configuration.getAxesMax()); 358 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; // Use these two lines for 255 max 359 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 360 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; // Use these two lines for +32767 max 361 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x7F; 362 | 363 | // REPORT_SIZE (16) 364 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 365 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x10; 366 | 367 | // REPORT_COUNT (configuration.getAxisCount()) 368 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 369 | tempHidReportDescriptor[hidReportDescriptorSize++] = configuration.getAxisCount(); 370 | 371 | // COLLECTION (Physical) 372 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xA1; 373 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 374 | 375 | if (configuration.getIncludeXAxis()) 376 | { 377 | // USAGE (X) 378 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 379 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x30; 380 | } 381 | 382 | if (configuration.getIncludeYAxis()) 383 | { 384 | // USAGE (Y) 385 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 386 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x31; 387 | } 388 | 389 | if (configuration.getIncludeZAxis()) 390 | { 391 | // USAGE (Z) 392 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 393 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x32; 394 | } 395 | 396 | if (configuration.getIncludeRzAxis()) 397 | { 398 | // USAGE (Rz) 399 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 400 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x35; 401 | } 402 | 403 | if (configuration.getIncludeRxAxis()) 404 | { 405 | // USAGE (Rx) 406 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 407 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x33; 408 | } 409 | 410 | if (configuration.getIncludeRyAxis()) 411 | { 412 | // USAGE (Ry) 413 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 414 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x34; 415 | } 416 | 417 | if (configuration.getIncludeSlider1()) 418 | { 419 | // USAGE (Slider) 420 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 421 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x36; 422 | } 423 | 424 | if (configuration.getIncludeSlider2()) 425 | { 426 | // USAGE (Slider) 427 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 428 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x36; 429 | } 430 | 431 | // INPUT (Data,Var,Abs) 432 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 433 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 434 | 435 | // END_COLLECTION (Physical) 436 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; 437 | 438 | } // X, Y, Z, Rx, Ry, and Rz Axis 439 | 440 | if (configuration.getSimulationCount() > 0) 441 | { 442 | 443 | // USAGE_PAGE (Simulation Controls) 444 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 445 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 446 | 447 | // LOGICAL_MINIMUM (-32767) 448 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x16; 449 | tempHidReportDescriptor[hidReportDescriptorSize++] = lowByte(configuration.getSimulationMin()); 450 | tempHidReportDescriptor[hidReportDescriptorSize++] = highByte(configuration.getSimulationMin()); 451 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; // Use these two lines for 0 min 452 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 453 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; // Use these two lines for -32767 min 454 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x80; 455 | 456 | // LOGICAL_MAXIMUM (+32767) 457 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x26; 458 | tempHidReportDescriptor[hidReportDescriptorSize++] = lowByte(configuration.getSimulationMax()); 459 | tempHidReportDescriptor[hidReportDescriptorSize++] = highByte(configuration.getSimulationMax()); 460 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; // Use these two lines for 255 max 461 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 462 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; // Use these two lines for +32767 max 463 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x7F; 464 | 465 | // REPORT_SIZE (16) 466 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 467 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x10; 468 | 469 | // REPORT_COUNT (configuration.getSimulationCount()) 470 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 471 | tempHidReportDescriptor[hidReportDescriptorSize++] = configuration.getSimulationCount(); 472 | 473 | // COLLECTION (Physical) 474 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xA1; 475 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 476 | 477 | if (configuration.getIncludeRudder()) 478 | { 479 | // USAGE (Rudder) 480 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 481 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xBA; 482 | } 483 | 484 | if (configuration.getIncludeThrottle()) 485 | { 486 | // USAGE (Throttle) 487 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 488 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xBB; 489 | } 490 | 491 | if (configuration.getIncludeAccelerator()) 492 | { 493 | // USAGE (Accelerator) 494 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 495 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xC4; 496 | } 497 | 498 | if (configuration.getIncludeBrake()) 499 | { 500 | // USAGE (Brake) 501 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 502 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xC5; 503 | } 504 | 505 | if (configuration.getIncludeSteering()) 506 | { 507 | // USAGE (Steering) 508 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 509 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xC8; 510 | } 511 | 512 | // INPUT (Data,Var,Abs) 513 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 514 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 515 | 516 | // END_COLLECTION (Physical) 517 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; 518 | 519 | } // Simulation Controls 520 | 521 | 522 | // Gyroscope 523 | if (configuration.getIncludeGyroscope()) 524 | { 525 | // COLLECTION (Physical) 526 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xA1; 527 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 528 | 529 | // USAGE_PAGE (Generic Desktop) 530 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 531 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 532 | 533 | // USAGE (Gyroscope - Rotational X - Rx) 534 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 535 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x33; 536 | 537 | // USAGE (Rotational - Rotational Y - Ry) 538 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 539 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x34; 540 | 541 | // USAGE (Rotational - Rotational Z - Rz) 542 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 543 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x35; 544 | 545 | // LOGICAL_MINIMUM (-32767) 546 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x16; 547 | tempHidReportDescriptor[hidReportDescriptorSize++] = lowByte(configuration.getMotionMin()); 548 | tempHidReportDescriptor[hidReportDescriptorSize++] = highByte(configuration.getMotionMin()); 549 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; // Use these two lines for 0 min 550 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 551 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; // Use these two lines for -32767 min 552 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x80; 553 | 554 | // LOGICAL_MAXIMUM (+32767) 555 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x26; 556 | tempHidReportDescriptor[hidReportDescriptorSize++] = lowByte(configuration.getMotionMax()); 557 | tempHidReportDescriptor[hidReportDescriptorSize++] = highByte(configuration.getMotionMax()); 558 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; // Use these two lines for 255 max 559 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 560 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; // Use these two lines for +32767 max 561 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x7F; 562 | 563 | // REPORT_SIZE (16) 564 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 565 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x10; 566 | 567 | // REPORT_COUNT (3) 568 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 569 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x03; 570 | 571 | // INPUT (Data,Var,Abs) 572 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 573 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 574 | 575 | // END_COLLECTION (Physical) 576 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; 577 | 578 | } //Gyroscope 579 | 580 | // Accelerometer 581 | if (configuration.getIncludeAccelerometer()) 582 | { 583 | // COLLECTION (Physical) 584 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xA1; 585 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 586 | 587 | // USAGE_PAGE (Generic Desktop) 588 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x05; 589 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 590 | 591 | // USAGE (Accelerometer - Vector X - Vx) 592 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 593 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x40; 594 | 595 | // USAGE (Accelerometer - Vector Y - Vy) 596 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 597 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x41; 598 | 599 | // USAGE (Accelerometer - Vector Z - Vz) 600 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 601 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x42; 602 | 603 | // LOGICAL_MINIMUM (-32767) 604 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x16; 605 | tempHidReportDescriptor[hidReportDescriptorSize++] = lowByte(configuration.getMotionMin()); 606 | tempHidReportDescriptor[hidReportDescriptorSize++] = highByte(configuration.getMotionMin()); 607 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; // Use these two lines for 0 min 608 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 609 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; // Use these two lines for -32767 min 610 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x80; 611 | 612 | // LOGICAL_MAXIMUM (+32767) 613 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x26; 614 | tempHidReportDescriptor[hidReportDescriptorSize++] = lowByte(configuration.getMotionMax()); 615 | tempHidReportDescriptor[hidReportDescriptorSize++] = highByte(configuration.getMotionMax()); 616 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; // Use these two lines for 255 max 617 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 618 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; // Use these two lines for +32767 max 619 | //tempHidReportDescriptor[hidReportDescriptorSize++] = 0x7F; 620 | 621 | // REPORT_SIZE (16) 622 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 623 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x10; 624 | 625 | // REPORT_COUNT (3) 626 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 627 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x03; 628 | 629 | // INPUT (Data,Var,Abs) 630 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 631 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 632 | 633 | // END_COLLECTION (Physical) 634 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; 635 | 636 | } //Accelerometer 637 | 638 | if (configuration.getHatSwitchCount() > 0) 639 | { 640 | 641 | // COLLECTION (Physical) 642 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xA1; 643 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 644 | 645 | // USAGE_PAGE (Generic Desktop) 646 | tempHidReportDescriptor[hidReportDescriptorSize++] = USAGE_PAGE(1); 647 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 648 | 649 | // USAGE (Hat Switch) 650 | for (int currentHatIndex = 0; currentHatIndex < configuration.getHatSwitchCount(); currentHatIndex++) 651 | { 652 | tempHidReportDescriptor[hidReportDescriptorSize++] = USAGE(1); 653 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x39; 654 | } 655 | 656 | // Logical Min (1) 657 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; 658 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 659 | 660 | // Logical Max (8) 661 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x25; 662 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x08; 663 | 664 | // Physical Min (0) 665 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x35; 666 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 667 | 668 | // Physical Max (315) 669 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x46; 670 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x3B; 671 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 672 | 673 | // Unit (SI Rot : Ang Pos) 674 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x65; 675 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x12; 676 | 677 | // Report Size (8) 678 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 679 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x08; 680 | 681 | // Report Count (4) 682 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 683 | tempHidReportDescriptor[hidReportDescriptorSize++] = configuration.getHatSwitchCount(); 684 | 685 | // Input (Data, Variable, Absolute) 686 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x81; 687 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x42; 688 | 689 | // END_COLLECTION (Physical) 690 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; 691 | } // Hat Switches 692 | 693 | 694 | if (configuration.getEnableOutputReport()) 695 | { 696 | // Usage Page (Vendor Defined 0xFF00) 697 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x06; 698 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 699 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; 700 | 701 | // Usage (Vendor Usage 0x01) 702 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 703 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 704 | 705 | // Usage (0x01) 706 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x09; 707 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x01; 708 | 709 | // Logical Minimum (0) 710 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x15; 711 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 712 | 713 | // Logical Maximum (255) 714 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x26; 715 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xFF; 716 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x00; 717 | 718 | // Report Size (8 bits) 719 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x75; 720 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x08; 721 | 722 | if (configuration.getOutputReportLength() <= 0xFF) 723 | { 724 | // Report Count (0~255 bytes) 725 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x95; 726 | tempHidReportDescriptor[hidReportDescriptorSize++] = configuration.getOutputReportLength(); 727 | } 728 | else 729 | { 730 | // Report Count (0~65535 bytes) 731 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x96; 732 | tempHidReportDescriptor[hidReportDescriptorSize++] = lowByte(configuration.getOutputReportLength()); 733 | tempHidReportDescriptor[hidReportDescriptorSize++] = highByte(configuration.getOutputReportLength()); 734 | } 735 | 736 | // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 737 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x91; 738 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0x02; 739 | } 740 | 741 | // END_COLLECTION (Application) 742 | tempHidReportDescriptor[hidReportDescriptorSize++] = 0xc0; 743 | 744 | // Set task priority from 5 to 1 in order to get ESP32-C3 working 745 | xTaskCreate(this->taskServer, "server", 20000, (void *)this, 1, NULL); 746 | } 747 | 748 | void BleGamepad::end(void) 749 | { 750 | } 751 | 752 | void BleGamepad::setAxes(int16_t x, int16_t y, int16_t z, int16_t rX, int16_t rY, int16_t rZ, int16_t slider1, int16_t slider2) 753 | { 754 | if (x == -32768) 755 | { 756 | x = -32767; 757 | } 758 | if (y == -32768) 759 | { 760 | y = -32767; 761 | } 762 | if (z == -32768) 763 | { 764 | z = -32767; 765 | } 766 | if (rZ == -32768) 767 | { 768 | rZ = -32767; 769 | } 770 | if (rX == -32768) 771 | { 772 | rX = -32767; 773 | } 774 | if (rY == -32768) 775 | { 776 | rY = -32767; 777 | } 778 | if (slider1 == -32768) 779 | { 780 | slider1 = -32767; 781 | } 782 | if (slider2 == -32768) 783 | { 784 | slider2 = -32767; 785 | } 786 | 787 | _x = x; 788 | _y = y; 789 | _z = z; 790 | _rZ = rZ; 791 | _rX = rX; 792 | _rY = rY; 793 | _slider1 = slider1; 794 | _slider2 = slider2; 795 | 796 | if (configuration.getAutoReport()) 797 | { 798 | sendReport(); 799 | } 800 | } 801 | 802 | void BleGamepad::setHIDAxes(int16_t x, int16_t y, int16_t z, int16_t rZ, int16_t rX, int16_t rY, int16_t slider1, int16_t slider2) 803 | { 804 | if (x == -32768) 805 | { 806 | x = -32767; 807 | } 808 | if (y == -32768) 809 | { 810 | y = -32767; 811 | } 812 | if (z == -32768) 813 | { 814 | z = -32767; 815 | } 816 | if (rZ == -32768) 817 | { 818 | rZ = -32767; 819 | } 820 | if (rX == -32768) 821 | { 822 | rX = -32767; 823 | } 824 | if (rY == -32768) 825 | { 826 | rY = -32767; 827 | } 828 | if (slider1 == -32768) 829 | { 830 | slider1 = -32767; 831 | } 832 | if (slider2 == -32768) 833 | { 834 | slider2 = -32767; 835 | } 836 | 837 | _x = x; 838 | _y = y; 839 | _z = z; 840 | _rZ = rZ; 841 | _rX = rX; 842 | _rY = rY; 843 | _slider1 = slider1; 844 | _slider2 = slider2; 845 | 846 | if (configuration.getAutoReport()) 847 | { 848 | sendReport(); 849 | } 850 | } 851 | 852 | void BleGamepad::setSimulationControls(int16_t rudder, int16_t throttle, int16_t accelerator, int16_t brake, int16_t steering) 853 | { 854 | if (rudder == -32768) 855 | { 856 | rudder = -32767; 857 | } 858 | if (throttle == -32768) 859 | { 860 | throttle = -32767; 861 | } 862 | if (accelerator == -32768) 863 | { 864 | accelerator = -32767; 865 | } 866 | if (brake == -32768) 867 | { 868 | brake = -32767; 869 | } 870 | if (steering == -32768) 871 | { 872 | steering = -32767; 873 | } 874 | 875 | _rudder = rudder; 876 | _throttle = throttle; 877 | _accelerator = accelerator; 878 | _brake = brake; 879 | _steering = steering; 880 | 881 | if (configuration.getAutoReport()) 882 | { 883 | sendReport(); 884 | } 885 | } 886 | 887 | void BleGamepad::setHats(signed char hat1, signed char hat2, signed char hat3, signed char hat4) 888 | { 889 | _hat1 = hat1; 890 | _hat2 = hat2; 891 | _hat3 = hat3; 892 | _hat4 = hat4; 893 | 894 | if (configuration.getAutoReport()) 895 | { 896 | sendReport(); 897 | } 898 | } 899 | 900 | void BleGamepad::setSliders(int16_t slider1, int16_t slider2) 901 | { 902 | if (slider1 == -32768) 903 | { 904 | slider1 = -32767; 905 | } 906 | if (slider2 == -32768) 907 | { 908 | slider2 = -32767; 909 | } 910 | 911 | _slider1 = slider1; 912 | _slider2 = slider2; 913 | 914 | if (configuration.getAutoReport()) 915 | { 916 | sendReport(); 917 | } 918 | } 919 | 920 | void BleGamepad::sendReport(void) 921 | { 922 | if (this->isConnected()) 923 | { 924 | uint8_t currentReportIndex = 0; 925 | 926 | uint8_t m[hidReportSize]; 927 | 928 | memset(&m, 0, sizeof(m)); 929 | memcpy(&m, &_buttons, sizeof(_buttons)); 930 | 931 | currentReportIndex += numOfButtonBytes; 932 | 933 | if (configuration.getTotalSpecialButtonCount() > 0) 934 | { 935 | m[currentReportIndex++] = _specialButtons; 936 | } 937 | 938 | if (configuration.getIncludeXAxis()) 939 | { 940 | m[currentReportIndex++] = _x; 941 | m[currentReportIndex++] = (_x >> 8); 942 | } 943 | if (configuration.getIncludeYAxis()) 944 | { 945 | m[currentReportIndex++] = _y; 946 | m[currentReportIndex++] = (_y >> 8); 947 | } 948 | if (configuration.getIncludeZAxis()) 949 | { 950 | m[currentReportIndex++] = _z; 951 | m[currentReportIndex++] = (_z >> 8); 952 | } 953 | if (configuration.getIncludeRzAxis()) 954 | { 955 | m[currentReportIndex++] = _rZ; 956 | m[currentReportIndex++] = (_rZ >> 8); 957 | } 958 | if (configuration.getIncludeRxAxis()) 959 | { 960 | m[currentReportIndex++] = _rX; 961 | m[currentReportIndex++] = (_rX >> 8); 962 | } 963 | if (configuration.getIncludeRyAxis()) 964 | { 965 | m[currentReportIndex++] = _rY; 966 | m[currentReportIndex++] = (_rY >> 8); 967 | } 968 | 969 | if (configuration.getIncludeSlider1()) 970 | { 971 | m[currentReportIndex++] = _slider1; 972 | m[currentReportIndex++] = (_slider1 >> 8); 973 | } 974 | if (configuration.getIncludeSlider2()) 975 | { 976 | m[currentReportIndex++] = _slider2; 977 | m[currentReportIndex++] = (_slider2 >> 8); 978 | } 979 | 980 | if (configuration.getIncludeRudder()) 981 | { 982 | m[currentReportIndex++] = _rudder; 983 | m[currentReportIndex++] = (_rudder >> 8); 984 | } 985 | if (configuration.getIncludeThrottle()) 986 | { 987 | m[currentReportIndex++] = _throttle; 988 | m[currentReportIndex++] = (_throttle >> 8); 989 | } 990 | if (configuration.getIncludeAccelerator()) 991 | { 992 | m[currentReportIndex++] = _accelerator; 993 | m[currentReportIndex++] = (_accelerator >> 8); 994 | } 995 | if (configuration.getIncludeBrake()) 996 | { 997 | m[currentReportIndex++] = _brake; 998 | m[currentReportIndex++] = (_brake >> 8); 999 | } 1000 | if (configuration.getIncludeSteering()) 1001 | { 1002 | m[currentReportIndex++] = _steering; 1003 | m[currentReportIndex++] = (_steering >> 8); 1004 | } 1005 | 1006 | if (configuration.getIncludeGyroscope()) 1007 | { 1008 | m[currentReportIndex++] = _gX; 1009 | m[currentReportIndex++] = (_gX >> 8); 1010 | m[currentReportIndex++] = _gY; 1011 | m[currentReportIndex++] = (_gY >> 8); 1012 | m[currentReportIndex++] = _gZ; 1013 | m[currentReportIndex++] = (_gZ >> 8); 1014 | } 1015 | 1016 | if (configuration.getIncludeAccelerometer()) 1017 | { 1018 | m[currentReportIndex++] = _aX; 1019 | m[currentReportIndex++] = (_aX >> 8); 1020 | m[currentReportIndex++] = _aY; 1021 | m[currentReportIndex++] = (_aY >> 8); 1022 | m[currentReportIndex++] = _aZ; 1023 | m[currentReportIndex++] = (_aZ >> 8); 1024 | } 1025 | 1026 | if (configuration.getHatSwitchCount() > 0) 1027 | { 1028 | signed char hats[4]; 1029 | 1030 | hats[0] = _hat1; 1031 | hats[1] = _hat2; 1032 | hats[2] = _hat3; 1033 | hats[3] = _hat4; 1034 | 1035 | for (int currentHatIndex = configuration.getHatSwitchCount() - 1; currentHatIndex >= 0; currentHatIndex--) 1036 | { 1037 | m[currentReportIndex++] = hats[currentHatIndex]; 1038 | } 1039 | } 1040 | 1041 | this->inputGamepad->setValue(m, sizeof(m)); 1042 | this->inputGamepad->notify(); 1043 | 1044 | // Testing 1045 | //Serial.println("HID Report"); 1046 | //for (int i = 0; i < sizeof(m); i++) 1047 | //{ 1048 | // Serial.printf("%02x", m[i]); 1049 | // Serial.println(); 1050 | //} 1051 | } 1052 | } 1053 | 1054 | void BleGamepad::press(uint8_t b) 1055 | { 1056 | uint8_t index = (b - 1) / 8; 1057 | uint8_t bit = (b - 1) % 8; 1058 | uint8_t bitmask = (1 << bit); 1059 | 1060 | uint8_t result = _buttons[index] | bitmask; 1061 | 1062 | if (result != _buttons[index]) 1063 | { 1064 | _buttons[index] = result; 1065 | } 1066 | 1067 | if (configuration.getAutoReport()) 1068 | { 1069 | sendReport(); 1070 | } 1071 | } 1072 | 1073 | void BleGamepad::release(uint8_t b) 1074 | { 1075 | uint8_t index = (b - 1) / 8; 1076 | uint8_t bit = (b - 1) % 8; 1077 | uint8_t bitmask = (1 << bit); 1078 | 1079 | uint64_t result = _buttons[index] & ~bitmask; 1080 | 1081 | if (result != _buttons[index]) 1082 | { 1083 | _buttons[index] = result; 1084 | } 1085 | 1086 | if (configuration.getAutoReport()) 1087 | { 1088 | sendReport(); 1089 | } 1090 | } 1091 | 1092 | uint8_t BleGamepad::specialButtonBitPosition(uint8_t b) 1093 | { 1094 | if (b >= POSSIBLESPECIALBUTTONS) 1095 | throw std::invalid_argument("Index out of range"); 1096 | uint8_t bit = 0; 1097 | 1098 | for (int i = 0; i < b; i++) 1099 | { 1100 | if (configuration.getWhichSpecialButtons()[i]) 1101 | bit++; 1102 | } 1103 | return bit; 1104 | } 1105 | 1106 | void BleGamepad::pressSpecialButton(uint8_t b) 1107 | { 1108 | uint8_t button = specialButtonBitPosition(b); 1109 | uint8_t bit = button % 8; 1110 | uint8_t bitmask = (1 << bit); 1111 | 1112 | uint64_t result = _specialButtons | bitmask; 1113 | 1114 | if (result != _specialButtons) 1115 | { 1116 | _specialButtons = result; 1117 | } 1118 | 1119 | if (configuration.getAutoReport()) 1120 | { 1121 | sendReport(); 1122 | } 1123 | } 1124 | 1125 | void BleGamepad::releaseSpecialButton(uint8_t b) 1126 | { 1127 | uint8_t button = specialButtonBitPosition(b); 1128 | uint8_t bit = button % 8; 1129 | uint8_t bitmask = (1 << bit); 1130 | 1131 | uint64_t result = _specialButtons & ~bitmask; 1132 | 1133 | if (result != _specialButtons) 1134 | { 1135 | _specialButtons = result; 1136 | } 1137 | 1138 | if (configuration.getAutoReport()) 1139 | { 1140 | sendReport(); 1141 | } 1142 | } 1143 | 1144 | void BleGamepad::pressStart() 1145 | { 1146 | pressSpecialButton(START_BUTTON); 1147 | } 1148 | 1149 | void BleGamepad::releaseStart() 1150 | { 1151 | releaseSpecialButton(START_BUTTON); 1152 | } 1153 | 1154 | void BleGamepad::pressSelect() 1155 | { 1156 | pressSpecialButton(SELECT_BUTTON); 1157 | } 1158 | 1159 | void BleGamepad::releaseSelect() 1160 | { 1161 | releaseSpecialButton(SELECT_BUTTON); 1162 | } 1163 | 1164 | void BleGamepad::pressMenu() 1165 | { 1166 | pressSpecialButton(MENU_BUTTON); 1167 | } 1168 | 1169 | void BleGamepad::releaseMenu() 1170 | { 1171 | releaseSpecialButton(MENU_BUTTON); 1172 | } 1173 | 1174 | void BleGamepad::pressHome() 1175 | { 1176 | pressSpecialButton(HOME_BUTTON); 1177 | } 1178 | 1179 | void BleGamepad::releaseHome() 1180 | { 1181 | releaseSpecialButton(HOME_BUTTON); 1182 | } 1183 | 1184 | void BleGamepad::pressBack() 1185 | { 1186 | pressSpecialButton(BACK_BUTTON); 1187 | } 1188 | 1189 | void BleGamepad::releaseBack() 1190 | { 1191 | releaseSpecialButton(BACK_BUTTON); 1192 | } 1193 | 1194 | void BleGamepad::pressVolumeInc() 1195 | { 1196 | pressSpecialButton(VOLUME_INC_BUTTON); 1197 | } 1198 | 1199 | void BleGamepad::releaseVolumeInc() 1200 | { 1201 | releaseSpecialButton(VOLUME_INC_BUTTON); 1202 | } 1203 | 1204 | void BleGamepad::pressVolumeDec() 1205 | { 1206 | pressSpecialButton(VOLUME_DEC_BUTTON); 1207 | } 1208 | 1209 | void BleGamepad::releaseVolumeDec() 1210 | { 1211 | releaseSpecialButton(VOLUME_DEC_BUTTON); 1212 | } 1213 | 1214 | void BleGamepad::pressVolumeMute() 1215 | { 1216 | pressSpecialButton(VOLUME_MUTE_BUTTON); 1217 | } 1218 | 1219 | void BleGamepad::releaseVolumeMute() 1220 | { 1221 | releaseSpecialButton(VOLUME_MUTE_BUTTON); 1222 | } 1223 | 1224 | void BleGamepad::setLeftThumb(int16_t x, int16_t y) 1225 | { 1226 | if (x == -32768) 1227 | { 1228 | x = -32767; 1229 | } 1230 | if (y == -32768) 1231 | { 1232 | y = -32767; 1233 | } 1234 | 1235 | _x = x; 1236 | _y = y; 1237 | 1238 | if (configuration.getAutoReport()) 1239 | { 1240 | sendReport(); 1241 | } 1242 | } 1243 | 1244 | void BleGamepad::setRightThumb(int16_t z, int16_t rZ) 1245 | { 1246 | if (z == -32768) 1247 | { 1248 | z = -32767; 1249 | } 1250 | if (rZ == -32768) 1251 | { 1252 | rZ = -32767; 1253 | } 1254 | 1255 | _z = z; 1256 | _rZ = rZ; 1257 | 1258 | if (configuration.getAutoReport()) 1259 | { 1260 | sendReport(); 1261 | } 1262 | } 1263 | 1264 | void BleGamepad::setRightThumbAndroid(int16_t z, int16_t rX) 1265 | { 1266 | if (z == -32768) 1267 | { 1268 | z = -32767; 1269 | } 1270 | if (rX == -32768) 1271 | { 1272 | rX = -32767; 1273 | } 1274 | 1275 | _z = z; 1276 | _rX = rX; 1277 | 1278 | if (configuration.getAutoReport()) 1279 | { 1280 | sendReport(); 1281 | } 1282 | } 1283 | 1284 | void BleGamepad::setLeftTrigger(int16_t rX) 1285 | { 1286 | if (rX == -32768) 1287 | { 1288 | rX = -32767; 1289 | } 1290 | 1291 | _rX = rX; 1292 | 1293 | if (configuration.getAutoReport()) 1294 | { 1295 | sendReport(); 1296 | } 1297 | } 1298 | 1299 | void BleGamepad::setRightTrigger(int16_t rY) 1300 | { 1301 | if (rY == -32768) 1302 | { 1303 | rY = -32767; 1304 | } 1305 | 1306 | _rY = rY; 1307 | 1308 | if (configuration.getAutoReport()) 1309 | { 1310 | sendReport(); 1311 | } 1312 | } 1313 | 1314 | void BleGamepad::setTriggers(int16_t rX, int16_t rY) 1315 | { 1316 | if (rX == -32768) 1317 | { 1318 | rX = -32767; 1319 | } 1320 | if (rY == -32768) 1321 | { 1322 | rY = -32767; 1323 | } 1324 | 1325 | _rX = rX; 1326 | _rY = rY; 1327 | 1328 | if (configuration.getAutoReport()) 1329 | { 1330 | sendReport(); 1331 | } 1332 | } 1333 | 1334 | void BleGamepad::setHat(signed char hat) 1335 | { 1336 | _hat1 = hat; 1337 | 1338 | if (configuration.getAutoReport()) 1339 | { 1340 | sendReport(); 1341 | } 1342 | } 1343 | 1344 | void BleGamepad::setHat1(signed char hat1) 1345 | { 1346 | _hat1 = hat1; 1347 | 1348 | if (configuration.getAutoReport()) 1349 | { 1350 | sendReport(); 1351 | } 1352 | } 1353 | 1354 | void BleGamepad::setHat2(signed char hat2) 1355 | { 1356 | _hat2 = hat2; 1357 | 1358 | if (configuration.getAutoReport()) 1359 | { 1360 | sendReport(); 1361 | } 1362 | } 1363 | 1364 | void BleGamepad::setHat3(signed char hat3) 1365 | { 1366 | _hat3 = hat3; 1367 | 1368 | if (configuration.getAutoReport()) 1369 | { 1370 | sendReport(); 1371 | } 1372 | } 1373 | 1374 | void BleGamepad::setHat4(signed char hat4) 1375 | { 1376 | _hat4 = hat4; 1377 | 1378 | if (configuration.getAutoReport()) 1379 | { 1380 | sendReport(); 1381 | } 1382 | } 1383 | 1384 | void BleGamepad::setX(int16_t x) 1385 | { 1386 | if (x == -32768) 1387 | { 1388 | x = -32767; 1389 | } 1390 | 1391 | _x = x; 1392 | 1393 | if (configuration.getAutoReport()) 1394 | { 1395 | sendReport(); 1396 | } 1397 | } 1398 | 1399 | void BleGamepad::setY(int16_t y) 1400 | { 1401 | if (y == -32768) 1402 | { 1403 | y = -32767; 1404 | } 1405 | 1406 | _y = y; 1407 | 1408 | if (configuration.getAutoReport()) 1409 | { 1410 | sendReport(); 1411 | } 1412 | } 1413 | 1414 | void BleGamepad::setZ(int16_t z) 1415 | { 1416 | if (z == -32768) 1417 | { 1418 | z = -32767; 1419 | } 1420 | 1421 | _z = z; 1422 | 1423 | if (configuration.getAutoReport()) 1424 | { 1425 | sendReport(); 1426 | } 1427 | } 1428 | 1429 | void BleGamepad::setRZ(int16_t rZ) 1430 | { 1431 | if (rZ == -32768) 1432 | { 1433 | rZ = -32767; 1434 | } 1435 | 1436 | _rZ = rZ; 1437 | 1438 | if (configuration.getAutoReport()) 1439 | { 1440 | sendReport(); 1441 | } 1442 | } 1443 | 1444 | void BleGamepad::setRX(int16_t rX) 1445 | { 1446 | if (rX == -32768) 1447 | { 1448 | rX = -32767; 1449 | } 1450 | 1451 | _rX = rX; 1452 | 1453 | if (configuration.getAutoReport()) 1454 | { 1455 | sendReport(); 1456 | } 1457 | } 1458 | 1459 | void BleGamepad::setRY(int16_t rY) 1460 | { 1461 | if (rY == -32768) 1462 | { 1463 | rY = -32767; 1464 | } 1465 | 1466 | _rY = rY; 1467 | 1468 | if (configuration.getAutoReport()) 1469 | { 1470 | sendReport(); 1471 | } 1472 | } 1473 | 1474 | void BleGamepad::setSlider(int16_t slider) 1475 | { 1476 | if (slider == -32768) 1477 | { 1478 | slider = -32767; 1479 | } 1480 | 1481 | _slider1 = slider; 1482 | 1483 | if (configuration.getAutoReport()) 1484 | { 1485 | sendReport(); 1486 | } 1487 | } 1488 | 1489 | void BleGamepad::setSlider1(int16_t slider1) 1490 | { 1491 | if (slider1 == -32768) 1492 | { 1493 | slider1 = -32767; 1494 | } 1495 | 1496 | _slider1 = slider1; 1497 | 1498 | if (configuration.getAutoReport()) 1499 | { 1500 | sendReport(); 1501 | } 1502 | } 1503 | 1504 | void BleGamepad::setSlider2(int16_t slider2) 1505 | { 1506 | if (slider2 == -32768) 1507 | { 1508 | slider2 = -32767; 1509 | } 1510 | 1511 | _slider2 = slider2; 1512 | 1513 | if (configuration.getAutoReport()) 1514 | { 1515 | sendReport(); 1516 | } 1517 | } 1518 | 1519 | void BleGamepad::setRudder(int16_t rudder) 1520 | { 1521 | if (rudder == -32768) 1522 | { 1523 | rudder = -32767; 1524 | } 1525 | 1526 | _rudder = rudder; 1527 | 1528 | if (configuration.getAutoReport()) 1529 | { 1530 | sendReport(); 1531 | } 1532 | } 1533 | 1534 | void BleGamepad::setThrottle(int16_t throttle) 1535 | { 1536 | if (throttle == -32768) 1537 | { 1538 | throttle = -32767; 1539 | } 1540 | 1541 | _throttle = throttle; 1542 | 1543 | if (configuration.getAutoReport()) 1544 | { 1545 | sendReport(); 1546 | } 1547 | } 1548 | 1549 | void BleGamepad::setAccelerator(int16_t accelerator) 1550 | { 1551 | if (accelerator == -32768) 1552 | { 1553 | accelerator = -32767; 1554 | } 1555 | 1556 | _accelerator = accelerator; 1557 | 1558 | if (configuration.getAutoReport()) 1559 | { 1560 | sendReport(); 1561 | } 1562 | } 1563 | 1564 | void BleGamepad::setBrake(int16_t brake) 1565 | { 1566 | if (brake == -32768) 1567 | { 1568 | brake = -32767; 1569 | } 1570 | 1571 | _brake = brake; 1572 | 1573 | if (configuration.getAutoReport()) 1574 | { 1575 | sendReport(); 1576 | } 1577 | } 1578 | 1579 | void BleGamepad::setSteering(int16_t steering) 1580 | { 1581 | if (steering == -32768) 1582 | { 1583 | steering = -32767; 1584 | } 1585 | 1586 | _steering = steering; 1587 | 1588 | if (configuration.getAutoReport()) 1589 | { 1590 | sendReport(); 1591 | } 1592 | } 1593 | 1594 | bool BleGamepad::isPressed(uint8_t b) 1595 | { 1596 | uint8_t index = (b - 1) / 8; 1597 | uint8_t bit = (b - 1) % 8; 1598 | uint8_t bitmask = (1 << bit); 1599 | 1600 | if ((bitmask & _buttons[index]) > 0) 1601 | return true; 1602 | return false; 1603 | } 1604 | 1605 | bool BleGamepad::isConnected(void) 1606 | { 1607 | return this->connectionStatus->connected; 1608 | } 1609 | 1610 | void BleGamepad::setBatteryLevel(uint8_t level) 1611 | { 1612 | this->batteryLevel = level; 1613 | if (hid != 0) 1614 | { 1615 | 1616 | this->hid->setBatteryLevel(this->batteryLevel, this->isConnected() ? true : false); 1617 | 1618 | if (configuration.getAutoReport()) 1619 | { 1620 | sendReport(); 1621 | } 1622 | } 1623 | } 1624 | 1625 | bool BleGamepad::isOutputReceived() 1626 | { 1627 | if (enableOutputReport && outputReceiver) 1628 | { 1629 | if (this->outputReceiver->outputFlag) 1630 | { 1631 | this->outputReceiver->outputFlag = false; // Clear Flag 1632 | return true; 1633 | } 1634 | } 1635 | return false; 1636 | } 1637 | 1638 | uint8_t* BleGamepad::getOutputBuffer() 1639 | { 1640 | if (enableOutputReport && outputReceiver) 1641 | { 1642 | memcpy(outputBackupBuffer, outputReceiver->outputBuffer, outputReportLength); // Creating a backup to avoid buffer being overwritten while processing data 1643 | return outputBackupBuffer; 1644 | } 1645 | return nullptr; 1646 | } 1647 | 1648 | bool BleGamepad::deleteAllBonds(bool resetBoard) 1649 | { 1650 | bool success = false; 1651 | 1652 | NimBLEDevice::deleteAllBonds(); 1653 | NIMBLE_LOGD(LOG_TAG, "deleteAllBonds - All bonds deleted"); 1654 | success = true; 1655 | delay(500); 1656 | 1657 | if (resetBoard) 1658 | { 1659 | NIMBLE_LOGD(LOG_TAG, "deleteAllBonds - Reboot ESP32"); 1660 | ESP.restart(); 1661 | } 1662 | 1663 | return success; // Returns false if all bonds are not deleted 1664 | } 1665 | 1666 | bool BleGamepad::deleteBond(bool resetBoard) 1667 | { 1668 | bool success = false; 1669 | 1670 | NimBLEServer* server = NimBLEDevice::getServer(); 1671 | 1672 | if (server) 1673 | { 1674 | NimBLEConnInfo info = server->getPeerInfo(0); 1675 | NimBLEAddress address = info.getAddress(); 1676 | 1677 | success = NimBLEDevice::deleteBond(address); 1678 | NIMBLE_LOGD(LOG_TAG, "deleteBond - Bond for %s deleted", std::string(address).c_str()); 1679 | 1680 | delay(500); 1681 | 1682 | if (resetBoard) 1683 | { 1684 | NIMBLE_LOGD(LOG_TAG, "deleteBond - Reboot ESP32"); 1685 | ESP.restart(); 1686 | } 1687 | } 1688 | return success; // Returns false if current bond is not deleted 1689 | } 1690 | 1691 | bool BleGamepad::enterPairingMode() 1692 | { 1693 | NimBLEServer* server = NimBLEDevice::getServer(); 1694 | 1695 | if (server) 1696 | { 1697 | NIMBLE_LOGD(LOG_TAG, "enterPairingMode - Pairing mode entered"); 1698 | 1699 | // Get current connection information and address 1700 | NimBLEConnInfo currentConnInfo = server->getPeerInfo(0); 1701 | NimBLEAddress currentAddress = currentConnInfo.getAddress(); 1702 | NIMBLE_LOGD(LOG_TAG, "enterPairingMode - Connected Address: %s", std::string(currentAddress).c_str()); 1703 | 1704 | // Disconnect from current connection 1705 | for (uint16_t connHandle : server->getPeerDevices()) 1706 | { 1707 | server->disconnect(connHandle); // Disconnect the client 1708 | NIMBLE_LOGD(LOG_TAG, "enterPairingMode - Disconnected from client"); 1709 | delay(500); 1710 | } 1711 | 1712 | bool connectedToOldDevice = true; 1713 | 1714 | // While connected to old device, keep allowing to connect new new devices 1715 | NIMBLE_LOGD(LOG_TAG, "enterPairingMode - Advertising for clients..."); 1716 | while (connectedToOldDevice) 1717 | { 1718 | delay(10); // Needs a delay to work - do not remove! 1719 | 1720 | if (this->isConnected()) 1721 | { 1722 | NimBLEConnInfo newConnInfo = server->getPeerInfo(0); 1723 | NimBLEAddress newAddress = newConnInfo.getAddress(); 1724 | 1725 | // Block specific MAC address 1726 | if (newAddress == currentAddress) 1727 | { 1728 | NIMBLE_LOGD(LOG_TAG, "enterPairingMode - Connected to previous client, so disconnect and continue advertising for new client"); 1729 | server->disconnect(newConnInfo.getConnHandle()); 1730 | delay(500); 1731 | } 1732 | else 1733 | { 1734 | NIMBLE_LOGD(LOG_TAG, "enterPairingMode - Connected to new client"); 1735 | NIMBLE_LOGD(LOG_TAG, "enterPairingMode - Exit pairing mode"); 1736 | connectedToOldDevice = false; 1737 | return true; 1738 | } 1739 | } 1740 | } 1741 | return false; // Might want to adjust this function to stay in pairing mode for a while, and then return false after a while if no other device pairs with it 1742 | } 1743 | return false; 1744 | } 1745 | 1746 | NimBLEAddress BleGamepad::getAddress() 1747 | { 1748 | NimBLEServer* server = NimBLEDevice::getServer(); 1749 | 1750 | if (server) 1751 | { 1752 | // Get current connection information and address 1753 | NimBLEConnInfo currentConnInfo = server->getPeerInfo(0); 1754 | NimBLEAddress currentAddress = currentConnInfo.getAddress(); 1755 | return currentAddress; 1756 | } 1757 | NimBLEAddress blankAddress("00:00:00:00:00:00", 0); 1758 | return blankAddress; 1759 | } 1760 | 1761 | String BleGamepad::getStringAddress() 1762 | { 1763 | NimBLEServer* server = NimBLEDevice::getServer(); 1764 | 1765 | if (server) 1766 | { 1767 | // Get current connection information and address 1768 | NimBLEConnInfo currentConnInfo = server->getPeerInfo(0); 1769 | NimBLEAddress currentAddress = currentConnInfo.getAddress(); 1770 | return currentAddress.toString().c_str(); 1771 | } 1772 | NimBLEAddress blankAddress("00:00:00:00:00:00", 0); 1773 | return blankAddress.toString().c_str(); 1774 | } 1775 | 1776 | NimBLEConnInfo BleGamepad::getPeerInfo() 1777 | { 1778 | NimBLEServer* server = NimBLEDevice::getServer(); 1779 | NimBLEConnInfo currentConnInfo = server->getPeerInfo(0); 1780 | return currentConnInfo; 1781 | } 1782 | 1783 | String BleGamepad::getDeviceName() 1784 | { 1785 | return this->deviceName.c_str(); 1786 | } 1787 | 1788 | String BleGamepad::getDeviceManufacturer() 1789 | { 1790 | return this->deviceManufacturer.c_str(); 1791 | } 1792 | 1793 | int8_t BleGamepad::getTXPowerLevel() 1794 | { 1795 | return NimBLEDevice::getPower(); 1796 | } 1797 | 1798 | void BleGamepad::setTXPowerLevel(int8_t level) 1799 | { 1800 | NimBLEDevice::setPower(level); // The only valid values are: -12, -9, -6, -3, 0, 3, 6 and 9 1801 | configuration.setTXPowerLevel(level); 1802 | } 1803 | 1804 | void BleGamepad::setGyroscope(int16_t gX, int16_t gY, int16_t gZ) 1805 | { 1806 | if (gX == -32768) 1807 | { 1808 | gX = -32767; 1809 | } 1810 | 1811 | if (gY == -32768) 1812 | { 1813 | gY = -32767; 1814 | } 1815 | 1816 | if (gY == -32768) 1817 | { 1818 | gY = -32767; 1819 | } 1820 | 1821 | _gX = gX; 1822 | _gY = gY; 1823 | _gZ = gZ; 1824 | 1825 | if (configuration.getAutoReport()) 1826 | { 1827 | sendReport(); 1828 | } 1829 | } 1830 | 1831 | void BleGamepad::setAccelerometer(int16_t aX, int16_t aY, int16_t aZ) 1832 | { 1833 | _aX = aX; 1834 | _aY = aY; 1835 | _aZ = aZ; 1836 | 1837 | if (configuration.getAutoReport()) 1838 | { 1839 | sendReport(); 1840 | } 1841 | } 1842 | 1843 | void BleGamepad::setMotionControls(int16_t gX, int16_t gY, int16_t gZ, int16_t aX, int16_t aY, int16_t aZ) 1844 | { 1845 | if (gX == -32768) 1846 | { 1847 | gX = -32767; 1848 | } 1849 | 1850 | if (gY == -32768) 1851 | { 1852 | gY = -32767; 1853 | } 1854 | 1855 | if (gZ == -32768) 1856 | { 1857 | gZ = -32767; 1858 | } 1859 | 1860 | if (aX == -32768) 1861 | { 1862 | aX = -32767; 1863 | } 1864 | 1865 | if (aY == -32768) 1866 | { 1867 | aY = -32767; 1868 | } 1869 | 1870 | if (aZ == -32768) 1871 | { 1872 | aZ = -32767; 1873 | } 1874 | 1875 | _gX = gX; 1876 | _gY = gY; 1877 | _gZ = gZ; 1878 | _aX = aX; 1879 | _aY = aY; 1880 | _aZ = aZ; 1881 | 1882 | if (configuration.getAutoReport()) 1883 | { 1884 | sendReport(); 1885 | } 1886 | } 1887 | 1888 | void BleGamepad::setPowerStateAll(uint8_t batteryPowerInformation, uint8_t dischargingState, uint8_t chargingState, uint8_t powerLevel) 1889 | { 1890 | uint8_t powerStateBits = 0b00000000; 1891 | 1892 | _batteryPowerInformation = batteryPowerInformation; 1893 | _dischargingState = dischargingState; 1894 | _chargingState = chargingState; 1895 | _powerLevel = powerLevel; 1896 | 1897 | // HID Battery Power State Bits: 1898 | // Bits 0 and 1: Battery Power Information : 0(0b00) = Unknown, 1(0b01) = Not Supported, 2(0b10) = Not Present, 3(0b11) = Present 1899 | // Bits 2 and 3: Discharging State : 0(0b00) = Unknown, 1(0b01) = Not Supported, 2(0b10) = Not Discharging, 3(0b11) = Discharging 1900 | // Bits 4 and 5: Charging State : 0(0b00) = Unknown, 1(0b01) = Not Chargeable, 2(0b10) = Not Charging (Chargeable), 3(0b11) = Charging (Chargeable) 1901 | // Bits 6 and 7: Power Level : 0(0b00) = Unknown, 1(0b01) = Not Supported, 2(0b10) = Good Level, 3(0b11) = Critically Low Level 1902 | 1903 | powerStateBits |= (_batteryPowerInformation << 0); // Populate first 2 bits with data 1904 | powerStateBits |= (_dischargingState << 2); // Populate second 2 bits with data 1905 | powerStateBits |= (_chargingState << 4); // Populate third 2 bits with data 1906 | powerStateBits |= (_powerLevel << 6); // Populate last 2 bits with data 1907 | 1908 | if (this->pCharacteristic_Power_State) 1909 | { 1910 | this->pCharacteristic_Power_State->setValue(&powerStateBits, 1); 1911 | this->pCharacteristic_Power_State->notify(); 1912 | } 1913 | } 1914 | 1915 | 1916 | void BleGamepad::setBatteryPowerInformation(uint8_t batteryPowerInformation) 1917 | { 1918 | _batteryPowerInformation = batteryPowerInformation; 1919 | setPowerStateAll(_batteryPowerInformation, _dischargingState, _chargingState, _powerLevel); 1920 | } 1921 | 1922 | void BleGamepad::setDischargingState(uint8_t dischargingState) 1923 | { 1924 | _dischargingState = dischargingState; 1925 | setPowerStateAll(_batteryPowerInformation, _dischargingState, _chargingState, _powerLevel); 1926 | } 1927 | 1928 | void BleGamepad::setChargingState(uint8_t chargingState) 1929 | { 1930 | _chargingState = chargingState; 1931 | setPowerStateAll(_batteryPowerInformation, _dischargingState, _chargingState, _powerLevel); 1932 | } 1933 | 1934 | void BleGamepad::setPowerLevel(uint8_t powerLevel) 1935 | { 1936 | _powerLevel = powerLevel; 1937 | setPowerStateAll(_batteryPowerInformation, _dischargingState, _chargingState, _powerLevel); 1938 | } 1939 | 1940 | void BleGamepad::beginNUS() 1941 | { 1942 | if (!this->nusInitialized) 1943 | { 1944 | // Extrememly important to make sure that the pointer to server is actually valid 1945 | while(!NimBLEDevice::isInitialized ()){} // Wait until the server is initialized 1946 | while(NimBLEDevice::getServer() == nullptr){} // Ensure pointer to server is actually valid 1947 | 1948 | // Now server is nkown to be valid, initialise nus to new BleNUS instance 1949 | nus = new BleNUS(NimBLEDevice::getServer()); // Pass the existing BLE server 1950 | nus->begin(); 1951 | nusInitialized = true; 1952 | } 1953 | } 1954 | 1955 | BleNUS* BleGamepad::getNUS() 1956 | { 1957 | return nus; // Return a pointer instead of a reference 1958 | } 1959 | 1960 | void BleGamepad::sendDataOverNUS(const uint8_t* data, size_t length) 1961 | { 1962 | if (nus) 1963 | { 1964 | nus->sendData(data, length); 1965 | } 1966 | } 1967 | 1968 | void BleGamepad::setNUSDataReceivedCallback(void (*callback)(const uint8_t* data, size_t length)) 1969 | { 1970 | if (nus) 1971 | { 1972 | nus->setDataReceivedCallback(callback); 1973 | } 1974 | } 1975 | 1976 | void BleGamepad::taskServer(void *pvParameter) 1977 | { 1978 | BleGamepad *BleGamepadInstance = (BleGamepad *)pvParameter; // static_cast(pvParameter); 1979 | 1980 | NimBLEDevice::init(BleGamepadInstance->deviceName); 1981 | NimBLEDevice::setPower(BleGamepadInstance->configuration.getTXPowerLevel()); // Set transmit power for advertising (Range: -12 to +9 dBm) 1982 | NimBLEServer *pServer = NimBLEDevice::createServer(); 1983 | pServer->setCallbacks(BleGamepadInstance->connectionStatus); 1984 | pServer->advertiseOnDisconnect(true); 1985 | 1986 | BleGamepadInstance->hid = new NimBLEHIDDevice(pServer); 1987 | 1988 | BleGamepadInstance->inputGamepad = BleGamepadInstance->hid->getInputReport(BleGamepadInstance->configuration.getHidReportId()); // <-- input REPORTID from report map 1989 | BleGamepadInstance->connectionStatus->inputGamepad = BleGamepadInstance->inputGamepad; 1990 | 1991 | if (BleGamepadInstance->enableOutputReport) 1992 | { 1993 | BleGamepadInstance->outputGamepad = BleGamepadInstance->hid->getOutputReport(BleGamepadInstance->configuration.getHidReportId()); 1994 | BleGamepadInstance->outputReceiver = new BleOutputReceiver(BleGamepadInstance->outputReportLength); 1995 | BleGamepadInstance->outputBackupBuffer = new uint8_t[BleGamepadInstance->outputReportLength]; 1996 | BleGamepadInstance->outputGamepad->setCallbacks(BleGamepadInstance->outputReceiver); 1997 | } 1998 | 1999 | BleGamepadInstance->hid->setManufacturer(BleGamepadInstance->deviceManufacturer); 2000 | 2001 | NimBLEService *pService = pServer->getServiceByUUID(SERVICE_UUID_DEVICE_INFORMATION); 2002 | 2003 | BLECharacteristic* pCharacteristic_Model_Number = pService->createCharacteristic( 2004 | CHARACTERISTIC_UUID_MODEL_NUMBER, 2005 | NIMBLE_PROPERTY::READ 2006 | ); 2007 | pCharacteristic_Model_Number->setValue(std::string(BleGamepadInstance->configuration.getModelNumber())); 2008 | 2009 | BLECharacteristic* pCharacteristic_Software_Revision = pService->createCharacteristic( 2010 | CHARACTERISTIC_UUID_SOFTWARE_REVISION, 2011 | NIMBLE_PROPERTY::READ 2012 | ); 2013 | pCharacteristic_Software_Revision->setValue(std::string(BleGamepadInstance->configuration.getSoftwareRevision())); 2014 | 2015 | BLECharacteristic* pCharacteristic_Serial_Number = pService->createCharacteristic( 2016 | CHARACTERISTIC_UUID_SERIAL_NUMBER, 2017 | NIMBLE_PROPERTY::READ 2018 | ); 2019 | pCharacteristic_Serial_Number->setValue(std::string(BleGamepadInstance->configuration.getSerialNumber())); 2020 | 2021 | BLECharacteristic* pCharacteristic_Firmware_Revision = pService->createCharacteristic( 2022 | CHARACTERISTIC_UUID_FIRMWARE_REVISION, 2023 | NIMBLE_PROPERTY::READ 2024 | ); 2025 | pCharacteristic_Firmware_Revision->setValue(std::string(BleGamepadInstance->configuration.getFirmwareRevision())); 2026 | 2027 | BLECharacteristic* pCharacteristic_Hardware_Revision = pService->createCharacteristic( 2028 | CHARACTERISTIC_UUID_HARDWARE_REVISION, 2029 | NIMBLE_PROPERTY::READ 2030 | ); 2031 | pCharacteristic_Hardware_Revision->setValue(std::string(BleGamepadInstance->configuration.getHardwareRevision())); 2032 | 2033 | NimBLECharacteristic* pCharacteristic_Power_State = BleGamepadInstance->hid->getBatteryService()->createCharacteristic( 2034 | CHARACTERISTIC_UUID_BATTERY_POWER_STATE, 2035 | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY 2036 | ); 2037 | BleGamepadInstance->pCharacteristic_Power_State = pCharacteristic_Power_State; // Assign the created characteristic 2038 | BleGamepadInstance->pCharacteristic_Power_State->setValue(0b00000000); // Now it's safe to call setValue <- Set all to unknown by default 2039 | 2040 | BleGamepadInstance->hid->setPnp(0x01, BleGamepadInstance->configuration.getVid(), BleGamepadInstance->configuration.getPid(), BleGamepadInstance->configuration.getGuidVersion()); 2041 | BleGamepadInstance->hid->setHidInfo(0x00, 0x01); 2042 | 2043 | // NimBLEDevice::setSecurityAuth(BLE_SM_PAIR_AUTHREQ_BOND); 2044 | NimBLEDevice::setSecurityAuth(true, false, false); // enable bonding, no MITM, no SC 2045 | 2046 | 2047 | uint8_t *customHidReportDescriptor = new uint8_t[BleGamepadInstance->hidReportDescriptorSize]; 2048 | memcpy(customHidReportDescriptor, BleGamepadInstance->tempHidReportDescriptor, BleGamepadInstance->hidReportDescriptorSize); 2049 | 2050 | // Testing - Ask ChatGPT to convert it into a commented HID descriptor 2051 | //Serial.println("------- HID DESCRIPTOR START -------"); 2052 | //for (int i = 0; i < BleGamepadInstance->hidReportDescriptorSize; i++) 2053 | //{ 2054 | // Serial.printf("%02x", customHidReportDescriptor[i]); 2055 | // Serial.println(); 2056 | //} 2057 | //Serial.println("------- HID DESCRIPTOR END -------"); 2058 | 2059 | BleGamepadInstance->hid->setReportMap((uint8_t *)customHidReportDescriptor, BleGamepadInstance->hidReportDescriptorSize); 2060 | BleGamepadInstance->hid->startServices(); 2061 | 2062 | BleGamepadInstance->onStarted(pServer); 2063 | 2064 | NimBLEAdvertising *pAdvertising = pServer->getAdvertising(); 2065 | pAdvertising->setAppearance(HID_GAMEPAD); 2066 | pAdvertising->setName(BleGamepadInstance->deviceName); 2067 | pAdvertising->addServiceUUID(BleGamepadInstance->hid->getHidService()->getUUID()); 2068 | 2069 | if(BleGamepadInstance->delayAdvertising) 2070 | { 2071 | NIMBLE_LOGD(LOG_TAG, "Main NimBLE server advertising delayed (until Nordic UART Service added)"); 2072 | } 2073 | else 2074 | { 2075 | NIMBLE_LOGD(LOG_TAG, "Main NimBLE server advertising started!"); 2076 | pAdvertising->start(); 2077 | } 2078 | 2079 | BleGamepadInstance->hid->setBatteryLevel(BleGamepadInstance->batteryLevel); 2080 | 2081 | vTaskDelay(portMAX_DELAY); // delay(portMAX_DELAY); 2082 | } -------------------------------------------------------------------------------- /BleGamepad.h: -------------------------------------------------------------------------------- 1 | #ifndef ESP32_BLE_GAMEPAD_H 2 | #define ESP32_BLE_GAMEPAD_H 3 | #include "sdkconfig.h" 4 | #if defined(CONFIG_BT_ENABLED) 5 | 6 | #include "nimconfig.h" 7 | #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) 8 | 9 | #include "BleConnectionStatus.h" 10 | #include "NimBLEHIDDevice.h" 11 | #include "NimBLECharacteristic.h" 12 | #include "BleGamepadConfiguration.h" 13 | #include "BleOutputReceiver.h" 14 | #include "BleNUS.h" 15 | 16 | class BleGamepad 17 | { 18 | private: 19 | std::string deviceManufacturer; 20 | std::string deviceName; 21 | uint8_t tempHidReportDescriptor[150]; 22 | int hidReportDescriptorSize; 23 | uint8_t hidReportSize; 24 | uint8_t numOfButtonBytes; 25 | bool enableOutputReport; 26 | uint16_t outputReportLength; 27 | uint8_t _buttons[16]; // 8 bits x 16 bytes = 128 bits --> 128 button max 28 | uint8_t _specialButtons; 29 | int16_t _x; 30 | int16_t _y; 31 | int16_t _z; 32 | int16_t _rX; 33 | int16_t _rY; 34 | int16_t _rZ; 35 | int16_t _slider1; 36 | int16_t _slider2; 37 | int16_t _rudder; 38 | int16_t _throttle; 39 | int16_t _accelerator; 40 | int16_t _brake; 41 | int16_t _steering; 42 | int16_t _hat1; 43 | int16_t _hat2; 44 | int16_t _hat3; 45 | int16_t _hat4; 46 | int16_t _gX; 47 | int16_t _gY; 48 | int16_t _gZ; 49 | int16_t _aX; 50 | int16_t _aY; 51 | int16_t _aZ; 52 | uint8_t _batteryPowerInformation; 53 | uint8_t _dischargingState; 54 | uint8_t _chargingState; 55 | uint8_t _powerLevel; 56 | bool nusInitialized; 57 | 58 | BleConnectionStatus *connectionStatus; 59 | BleOutputReceiver *outputReceiver; 60 | NimBLEServer *pServer; 61 | BleNUS* nus; 62 | 63 | NimBLEHIDDevice *hid; 64 | NimBLECharacteristic *inputGamepad; 65 | NimBLECharacteristic *outputGamepad; 66 | NimBLECharacteristic *pCharacteristic_Power_State; 67 | 68 | uint8_t *outputBackupBuffer; 69 | 70 | void rawAction(uint8_t msg[], char msgSize); 71 | static void taskServer(void *pvParameter); 72 | uint8_t specialButtonBitPosition(uint8_t specialButton); 73 | 74 | public: 75 | BleGamepadConfiguration configuration; 76 | 77 | BleGamepad(std::string deviceName = "ESP32 BLE Gamepad", std::string deviceManufacturer = "Espressif", uint8_t batteryLevel = 100, bool delayAdvertising = false); 78 | void begin(BleGamepadConfiguration *config = new BleGamepadConfiguration()); 79 | void end(void); 80 | void setAxes(int16_t x = 0, int16_t y = 0, int16_t z = 0, int16_t rX = 0, int16_t rY = 0, int16_t rZ = 0, int16_t slider1 = 0, int16_t slider2 = 0); 81 | void setHIDAxes(int16_t x = 0, int16_t y = 0, int16_t z = 0, int16_t rZ = 0, int16_t rX = 0, int16_t rY = 0, int16_t slider1 = 0, int16_t slider2 = 0); 82 | void press(uint8_t b = BUTTON_1); // press BUTTON_1 by default 83 | void release(uint8_t b = BUTTON_1); // release BUTTON_1 by default 84 | void pressSpecialButton(uint8_t b); 85 | void releaseSpecialButton(uint8_t b); 86 | void pressStart(); 87 | void releaseStart(); 88 | void pressSelect(); 89 | void releaseSelect(); 90 | void pressMenu(); 91 | void releaseMenu(); 92 | void pressHome(); 93 | void releaseHome(); 94 | void pressBack(); 95 | void releaseBack(); 96 | void pressVolumeInc(); 97 | void releaseVolumeInc(); 98 | void pressVolumeDec(); 99 | void releaseVolumeDec(); 100 | void pressVolumeMute(); 101 | void releaseVolumeMute(); 102 | void setLeftThumb(int16_t x = 0, int16_t y = 0); 103 | void setRightThumb(int16_t z = 0, int16_t rZ = 0); 104 | void setRightThumbAndroid(int16_t z = 0, int16_t rX = 0); 105 | void setLeftTrigger(int16_t rX = 0); 106 | void setRightTrigger(int16_t rY = 0); 107 | void setTriggers(int16_t rX = 0, int16_t rY = 0); 108 | void setHats(signed char hat1 = 0, signed char hat2 = 0, signed char hat3 = 0, signed char hat4 = 0); 109 | void setHat(signed char hat = 0); 110 | void setHat1(signed char hat1 = 0); 111 | void setHat2(signed char hat2 = 0); 112 | void setHat3(signed char hat3 = 0); 113 | void setHat4(signed char hat4 = 0); 114 | void setX(int16_t x = 0); 115 | void setY(int16_t y = 0); 116 | void setZ(int16_t z = 0); 117 | void setRZ(int16_t rZ = 0); 118 | void setRX(int16_t rX = 0); 119 | void setRY(int16_t rY = 0); 120 | void setSliders(int16_t slider1 = 0, int16_t slider2 = 0); 121 | void setSlider(int16_t slider = 0); 122 | void setSlider1(int16_t slider1 = 0); 123 | void setSlider2(int16_t slider2 = 0); 124 | void setRudder(int16_t rudder = 0); 125 | void setThrottle(int16_t throttle = 0); 126 | void setAccelerator(int16_t accelerator = 0); 127 | void setBrake(int16_t brake = 0); 128 | void setSteering(int16_t steering = 0); 129 | void setSimulationControls(int16_t rudder = 0, int16_t throttle = 0, int16_t accelerator = 0, int16_t brake = 0, int16_t steering = 0); 130 | void sendReport(); 131 | bool isPressed(uint8_t b = BUTTON_1); // check BUTTON_1 by default 132 | bool isConnected(void); 133 | void resetButtons(); 134 | void setBatteryLevel(uint8_t level); 135 | void setPowerStateAll(uint8_t batteryPowerInformation, uint8_t dischargingState, uint8_t chargingState, uint8_t powerLevel); 136 | void setBatteryPowerInformation(uint8_t batteryPowerInformation); 137 | void setDischargingState(uint8_t dischargingState); 138 | void setChargingState(uint8_t chargingState); 139 | void setPowerLevel(uint8_t powerLevel); 140 | void setTXPowerLevel(int8_t level = 9); 141 | int8_t getTXPowerLevel(); 142 | uint8_t batteryLevel; 143 | bool delayAdvertising; 144 | bool isOutputReceived(); 145 | uint8_t* getOutputBuffer(); 146 | bool deleteBond(bool resetBoard = false); 147 | bool deleteAllBonds(bool resetBoard = false); 148 | bool enterPairingMode(); 149 | NimBLEAddress getAddress(); 150 | String getStringAddress(); 151 | NimBLEConnInfo getPeerInfo(); 152 | String getDeviceName(); 153 | String getDeviceManufacturer(); 154 | void setGyroscope(int16_t gX = 0, int16_t gY = 0, int16_t gZ = 0); 155 | void setAccelerometer(int16_t aX = 0, int16_t aY = 0, int16_t aZ = 0); 156 | void setMotionControls(int16_t gX = 0, int16_t gY = 0, int16_t gZ = 0, int16_t aX = 0, int16_t aY = 0, int16_t aZ = 0); 157 | void beginNUS(); 158 | void sendDataOverNUS(const uint8_t* data, size_t length); 159 | void setNUSDataReceivedCallback(void (*callback)(const uint8_t* data, size_t length)); 160 | BleNUS* getNUS(); 161 | 162 | protected: 163 | virtual void onStarted(NimBLEServer *pServer) {}; 164 | }; 165 | 166 | #endif // CONFIG_BT_NIMBLE_ROLE_PERIPHERAL 167 | #endif // CONFIG_BT_ENABLED 168 | #endif // ESP32_BLE_GAMEPAD_H -------------------------------------------------------------------------------- /BleGamepadConfiguration.cpp: -------------------------------------------------------------------------------- 1 | #include "BleGamepadConfiguration.h" 2 | 3 | BleGamepadConfiguration::BleGamepadConfiguration() : _controllerType(CONTROLLER_TYPE_GAMEPAD), 4 | _autoReport(true), 5 | _hidReportId(3), 6 | _buttonCount(16), 7 | _hatSwitchCount(1), 8 | _whichSpecialButtons{false, false, false, false, false, false, false, false}, 9 | _whichAxes{true, true, true, true, true, true, true, true}, 10 | _whichSimulationControls{false, false, false, false, false}, 11 | _includeGyroscope(false), 12 | _includeAccelerometer(false), 13 | _vid(0xe502), 14 | _pid(0xbbab), 15 | _guidVersion(0x0110), 16 | _axesMin(0x0000), 17 | _axesMax(0x7FFF), 18 | _simulationMin(0x0000), 19 | _simulationMax(0x7FFF), 20 | _motionMin(0x0000), 21 | _motionMax(0x7FFF), 22 | _modelNumber("1.0.0"), 23 | _softwareRevision("1.0.0"), 24 | _serialNumber("0123456789"), 25 | _firmwareRevision("0.7.4"), 26 | _hardwareRevision("1.0.0"), 27 | _enableOutputReport(false), 28 | _enableNordicUARTService(false), 29 | _outputReportLength(64), 30 | _transmitPowerLevel(9) 31 | { 32 | } 33 | 34 | uint8_t BleGamepadConfiguration::getTotalSpecialButtonCount() 35 | { 36 | int count = 0; 37 | for (int i = 0; i < POSSIBLESPECIALBUTTONS; i++) 38 | { 39 | count += (int)_whichSpecialButtons[i]; 40 | } 41 | 42 | return count; 43 | } 44 | 45 | uint8_t BleGamepadConfiguration::getDesktopSpecialButtonCount() 46 | { 47 | int count = 0; 48 | for (int i = 0; i < 3; i++) 49 | { 50 | count += (int)_whichSpecialButtons[i]; 51 | } 52 | 53 | return count; 54 | } 55 | 56 | uint8_t BleGamepadConfiguration::getConsumerSpecialButtonCount() 57 | { 58 | int count = 0; 59 | for (int i = 3; i < 8; i++) 60 | { 61 | count += (int)_whichSpecialButtons[i]; 62 | } 63 | 64 | return count; 65 | } 66 | 67 | uint8_t BleGamepadConfiguration::getAxisCount() 68 | { 69 | int count = 0; 70 | for (int i = 0; i < POSSIBLEAXES; i++) 71 | { 72 | count += (int)_whichAxes[i]; 73 | } 74 | 75 | return count; 76 | } 77 | 78 | uint8_t BleGamepadConfiguration::getSimulationCount() 79 | { 80 | int count = 0; 81 | for (int i = 0; i < POSSIBLESIMULATIONCONTROLS; i++) 82 | { 83 | count += (int)_whichSimulationControls[i]; 84 | } 85 | 86 | return count; 87 | } 88 | 89 | uint16_t BleGamepadConfiguration::getVid(){ return _vid; } 90 | uint16_t BleGamepadConfiguration::getPid(){ return _pid; } 91 | uint16_t BleGamepadConfiguration::getGuidVersion(){ return _guidVersion; } 92 | int16_t BleGamepadConfiguration::getAxesMin(){ return _axesMin; } 93 | int16_t BleGamepadConfiguration::getAxesMax(){ return _axesMax; } 94 | int16_t BleGamepadConfiguration::getSimulationMin(){ return _simulationMin; } 95 | int16_t BleGamepadConfiguration::getSimulationMax(){ return _simulationMax; } 96 | int16_t BleGamepadConfiguration::getMotionMin(){ return _motionMin; } 97 | int16_t BleGamepadConfiguration::getMotionMax(){ return _motionMax; } 98 | uint8_t BleGamepadConfiguration::getControllerType() { return _controllerType; } 99 | uint8_t BleGamepadConfiguration::getHidReportId() { return _hidReportId; } 100 | uint16_t BleGamepadConfiguration::getButtonCount() { return _buttonCount; } 101 | uint8_t BleGamepadConfiguration::getHatSwitchCount() { return _hatSwitchCount; } 102 | bool BleGamepadConfiguration::getAutoReport() { return _autoReport; } 103 | bool BleGamepadConfiguration::getIncludeStart() { return _whichSpecialButtons[START_BUTTON]; } 104 | bool BleGamepadConfiguration::getIncludeSelect() { return _whichSpecialButtons[SELECT_BUTTON]; } 105 | bool BleGamepadConfiguration::getIncludeMenu() { return _whichSpecialButtons[MENU_BUTTON]; } 106 | bool BleGamepadConfiguration::getIncludeHome() { return _whichSpecialButtons[HOME_BUTTON]; } 107 | bool BleGamepadConfiguration::getIncludeBack() { return _whichSpecialButtons[BACK_BUTTON]; } 108 | bool BleGamepadConfiguration::getIncludeVolumeInc() { return _whichSpecialButtons[VOLUME_INC_BUTTON]; } 109 | bool BleGamepadConfiguration::getIncludeVolumeDec() { return _whichSpecialButtons[VOLUME_DEC_BUTTON]; } 110 | bool BleGamepadConfiguration::getIncludeVolumeMute() { return _whichSpecialButtons[VOLUME_MUTE_BUTTON]; } 111 | const bool *BleGamepadConfiguration::getWhichSpecialButtons() const { return _whichSpecialButtons; } 112 | bool BleGamepadConfiguration::getIncludeXAxis() { return _whichAxes[X_AXIS]; } 113 | bool BleGamepadConfiguration::getIncludeYAxis() { return _whichAxes[Y_AXIS]; } 114 | bool BleGamepadConfiguration::getIncludeZAxis() { return _whichAxes[Z_AXIS]; } 115 | bool BleGamepadConfiguration::getIncludeRxAxis() { return _whichAxes[RX_AXIS]; } 116 | bool BleGamepadConfiguration::getIncludeRyAxis() { return _whichAxes[RY_AXIS]; } 117 | bool BleGamepadConfiguration::getIncludeRzAxis() { return _whichAxes[RZ_AXIS]; } 118 | bool BleGamepadConfiguration::getIncludeSlider1() { return _whichAxes[SLIDER1]; } 119 | bool BleGamepadConfiguration::getIncludeSlider2() { return _whichAxes[SLIDER2]; } 120 | const bool *BleGamepadConfiguration::getWhichAxes() const { return _whichAxes; } 121 | bool BleGamepadConfiguration::getIncludeRudder() { return _whichSimulationControls[RUDDER]; } 122 | bool BleGamepadConfiguration::getIncludeThrottle() { return _whichSimulationControls[THROTTLE]; } 123 | bool BleGamepadConfiguration::getIncludeAccelerator() { return _whichSimulationControls[ACCELERATOR]; } 124 | bool BleGamepadConfiguration::getIncludeBrake() { return _whichSimulationControls[BRAKE]; } 125 | bool BleGamepadConfiguration::getIncludeSteering() { return _whichSimulationControls[STEERING]; } 126 | const bool *BleGamepadConfiguration::getWhichSimulationControls() const { return _whichSimulationControls; } 127 | bool BleGamepadConfiguration::getIncludeGyroscope() { return _includeGyroscope; } 128 | bool BleGamepadConfiguration::getIncludeAccelerometer() { return _includeAccelerometer; } 129 | const char *BleGamepadConfiguration::getModelNumber(){ return _modelNumber; } 130 | const char *BleGamepadConfiguration::getSoftwareRevision(){ return _softwareRevision; } 131 | const char *BleGamepadConfiguration::getSerialNumber(){ return _serialNumber; } 132 | const char *BleGamepadConfiguration::getFirmwareRevision(){ return _firmwareRevision; } 133 | const char *BleGamepadConfiguration::getHardwareRevision(){ return _hardwareRevision; } 134 | bool BleGamepadConfiguration::getEnableOutputReport(){ return _enableOutputReport; } 135 | bool BleGamepadConfiguration::getEnableNordicUARTService(){ return _enableNordicUARTService; } 136 | uint16_t BleGamepadConfiguration::getOutputReportLength(){ return _outputReportLength; } 137 | int8_t BleGamepadConfiguration::getTXPowerLevel(){ return _transmitPowerLevel; } // Returns the power level that was set as the server started 138 | 139 | void BleGamepadConfiguration::setWhichSpecialButtons(bool start, bool select, bool menu, bool home, bool back, bool volumeInc, bool volumeDec, bool volumeMute) 140 | { 141 | _whichSpecialButtons[START_BUTTON] = start; 142 | _whichSpecialButtons[SELECT_BUTTON] = select; 143 | _whichSpecialButtons[MENU_BUTTON] = menu; 144 | _whichSpecialButtons[HOME_BUTTON] = home; 145 | _whichSpecialButtons[BACK_BUTTON] = back; 146 | _whichSpecialButtons[VOLUME_INC_BUTTON] = volumeInc; 147 | _whichSpecialButtons[VOLUME_DEC_BUTTON] = volumeDec; 148 | _whichSpecialButtons[VOLUME_MUTE_BUTTON] = volumeMute; 149 | } 150 | 151 | void BleGamepadConfiguration::setWhichAxes(bool xAxis, bool yAxis, bool zAxis, bool rxAxis, bool ryAxis, bool rzAxis, bool slider1, bool slider2) 152 | { 153 | _whichAxes[X_AXIS] = xAxis; 154 | _whichAxes[Y_AXIS] = yAxis; 155 | _whichAxes[Z_AXIS] = zAxis; 156 | _whichAxes[RZ_AXIS] = rzAxis; 157 | _whichAxes[RX_AXIS] = rxAxis; 158 | _whichAxes[RY_AXIS] = ryAxis; 159 | _whichAxes[SLIDER1] = slider1; 160 | _whichAxes[SLIDER2] = slider2; 161 | } 162 | 163 | void BleGamepadConfiguration::setWhichSimulationControls(bool rudder, bool throttle, bool accelerator, bool brake, bool steering) 164 | { 165 | _whichSimulationControls[RUDDER] = rudder; 166 | _whichSimulationControls[THROTTLE] = throttle; 167 | _whichSimulationControls[ACCELERATOR] = accelerator; 168 | _whichSimulationControls[BRAKE] = brake; 169 | _whichSimulationControls[STEERING] = steering; 170 | } 171 | 172 | void BleGamepadConfiguration::setControllerType(uint8_t value) { _controllerType = value; } 173 | void BleGamepadConfiguration::setHidReportId(uint8_t value) { _hidReportId = value; } 174 | void BleGamepadConfiguration::setButtonCount(uint16_t value) { _buttonCount = value; } 175 | void BleGamepadConfiguration::setHatSwitchCount(uint8_t value) { _hatSwitchCount = value; } 176 | void BleGamepadConfiguration::setAutoReport(bool value) { _autoReport = value; } 177 | void BleGamepadConfiguration::setIncludeStart(bool value) { _whichSpecialButtons[START_BUTTON] = value; } 178 | void BleGamepadConfiguration::setIncludeSelect(bool value) { _whichSpecialButtons[SELECT_BUTTON] = value; } 179 | void BleGamepadConfiguration::setIncludeMenu(bool value) { _whichSpecialButtons[MENU_BUTTON] = value; } 180 | void BleGamepadConfiguration::setIncludeHome(bool value) { _whichSpecialButtons[HOME_BUTTON] = value; } 181 | void BleGamepadConfiguration::setIncludeBack(bool value) { _whichSpecialButtons[BACK_BUTTON] = value; } 182 | void BleGamepadConfiguration::setIncludeVolumeInc(bool value) { _whichSpecialButtons[VOLUME_INC_BUTTON] = value; } 183 | void BleGamepadConfiguration::setIncludeVolumeDec(bool value) { _whichSpecialButtons[VOLUME_DEC_BUTTON] = value; } 184 | void BleGamepadConfiguration::setIncludeVolumeMute(bool value) { _whichSpecialButtons[VOLUME_MUTE_BUTTON] = value; } 185 | void BleGamepadConfiguration::setIncludeXAxis(bool value) { _whichAxes[X_AXIS] = value; } 186 | void BleGamepadConfiguration::setIncludeYAxis(bool value) { _whichAxes[Y_AXIS] = value; } 187 | void BleGamepadConfiguration::setIncludeZAxis(bool value) { _whichAxes[Z_AXIS] = value; } 188 | void BleGamepadConfiguration::setIncludeRzAxis(bool value) { _whichAxes[RZ_AXIS] = value; } 189 | void BleGamepadConfiguration::setIncludeRxAxis(bool value) { _whichAxes[RX_AXIS] = value; } 190 | void BleGamepadConfiguration::setIncludeRyAxis(bool value) { _whichAxes[RY_AXIS] = value; } 191 | void BleGamepadConfiguration::setIncludeSlider1(bool value) { _whichAxes[SLIDER1] = value; } 192 | void BleGamepadConfiguration::setIncludeSlider2(bool value) { _whichAxes[SLIDER2] = value; } 193 | void BleGamepadConfiguration::setIncludeRudder(bool value) { _whichSimulationControls[RUDDER] = value; } 194 | void BleGamepadConfiguration::setIncludeThrottle(bool value) { _whichSimulationControls[THROTTLE] = value; } 195 | void BleGamepadConfiguration::setIncludeAccelerator(bool value) { _whichSimulationControls[ACCELERATOR] = value; } 196 | void BleGamepadConfiguration::setIncludeBrake(bool value) { _whichSimulationControls[BRAKE] = value; } 197 | void BleGamepadConfiguration::setIncludeSteering(bool value) { _whichSimulationControls[STEERING] = value; } 198 | void BleGamepadConfiguration::setIncludeGyroscope(bool value) { _includeGyroscope = value; } 199 | void BleGamepadConfiguration::setIncludeAccelerometer(bool value) { _includeAccelerometer = value; } 200 | void BleGamepadConfiguration::setVid(uint16_t value) { _vid = value; } 201 | void BleGamepadConfiguration::setPid(uint16_t value) { _pid = value; } 202 | void BleGamepadConfiguration::setGuidVersion(uint16_t value) { _guidVersion = value; } 203 | void BleGamepadConfiguration::setAxesMin(int16_t value) { _axesMin = value; } 204 | void BleGamepadConfiguration::setAxesMax(int16_t value) { _axesMax = value; } 205 | void BleGamepadConfiguration::setSimulationMin(int16_t value) { _simulationMin = value; } 206 | void BleGamepadConfiguration::setSimulationMax(int16_t value) { _simulationMax = value; } 207 | void BleGamepadConfiguration::setMotionMin(int16_t value) { _motionMin = value; } 208 | void BleGamepadConfiguration::setMotionMax(int16_t value) { _motionMax = value; } 209 | void BleGamepadConfiguration::setModelNumber(char *value) { _modelNumber = value; } 210 | void BleGamepadConfiguration::setSoftwareRevision(char *value) { _softwareRevision = value; } 211 | void BleGamepadConfiguration::setSerialNumber(char *value) { _serialNumber = value; } 212 | void BleGamepadConfiguration::setFirmwareRevision(char *value) { _firmwareRevision = value; } 213 | void BleGamepadConfiguration::setHardwareRevision(char *value) { _hardwareRevision = value; } 214 | void BleGamepadConfiguration::setEnableOutputReport(bool value) { _enableOutputReport = value; } 215 | void BleGamepadConfiguration::setEnableNordicUARTService(bool value) { _enableNordicUARTService = value; } 216 | void BleGamepadConfiguration::setOutputReportLength(uint16_t value) { _outputReportLength = value; } 217 | void BleGamepadConfiguration::setTXPowerLevel(int8_t value) { _transmitPowerLevel = value; } 218 | -------------------------------------------------------------------------------- /BleGamepadConfiguration.h: -------------------------------------------------------------------------------- 1 | #ifndef ESP32_BLE_GAMEPAD_CONFIG_H 2 | #define ESP32_BLE_GAMEPAD_CONFIG_H 3 | 4 | #define POSSIBLESPECIALBUTTONS 8 5 | #define POSSIBLEAXES 8 6 | #define POSSIBLESIMULATIONCONTROLS 5 7 | 8 | #include 9 | 10 | #define CONTROLLER_TYPE_JOYSTICK 0x04 11 | #define CONTROLLER_TYPE_GAMEPAD 0x05 12 | #define CONTROLLER_TYPE_MULTI_AXIS 0x08 13 | 14 | #define BUTTON_1 0x1 15 | #define BUTTON_2 0x2 16 | #define BUTTON_3 0x3 17 | #define BUTTON_4 0x4 18 | #define BUTTON_5 0x5 19 | #define BUTTON_6 0x6 20 | #define BUTTON_7 0x7 21 | #define BUTTON_8 0x8 22 | 23 | #define BUTTON_9 0x9 24 | #define BUTTON_10 0xa 25 | #define BUTTON_11 0xb 26 | #define BUTTON_12 0xc 27 | #define BUTTON_13 0xd 28 | #define BUTTON_14 0xe 29 | #define BUTTON_15 0xf 30 | #define BUTTON_16 0x10 31 | 32 | #define BUTTON_17 0x11 33 | #define BUTTON_18 0x12 34 | #define BUTTON_19 0x13 35 | #define BUTTON_20 0x14 36 | #define BUTTON_21 0x15 37 | #define BUTTON_22 0x16 38 | #define BUTTON_23 0x17 39 | #define BUTTON_24 0x18 40 | 41 | #define BUTTON_25 0x19 42 | #define BUTTON_26 0x1a 43 | #define BUTTON_27 0x1b 44 | #define BUTTON_28 0x1c 45 | #define BUTTON_29 0x1d 46 | #define BUTTON_30 0x1e 47 | #define BUTTON_31 0x1f 48 | #define BUTTON_32 0x20 49 | 50 | #define BUTTON_33 0x21 51 | #define BUTTON_34 0x22 52 | #define BUTTON_35 0x23 53 | #define BUTTON_36 0x24 54 | #define BUTTON_37 0x25 55 | #define BUTTON_38 0x26 56 | #define BUTTON_39 0x27 57 | #define BUTTON_40 0x28 58 | 59 | #define BUTTON_41 0x29 60 | #define BUTTON_42 0x2a 61 | #define BUTTON_43 0x2b 62 | #define BUTTON_44 0x2c 63 | #define BUTTON_45 0x2d 64 | #define BUTTON_46 0x2e 65 | #define BUTTON_47 0x2f 66 | #define BUTTON_48 0x30 67 | 68 | #define BUTTON_49 0x31 69 | #define BUTTON_50 0x32 70 | #define BUTTON_51 0x33 71 | #define BUTTON_52 0x34 72 | #define BUTTON_53 0x35 73 | #define BUTTON_54 0x36 74 | #define BUTTON_55 0x37 75 | #define BUTTON_56 0x38 76 | 77 | #define BUTTON_57 0x39 78 | #define BUTTON_58 0x3a 79 | #define BUTTON_59 0x3b 80 | #define BUTTON_60 0x3c 81 | #define BUTTON_61 0x3d 82 | #define BUTTON_62 0x3e 83 | #define BUTTON_63 0x3f 84 | #define BUTTON_64 0x40 85 | 86 | #define BUTTON_65 0x41 87 | #define BUTTON_66 0x42 88 | #define BUTTON_67 0x43 89 | #define BUTTON_68 0x44 90 | #define BUTTON_69 0x45 91 | #define BUTTON_70 0x46 92 | #define BUTTON_71 0x47 93 | #define BUTTON_72 0x48 94 | 95 | #define BUTTON_73 0x49 96 | #define BUTTON_74 0x4a 97 | #define BUTTON_75 0x4b 98 | #define BUTTON_76 0x4c 99 | #define BUTTON_77 0x4d 100 | #define BUTTON_78 0x4e 101 | #define BUTTON_79 0x4f 102 | #define BUTTON_80 0x50 103 | 104 | #define BUTTON_81 0x51 105 | #define BUTTON_82 0x52 106 | #define BUTTON_83 0x53 107 | #define BUTTON_84 0x54 108 | #define BUTTON_85 0x55 109 | #define BUTTON_86 0x56 110 | #define BUTTON_87 0x57 111 | #define BUTTON_88 0x58 112 | 113 | #define BUTTON_89 0x59 114 | #define BUTTON_90 0x5a 115 | #define BUTTON_91 0x5b 116 | #define BUTTON_92 0x5c 117 | #define BUTTON_93 0x5d 118 | #define BUTTON_94 0x5e 119 | #define BUTTON_95 0x5f 120 | #define BUTTON_96 0x60 121 | 122 | #define BUTTON_97 0x61 123 | #define BUTTON_98 0x62 124 | #define BUTTON_99 0x63 125 | #define BUTTON_100 0x64 126 | #define BUTTON_101 0x65 127 | #define BUTTON_102 0x66 128 | #define BUTTON_103 0x67 129 | #define BUTTON_104 0x68 130 | 131 | #define BUTTON_105 0x69 132 | #define BUTTON_106 0x6a 133 | #define BUTTON_107 0x6b 134 | #define BUTTON_108 0x6c 135 | #define BUTTON_109 0x6d 136 | #define BUTTON_110 0x6e 137 | #define BUTTON_111 0x6f 138 | #define BUTTON_112 0x70 139 | 140 | #define BUTTON_113 0x71 141 | #define BUTTON_114 0x72 142 | #define BUTTON_115 0x73 143 | #define BUTTON_116 0x74 144 | #define BUTTON_117 0x75 145 | #define BUTTON_118 0x76 146 | #define BUTTON_119 0x77 147 | #define BUTTON_120 0x78 148 | 149 | #define BUTTON_121 0x79 150 | #define BUTTON_122 0x7a 151 | #define BUTTON_123 0x7b 152 | #define BUTTON_124 0x7c 153 | #define BUTTON_125 0x7d 154 | #define BUTTON_126 0x7e 155 | #define BUTTON_127 0x7f 156 | #define BUTTON_128 0x80 157 | 158 | #define DPAD_CENTERED 0 159 | #define DPAD_UP 1 160 | #define DPAD_UP_RIGHT 2 161 | #define DPAD_RIGHT 3 162 | #define DPAD_DOWN_RIGHT 4 163 | #define DPAD_DOWN 5 164 | #define DPAD_DOWN_LEFT 6 165 | #define DPAD_LEFT 7 166 | #define DPAD_UP_LEFT 8 167 | 168 | #define HAT_CENTERED 0 169 | #define HAT_UP 1 170 | #define HAT_UP_RIGHT 2 171 | #define HAT_RIGHT 3 172 | #define HAT_DOWN_RIGHT 4 173 | #define HAT_DOWN 5 174 | #define HAT_DOWN_LEFT 6 175 | #define HAT_LEFT 7 176 | #define HAT_UP_LEFT 8 177 | 178 | #define X_AXIS 0 179 | #define Y_AXIS 1 180 | #define Z_AXIS 2 181 | #define RX_AXIS 3 182 | #define RY_AXIS 4 183 | #define RZ_AXIS 5 184 | #define SLIDER1 6 185 | #define SLIDER2 7 186 | 187 | #define RUDDER 0 188 | #define THROTTLE 1 189 | #define ACCELERATOR 2 190 | #define BRAKE 3 191 | #define STEERING 4 192 | 193 | #define START_BUTTON 0 194 | #define SELECT_BUTTON 1 195 | #define MENU_BUTTON 2 196 | #define HOME_BUTTON 3 197 | #define BACK_BUTTON 4 198 | #define VOLUME_INC_BUTTON 5 199 | #define VOLUME_DEC_BUTTON 6 200 | #define VOLUME_MUTE_BUTTON 7 201 | 202 | #define POWER_STATE_UNKNOWN 0 // 0b00 203 | #define POWER_STATE_NOT_SUPPORTED 1 // 0b01 204 | #define POWER_STATE_NOT_PRESENT 2 // 0b10 205 | #define POWER_STATE_NOT_DISCHARGING 2 // 0b10 206 | #define POWER_STATE_NOT_CHARGING 2 // 0b10 207 | #define POWER_STATE_GOOD 2 // 0b10 208 | #define POWER_STATE_PRESENT 3 // 0b11 209 | #define POWER_STATE_DISCHARGING 3 // 0b11 210 | #define POWER_STATE_CHARGING 3 // 0b11 211 | #define POWER_STATE_CRITICAL 3 // 0b11 212 | 213 | class BleGamepadConfiguration 214 | { 215 | private: 216 | uint8_t _controllerType; 217 | bool _autoReport; 218 | uint8_t _hidReportId; 219 | uint16_t _buttonCount; 220 | uint8_t _hatSwitchCount; 221 | bool _whichSpecialButtons[POSSIBLESPECIALBUTTONS]; 222 | bool _whichAxes[POSSIBLEAXES]; 223 | bool _whichSimulationControls[POSSIBLESIMULATIONCONTROLS]; 224 | bool _includeGyroscope; 225 | bool _includeAccelerometer; 226 | uint16_t _vid; 227 | uint16_t _pid; 228 | uint16_t _guidVersion; 229 | int16_t _axesMin; 230 | int16_t _axesMax; 231 | int16_t _simulationMin; 232 | int16_t _simulationMax; 233 | int16_t _motionMin; 234 | int16_t _motionMax; 235 | const char *_modelNumber; 236 | const char *_softwareRevision; 237 | const char *_serialNumber; 238 | const char *_firmwareRevision; 239 | const char *_hardwareRevision; 240 | bool _enableOutputReport; 241 | bool _enableNordicUARTService; 242 | uint16_t _outputReportLength; 243 | int8_t _transmitPowerLevel; 244 | 245 | 246 | public: 247 | BleGamepadConfiguration(); 248 | 249 | bool getAutoReport(); 250 | uint8_t getControllerType(); 251 | uint8_t getHidReportId(); 252 | uint16_t getButtonCount(); 253 | uint8_t getTotalSpecialButtonCount(); 254 | uint8_t getDesktopSpecialButtonCount(); 255 | uint8_t getConsumerSpecialButtonCount(); 256 | uint8_t getHatSwitchCount(); 257 | uint8_t getAxisCount(); 258 | uint8_t getSimulationCount(); 259 | bool getIncludeStart(); 260 | bool getIncludeSelect(); 261 | bool getIncludeMenu(); 262 | bool getIncludeHome(); 263 | bool getIncludeBack(); 264 | bool getIncludeVolumeInc(); 265 | bool getIncludeVolumeDec(); 266 | bool getIncludeVolumeMute(); 267 | const bool *getWhichSpecialButtons() const; 268 | bool getIncludeXAxis(); 269 | bool getIncludeYAxis(); 270 | bool getIncludeZAxis(); 271 | bool getIncludeRxAxis(); 272 | bool getIncludeRyAxis(); 273 | bool getIncludeRzAxis(); 274 | bool getIncludeSlider1(); 275 | bool getIncludeSlider2(); 276 | const bool *getWhichAxes() const; 277 | bool getIncludeRudder(); 278 | bool getIncludeThrottle(); 279 | bool getIncludeAccelerator(); 280 | bool getIncludeBrake(); 281 | bool getIncludeSteering(); 282 | const bool *getWhichSimulationControls() const; 283 | bool getIncludeAccelerometer(); 284 | bool getIncludeGyroscope(); 285 | uint16_t getVid(); 286 | uint16_t getPid(); 287 | uint16_t getGuidVersion(); 288 | int16_t getAxesMin(); 289 | int16_t getAxesMax(); 290 | int16_t getSimulationMin(); 291 | int16_t getSimulationMax(); 292 | int16_t getMotionMin(); 293 | int16_t getMotionMax(); 294 | const char *getModelNumber(); 295 | const char *getSoftwareRevision(); 296 | const char *getSerialNumber(); 297 | const char *getFirmwareRevision(); 298 | const char *getHardwareRevision(); 299 | bool getEnableOutputReport(); 300 | bool getEnableNordicUARTService(); 301 | uint16_t getOutputReportLength(); 302 | int8_t getTXPowerLevel(); 303 | 304 | void setControllerType(uint8_t controllerType); 305 | void setAutoReport(bool value); 306 | void setHidReportId(uint8_t value); 307 | void setButtonCount(uint16_t value); 308 | void setHatSwitchCount(uint8_t value); 309 | void setIncludeStart(bool value); 310 | void setIncludeSelect(bool value); 311 | void setIncludeMenu(bool value); 312 | void setIncludeHome(bool value); 313 | void setIncludeBack(bool value); 314 | void setIncludeVolumeInc(bool value); 315 | void setIncludeVolumeDec(bool value); 316 | void setIncludeVolumeMute(bool value); 317 | void setWhichSpecialButtons(bool start, bool select, bool menu, bool home, bool back, bool volumeInc, bool volumeDec, bool volumeMute); 318 | void setIncludeXAxis(bool value); 319 | void setIncludeYAxis(bool value); 320 | void setIncludeZAxis(bool value); 321 | void setIncludeRxAxis(bool value); 322 | void setIncludeRyAxis(bool value); 323 | void setIncludeRzAxis(bool value); 324 | void setIncludeSlider1(bool value); 325 | void setIncludeSlider2(bool value); 326 | void setWhichAxes(bool xAxis, bool yAxis, bool zAxis, bool rxAxis, bool ryAxis, bool rzAxis, bool slider1, bool slider2); 327 | void setIncludeRudder(bool value); 328 | void setIncludeThrottle(bool value); 329 | void setIncludeAccelerator(bool value); 330 | void setIncludeBrake(bool value); 331 | void setIncludeSteering(bool value); 332 | void setWhichSimulationControls(bool rudder, bool throttle, bool accelerator, bool brake, bool steering); 333 | void setIncludeGyroscope(bool value); 334 | void setIncludeAccelerometer(bool value); 335 | void setVid(uint16_t value); 336 | void setPid(uint16_t value); 337 | void setGuidVersion(uint16_t value); 338 | void setAxesMin(int16_t value); 339 | void setAxesMax(int16_t value); 340 | void setSimulationMin(int16_t value); 341 | void setSimulationMax(int16_t value); 342 | void setMotionMin(int16_t value); 343 | void setMotionMax(int16_t value); 344 | void setModelNumber(char *value); 345 | void setSoftwareRevision(char *value); 346 | void setSerialNumber(char *value); 347 | void setFirmwareRevision(char *value); 348 | void setHardwareRevision(char *value); 349 | void setEnableOutputReport(bool value); 350 | void setEnableNordicUARTService(bool value); 351 | void setOutputReportLength(uint16_t value); 352 | void setTXPowerLevel(int8_t value); 353 | }; 354 | 355 | #endif 356 | -------------------------------------------------------------------------------- /BleNUS.cpp: -------------------------------------------------------------------------------- 1 | #include "BleNUS.h" 2 | #include 3 | #include "NimBLELog.h" 4 | 5 | #if defined(CONFIG_ARDUHAL_ESP_LOG) 6 | #include "esp32-hal-log.h" 7 | #define LOG_TAG "BleNUS" 8 | #else 9 | #include "esp_log.h" 10 | static const char *LOG_TAG = "BleNUS"; 11 | #endif 12 | 13 | BleNUS::BleNUS(NimBLEServer* existingServer) 14 | : pServer(existingServer), pService(nullptr), pTxCharacteristic(nullptr), pRxCharacteristic(nullptr), dataReceivedCallback(nullptr) {} 15 | 16 | BleNUS::~BleNUS() { 17 | end(); 18 | } 19 | 20 | void BleNUS::begin() { 21 | delay(1000); // Give some time for other services to complete their business 22 | 23 | if (!pServer) { 24 | Serial.println("No pServer"); 25 | NIMBLE_LOGD(LOG_TAG, "No existing pServer available"); 26 | return; 27 | } 28 | 29 | NimBLEAdvertising* pAdvertising = pServer->getAdvertising(); 30 | NIMBLE_LOGD(LOG_TAG, "Stopping main NimBLE server from advertising (shouldn't be at this stage if you set delayAdvertising to true)"); 31 | pAdvertising->stop(); 32 | 33 | NIMBLE_LOGD(LOG_TAG, "Creating Nordic UART Service"); 34 | pService = pServer->createService(NUS_SERVICE_UUID); // This pService is local only to this class. Nothing to do with the ones from BleBamepad 35 | 36 | NIMBLE_LOGD(LOG_TAG, "Adding Nordic UART Service TX and RX characteristics"); 37 | pTxCharacteristic = pService->createCharacteristic(NUS_TX_CHARACTERISTIC_UUID, NIMBLE_PROPERTY::NOTIFY); 38 | pRxCharacteristic = pService->createCharacteristic(NUS_RX_CHARACTERISTIC_UUID, NIMBLE_PROPERTY::WRITE); 39 | NIMBLE_LOGD(LOG_TAG, "Registering Nordic UART Service callbacks"); 40 | pRxCharacteristic->setCallbacks(this); 41 | 42 | NIMBLE_LOGD(LOG_TAG, "Starting Nordic UART Service"); 43 | pService->start(); 44 | 45 | // Can't add Nordic UART Service UUID to the main advertisement as it makes it larger than 31 bytes 46 | // It's not strictly needed anyway 47 | //NIMBLE_LOGD(LOG_TAG, "Adding Nordic UART Service UUID to main NimBLE server advertising"); 48 | //pAdvertising->addServiceUUID(pService->getUUID()); 49 | 50 | // Get around the above issue by adding Nordic UART Service UUID to NimBLEAdvertisementData scanResponseData; 51 | // Add it in a scan response instead so devices can still see it has that capability 52 | // https://github.com/h2zero/NimBLE-Arduino/issues/135 53 | NIMBLE_LOGD(LOG_TAG, "Adding Nordic UART Service UUID to advertising scan response data"); 54 | NimBLEAdvertisementData scanResponseData; // Create NimBLEAdvertisementData object 55 | scanResponseData.addServiceUUID(pService->getUUID()); // Add UUID 56 | pAdvertising->setScanResponseData(scanResponseData); // Assign the scan response data 57 | 58 | NIMBLE_LOGD(LOG_TAG, "Main NimBLE server advertising started!"); 59 | pAdvertising->start(); 60 | } 61 | 62 | void BleNUS::end() { 63 | if (pService) { 64 | // Nothing I can think of 65 | } 66 | } 67 | 68 | void BleNUS::sendData(const uint8_t* data, size_t length) { 69 | if (pTxCharacteristic && pServer->getConnectedCount() > 0) { 70 | pTxCharacteristic->setValue(data, length); 71 | pTxCharacteristic->notify(); 72 | } 73 | } 74 | 75 | void BleNUS::setDataReceivedCallback(void (*callback)(const uint8_t* data, size_t length)) { 76 | dataReceivedCallback = callback; 77 | } 78 | 79 | void BleNUS::onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) { 80 | if (dataReceivedCallback) { 81 | std::string value = pCharacteristic->getValue(); 82 | buffer += value; // Append to internal buffer 83 | dataReceivedCallback((const uint8_t*)value.data(), value.length()); 84 | } 85 | } 86 | 87 | size_t BleNUS::available() { 88 | return buffer.length(); // Check the length of data in the internal buffer 89 | } 90 | 91 | int BleNUS::read() { 92 | if (buffer.length() > 0) { 93 | char c = buffer[0]; 94 | buffer.erase(0, 1); // Remove the first byte after reading 95 | return c; 96 | } 97 | return -1; // No data available 98 | } 99 | 100 | int BleNUS::peek() { 101 | if (buffer.length() > 0) { 102 | return buffer[0]; // Peek at the first byte without removing it 103 | } 104 | return -1; // No data available to peek 105 | } 106 | 107 | void BleNUS::flush() { 108 | buffer.clear(); // Clear the internal buffer 109 | } 110 | 111 | void BleNUS::print(const char* str) { 112 | sendData((const uint8_t*)str, strlen(str)); 113 | } 114 | 115 | void BleNUS::print(const String& str) { 116 | print(str.c_str()); 117 | } 118 | 119 | void BleNUS::print(int i) { 120 | char buf[32]; 121 | itoa(i, buf, 10); 122 | print(buf); 123 | } 124 | 125 | void BleNUS::print(long l) { 126 | char buf[32]; 127 | ltoa(l, buf, 10); 128 | print(buf); 129 | } 130 | 131 | void BleNUS::print(unsigned long ul) { 132 | char buf[32]; 133 | ultoa(ul, buf, 10); 134 | print(buf); 135 | } 136 | 137 | void BleNUS::print(float f, int digits) { 138 | char buf[32]; 139 | dtostrf(f, 6, digits, buf); 140 | print(buf); 141 | } 142 | 143 | void BleNUS::print(double d, int digits) { 144 | print((float)d, digits); 145 | } 146 | 147 | void BleNUS::print(char c) { 148 | char buf[2] = {c, '\0'}; 149 | print(buf); 150 | } 151 | 152 | void BleNUS::println(const char* str) { 153 | print(str); 154 | print("\n"); 155 | } 156 | 157 | void BleNUS::println(const String& str) { 158 | println(str.c_str()); 159 | } 160 | 161 | void BleNUS::println(int i) { 162 | print(i); 163 | print("\n"); 164 | } 165 | 166 | void BleNUS::println(long l) { 167 | print(l); 168 | print("\n"); 169 | } 170 | 171 | void BleNUS::println(unsigned long ul) { 172 | print(ul); 173 | print("\n"); 174 | } 175 | 176 | void BleNUS::println(float f, int digits) { 177 | print(f, digits); 178 | print("\n"); 179 | } 180 | 181 | void BleNUS::println(double d, int digits) { 182 | print(d, digits); 183 | print("\n"); 184 | } 185 | 186 | void BleNUS::println(char c) { 187 | print(c); 188 | print("\n"); 189 | } 190 | 191 | void BleNUS::write(uint8_t byte) { 192 | print(byte); // Just use the print method to send 1 byte 193 | } 194 | 195 | void BleNUS::write(const uint8_t *buffer, size_t size) { 196 | sendData(buffer, size); 197 | } 198 | -------------------------------------------------------------------------------- /BleNUS.h: -------------------------------------------------------------------------------- 1 | #ifndef BleNUS_h 2 | #define BleNUS_h 3 | 4 | #include 5 | 6 | #define NUS_SERVICE_UUID "6e400001-b5a3-f393-e0a9-e50e24dcca9e" 7 | #define NUS_RX_CHARACTERISTIC_UUID "6e400002-b5a3-f393-e0a9-e50e24dcca9e" 8 | #define NUS_TX_CHARACTERISTIC_UUID "6e400003-b5a3-f393-e0a9-e50e24dcca9e" 9 | 10 | class BleNUS : public NimBLECharacteristicCallbacks { 11 | public: 12 | BleNUS(NimBLEServer* existingServer); 13 | ~BleNUS(); 14 | 15 | void begin(); 16 | void end(); 17 | 18 | void sendData(const uint8_t* data, size_t length); 19 | 20 | void setDataReceivedCallback(void (*callback)(const uint8_t* data, size_t length)); 21 | 22 | size_t available(); 23 | int read(); 24 | void flush(); 25 | int peek(); // Add peek method 26 | 27 | void print(const char* str); 28 | void print(const String& str); 29 | void print(int i); 30 | void print(long l); 31 | void print(unsigned long ul); 32 | void print(float f, int digits = 2); 33 | void print(double d, int digits = 2); 34 | void print(char c); 35 | 36 | void println(const char* str); 37 | void println(const String& str); 38 | void println(int i); 39 | void println(long l); 40 | void println(unsigned long ul); 41 | void println(float f, int digits = 2); 42 | void println(double d, int digits = 2); 43 | void println(char c); 44 | 45 | void write(uint8_t byte); 46 | void write(const uint8_t *buffer, size_t size); 47 | 48 | void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo) override; 49 | 50 | private: 51 | NimBLEServer* pServer; 52 | NimBLEService* pService; 53 | NimBLECharacteristic* pTxCharacteristic; 54 | NimBLECharacteristic* pRxCharacteristic; 55 | 56 | void (*dataReceivedCallback)(const uint8_t* data, size_t length); 57 | std::string buffer; // For storing received data 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /BleOutputReceiver.cpp: -------------------------------------------------------------------------------- 1 | #include "BleOutputReceiver.h" 2 | 3 | BleOutputReceiver::BleOutputReceiver(uint16_t outputReportLength) 4 | { 5 | this->outputReportLength = outputReportLength; 6 | outputBuffer = new uint8_t[outputReportLength]; 7 | } 8 | 9 | BleOutputReceiver::~BleOutputReceiver() 10 | { 11 | // Release memory 12 | if (outputBuffer) 13 | { 14 | delete[] outputBuffer; 15 | } 16 | } 17 | 18 | void BleOutputReceiver::onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo& connInfo) 19 | { 20 | // Retrieve data sent from the host 21 | std::string value = pCharacteristic->getValue(); 22 | 23 | // Store the received data in the buffer 24 | for (int i = 0; i < std::min(value.length(), (size_t)outputReportLength); i++) 25 | { 26 | outputBuffer[i] = (uint8_t)value[i]; 27 | } 28 | 29 | // Testing 30 | // Serial.println("Received data from host:"); 31 | // for (size_t i = 0; i < value.length(); i++) { 32 | // Serial.print((uint8_t)value[i], HEX); 33 | // Serial.print(" "); 34 | // } 35 | // Serial.println(); 36 | 37 | outputFlag = true; 38 | } -------------------------------------------------------------------------------- /BleOutputReceiver.h: -------------------------------------------------------------------------------- 1 | #ifndef BLE_OUTPUT_RECEIVER_H 2 | #define BLE_OUTPUT_RECEIVER_H 3 | #include "sdkconfig.h" 4 | #if defined(CONFIG_BT_ENABLED) 5 | 6 | #include "nimconfig.h" 7 | #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) 8 | 9 | #include 10 | #include "NimBLECharacteristic.h" 11 | #include "NimBLEConnInfo.h" 12 | 13 | class BleOutputReceiver : public NimBLECharacteristicCallbacks 14 | { 15 | public: 16 | BleOutputReceiver(uint16_t outputReportLength); 17 | ~BleOutputReceiver(); 18 | void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo& connInfo) override; 19 | bool outputFlag = false; 20 | uint16_t outputReportLength; 21 | uint8_t *outputBuffer; 22 | }; 23 | 24 | #endif // CONFIG_BT_NIMBLE_ROLE_PERIPHERAL 25 | #endif // CONFIG_BT_ENABLED 26 | #endif // BLE_OUTPUT_RECEIVER_H 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32-BLE-Gamepad 2 | 3 | ## License 4 | Published under the MIT license. Please see license.txt. 5 | 6 | It would be great however if any improvements are fed back into this version. 7 | 8 | ## Features 9 | 10 | - [x] Button press (128 buttons) 11 | - [x] Button release (128 buttons) 12 | - [x] Axes movement (6 axes (configurable resolution up to 16 bit) (x, y, z, rX, rY, rZ) --> In Windows usually (Left Thumb X, Left Thumb Y, Right Thumb X, Left Trigger, Right Trigger, Right Thumb Y)) 13 | - [x] Gyroscope and Accelerometer 14 | - [x] Set battery percentage 15 | - [x] Set battery power state information using UUID 0x2A1A. Use nRF Connect on Android for example to see this information 16 | - [x] 2 Sliders (configurable resolution up to 16 bit) (Slider 1 and Slider 2) 17 | - [x] 4 point of view hats (ie. d-pad plus 3 other hat switches) 18 | - [x] Simulation controls (rudder, throttle, accelerator, brake, steering) 19 | - [x] Special buttons (start, select, menu, home, back, volume up, volume down, volume mute) all disabled by default 20 | - [x] Configurable HID descriptor 21 | - [x] Configurable VID and PID values 22 | - [x] Configurable BLE characteristics (name, manufacturer, model number, software revision, serial number, firmware revision, hardware revision) 23 | - [x] Report optional battery level to host 24 | - [x] Uses efficient NimBLE bluetooth library 25 | - [x] Output report function 26 | - [x] Functions available for force pairing/ignore current client and/or delete pairings 27 | - [x] Nordic UART Service functionality at same time as gamepad. See examples 28 | - [x] Compatible with Windows 29 | - [x] Compatible with Android (Android OS maps default buttons / axes / hats slightly differently than Windows) (see notes) 30 | - [x] Compatible with Linux (limited testing) 31 | - [x] Compatible with MacOS X (limited testing) 32 | - [ ] Compatible with iOS (No - not even for accessibility switch - This is not a “Made for iPhone” (MFI) compatible device) 33 | (Use the Xinput fork suggested below which has been tested to work) 34 | 35 | ## NimBLE 36 | Since version 3 of this library, the more efficient NimBLE library is used instead of the default BLE implementation 37 | Please use the library manager to install it, or get it from here: https://github.com/h2zero/NimBLE-Arduino 38 | Since version 3, this library also supports a configurable HID desciptor, which allows users to customise how the device presents itself to the OS (number of buttons, hats, axes, sliders, simulation controls etc). 39 | See the examples for guidance. 40 | 41 | ## POSSIBLE BREAKING CHANGES - PLEASE READ 42 | A large code rebase (configuration class) along with some extra features (start, select, menu, home, back, volume up, volume down and volume mute buttons) has been committed thanks to @dexterdy 43 | 44 | Since version 5 of this library, the axes and simulation controls have configurable min and max values 45 | The decision was made to set defaults to 0 for minimum and 32767 for maximum (previously -32767 to 32767) 46 | This was due to the fact that non-Windows operating systems and some online web-based game controller testers didn't play well with negative numbers. Existing sketches should take note, and see the DrivingControllerTest example for how to set back to -32767 if wanted 47 | 48 | This version endeavors to be compatible with the latest released version of NimBLE-Arduino through the Arduino Library Manager; currently version 2.2.1 at the time of this writing; --> https://github.com/h2zero/NimBLE-Arduino/releases/tag/2.2.1 49 | 50 | setAxes accepts axes in the order (x, y, z, rx, ry, rz) 51 | setHIDAxes accepts them in the order (x, y, z, rz, rx, ry) 52 | 53 | Please see updated examples 54 | 55 | ## Installation 56 | - (Make sure you can use the ESP32 with the Arduino IDE. [Instructions can be found here.](https://github.com/espressif/arduino-esp32#installation-instructions)) 57 | - [Download the latest release of this library from the release page.](https://github.com/lemmingDev/ESP32-BLE-Gamepad/releases) 58 | - In the Arduino IDE go to "Sketch" -> "Include Library" -> "Add .ZIP Library..." and select the file you just downloaded. 59 | - In the Arduino IDE go to "Tools" -> "Manage Libraries..." -> Filter for "NimBLE-Arduino" by h2zero and install. 60 | - You can now go to "File" -> "Examples" -> "ESP32 BLE Gamepad" and select an example to get started. 61 | 62 | ## Example 63 | 64 | ``` C++ 65 | /* 66 | * This example turns the ESP32 into a Bluetooth LE gamepad that presses buttons and moves axis 67 | * 68 | * At the moment we are using the default settings, but they can be canged using a BleGamepadConfig instance as parameter for the begin function. 69 | * 70 | * Possible buttons are: 71 | * BUTTON_1 through to BUTTON_16 72 | * (16 buttons by default. Library can be configured to use up to 128) 73 | * 74 | * Possible DPAD/HAT switch position values are: 75 | * DPAD_CENTERED, DPAD_UP, DPAD_UP_RIGHT, DPAD_RIGHT, DPAD_DOWN_RIGHT, DPAD_DOWN, DPAD_DOWN_LEFT, DPAD_LEFT, DPAD_UP_LEFT 76 | * (or HAT_CENTERED, HAT_UP etc) 77 | * 78 | * bleGamepad.setAxes sets all axes at once. There are a few: 79 | * (x axis, y axis, z axis, rx axis, ry axis, rz axis, slider 1, slider 2) 80 | * 81 | * Alternatively, bleGamepad.setHIDAxes sets all axes at once. in the order of: 82 | * (x axis, y axis, z axis, rz axis, ry axis, rz axis, slider 1, slider 2) <- order HID report is actually given in 83 | * 84 | * Library can also be configured to support up to 5 simulation controls 85 | * (rudder, throttle, accelerator, brake, steering), but they are not enabled by default. 86 | * 87 | * Library can also be configured to support different function buttons 88 | * (start, select, menu, home, back, volume increase, volume decrease, volume mute) 89 | * start and select are enabled by default 90 | */ 91 | 92 | #include 93 | #include 94 | 95 | BleGamepad bleGamepad; 96 | 97 | void setup() 98 | { 99 | Serial.begin(115200); 100 | Serial.println("Starting BLE work!"); 101 | bleGamepad.begin(); 102 | // The default bleGamepad.begin() above enables 16 buttons, all axes, one hat, and no simulation controls or special buttons 103 | } 104 | 105 | void loop() 106 | { 107 | if (bleGamepad.isConnected()) 108 | { 109 | Serial.println("Press buttons 5, 16 and start. Move all enabled axes to max. Set DPAD (hat 1) to down right."); 110 | bleGamepad.press(BUTTON_5); 111 | bleGamepad.press(BUTTON_16); 112 | bleGamepad.pressStart(); 113 | bleGamepad.setAxes(32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767); //(X, Y, Z, RX, RY, RZ) 114 | //bleGamepad.setHIDAxes(32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767); //(X, Y, Z, RZ, RX, RY) 115 | bleGamepad.setHat1(HAT_DOWN_RIGHT); 116 | // All axes, sliders, hats etc can also be set independently. See the IndividualAxes.ino example 117 | delay(500); 118 | 119 | Serial.println("Release button 5 and start. Move all axes to min. Set DPAD (hat 1) to centred."); 120 | bleGamepad.release(BUTTON_5); 121 | bleGamepad.releaseStart(); 122 | bleGamepad.setHat1(HAT_CENTERED); 123 | bleGamepad.setAxes(0, 0, 0, 0, 0, 0, 0, 0); //(X, Y, Z, RX, RY, RZ) 124 | //bleGamepad.setHIDAxes(0, 0, 0, 0, 0, 0, 0, 0); //(X, Y, Z, RZ, RX, RY) 125 | delay(500); 126 | } 127 | } 128 | 129 | ``` 130 | By default, reports are sent on every button press/release or axis/slider/hat/simulation movement, however this can be disabled, and then you manually call sendReport on the gamepad instance as shown in the IndividualAxes.ino example. 131 | 132 | VID and PID values can be set. See TestAll.ino for example. 133 | 134 | There is also Bluetooth specific information that you can use (optional): 135 | 136 | Instead of `BleGamepad bleGamepad;` you can do `BleGamepad bleGamepad("Bluetooth Device Name", "Bluetooth Device Manufacturer", 100);`. 137 | The third parameter is the initial battery level of your device. 138 | By default the battery level will be set to 100%, the device name will be `ESP32 BLE Gamepad` and the manufacturer will be `Espressif`. 139 | 140 | Battery level can be set during operation by calling, for example, bleGamepad.setBatteryLevel(80); 141 | Update sent on next gamepad update if auto reporting is not enabled 142 | 143 | 144 | ## Troubleshooting Guide 145 | Troubleshooting guide and suggestions can be found in [TroubleshootingGuide](TroubleshootingGuide.md) 146 | 147 | ## Credits 148 | Credits to [T-vK](https://github.com/T-vK) as this library is based on his ESP32-BLE-Mouse library (https://github.com/T-vK/ESP32-BLE-Mouse) that he provided. 149 | 150 | Credits to [chegewara](https://github.com/chegewara) as the ESP32-BLE-Mouse library is based on [this piece of code](https://github.com/nkolban/esp32-snippets/issues/230#issuecomment-473135679) that he provided. 151 | 152 | Credits to [wakwak-koba](https://github.com/wakwak-koba) for the NimBLE [code](https://github.com/wakwak-koba/ESP32-NimBLE-Gamepad) that he provided. 153 | 154 | ## Notes 155 | This library allows you to make the ESP32 act as a Bluetooth Gamepad and control what it does. 156 | Relies on [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino) 157 | 158 | Use [this](http://www.planetpointy.co.uk/joystick-test-application/) Windows test app to test/see all of the buttons 159 | Ensure you have Direct X 9 installed 160 | 161 | Gamepads desgined for Android use a different button mapping. This effects analog triggers, where the standard left and right trigger axes are not detected. 162 | Android calls the HID report for right trigger `"GAS"` and left trigger `"BRAKE"`. Enabling the `"Accelerator"` and `"Brake"` simulation controls allows them to be used instead of right and left trigger. 163 | 164 | Right thumbstick on Windows is usually z, rz, whereas on Android, this may be z, rx, so you may want to set them separately with setZ and setRX, instead of using setRightThumb(z, rz), or use setRightThumbAndroid(z, rx) 165 | 166 | You might also be interested in: 167 | - [ESP32-BLE-Mouse](https://github.com/T-vK/ESP32-BLE-Mouse) 168 | - [ESP32-BLE-Keyboard](https://github.com/T-vK/ESP32-BLE-Keyboard) 169 | - [Composite Gamepad/Mouse/Keyboard and Xinput capable fork of this library](https://github.com/Mystfit/ESP32-BLE-CompositeHID) 170 | 171 | or the NimBLE versions at 172 | 173 | - [ESP32-NimBLE-Mouse](https://github.com/wakwak-koba/ESP32-NimBLE-Mouse) 174 | - [ESP32-NimBLE-Keyboard](https://github.com/wakwak-koba/ESP32-NimBLE-Keyboard) 175 | -------------------------------------------------------------------------------- /TroubleshootingGuide.md: -------------------------------------------------------------------------------- 1 | # Based on common issues reported by users: 2 | 3 | ## Configuration Changes Not Taking Effect: 4 | Symptom: Updated configurations (e.g., button count, button mappings, device name, HID settings) are not reflected when reconnecting to the host device. 5 | 6 | Cause: Many host operating systems cache Bluetooth device characteristics (e.g., device name, button configurations, and HID descriptors) for faster reconnections. When the ESP32 configuration changes, the cached data may no longer match the updated settings, leading to inconsistencies. 7 | 8 | Solution: 9 | - If you modify the BLE Gamepad configuration, such as device name, device features, button count, axes count or button assignments, you must remove the previously paired connection from the host device (PC, mobile, or console). 10 | - Go to the host's Bluetooth settings, locate the ESP32 device, and select "Remove," "Delete," "Unpair," or "Forget." 11 | - Restart both the ESP32 and the host device, then re-pair them to ensure the updated configuration is applied. 12 | - Ensure you're actually using a custom configuration with ``bleGamepad.begin(&bleGamepadConfig);`` as shown in the CharacteristicsConfiguration.ino example 13 | 14 | ## Button Mapping Issues: 15 | Symptom: Buttons not responding as expected or incorrect button assignments. 16 | 17 | Solution: 18 | - Review the button mapping configuration in your code. 19 | - Ensure that each button is correctly assigned according to the library's documentation. 20 | 21 | ## Connection Problems: 22 | Symptom: The ESP32 device does not appear on the PC and/or is visible on mobile devices. 23 | 24 | Solution: 25 | - Verify that your PC's Bluetooth drivers are up to date. 26 | - Additionally, ensure that the ESP32 is not already paired with another device, which might prevent it from appearing on the PC. 27 | - See ForcePairingMode.ino example for the 3 different methods of dealing with this 28 | - ESP32-S2 Series has no Bluetooth 29 | - ESP32, ESP32-C3 and ESP32-S3 series have been tested to work 30 | 31 | ## Custom Configuration Challenges: 32 | Symptom: Difficulties in setting up a custom configuration for the gamepad. 33 | 34 | Solution: 35 | - Refer to the library's examples and documentation to understand the correct procedures for custom configurations. 36 | - Ensure that all parameters are set correctly and that there are no conflicts in the configuration. 37 | - Ensure you're actually using a custom configuration with ``bleGamepad.begin(&bleGamepadConfig);`` as shown in the CharacteristicsConfiguration.ino example 38 | 39 | ## Multiple Device Pairing Issues: 40 | Symptom: Inability to pair multiple devices simultaneously. 41 | 42 | Solution: 43 | - Update your ESP32-BLE-Gamepad and NimBLE-Arduino libraries to the latest versions 44 | - Try setting different PID values, though this shouldn't be needed 45 | 46 | ## Slow or Inconsistent Connections: 47 | Symptom: The ESP32 takes a long time to connect or fails to connect intermittently. 48 | 49 | Solution: 50 | - Ensure that the ESP32 is in close proximity to the host device to avoid signal interference. 51 | - Check for any power supply issues; unstable power can affect Bluetooth performance. 52 | - Update the ESP32 firmware and the BLE-Gamepad library to the latest versions, as updates often include connectivity improvements. 53 | - Try setting a stronger TX power level (9 is the highest) as shown in example CharacteristicsConfiguration.ino 54 | 55 | ## High Latency or Unresponsive Controls: 56 | Symptom: Noticeable delay between input on the gamepad and action on the host device. 57 | 58 | Solution: 59 | - Optimize the code to reduce any delays in the loop handling the gamepad inputs. 60 | - Minimize the use of delay functions in the code, as they can introduce latency. 61 | - Ensure that the BLE connection interval is set appropriately for low-latency communication. 62 | 63 | ## Pin Functionality Issues: 64 | Symptom: Certain GPIO pins not working as intended for button inputs or analog readings. 65 | 66 | Solution: 67 | - Consult the ESP32 pinout to ensure that the selected pins support the desired functions. 68 | - Avoid using pins that are reserved or have dual functions that might interfere with input readings. 69 | - Test the pins independently to confirm their functionality before integrating them into the gamepad setup. 70 | - Pinouts: 71 | - [ESP32](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html) 72 | - [ESP32-C3](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/gpio.html) 73 | - [ESP32-S3](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/gpio.html) 74 | 75 | ## Device Name: 76 | Symptom: Name in testing software is generic such as '8 axis 16 button device with hat switch'. 77 | 78 | Solution: **Linux**: 79 | - Please contribute method if you know how 80 | 81 | Solution: **Windows**: You need to change a registry entry if you want that name changed 82 | 83 | **Custom display name by means of the Windows registry editor (Windows only)** 84 | 85 | - Open the "registry editor" (regedit.exe) with user privileges (not administrator). 86 | - Navigate to the following folder (registry key): 87 | HKEY_CURRENT_USER\System\CurrentControlSet\Control\MediaProperties\PrivateProperties\Joystick\OEM 88 | - Inside that folder, locate the subfolder (registry key) for your device. 89 | - Those keys are named using this pattern: "VID -hex number- &PID -hex number- ", where "VID" means "vendor identifier" and "PID" means "product identifier". Together, they are a (plug-and-play) hardware identifier (ID). - The default hardware ID set in this project is "VID_E502&PID_BBAB". 90 | - Double-click (edit) the value "OEMName". 91 | - Set a custom display name and hit "enter". 92 | - NOTE: Info taken from [here](https://github.com/afpineda/OpenSourceSimWheelESP32/blob/main/doc/RenameDeviceWin_en.md#why-all-this-mess) 93 | -------------------------------------------------------------------------------- /examples/CharacteristicsConfiguration/CharacteristicsConfiguration.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Sets BLE characteristic options 3 | Use BLE Scanner etc on Android to see them 4 | 5 | Also shows how to set transmit power during initial configuration, 6 | or at any stage whilst running by using bleGamepad.setTXPowerLevel(int8_t) 7 | 8 | The only valid values are: -12, -9, -6, -3, 0, 3, 6 and 9 9 | Values correlate to dbm 10 | 11 | You can get the currently set TX power level by calling bleGamepad.setTXPowerLevel() 12 | 13 | */ 14 | 15 | #include 16 | #include 17 | 18 | int8_t txPowerLevel = 3; 19 | 20 | BleGamepad bleGamepad("Custom Contoller Name", "lemmingDev", 100); // Set custom device name, manufacturer and initial battery level 21 | BleGamepadConfiguration bleGamepadConfig; // Create a BleGamepadConfiguration object to store all of the options 22 | 23 | void setup() 24 | { 25 | Serial.begin(115200); 26 | Serial.println("Starting BLE work!"); 27 | bleGamepadConfig.setAutoReport(false); 28 | bleGamepadConfig.setControllerType(CONTROLLER_TYPE_GAMEPAD); // CONTROLLER_TYPE_JOYSTICK, CONTROLLER_TYPE_GAMEPAD (DEFAULT), CONTROLLER_TYPE_MULTI_AXIS 29 | bleGamepadConfig.setVid(0xe502); 30 | bleGamepadConfig.setPid(0xabcd); 31 | bleGamepadConfig.setTXPowerLevel(txPowerLevel); // Defaults to 9 if not set. (Range: -12 to 9 dBm) 32 | 33 | bleGamepadConfig.setModelNumber("1.0"); 34 | bleGamepadConfig.setSoftwareRevision("Software Rev 1"); 35 | bleGamepadConfig.setSerialNumber("9876543210"); 36 | bleGamepadConfig.setFirmwareRevision("2.0"); 37 | bleGamepadConfig.setHardwareRevision("1.7"); 38 | 39 | // Some non-Windows operating systems and web based gamepad testers don't like min axis set below 0, so 0 is set by default 40 | //bleGamepadConfig.setAxesMin(0x8001); // -32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 41 | bleGamepadConfig.setAxesMin(0x0000); // 0 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 42 | bleGamepadConfig.setAxesMax(0x7FFF); // 32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 43 | 44 | bleGamepad.begin(&bleGamepadConfig); // Begin gamepad with configuration options 45 | 46 | // Change power level to 6 47 | bleGamepad.setTXPowerLevel(6); // The default of 9 (strongest transmit power level) will be used if not set 48 | 49 | } 50 | 51 | void loop() 52 | { 53 | if (bleGamepad.isConnected()) 54 | { 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /examples/DrivingControllerTest/DrivingControllerTest.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Driving controller test 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #define numOfButtons 10 9 | #define numOfHatSwitches 0 10 | #define enableX false 11 | #define enableY false 12 | #define enableZ false 13 | #define enableRX false 14 | #define enableRY false 15 | #define enableRZ false 16 | #define enableSlider1 false 17 | #define enableSlider2 false 18 | #define enableRudder false 19 | #define enableThrottle false 20 | #define enableAccelerator true 21 | #define enableBrake true 22 | #define enableSteering true 23 | 24 | //int16_t simMin = 0x8000; // -32767 --> Some non-Windows operating systems and web based gamepad testers don't like min axis set below 0, so 0 is set by default 25 | //int16_t axesCenter = 0x00; 26 | int16_t simMin = 0x00; // Set simulation minimum axes to zero. 27 | int16_t axesCenter = 0x3FFF; 28 | int16_t simMax = 0x7FFF; // 32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 29 | int16_t stepAmount = 0xFF; 30 | uint16_t delayAmount = 25; 31 | 32 | BleGamepad bleGamepad("BLE Driving Controller", "lemmingDev", 100); 33 | 34 | void setup() 35 | { 36 | Serial.begin(115200); 37 | Serial.println("Starting BLE work!"); 38 | 39 | // Setup controller with 10 buttons, accelerator, brake and steering 40 | BleGamepadConfiguration bleGamepadConfig; 41 | bleGamepadConfig.setAutoReport(false); 42 | bleGamepadConfig.setControllerType(CONTROLLER_TYPE_GAMEPAD); // CONTROLLER_TYPE_JOYSTICK, CONTROLLER_TYPE_GAMEPAD (DEFAULT), CONTROLLER_TYPE_MULTI_AXIS 43 | bleGamepadConfig.setButtonCount(numOfButtons); 44 | bleGamepadConfig.setWhichAxes(enableX, enableY, enableZ, enableRX, enableRY, enableRZ, enableSlider1, enableSlider2); // Can also be done per-axis individually. All are true by default 45 | bleGamepadConfig.setWhichSimulationControls(enableRudder, enableThrottle, enableAccelerator, enableBrake, enableSteering); // Can also be done per-control individually. All are false by default 46 | bleGamepadConfig.setHatSwitchCount(numOfHatSwitches); // 1 by default 47 | bleGamepadConfig.setSimulationMin(simMin); 48 | bleGamepadConfig.setSimulationMax(simMax); 49 | 50 | bleGamepad.begin(&bleGamepadConfig); 51 | // changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again 52 | 53 | // Set steering to center 54 | bleGamepad.setSteering(axesCenter); 55 | 56 | // Set brake and accelerator to min 57 | bleGamepad.setBrake(simMin); 58 | bleGamepad.setAccelerator(simMax); 59 | 60 | bleGamepad.sendReport(); 61 | } 62 | 63 | void loop() 64 | { 65 | if (bleGamepad.isConnected()) 66 | { 67 | Serial.println("Press all buttons one by one"); 68 | for (int i = 1; i <= numOfButtons; i += 1) 69 | { 70 | bleGamepad.press(i); 71 | bleGamepad.sendReport(); 72 | delay(100); 73 | bleGamepad.release(i); 74 | bleGamepad.sendReport(); 75 | delay(25); 76 | } 77 | 78 | Serial.println("Move steering from center to min"); 79 | for (int i = axesCenter; i > simMin; i -= stepAmount) 80 | { 81 | bleGamepad.setSteering(i); 82 | bleGamepad.sendReport(); 83 | delay(delayAmount); 84 | } 85 | 86 | Serial.println("Move steering from min to max"); 87 | for (int i = simMin; i < simMax; i += stepAmount) 88 | { 89 | bleGamepad.setSteering(i); 90 | bleGamepad.sendReport(); 91 | delay(delayAmount); 92 | } 93 | 94 | Serial.println("Move steering from max to center"); 95 | for (int i = simMax; i > axesCenter; i -= stepAmount) 96 | { 97 | bleGamepad.setSteering(i); 98 | bleGamepad.sendReport(); 99 | delay(delayAmount); 100 | } 101 | bleGamepad.setSteering(axesCenter); 102 | bleGamepad.sendReport(); 103 | 104 | Serial.println("Move accelerator from min to max"); 105 | // Axis is reversed, so swap min <--> max 106 | for (int i = simMax; i > simMin; i -= stepAmount) 107 | { 108 | bleGamepad.setAccelerator(i); 109 | bleGamepad.sendReport(); 110 | delay(delayAmount); 111 | } 112 | 113 | Serial.println("Move accelerator from max to min"); 114 | // Axis is reversed, so swap min <--> max 115 | for (int i = simMin; i < simMax; i += stepAmount) 116 | { 117 | bleGamepad.setAccelerator(i); 118 | bleGamepad.sendReport(); 119 | delay(delayAmount); 120 | } 121 | bleGamepad.setAccelerator(simMax); 122 | bleGamepad.sendReport(); 123 | 124 | Serial.println("Move brake from min to max"); 125 | for (int i = simMin; i < simMax; i += stepAmount) 126 | { 127 | bleGamepad.setBrake(i); 128 | bleGamepad.sendReport(); 129 | delay(delayAmount); 130 | } 131 | 132 | Serial.println("Move brake from max to min"); 133 | for (int i = simMax; i > simMin; i -= stepAmount) 134 | { 135 | bleGamepad.setBrake(i); 136 | bleGamepad.sendReport(); 137 | delay(delayAmount); 138 | } 139 | bleGamepad.setBrake(simMin); 140 | bleGamepad.sendReport(); 141 | } 142 | } -------------------------------------------------------------------------------- /examples/Fightstick/Fightstick.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This code programs an ESP32 as a BLE fightstick 3 | * 4 | * Before using, adjust the numOfButtons, buttonPins, physicalButtons, hatPins etc to suit your scenario 5 | * 6 | * If you're looking for a more compatible fighstick experience in games, try the xinput compatible fork at: 7 | * https://github.com/Mystfit/ESP32-BLE-CompositeHID 8 | * 9 | */ 10 | 11 | #include 12 | #include // https://github.com/lemmingDev/ESP32-BLE-Gamepad 13 | 14 | BleGamepad bleGamepad("BLE Fightstick", "lemmingDev", 100); 15 | 16 | #define numOfButtons 11 17 | #define numOfHats 1 // Maximum 4 hat switches supported 18 | 19 | byte previousButtonStates[numOfButtons]; 20 | byte currentButtonStates[numOfButtons]; 21 | byte buttonPins[numOfButtons] = {13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25}; 22 | byte physicalButtons[numOfButtons] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; 23 | 24 | byte previousHatStates[numOfHats * 4]; 25 | byte currentHatStates[numOfHats * 4]; 26 | byte hatPins[numOfHats * 4] = {26, 27, 32, 33}; // In order UP, LEFT, DOWN, RIGHT. 4 pins per hat switch (Eg. List 12 pins if there are 3 hat switches) 27 | 28 | void setup() 29 | { 30 | // Setup Buttons 31 | for (byte currentPinIndex = 0; currentPinIndex < numOfButtons; currentPinIndex++) 32 | { 33 | pinMode(buttonPins[currentPinIndex], INPUT_PULLUP); 34 | previousButtonStates[currentPinIndex] = HIGH; 35 | currentButtonStates[currentPinIndex] = HIGH; 36 | } 37 | 38 | // Setup Hat Switches 39 | for (byte currentPinIndex = 0; currentPinIndex < numOfHats * 4; currentPinIndex++) 40 | { 41 | pinMode(hatPins[currentPinIndex], INPUT_PULLUP); 42 | previousHatStates[currentPinIndex] = HIGH; 43 | currentHatStates[currentPinIndex] = HIGH; 44 | } 45 | 46 | BleGamepadConfiguration bleGamepadConfig; 47 | bleGamepadConfig.setAutoReport(false); 48 | bleGamepadConfig.setWhichAxes(0, 0, 0, 0, 0, 0, 0, 0); // Disable all axes 49 | bleGamepadConfig.setButtonCount(numOfButtons); 50 | bleGamepadConfig.setHatSwitchCount(numOfHats); 51 | bleGamepad.begin(&bleGamepadConfig); 52 | 53 | // changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again 54 | } 55 | 56 | void loop() 57 | { 58 | if (bleGamepad.isConnected()) 59 | { 60 | // Deal with buttons 61 | for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++) 62 | { 63 | currentButtonStates[currentIndex] = digitalRead(buttonPins[currentIndex]); 64 | 65 | if (currentButtonStates[currentIndex] != previousButtonStates[currentIndex]) 66 | { 67 | if (currentButtonStates[currentIndex] == LOW) 68 | { 69 | bleGamepad.press(physicalButtons[currentIndex]); 70 | } 71 | else 72 | { 73 | bleGamepad.release(physicalButtons[currentIndex]); 74 | } 75 | } 76 | } 77 | 78 | // Update hat switch pin states 79 | for (byte currentHatPinsIndex = 0; currentHatPinsIndex < numOfHats * 4; currentHatPinsIndex++) 80 | { 81 | currentHatStates[currentHatPinsIndex] = digitalRead(hatPins[currentHatPinsIndex]); 82 | } 83 | 84 | // Update hats 85 | signed char hatValues[4] = {0, 0, 0, 0}; 86 | 87 | for (byte currentHatIndex = 0; currentHatIndex < numOfHats; currentHatIndex++) 88 | { 89 | signed char hatValueToSend = 0; 90 | 91 | for (byte currentHatPin = 0; currentHatPin < 4; currentHatPin++) 92 | { 93 | // Check for direction 94 | if (currentHatStates[currentHatPin + currentHatIndex * 4] == LOW) 95 | { 96 | hatValueToSend = currentHatPin * 2 + 1; 97 | 98 | // Account for last diagonal 99 | if (currentHatPin == 0) 100 | { 101 | if (currentHatStates[currentHatIndex * 4 + 3] == LOW) 102 | { 103 | hatValueToSend = 8; 104 | break; 105 | } 106 | } 107 | 108 | // Account for first 3 diagonals 109 | if (currentHatPin < 3) 110 | { 111 | if (currentHatStates[currentHatPin + currentHatIndex * 4 + 1] == LOW) 112 | { 113 | hatValueToSend += 1; 114 | break; 115 | } 116 | } 117 | } 118 | } 119 | 120 | hatValues[currentHatIndex] = hatValueToSend; 121 | } 122 | 123 | // Set hat values 124 | bleGamepad.setHats(hatValues[0], hatValues[1], hatValues[2], hatValues[3]); 125 | 126 | // Update previous states to current states and send report 127 | // readable, but with compiler warning: 128 | // if (currentButtonStates != previousButtonStates || currentHatStates != previousHatStates) 129 | if ((memcmp((const void *)currentButtonStates, (const void *)previousButtonStates, sizeof(currentButtonStates)) != 0) && (memcmp((const void *)currentHatStates, (const void *)previousHatStates, sizeof(currentHatStates)) != 0)) 130 | { 131 | for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++) 132 | { 133 | previousButtonStates[currentIndex] = currentButtonStates[currentIndex]; 134 | } 135 | 136 | for (byte currentIndex = 0; currentIndex < numOfHats * 4; currentIndex++) 137 | { 138 | previousHatStates[currentIndex] = currentHatStates[currentIndex]; 139 | } 140 | 141 | bleGamepad.sendReport(); // Send a report if any of the button states or hat directions have changed 142 | } 143 | 144 | delay(10); // Reduce for less latency 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /examples/FlightControllerTest/FlightControllerTest.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Flight controller test 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #define numOfButtons 16 9 | #define numOfHatSwitches 0 10 | #define enableX true 11 | #define enableY true 12 | #define enableZ false 13 | #define enableRX false 14 | #define enableRY false 15 | #define enableRZ false 16 | #define enableSlider1 false 17 | #define enableSlider2 false 18 | #define enableRudder true 19 | #define enableThrottle true 20 | #define enableAccelerator false 21 | #define enableBrake true 22 | #define enableSteering false 23 | 24 | BleGamepad bleGamepad("BLE Flight Controller", "lemmingDev", 100); 25 | 26 | void setup() 27 | { 28 | Serial.begin(115200); 29 | Serial.println("Starting BLE work!"); 30 | 31 | // Setup controller with 16 buttons (plus start and select), accelerator, brake and steering 32 | BleGamepadConfiguration bleGamepadConfig; 33 | bleGamepadConfig.setAutoReport(false); 34 | bleGamepadConfig.setControllerType(CONTROLLER_TYPE_MULTI_AXIS); // CONTROLLER_TYPE_JOYSTICK, CONTROLLER_TYPE_GAMEPAD (DEFAULT), CONTROLLER_TYPE_MULTI_AXIS 35 | bleGamepadConfig.setButtonCount(numOfButtons); 36 | bleGamepadConfig.setIncludeStart(true); 37 | bleGamepadConfig.setIncludeSelect(true); 38 | bleGamepadConfig.setWhichAxes(enableX, enableY, enableZ, enableRX, enableRY, enableRZ, enableSlider1, enableSlider2); // Can also be done per-axis individually. All are true by default 39 | bleGamepadConfig.setWhichSimulationControls(enableRudder, enableThrottle, enableAccelerator, enableBrake, enableSteering); // Can also be done per-control individually. All are false by default 40 | bleGamepadConfig.setHatSwitchCount(numOfHatSwitches); // 1 by default 41 | // Some non-Windows operating systems and web based gamepad testers don't like min axis set below 0, so 0 is set by default 42 | bleGamepadConfig.setAxesMin(0x8001); // -32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 43 | bleGamepadConfig.setAxesMax(0x7FFF); // 32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 44 | // Shows how simulation control min/max axes can be set independently of the other axes 45 | bleGamepadConfig.setSimulationMin(-255); // -255 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 46 | bleGamepadConfig.setSimulationMax(255); // 255 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 47 | bleGamepad.begin(&bleGamepadConfig); 48 | 49 | // changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again 50 | 51 | // Set throttle and rudder to min 52 | bleGamepad.setThrottle(-255); 53 | bleGamepad.setRudder(-255); 54 | 55 | // Set x and y axes to center 56 | bleGamepad.setX(0); 57 | bleGamepad.setY(0); 58 | } 59 | 60 | void loop() 61 | { 62 | if (bleGamepad.isConnected()) 63 | { 64 | Serial.println("Press all buttons one by one"); 65 | for (int i = 1; i <= numOfButtons; i += 1) 66 | { 67 | bleGamepad.press(i); 68 | bleGamepad.sendReport(); 69 | delay(100); 70 | bleGamepad.release(i); 71 | bleGamepad.sendReport(); 72 | delay(25); 73 | } 74 | 75 | Serial.println("Press start and select"); 76 | bleGamepad.pressSelect(); 77 | bleGamepad.sendReport(); 78 | delay(100); 79 | bleGamepad.releaseSelect(); 80 | bleGamepad.sendReport(); 81 | delay(100); 82 | 83 | bleGamepad.pressStart(); 84 | bleGamepad.sendReport(); 85 | delay(100); 86 | bleGamepad.releaseStart(); 87 | bleGamepad.sendReport(); 88 | delay(100); 89 | 90 | Serial.println("Move x axis from center to max"); 91 | for (int i = 0; i > -32767; i -= 256) 92 | { 93 | bleGamepad.setX(i); 94 | bleGamepad.sendReport(); 95 | delay(10); 96 | } 97 | 98 | Serial.println("Move x axis from min to max"); 99 | for (int i = -32767; i < 32767; i += 256) 100 | { 101 | bleGamepad.setX(i); 102 | bleGamepad.sendReport(); 103 | delay(10); 104 | } 105 | 106 | Serial.println("Move x axis from max to center"); 107 | for (int i = 32767; i > 0; i -= 256) 108 | { 109 | bleGamepad.setX(i); 110 | bleGamepad.sendReport(); 111 | delay(10); 112 | } 113 | bleGamepad.setX(0); 114 | bleGamepad.sendReport(); 115 | 116 | Serial.println("Move y axis from center to max"); 117 | for (int i = 0; i > -32767; i -= 256) 118 | { 119 | bleGamepad.setY(i); 120 | bleGamepad.sendReport(); 121 | delay(10); 122 | } 123 | 124 | Serial.println("Move y axis from min to max"); 125 | for (int i = -32767; i < 32767; i += 256) 126 | { 127 | bleGamepad.setY(i); 128 | bleGamepad.sendReport(); 129 | delay(10); 130 | } 131 | 132 | Serial.println("Move y axis from max to center"); 133 | for (int i = 32767; i > 0; i -= 256) 134 | { 135 | bleGamepad.setY(i); 136 | bleGamepad.sendReport(); 137 | delay(10); 138 | } 139 | bleGamepad.setY(0); 140 | bleGamepad.sendReport(); 141 | 142 | Serial.println("Move rudder from min to max"); 143 | // for(int i = 255 ; i > -255 ; i -= 2) //Use this for loop setup instead if rudder is reversed 144 | for (int i = -255; i < 255; i += 2) 145 | { 146 | bleGamepad.setRudder(i); 147 | bleGamepad.sendReport(); 148 | delay(10); 149 | } 150 | bleGamepad.setRudder(0); 151 | bleGamepad.sendReport(); 152 | 153 | Serial.println("Move throttle from min to max"); 154 | for (int i = -255; i < 255; i += 2) 155 | { 156 | bleGamepad.setThrottle(i); 157 | bleGamepad.sendReport(); 158 | delay(10); 159 | } 160 | bleGamepad.setThrottle(-255); 161 | bleGamepad.sendReport(); 162 | 163 | Serial.println("Move brake from min to max"); 164 | for (int i = -255; i < 255; i += 2) 165 | { 166 | bleGamepad.setBrake(i); 167 | bleGamepad.sendReport(); 168 | delay(10); 169 | } 170 | bleGamepad.setBrake(-255); 171 | bleGamepad.sendReport(); 172 | } 173 | } -------------------------------------------------------------------------------- /examples/ForcePairingMode/ForcePairingMode.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A simple sketch that, upon a button press, disconnects from an aggressive client 3 | * until a new client connects to the gamepad 4 | * 5 | * If it finds another client that's already paired, it will connect to that 6 | * 7 | * If it doesn't find another paired device, it will allow you to pair a new one 8 | * 9 | * If you want a more permanent solution, instead use bleGamepad.deleteBond() 10 | * which will delete the bond for the currently connected client and allow other 11 | * clients to connect to it 12 | * 13 | * Use bleGamepad.deletAllBonds() to delete all bonds from the gamepad 14 | * 15 | * After deleting bonds, it is best to unpair them from the client device such 16 | * as your phone or PC otherwise the gamepad may briefly connect while searching 17 | * 18 | * The deleteBond and deletAllBonds functions can optionally reset the gamepad with 19 | * deletAllBonds(true) or deleteBond(true), although it shouldn't be needed 20 | * as the advertising should now start again after a client is disconnected 21 | * 22 | * They all return a boolean for success or failure if wanted 23 | */ 24 | 25 | #include 26 | #include // https://github.com/lemmingDev/ESP32-BLE-Gamepad 27 | 28 | #define DISCONNECTPIN 0 // Pin disconnect button is attached to 29 | 30 | BleGamepad bleGamepad; 31 | 32 | void setup() 33 | { 34 | Serial.begin(115200); 35 | pinMode(DISCONNECTPIN, INPUT_PULLUP); 36 | bleGamepad.begin(); 37 | } 38 | 39 | void loop() 40 | { 41 | if (bleGamepad.isConnected()) 42 | { 43 | // Enter forced pairing mode 44 | // It repeatedly disconnects from currently connected device until a new device is paired 45 | // Returns true if a new device is connected 46 | // For now, enters an endless loop if no new device found 47 | // Simply reset device to have it revert to previous behaviour 48 | if (digitalRead(DISCONNECTPIN) == LOW) // On my board, pin 0 is LOW for pressed 49 | { 50 | bool pairingResult = bleGamepad.enterPairingMode(); 51 | 52 | if(pairingResult) 53 | { 54 | Serial.println("New device paired successfully"); 55 | } 56 | else 57 | { 58 | Serial.println("No new device paired"); 59 | } 60 | } 61 | } 62 | else 63 | { 64 | Serial.println("No device connected"); 65 | delay(1000); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /examples/Gamepad/Gamepad.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This example turns the ESP32 into a Bluetooth LE gamepad that presses buttons and moves axis 3 | * 4 | * At the moment we are using the default settings, but they can be canged using a BleGamepadConfig instance as parameter for the begin function. 5 | * 6 | * Possible buttons are: 7 | * BUTTON_1 through to BUTTON_16 8 | * (16 buttons by default. Library can be configured to use up to 128) 9 | * 10 | * Possible DPAD/HAT switch position values are: 11 | * DPAD_CENTERED, DPAD_UP, DPAD_UP_RIGHT, DPAD_RIGHT, DPAD_DOWN_RIGHT, DPAD_DOWN, DPAD_DOWN_LEFT, DPAD_LEFT, DPAD_UP_LEFT 12 | * (or HAT_CENTERED, HAT_UP etc) 13 | * 14 | * bleGamepad.setAxes sets all axes at once. There are a few: 15 | * (x axis, y axis, z axis, rx axis, ry axis, rz axis, slider 1, slider 2) 16 | * 17 | * Alternatively, bleGamepad.setHIDAxes sets all axes at once. in the order of: 18 | * (x axis, y axis, z axis, rZ axis, rX axis, rY axis, slider 1, slider 2) <- order HID report is actually given in 19 | * 20 | * Library can also be configured to support up to 5 simulation controls 21 | * (rudder, throttle, accelerator, brake, steering), but they are not enabled by default. 22 | * 23 | * Library can also be configured to support different function buttons 24 | * (start, select, menu, home, back, volume increase, volume decrease, volume mute) 25 | * start and select are enabled by default 26 | */ 27 | 28 | #include 29 | #include 30 | 31 | BleGamepad bleGamepad; 32 | 33 | void setup() 34 | { 35 | Serial.begin(115200); 36 | Serial.println("Starting BLE work!"); 37 | bleGamepad.begin(); 38 | // The default bleGamepad.begin() above enables 16 buttons, all axes, one hat, and no simulation controls or special buttons 39 | } 40 | 41 | void loop() 42 | { 43 | if (bleGamepad.isConnected()) 44 | { 45 | Serial.println("Press buttons 5, 16 and start. Move all enabled axes to max. Set DPAD (hat 1) to down right."); 46 | bleGamepad.press(BUTTON_5); 47 | bleGamepad.press(BUTTON_16); 48 | bleGamepad.pressStart(); 49 | bleGamepad.setAxes(32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767); //(X, Y, Z, RX, RY, RZ) 50 | //bleGamepad.setHIDAxes(32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767); //(X, Y, Z, RZ, RX, RY) 51 | bleGamepad.setHat1(HAT_DOWN_RIGHT); 52 | // All axes, sliders, hats etc can also be set independently. See the IndividualAxes.ino example 53 | delay(500); 54 | 55 | Serial.println("Release button 5 and start. Move all axes to min. Set DPAD (hat 1) to centred."); 56 | bleGamepad.release(BUTTON_5); 57 | bleGamepad.releaseStart(); 58 | bleGamepad.setHat1(HAT_CENTERED); 59 | bleGamepad.setAxes(0, 0, 0, 0, 0, 0, 0, 0); //(X, Y, Z, RX, RY, RZ) 60 | //bleGamepad.setHIDAxes(0, 0, 0, 0, 0, 0, 0, 0); //(X, Y, Z, RZ, RX, RY) 61 | delay(500); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /examples/GetPeerInfo/GetPeerInfo.ino: -------------------------------------------------------------------------------- 1 | /* 2 | A simple sketch that, upon a button press, shows peer info 3 | 4 | bleGamepad.getPeerInfo(); returns type NimBLEConnInfo 5 | From there, you have access to all info here https://h2zero.github.io/NimBLE-Arduino/class_nim_b_l_e_conn_info.html 6 | 7 | If you just need the MAC address, you can instead call bleGamepad.getAddress() which returns type NimBLEAddress 8 | or bleGamepad.getStringAddress() to get it directly as a string 9 | 10 | This sketch also shows how to access the information used to configure the BLE device such as vid, pid, model number and software revision etc 11 | See CharacteristicsConfiguration.ino example to see how to set them 12 | */ 13 | 14 | #include 15 | #include // https://github.com/lemmingDev/ESP32-BLE-Gamepad 16 | 17 | #define PEER_INFO_PIN 0 // Pin button is attached to 18 | 19 | BleGamepad bleGamepad; 20 | 21 | void setup() 22 | { 23 | Serial.begin(115200); 24 | pinMode(PEER_INFO_PIN, INPUT_PULLUP); 25 | bleGamepad.begin(); 26 | } 27 | 28 | void loop() 29 | { 30 | if (bleGamepad.isConnected()) 31 | { 32 | 33 | if (digitalRead(PEER_INFO_PIN) == LOW) // On my board, pin 0 is LOW for pressed 34 | { 35 | Serial.println("\n----- OUPTPUT PEER INFORMATION -----\n"); 36 | 37 | // Get the HEX address as a string; 38 | Serial.println(bleGamepad.getStringAddress()); 39 | 40 | // Get the HEX address as an NimBLEAddress instance 41 | NimBLEAddress bleAddress = bleGamepad.getAddress(); 42 | Serial.println(bleAddress.toString().c_str()); 43 | 44 | // Get values directly from an NimBLEConnInfo instance 45 | NimBLEConnInfo peerInfo = bleGamepad.getPeerInfo(); 46 | 47 | Serial.println(peerInfo.getAddress().toString().c_str()); // NimBLEAddress 48 | Serial.println(peerInfo.getIdAddress().toString().c_str()); // NimBLEAddress 49 | Serial.println(peerInfo.getConnHandle()); // uint16_t 50 | Serial.println(peerInfo.getConnInterval()); // uint16_t 51 | Serial.println(peerInfo.getConnTimeout()); // uint16_t 52 | Serial.println(peerInfo.getConnLatency()); // uint16_t 53 | Serial.println(peerInfo.getMTU()); // uint16_t 54 | Serial.println(peerInfo.isMaster()); // bool 55 | Serial.println(peerInfo.isSlave()); // bool 56 | Serial.println(peerInfo.isBonded()); // bool 57 | Serial.println(peerInfo.isEncrypted()); // bool 58 | Serial.println(peerInfo.isAuthenticated()); // bool 59 | Serial.println(peerInfo.getSecKeySize()); // uint8_t 60 | 61 | Serial.println("\n----- OUPTPUT CONFIGURATION INFORMATION -----\n"); 62 | Serial.println(bleGamepad.getDeviceName()); 63 | Serial.println(bleGamepad.getDeviceManufacturer()); 64 | Serial.println(bleGamepad.configuration.getModelNumber()); 65 | Serial.println(bleGamepad.configuration.getSoftwareRevision()); 66 | Serial.println(bleGamepad.configuration.getSerialNumber()); 67 | Serial.println(bleGamepad.configuration.getFirmwareRevision()); 68 | Serial.println(bleGamepad.configuration.getHardwareRevision()); 69 | Serial.println(bleGamepad.configuration.getVid(), HEX); 70 | Serial.println(bleGamepad.configuration.getPid(), HEX); 71 | Serial.println(bleGamepad.configuration.getGuidVersion()); 72 | Serial.println(bleGamepad.configuration.getTXPowerLevel()); 73 | Serial.println(); 74 | delay(1000); 75 | } 76 | } 77 | else 78 | { 79 | Serial.println("No device connected"); 80 | delay(1000); 81 | } 82 | } -------------------------------------------------------------------------------- /examples/IndividualAxes/IndividualAxes.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This example turns the ESP32 into a Bluetooth LE gamepad that presses buttons and moves axis 3 | * 4 | * Possible buttons are: 5 | * BUTTON_1 through to BUTTON_128 (Windows gamepad tester only visualises the first 32) 6 | ^ Use http://www.planetpointy.co.uk/joystick-test-application/ to visualise all of them 7 | * Whenever you adjust the amount of buttons/axes etc, make sure you unpair and repair the BLE device 8 | * 9 | * Possible DPAD/HAT switch position values are: 10 | * DPAD_CENTERED, DPAD_UP, DPAD_UP_RIGHT, DPAD_RIGHT, DPAD_DOWN_RIGHT, DPAD_DOWN, DPAD_DOWN_LEFT, DPAD_LEFT, DPAD_UP_LEFT 11 | * 12 | * bleGamepad.setAxes takes the following int16_t parameters for the Left/Right Thumb X/Y, Left/Right Triggers plus slider1 and slider2: 13 | * (Left Thumb X, Left Thumb Y, Right Thumb X, Right Thumb Y, Left Trigger, Right Trigger, Slider 1, Slider 2) (x, y, z, rx, ry, rz) 14 | * 15 | * bleGamepad.setHIDAxes instead takes them in a slightly different order (x, y, z, rz, rx, ry) 16 | * 17 | * bleGamepad.setLeftThumb (or setRightThumb) takes 2 int16_t parameters for x and y axes (or z and rZ axes) 18 | * bleGamepad.setRightThumbAndroid takes 2 int16_t parameters for z and rx axes 19 | * 20 | * bleGamepad.setLeftTrigger (or setRightTrigger) takes 1 int16_t parameter for rX axis (or rY axis) 21 | * 22 | * bleGamepad.setSlider1 (or setSlider2) takes 1 int16_t parameter for slider 1 (or slider 2) 23 | * 24 | * bleGamepad.setHat1 takes a hat position as above (or 0 = centered and 1~8 are the 8 possible directions) 25 | * 26 | * setHats, setTriggers and setSliders functions are also available for setting all hats/triggers/sliders at once 27 | * 28 | * The example shows that you can set axes/hats independantly, or together. 29 | * 30 | * It also shows that you can disable the autoReport feature (enabled by default), and manually call the sendReport() function when wanted 31 | * 32 | */ 33 | 34 | #include 35 | #include 36 | 37 | BleGamepad bleGamepad; 38 | 39 | void setup() 40 | { 41 | Serial.begin(115200); 42 | Serial.println("Starting BLE work!"); 43 | BleGamepadConfiguration bleGamepadConfig; 44 | bleGamepadConfig.setAutoReport(false); // This is true by default 45 | bleGamepadConfig.setButtonCount(128); 46 | bleGamepadConfig.setHatSwitchCount(2); 47 | bleGamepad.begin(&bleGamepadConfig); // Creates a gamepad with 128 buttons, 2 hat switches and x, y, z, rZ, rX, rY and 2 sliders (no simulation controls enabled by default) 48 | 49 | // Changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again 50 | } 51 | 52 | void loop() 53 | { 54 | if (bleGamepad.isConnected()) 55 | { 56 | Serial.println("Press buttons 1, 32, 64 and 128. Set hat 1 to down right and hat 2 to up left"); 57 | 58 | // Press buttons 5, 32, 64 and 128 59 | bleGamepad.press(BUTTON_5); 60 | bleGamepad.press(BUTTON_32); 61 | bleGamepad.press(BUTTON_64); 62 | bleGamepad.press(BUTTON_128); 63 | 64 | // Move all axes to max. 65 | bleGamepad.setLeftThumb(32767, 32767); // or bleGamepad.setX(32767); and bleGamepad.setY(32767); 66 | bleGamepad.setRightThumb(32767, 32767); // or bleGamepad.setZ(32767); and bleGamepad.setRZ(32767); 67 | bleGamepad.setLeftTrigger(32767); // or bleGamepad.setRX(32767); 68 | bleGamepad.setRightTrigger(32767); // or bleGamepad.setRY(32767); 69 | bleGamepad.setSlider1(32767); 70 | bleGamepad.setSlider2(32767); 71 | 72 | // Set hat 1 to down right and hat 2 to up left (hats are otherwise centered by default) 73 | bleGamepad.setHat1(DPAD_DOWN_RIGHT); // or bleGamepad.setHat1(HAT_DOWN_RIGHT); 74 | bleGamepad.setHat2(DPAD_UP_LEFT); // or bleGamepad.setHat2(HAT_UP_LEFT); 75 | // Or bleGamepad.setHats(DPAD_DOWN_RIGHT, DPAD_UP_LEFT); 76 | 77 | // Send the gamepad report 78 | bleGamepad.sendReport(); 79 | delay(500); 80 | 81 | Serial.println("Release button 5 and 64. Move all axes to min. Set hat 1 and 2 to centred."); 82 | bleGamepad.release(BUTTON_5); 83 | bleGamepad.release(BUTTON_64); 84 | bleGamepad.setAxes(0, 0, 0, 0, 0, 0, 0, 0); 85 | bleGamepad.setHats(DPAD_CENTERED, HAT_CENTERED); 86 | bleGamepad.sendReport(); 87 | delay(500); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /examples/Keypad4x4/Keypad4x4.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Shows how to use a 4 x 4 keypad, commonly seen in Arduino starter kits, with the library 3 | * https://www.aliexpress.com/w/wholesale-Arduino-4*4-Matrix-Keyboard.html 4 | * It maps the 16 buttons to the first 16 buttons of the controller 5 | * Only certain combinations work for multiple presses over 2 buttons 6 | */ 7 | 8 | #include 9 | #include // https://github.com/Chris--A/Keypad 10 | #include // https://github.com/lemmingDev/ESP32-BLE-Gamepad 11 | 12 | BleGamepad bleGamepad("ESP32 Keypad", "lemmingDev", 100); // Shows how you can customise the device name, manufacturer name and initial battery level 13 | 14 | #define ROWS 4 15 | #define COLS 4 16 | uint8_t rowPins[ROWS] = {2, 13, 14, 25}; // ESP32 pins used for rows --> adjust to suit --> Pinout on board: R1, R2, R3, R4 17 | uint8_t colPins[COLS] = {4, 23, 21, 22}; // ESP32 pins used for columns --> adjust to suit --> Pinout on board: Q1, Q2, Q3, Q4 18 | uint8_t keymap[ROWS][COLS] = 19 | { 20 | {1, 2, 3, 4}, // Buttons 1, 2, 3, 4 --> Used for calulating the bitmask for sending to the library 21 | {5, 6, 7, 8}, // Buttons 5, 6, 7, 8 --> Adjust to suit which buttons you want the library to send 22 | {9, 10, 11, 12}, // Buttons 9, 10, 11, 12 --> 23 | {13, 14, 15, 16} // Buttons 13, 14, 15, 16 --> Eg. The value 12 in the array refers to button 12 24 | }; 25 | 26 | Keypad customKeypad = Keypad(makeKeymap(keymap), rowPins, colPins, ROWS, COLS); 27 | 28 | void setup() 29 | { 30 | BleGamepadConfiguration bleGamepadConfig; 31 | bleGamepadConfig.setAutoReport(false); // Disable auto reports --> You then need to force HID updates with bleGamepad.sendReport() 32 | bleGamepad.begin(&bleGamepadConfig); // Begin library with set values 33 | 34 | // changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again 35 | } 36 | 37 | void loop() 38 | { 39 | KeypadUpdate(); 40 | delay(10); 41 | } 42 | 43 | void KeypadUpdate() 44 | { 45 | customKeypad.getKeys(); 46 | 47 | for (int i = 0; i < LIST_MAX; i++) // Scan the whole key list. //LIST_MAX is provided by the Keypad library and gives the number of buttons of the Keypad instance 48 | { 49 | if (customKeypad.key[i].stateChanged) // Only find keys that have changed state. 50 | { 51 | uint8_t keystate = customKeypad.key[i].kstate; 52 | 53 | if (bleGamepad.isConnected()) 54 | { 55 | if (keystate == PRESSED) 56 | { 57 | bleGamepad.press(customKeypad.key[i].kchar); 58 | } // Press or release button based on the current state 59 | if (keystate == RELEASED) 60 | { 61 | bleGamepad.release(customKeypad.key[i].kchar); 62 | } 63 | 64 | bleGamepad.sendReport(); // Send the HID report after values for all button states are updated, and at least one button state had changed 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/MotionController/MotionController.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Motion controller test 3 | 4 | USB HID specification can only support 8 axes, plus hat (shows as 9th axis in online testers), 5 | so Please don't add more than that 6 | 7 | So, if you have left thumbstick (X, Y -> 2 axes), then add gyroscope (Rx, Ry, Rz -> 3 axes) and 8 | accelerometer (rX, rY, rZ -> 3 axes), then you're already at 8 9 | 10 | Windows joy.cpl will show the gyroscope data as X Rotation, Y Rotation and Z Rotation 11 | Windows joy.cpl will not show the accelerometer data, but you can see it using online 12 | tools such as https://hardwaretester.com/gamepad 13 | 14 | This HID descriptor uses the correct HID usage IDs for gyroscope (33, 34, 35 -> Rotation -> Rx, Ry, Rz) and 15 | accelerometer (40, 41, 42 -> Vector -> Vx, Vy, Vz) 16 | https://www.usb.org/sites/default/files/hut1_6.pdf 17 | 18 | Unfortunately, Windows and other OSs don't usually know exactly what their intent is and may not 19 | map them exactly how you think 20 | 21 | Controllers such as PS3/4 Dual Shock use custom HID reports and drivers to get around this 22 | 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | #define numOfButtons 28 29 | #define numOfHatSwitches 0 30 | #define enableX false 31 | #define enableY false 32 | #define enableZ false 33 | #define enableRX false 34 | #define enableRY false 35 | #define enableRZ false 36 | #define enableSlider1 false 37 | #define enableSlider2 false 38 | 39 | int16_t motMin = 0x8000; // -32767 --> Some non-Windows operating systems and web based gamepad testers don't like min axis set below 0, so 0 is set by default 40 | int16_t motCenter = 0x00; 41 | //int16_t motMin = 0x00; // Set motionulation minimum axes to zero. 42 | //int16_t motCenter = 0x3FFF; 43 | int16_t motMax = 0x7FFF; // 32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 44 | int16_t stepAmount = 0xFF; // 255 45 | uint16_t delayAmount = 25; 46 | 47 | BleGamepad bleGamepad("BLE Motion Controller", "lemmingDev", 100); 48 | 49 | void setup() 50 | { 51 | Serial.begin(115200); 52 | Serial.println("Starting BLE work!"); 53 | 54 | // Setup controller with 10 buttons, gyroscope and accelerometer 55 | BleGamepadConfiguration bleGamepadConfig; 56 | bleGamepadConfig.setAutoReport(false); 57 | bleGamepadConfig.setControllerType(CONTROLLER_TYPE_MULTI_AXIS); // CONTROLLER_TYPE_JOYSTICK, CONTROLLER_TYPE_GAMEPAD (DEFAULT), CONTROLLER_TYPE_MULTI_AXIS 58 | bleGamepadConfig.setButtonCount(numOfButtons); 59 | bleGamepadConfig.setWhichAxes(enableX, enableY, enableZ, enableRX, enableRY, enableRZ, enableSlider1, enableSlider2); // Can also be done per-axis individually. All are true by default 60 | bleGamepadConfig.setHatSwitchCount(numOfHatSwitches); // 1 by default 61 | bleGamepadConfig.setIncludeGyroscope(true); 62 | bleGamepadConfig.setIncludeAccelerometer(true); 63 | bleGamepadConfig.setMotionMin(motMin); 64 | bleGamepadConfig.setMotionMax(motMax); 65 | 66 | bleGamepad.begin(&bleGamepadConfig); 67 | // Changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again 68 | 69 | // Set gyroscope and accelerometer to center (first 3 are gyroscope, last 3 are accelerometer) 70 | bleGamepad.setMotionControls(motCenter, motCenter, motCenter, motCenter, motCenter, motCenter); 71 | 72 | bleGamepad.sendReport(); 73 | } 74 | 75 | void loop() 76 | { 77 | if (bleGamepad.isConnected()) 78 | { 79 | // // BUTTONS 80 | // Serial.println("Press all buttons one by one"); 81 | // for (int i = 1; i <= numOfButtons; i += 1) 82 | // { 83 | // bleGamepad.press(i); 84 | // bleGamepad.sendReport(); 85 | // delay(100); 86 | // bleGamepad.release(i); 87 | // bleGamepad.sendReport(); 88 | // delay(25); 89 | // } 90 | 91 | // GYROSCOPE 92 | Serial.println("Move all 3 gyroscope axes from center to min"); 93 | for (int i = motCenter; i > motMin; i -= stepAmount) 94 | { 95 | bleGamepad.setGyroscope(i, i, i); 96 | bleGamepad.sendReport(); 97 | delay(delayAmount); 98 | } 99 | 100 | Serial.println("Move all 3 gyroscope axes from min to max"); 101 | for (int i = motMin; i < motMax; i += stepAmount) 102 | { 103 | bleGamepad.setGyroscope(i, i, i); 104 | bleGamepad.sendReport(); 105 | delay(delayAmount); 106 | } 107 | 108 | Serial.println("Move all 3 gyroscope axes from max to center"); 109 | for (int i = motMax; i > motCenter; i -= stepAmount) 110 | { 111 | bleGamepad.setGyroscope(i, i, i); 112 | bleGamepad.sendReport(); 113 | delay(delayAmount); 114 | } 115 | bleGamepad.setGyroscope(motCenter); 116 | bleGamepad.sendReport(); 117 | 118 | // ACCELEROMETER 119 | Serial.println("Move all 3 accelerometer axes from center to min"); 120 | for (int i = motCenter; i > motMin; i -= stepAmount) 121 | { 122 | bleGamepad.setAccelerometer(i, i, i); 123 | bleGamepad.sendReport(); 124 | delay(delayAmount); 125 | } 126 | 127 | Serial.println("Move all 3 accelerometer axes from min to max"); 128 | for (int i = motMin; i < motMax; i += stepAmount) 129 | { 130 | bleGamepad.setAccelerometer(i, i, i); 131 | bleGamepad.sendReport(); 132 | delay(delayAmount); 133 | } 134 | 135 | Serial.println("Move all 3 accelerometer axes from max to center"); 136 | for (int i = motMax; i > motCenter; i -= stepAmount) 137 | { 138 | bleGamepad.setAccelerometer(i, i, i); 139 | bleGamepad.sendReport(); 140 | delay(delayAmount); 141 | } 142 | bleGamepad.setAccelerometer(motCenter); 143 | bleGamepad.sendReport(); 144 | } 145 | } -------------------------------------------------------------------------------- /examples/MultipleButtons/MultipleButtons.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This code programs a number of pins on an ESP32 as buttons on a BLE gamepad 3 | * 4 | * It uses arrays to cut down on code 5 | * 6 | * Before using, adjust the numOfButtons, buttonPins and physicalButtons to suit your senario 7 | * 8 | */ 9 | 10 | #include 11 | #include // https://github.com/lemmingDev/ESP32-BLE-Gamepad 12 | 13 | BleGamepad bleGamepad; 14 | 15 | #define numOfButtons 10 16 | 17 | byte previousButtonStates[numOfButtons]; 18 | byte currentButtonStates[numOfButtons]; 19 | byte buttonPins[numOfButtons] = {0, 35, 17, 18, 19, 23, 25, 26, 27, 32}; 20 | byte physicalButtons[numOfButtons] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 21 | 22 | void setup() 23 | { 24 | for (byte currentPinIndex = 0; currentPinIndex < numOfButtons; currentPinIndex++) 25 | { 26 | pinMode(buttonPins[currentPinIndex], INPUT_PULLUP); 27 | previousButtonStates[currentPinIndex] = HIGH; 28 | currentButtonStates[currentPinIndex] = HIGH; 29 | } 30 | 31 | BleGamepadConfiguration bleGamepadConfig; 32 | bleGamepadConfig.setAutoReport(false); 33 | bleGamepadConfig.setButtonCount(numOfButtons); 34 | bleGamepad.begin(&bleGamepadConfig); 35 | 36 | // changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again 37 | } 38 | 39 | void loop() 40 | { 41 | if (bleGamepad.isConnected()) 42 | { 43 | for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++) 44 | { 45 | currentButtonStates[currentIndex] = digitalRead(buttonPins[currentIndex]); 46 | 47 | if (currentButtonStates[currentIndex] != previousButtonStates[currentIndex]) 48 | { 49 | if (currentButtonStates[currentIndex] == LOW) 50 | { 51 | bleGamepad.press(physicalButtons[currentIndex]); 52 | } 53 | else 54 | { 55 | bleGamepad.release(physicalButtons[currentIndex]); 56 | } 57 | } 58 | } 59 | 60 | if (memcmp((const void *)currentButtonStates, (const void *)previousButtonStates, sizeof(currentButtonStates)) != 0) 61 | { 62 | for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++) 63 | { 64 | previousButtonStates[currentIndex] = currentButtonStates[currentIndex]; 65 | } 66 | 67 | bleGamepad.sendReport(); 68 | } 69 | 70 | delay(20); 71 | } 72 | } -------------------------------------------------------------------------------- /examples/MultipleButtonsAndHats/MultipleButtonsAndHats.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This code programs a number of pins on an ESP32 as buttons and hats on a BLE gamepad 3 | * 4 | * It uses arrays to cut down on code 5 | * 6 | * Before using, adjust the numOfButtons, buttonPins, physicalButtons, numOfHats and hatPins to suit your scenario 7 | * 8 | */ 9 | 10 | #include 11 | #include // https://github.com/lemmingDev/ESP32-BLE-Gamepad 12 | 13 | BleGamepad bleGamepad; 14 | 15 | #define numOfButtons 4 16 | #define numOfHats 1 // Maximum 4 hat switches supported 17 | 18 | byte previousButtonStates[numOfButtons]; 19 | byte currentButtonStates[numOfButtons]; 20 | byte buttonPins[numOfButtons] = {18, 19, 25, 26}; 21 | byte physicalButtons[numOfButtons] = {1, 2, 3, 4}; 22 | 23 | byte previousHatStates[numOfHats * 4]; 24 | byte currentHatStates[numOfHats * 4]; 25 | byte hatPins[numOfHats * 4] = {0, 23, 18, 35}; // in order UP, LEFT, DOWN, RIGHT. 4 pins per hat switch (Eg. List 12 pins if there are 3 hat switches) 26 | 27 | void setup() 28 | { 29 | // Setup Buttons 30 | for (byte currentPinIndex = 0; currentPinIndex < numOfButtons; currentPinIndex++) 31 | { 32 | pinMode(buttonPins[currentPinIndex], INPUT_PULLUP); 33 | previousButtonStates[currentPinIndex] = HIGH; 34 | currentButtonStates[currentPinIndex] = HIGH; 35 | } 36 | 37 | // Setup Hat Switches 38 | for (byte currentPinIndex = 0; currentPinIndex < numOfHats * 4; currentPinIndex++) 39 | { 40 | pinMode(hatPins[currentPinIndex], INPUT_PULLUP); 41 | previousHatStates[currentPinIndex] = HIGH; 42 | currentHatStates[currentPinIndex] = HIGH; 43 | } 44 | 45 | BleGamepadConfiguration bleGamepadConfig; 46 | bleGamepadConfig.setAutoReport(false); 47 | bleGamepadConfig.setButtonCount(numOfButtons); 48 | bleGamepadConfig.setHatSwitchCount(numOfHats); 49 | bleGamepad.begin(&bleGamepadConfig); 50 | 51 | // changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again 52 | } 53 | 54 | void loop() 55 | { 56 | if (bleGamepad.isConnected()) 57 | { 58 | // Deal with buttons 59 | for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++) 60 | { 61 | currentButtonStates[currentIndex] = digitalRead(buttonPins[currentIndex]); 62 | 63 | if (currentButtonStates[currentIndex] != previousButtonStates[currentIndex]) 64 | { 65 | if (currentButtonStates[currentIndex] == LOW) 66 | { 67 | bleGamepad.press(physicalButtons[currentIndex]); 68 | } 69 | else 70 | { 71 | bleGamepad.release(physicalButtons[currentIndex]); 72 | } 73 | } 74 | } 75 | 76 | // Update hat switch pin states 77 | for (byte currentHatPinsIndex = 0; currentHatPinsIndex < numOfHats * 4; currentHatPinsIndex++) 78 | { 79 | currentHatStates[currentHatPinsIndex] = digitalRead(hatPins[currentHatPinsIndex]); 80 | } 81 | 82 | // Update hats 83 | signed char hatValues[4] = {0, 0, 0, 0}; 84 | 85 | for (byte currentHatIndex = 0; currentHatIndex < numOfHats; currentHatIndex++) 86 | { 87 | signed char hatValueToSend = 0; 88 | 89 | for (byte currentHatPin = 0; currentHatPin < 4; currentHatPin++) 90 | { 91 | // Check for direction 92 | if (currentHatStates[currentHatPin + currentHatIndex * 4] == LOW) 93 | { 94 | hatValueToSend = currentHatPin * 2 + 1; 95 | 96 | // Account for last diagonal 97 | if (currentHatPin == 0) 98 | { 99 | if (currentHatStates[currentHatIndex * 4 + 3] == LOW) 100 | { 101 | hatValueToSend = 8; 102 | break; 103 | } 104 | } 105 | 106 | // Account for first 3 diagonals 107 | if (currentHatPin < 3) 108 | { 109 | if (currentHatStates[currentHatPin + currentHatIndex * 4 + 1] == LOW) 110 | { 111 | hatValueToSend += 1; 112 | break; 113 | } 114 | } 115 | } 116 | } 117 | 118 | hatValues[currentHatIndex] = hatValueToSend; 119 | } 120 | 121 | // Set hat values 122 | bleGamepad.setHats(hatValues[0], hatValues[1], hatValues[2], hatValues[3]); 123 | 124 | // Update previous states to current states if required and send report 125 | if ((memcmp((const void *)currentButtonStates, (const void *)previousButtonStates, sizeof(currentButtonStates)) != 0) && (memcmp((const void *)currentHatStates, (const void *)previousHatStates, sizeof(currentHatStates)) != 0)) 126 | { 127 | for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++) 128 | { 129 | previousButtonStates[currentIndex] = currentButtonStates[currentIndex]; 130 | } 131 | 132 | for (byte currentIndex = 0; currentIndex < numOfHats * 4; currentIndex++) 133 | { 134 | previousHatStates[currentIndex] = currentHatStates[currentIndex]; 135 | } 136 | 137 | bleGamepad.sendReport(); 138 | } 139 | 140 | delay(20); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /examples/MultipleButtonsDebounce/MultipleButtonsDebounce.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This code programs a number of pins on an ESP32 as buttons on a BLE gamepad 3 | * 4 | * It uses arrays to cut down on code 5 | * 6 | * Uses the Bounce2 library to debounce all buttons 7 | * 8 | * Uses the rose/fell states of the Bounce instance to track button states 9 | * 10 | * Before using, adjust the numOfButtons, buttonPins and physicalButtons to suit your senario 11 | * 12 | */ 13 | 14 | #define BOUNCE_WITH_PROMPT_DETECTION // Make button state changes available immediately 15 | 16 | #include 17 | #include // https://github.com/thomasfredericks/Bounce2 18 | #include // https://github.com/lemmingDev/ESP32-BLE-Gamepad 19 | 20 | #define numOfButtons 10 21 | 22 | Bounce debouncers[numOfButtons]; 23 | BleGamepad bleGamepad; 24 | 25 | byte buttonPins[numOfButtons] = {0, 35, 17, 18, 19, 23, 25, 26, 27, 32}; 26 | byte physicalButtons[numOfButtons] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 27 | 28 | void setup() 29 | { 30 | for (byte currentPinIndex = 0; currentPinIndex < numOfButtons; currentPinIndex++) 31 | { 32 | pinMode(buttonPins[currentPinIndex], INPUT_PULLUP); 33 | 34 | debouncers[currentPinIndex] = Bounce(); 35 | debouncers[currentPinIndex].attach(buttonPins[currentPinIndex]); // After setting up the button, setup the Bounce instance : 36 | debouncers[currentPinIndex].interval(5); 37 | } 38 | 39 | BleGamepadConfiguration bleGamepadConfig; 40 | bleGamepadConfig.setButtonCount(numOfButtons); 41 | bleGamepadConfig.setAutoReport(false); 42 | bleGamepad.begin(&bleGamepadConfig); 43 | 44 | // changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again 45 | 46 | Serial.begin(115200); 47 | } 48 | 49 | void loop() 50 | { 51 | if (bleGamepad.isConnected()) 52 | { 53 | bool sendReport = false; 54 | 55 | for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++) 56 | { 57 | debouncers[currentIndex].update(); 58 | 59 | if (debouncers[currentIndex].fell()) 60 | { 61 | bleGamepad.press(physicalButtons[currentIndex]); 62 | sendReport = true; 63 | Serial.print("Button "); 64 | Serial.print(physicalButtons[currentIndex]); 65 | Serial.println(" pushed."); 66 | } 67 | else if (debouncers[currentIndex].rose()) 68 | { 69 | bleGamepad.release(physicalButtons[currentIndex]); 70 | sendReport = true; 71 | Serial.print("Button "); 72 | Serial.print(physicalButtons[currentIndex]); 73 | Serial.println(" released."); 74 | } 75 | } 76 | 77 | if (sendReport) 78 | { 79 | bleGamepad.sendReport(); 80 | } 81 | 82 | // delay(20); // (Un)comment to remove/add delay between loops 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /examples/PotAsAxis/PotAsAxis.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Reads a potentiometer on pin 34 and maps the reading to the X axis 3 | * 4 | * Potentiometers can be noisy, so the sketch can take multiple samples to average out the readings 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | BleGamepad bleGamepad; 11 | 12 | const int potPin = 34; // Potentiometer is connected to GPIO 34 (Analog ADC1_CH6) 13 | const int numberOfPotSamples = 5; // Number of pot samples to take (to smooth the values) 14 | const int delayBetweenSamples = 4; // Delay in milliseconds between pot samples 15 | const int delayBetweenHIDReports = 5; // Additional delay in milliseconds between HID reports 16 | 17 | void setup() 18 | { 19 | Serial.begin(115200); 20 | Serial.println("Starting BLE work!"); 21 | bleGamepad.begin(); 22 | } 23 | 24 | void loop() 25 | { 26 | if (bleGamepad.isConnected()) 27 | { 28 | int potValues[numberOfPotSamples]; // Array to store pot readings 29 | int potValue = 0; // Variable to store calculated pot reading average 30 | 31 | // Populate readings 32 | for (int i = 0; i < numberOfPotSamples; i++) 33 | { 34 | potValues[i] = analogRead(potPin); 35 | potValue += potValues[i]; 36 | delay(delayBetweenSamples); 37 | } 38 | 39 | // Calculate the average 40 | potValue = potValue / numberOfPotSamples; 41 | 42 | // Map analog reading from 0 ~ 4095 to 32737 ~ 0 for use as an axis reading 43 | int adjustedValue = map(potValue, 0, 4095, 32737, 0); 44 | 45 | // Update X axis and auto-send report 46 | bleGamepad.setX(adjustedValue); 47 | delay(delayBetweenHIDReports); 48 | 49 | // The code below (apart from the 2 closing braces) is for pot value degugging, and can be removed 50 | // Print readings to serial port 51 | Serial.print("Sent: "); 52 | Serial.print(adjustedValue); 53 | Serial.print("\tRaw Avg: "); 54 | Serial.print(potValue); 55 | Serial.print("\tRaw: {"); 56 | 57 | // Iterate through raw pot values, printing them to the serial port 58 | for (int i = 0; i < numberOfPotSamples; i++) 59 | { 60 | Serial.print(potValues[i]); 61 | 62 | // Format the values into a comma seperated list 63 | if (i == numberOfPotSamples - 1) 64 | { 65 | Serial.println("}"); 66 | } 67 | else 68 | { 69 | Serial.print(", "); 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /examples/SetBatteryLevel/SetBatteryLevel.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This example shows how to set the battery level to be reported to the host OS 3 | * 4 | * It reduces the power by 1% every 30 seconds 5 | * 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | BleGamepad bleGamepad; 12 | 13 | int batteryLevel = 100; 14 | 15 | void setup() 16 | { 17 | Serial.begin(115200); 18 | Serial.println("Starting BLE work!"); 19 | bleGamepad.begin(); 20 | } 21 | 22 | void loop() 23 | { 24 | if (bleGamepad.isConnected()) 25 | { 26 | if(batteryLevel > 0) 27 | { 28 | batteryLevel -= 1; 29 | } 30 | 31 | Serial.print("Battery Level Set To: "); 32 | Serial.println(batteryLevel); 33 | bleGamepad.setBatteryLevel(batteryLevel); 34 | delay(30000); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/SetBatteryPowerState/SetBatteryPowerState.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This example builds on the SetBatteryLevel example by also showing how to set the battery power state information to be reported to the host OS 3 | Use nRF Connect on Android for example to see the set information 4 | 5 | You can set: 6 | 7 | - Whether there is a battery installed or not 8 | - Whether the device is discharging or not 9 | - Whether the device is charging or not 10 | - Whether the power level is good or critically low 11 | 12 | Each of the above also supports unknown or not supported states 13 | 14 | You can use the definitions as shown below, or direct values 15 | 16 | #define POWER_STATE_UNKNOWN 0 // 0b00 17 | #define POWER_STATE_NOT_SUPPORTED 1 // 0b01 18 | #define POWER_STATE_NOT_PRESENT 2 // 0b10 19 | #define POWER_STATE_NOT_DISCHARGING 2 // 0b10 20 | #define POWER_STATE_NOT_CHARGING 2 // 0b10 21 | #define POWER_STATE_GOOD 2 // 0b10 22 | #define POWER_STATE_PRESENT 3 // 0b11 23 | #define POWER_STATE_DISCHARGING 3 // 0b11 24 | #define POWER_STATE_CHARGING 3 // 0b11 25 | #define POWER_STATE_CRITICAL 3 // 0b11 26 | 27 | */ 28 | 29 | #include 30 | #include 31 | 32 | BleGamepad bleGamepad; 33 | 34 | int batteryLevel = 100; 35 | 36 | void setup() 37 | { 38 | Serial.begin(115200); 39 | Serial.println("Starting BLE work!"); 40 | bleGamepad.begin(); 41 | } 42 | 43 | void loop() 44 | { 45 | if (bleGamepad.isConnected()) 46 | { 47 | // bleGamepad.setPowerStateAll(POWER_STATE_PRESENT, POWER_STATE_NOT_DISCHARGING, POWER_STATE_CHARGING, POWER_STATE_GOOD); // Can set all values together or separate as below 48 | bleGamepad.setBatteryPowerInformation(POWER_STATE_PRESENT); // POWER_STATE_UNKNOWN or POWER_STATE_NOT_SUPPORTED or POWER_STATE_NOT_PRESENT or POWER_STATE_PRESENT 49 | bleGamepad.setDischargingState(POWER_STATE_NOT_DISCHARGING); // POWER_STATE_UNKNOWN or POWER_STATE_NOT_SUPPORTED or POWER_STATE_NOT_DISCHARGING or POWER_STATE_DISCHARGING 50 | bleGamepad.setChargingState(POWER_STATE_CHARGING); // POWER_STATE_UNKNOWN or POWER_STATE_NOT_SUPPORTED or POWER_STATE_NOT_CHARGING or POWER_STATE_CHARGING 51 | bleGamepad.setPowerLevel(POWER_STATE_GOOD); // POWER_STATE_UNKNOWN or POWER_STATE_NOT_SUPPORTED or POWER_STATE_GOOD or POWER_STATE_CRITICAL 52 | 53 | if (batteryLevel > 0) 54 | { 55 | batteryLevel -= 1; 56 | } 57 | 58 | Serial.print("Battery Level Set To: "); 59 | Serial.println(batteryLevel); 60 | bleGamepad.setBatteryLevel(batteryLevel); 61 | 62 | delay(10000); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /examples/SingleButton/SingleButton.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A simple sketch that maps a single pin on the ESP32 to a single button on the controller 3 | */ 4 | 5 | #include 6 | #include // https://github.com/lemmingDev/ESP32-BLE-Gamepad 7 | 8 | #define BUTTONPIN 35 // Pin button is attached to 9 | 10 | BleGamepad bleGamepad; 11 | 12 | int previousButton1State = HIGH; 13 | 14 | void setup() 15 | { 16 | pinMode(BUTTONPIN, INPUT_PULLUP); 17 | bleGamepad.begin(); 18 | } 19 | 20 | void loop() 21 | { 22 | if (bleGamepad.isConnected()) 23 | { 24 | 25 | int currentButton1State = digitalRead(BUTTONPIN); 26 | 27 | if (currentButton1State != previousButton1State) 28 | { 29 | if (currentButton1State == LOW) 30 | { 31 | bleGamepad.press(BUTTON_1); 32 | } 33 | else 34 | { 35 | bleGamepad.release(BUTTON_1); 36 | } 37 | } 38 | previousButton1State = currentButton1State; 39 | } 40 | } -------------------------------------------------------------------------------- /examples/SingleButtonDebounce/SingleButtonDebounce.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include // https://github.com/thomasfredericks/Bounce2 3 | #include // https://github.com/lemmingDev/ESP32-BLE-Gamepad 4 | 5 | #define BOUNCE_WITH_PROMPT_DETECTION // Make button state changes available immediately 6 | #define BUTTON_PIN 35 7 | #define LED_PIN 13 8 | 9 | Bounce debouncer = Bounce(); // Instantiate a Bounce object 10 | BleGamepad bleGamepad; // Instantiate a BleGamepad object 11 | 12 | void setup() 13 | { 14 | bleGamepad.begin(); // Begin the gamepad 15 | 16 | pinMode(BUTTON_PIN, INPUT_PULLUP); // Setup the button with an internal pull-up 17 | 18 | debouncer.attach(BUTTON_PIN); // After setting up the button, setup the Bounce instance : 19 | debouncer.interval(5); // interval in ms 20 | 21 | pinMode(LED_PIN, OUTPUT); // Setup the LED : 22 | } 23 | 24 | void loop() 25 | { 26 | if (bleGamepad.isConnected()) 27 | { 28 | debouncer.update(); // Update the Bounce instance 29 | 30 | int value = debouncer.read(); // Get the updated value 31 | 32 | // Press/release gamepad button and turn on or off the LED as determined by the state 33 | if (value == LOW) 34 | { 35 | digitalWrite(LED_PIN, HIGH); 36 | bleGamepad.press(BUTTON_1); 37 | } 38 | else 39 | { 40 | digitalWrite(LED_PIN, LOW); 41 | bleGamepad.release(BUTTON_1); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/SpecialButtons/SpecialButtons.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | BleGamepad bleGamepad; 5 | 6 | void setup() 7 | { 8 | Serial.begin(115200); 9 | BleGamepadConfiguration bleGamepadConfig; 10 | bleGamepadConfig.setWhichSpecialButtons(true, true, true, true, true, true, true, true); 11 | // Can also enable special buttons individually with the following <-- They are all disabled by default 12 | // bleGamepadConfig.setIncludeStart(true); 13 | // bleGamepadConfig.setIncludeSelect(true); 14 | // bleGamepadConfig.setIncludeMenu(true); 15 | // bleGamepadConfig.setIncludeHome(true); 16 | // bleGamepadConfig.setIncludeBack(true); 17 | // bleGamepadConfig.setIncludeVolumeInc(true); 18 | // bleGamepadConfig.setIncludeVolumeDec(true); 19 | // bleGamepadConfig.setIncludeVolumeMute(true); 20 | bleGamepad.begin(&bleGamepadConfig); 21 | 22 | // Changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again 23 | } 24 | 25 | void loop() 26 | { 27 | if (bleGamepad.isConnected()) 28 | { 29 | Serial.println("Pressing start and select"); 30 | bleGamepad.pressStart(); 31 | delay(100); 32 | bleGamepad.releaseStart(); 33 | delay(100); 34 | bleGamepad.pressSelect(); 35 | delay(100); 36 | bleGamepad.releaseSelect(); 37 | delay(100); 38 | 39 | Serial.println("Increasing volume"); 40 | bleGamepad.pressVolumeInc(); 41 | delay(100); 42 | bleGamepad.releaseVolumeInc(); 43 | delay(100); 44 | bleGamepad.pressVolumeInc(); 45 | delay(100); 46 | bleGamepad.releaseVolumeInc(); 47 | delay(100); 48 | 49 | Serial.println("Muting volume"); 50 | bleGamepad.pressVolumeMute(); 51 | delay(100); 52 | bleGamepad.releaseVolumeMute(); 53 | delay(1000); 54 | bleGamepad.pressVolumeMute(); 55 | delay(100); 56 | bleGamepad.releaseVolumeMute(); 57 | 58 | 59 | Serial.println("Pressing menu and back"); 60 | bleGamepad.pressMenu(); 61 | delay(100); 62 | bleGamepad.releaseMenu(); 63 | delay(100); 64 | bleGamepad.pressBack(); 65 | delay(100); 66 | bleGamepad.releaseBack(); 67 | delay(100); 68 | 69 | Serial.println("Pressing home"); 70 | bleGamepad.pressHome(); 71 | delay(100); 72 | bleGamepad.releaseHome(); 73 | delay(2000); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /examples/TestAll/TestAll.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Test all gamepad buttons, axes and dpad 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #define numOfButtons 64 9 | #define numOfHatSwitches 4 10 | 11 | BleGamepad bleGamepad; 12 | BleGamepadConfiguration bleGamepadConfig; 13 | 14 | void setup() 15 | { 16 | Serial.begin(115200); 17 | Serial.println("Starting BLE work!"); 18 | bleGamepadConfig.setAutoReport(false); 19 | bleGamepadConfig.setControllerType(CONTROLLER_TYPE_GAMEPAD); // CONTROLLER_TYPE_JOYSTICK, CONTROLLER_TYPE_GAMEPAD (DEFAULT), CONTROLLER_TYPE_MULTI_AXIS 20 | bleGamepadConfig.setButtonCount(numOfButtons); 21 | bleGamepadConfig.setHatSwitchCount(numOfHatSwitches); 22 | bleGamepadConfig.setVid(0xe502); 23 | bleGamepadConfig.setPid(0xabcd); 24 | // Some non-Windows operating systems and web based gamepad testers don't like min axis set below 0, so 0 is set by default 25 | //bleGamepadConfig.setAxesMin(0x8001); // -32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 26 | bleGamepadConfig.setAxesMin(0x0000); // 0 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 27 | bleGamepadConfig.setAxesMax(0x7FFF); // 32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 28 | bleGamepad.begin(&bleGamepadConfig); // Simulation controls, special buttons and hats 2/3/4 are disabled by default 29 | 30 | // Changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again 31 | } 32 | 33 | void loop() 34 | { 35 | if (bleGamepad.isConnected()) 36 | { 37 | Serial.println("\nn--- Axes Decimal ---"); 38 | Serial.print("Axes Min: "); 39 | Serial.println(bleGamepadConfig.getAxesMin()); 40 | Serial.print("Axes Max: "); 41 | Serial.println(bleGamepadConfig.getAxesMax()); 42 | Serial.println("\nn--- Axes Hex ---"); 43 | Serial.print("Axes Min: "); 44 | Serial.println(bleGamepadConfig.getAxesMin(), HEX); 45 | Serial.print("Axes Max: "); 46 | Serial.println(bleGamepadConfig.getAxesMax(), HEX); 47 | 48 | Serial.println("\n\nPress all buttons one by one"); 49 | for (int i = 1; i <= numOfButtons; i += 1) 50 | { 51 | bleGamepad.press(i); 52 | bleGamepad.sendReport(); 53 | delay(100); 54 | bleGamepad.release(i); 55 | bleGamepad.sendReport(); 56 | delay(25); 57 | } 58 | 59 | Serial.println("Press start and select"); 60 | bleGamepad.pressStart(); 61 | bleGamepad.sendReport(); 62 | delay(100); 63 | bleGamepad.pressSelect(); 64 | bleGamepad.sendReport(); 65 | delay(100); 66 | bleGamepad.releaseStart(); 67 | bleGamepad.sendReport(); 68 | delay(100); 69 | bleGamepad.releaseSelect(); 70 | bleGamepad.sendReport(); 71 | 72 | Serial.println("Move all axis simultaneously from min to max"); 73 | for (int i = bleGamepadConfig.getAxesMin(); i < bleGamepadConfig.getAxesMax(); i += (bleGamepadConfig.getAxesMax() / 256) + 1) 74 | { 75 | bleGamepad.setAxes(i, i, i, i, i, i); // (x, y, z, rx, ry, rz) 76 | //bleGamepad.setHIDAxes(i, i, i, i, i, i); // (x, y, z, rz, rx, ry) 77 | bleGamepad.sendReport(); 78 | delay(10); 79 | } 80 | bleGamepad.setAxes(); // Reset all axes to zero 81 | bleGamepad.sendReport(); 82 | 83 | Serial.println("Move all sliders simultaneously from min to max"); 84 | for (int i = bleGamepadConfig.getAxesMin(); i < bleGamepadConfig.getAxesMax(); i += (bleGamepadConfig.getAxesMax() / 256) + 1) 85 | { 86 | bleGamepad.setSliders(i, i); 87 | bleGamepad.sendReport(); 88 | delay(10); 89 | } 90 | bleGamepad.setSliders(); // Reset all sliders to zero 91 | bleGamepad.sendReport(); 92 | 93 | Serial.println("Send hat switch 1 one by one in an anticlockwise rotation"); 94 | for (int i = 8; i >= 0; i--) 95 | { 96 | bleGamepad.setHat1(i); 97 | bleGamepad.sendReport(); 98 | delay(200); 99 | } 100 | 101 | Serial.println("Send hat switch 2 one by one in an anticlockwise rotation"); 102 | for (int i = 8; i >= 0; i--) 103 | { 104 | bleGamepad.setHat2(i); 105 | bleGamepad.sendReport(); 106 | delay(200); 107 | } 108 | 109 | Serial.println("Send hat switch 3 one by one in an anticlockwise rotation"); 110 | for (int i = 8; i >= 0; i--) 111 | { 112 | bleGamepad.setHat3(i); 113 | bleGamepad.sendReport(); 114 | delay(200); 115 | } 116 | 117 | Serial.println("Send hat switch 4 one by one in an anticlockwise rotation"); 118 | for (int i = 8; i >= 0; i--) 119 | { 120 | bleGamepad.setHat4(i); 121 | bleGamepad.sendReport(); 122 | delay(200); 123 | } 124 | 125 | // Simulation controls are disabled by default 126 | // Serial.println("Move all simulation controls simultaneously from min to max"); 127 | // for (int i = bleGamepadConfig.getSimulationMin(); i < bleGamepadConfig.getSimulationMax(); i += (bleGamepadConfig.getAxesMax() / 256) + 1) 128 | // { 129 | // bleGamepad.setRudder(i); 130 | // bleGamepad.setThrottle(i); 131 | // bleGamepad.setAccelerator(i); 132 | // bleGamepad.setBrake(i); 133 | // bleGamepad.setSteering(i); 134 | // bleGamepad.sendReport(); 135 | // delay(10); 136 | // } 137 | // bleGamepad.setSimulationControls(); //Reset all simulation controls to zero 138 | bleGamepad.sendReport(); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /examples/TestReceivingOutputReport/TestReceivingOutputReport.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Test receiving Output Report 3 | * Here is a C# testing program for sending OutputReport from the host to the device 4 | * https://github.com/Sab1e-GitHub/HIDOutputDemo_CSharp 5 | */ 6 | 7 | #include 8 | 9 | #define numOfButtons 16 10 | 11 | BleGamepad bleGamepad; 12 | BleGamepadConfiguration bleGamepadConfig; 13 | 14 | void setup() 15 | { 16 | Serial.begin(115200); 17 | Serial.println("Starting BLE work!"); 18 | 19 | bleGamepadConfig.setAutoReport(false); 20 | bleGamepadConfig.setControllerType(CONTROLLER_TYPE_GAMEPAD); // CONTROLLER_TYPE_JOYSTICK, CONTROLLER_TYPE_GAMEPAD (DEFAULT), CONTROLLER_TYPE_MULTI_AXIS 21 | 22 | bleGamepadConfig.setEnableOutputReport(true); // (Necessary) Enable Output Report. Default is false. 23 | bleGamepadConfig.setOutputReportLength(128); // (Optional) Set Report Length 128(Bytes). The default value is 64 bytes. 24 | // Do not set the OutputReportLength too large, otherwise it will be truncated. For example, if the hexadecimal value of 10000 in decimal is 0x2710, it will be truncated to 0x710. 25 | 26 | bleGamepadConfig.setHidReportId(0x05); // (Optional) Set ReportID to 0x05. 27 | // When you send data from the upper computer to ESP32, you must send the ReportID in the first byte! The default ReportID is 3. 28 | 29 | bleGamepadConfig.setButtonCount(numOfButtons); 30 | 31 | bleGamepadConfig.setAxesMin(0x0000); // 0 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 32 | bleGamepadConfig.setAxesMax(0x7FFF); // 32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 33 | 34 | // Try NOT to modify VID, otherwise it may cause the host to be unable to send output reports to the device. 35 | bleGamepadConfig.setVid(0x1234); 36 | 37 | // You can freely set the PID 38 | bleGamepadConfig.setPid(0x0001); 39 | bleGamepad.begin(&bleGamepadConfig); 40 | 41 | // Changing bleGamepadConfig after the begin function has no effect, unless you call the begin function again 42 | } 43 | 44 | void loop() 45 | { 46 | if (bleGamepad.isConnected()) 47 | { 48 | if (bleGamepad.isOutputReceived()) 49 | { 50 | uint8_t* buffer = bleGamepad.getOutputBuffer(); 51 | Serial.print("Receive: "); 52 | 53 | for (int i = 0; i < 64; i++) 54 | { 55 | Serial.printf("0x%X ",buffer[i]); // Print data from buffer 56 | } 57 | 58 | Serial.println(""); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For ESP32 BLE Gamepad 3 | ####################################### 4 | # Class 5 | ####################################### 6 | 7 | BleGamepad KEYWORD1 8 | BleGamepadConfiguration KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions 12 | ####################################### 13 | 14 | begin KEYWORD2 15 | end KEYWORD2 16 | setAxes KEYWORD2 17 | setAxesHIDOrder KEYWORD2 18 | press KEYWORD2 19 | release KEYWORD2 20 | isPressed KEYWORD2 21 | isConnected KEYWORD2 22 | setLeftThumb KEYWORD2 23 | setRightThumb KEYWORD2 24 | setRightThumbAndroid KEYWORD2 25 | setLeftTrigger KEYWORD2 26 | setRightTrigger KEYWORD2 27 | setHat KEYWORD2 28 | setHat1 KEYWORD2 29 | setHat2 KEYWORD2 30 | setHat3 KEYWORD2 31 | setHat4 KEYWORD2 32 | setHats KEYWORD2 33 | setX KEYWORD2 34 | setY KEYWORD2 35 | setZ KEYWORD2 36 | setRZ KEYWORD2 37 | setRX KEYWORD2 38 | setRY KEYWORD2 39 | setSlider1 KEYWORD2 40 | setSlider2 KEYWORD2 41 | setSlider KEYWORD2 42 | setSliders KEYWORD2 43 | setRudder KEYWORD2 44 | setThrottle KEYWORD2 45 | setAccelerator KEYWORD2 46 | setBrake KEYWORD2 47 | setSteering KEYWORD2 48 | setSimulationControls KEYWORD2 49 | setControllerType KEYWORD2 50 | setAutoReport KEYWORD2 51 | setHidReportId KEYWORD2 52 | sendReport KEYWORD2 53 | setButtonCount KEYWORD2 54 | setHatSwitchCount KEYWORD2 55 | setIncludeStart KEYWORD2 56 | setIncludeSelect KEYWORD2 57 | setIncludeMenu KEYWORD2 58 | setIncludeHome KEYWORD2 59 | setIncludeBack KEYWORD2 60 | setIncludeVolumeInc EYWORD2 61 | setIncludeVolumeDec KEYWORD2 62 | setIncludeVolumeMute KEYWORD2 63 | setWhichSpecialButtons KEYWORD2 64 | setIncludeXAxis KEYWORD2 65 | setIncludeYAxis KEYWORD2 66 | setIncludeZAxis KEYWORD2 67 | setIncludeRxAxis KEYWORD2 68 | setIncludeRyAxis KEYWORD2 69 | setIncludeRzAxis KEYWORD2 70 | setIncludeSlider1 KEYWORD2 71 | setIncludeSlider2 KEYWORD2 72 | setWhichAxes KEYWORD2 73 | setIncludeRudder KEYWORD2 74 | setIncludeThrottle KEYWORD2 75 | setIncludeAccelerator KEYWORD2 76 | setIncludeBrake KEYWORD2 77 | setIncludeSteering KEYWORD2 78 | setIncludeGyroscope KEYWORD2 79 | setIncludeAccelerometer KEYWORD2 80 | setWhichSimulationControls KEYWORD2 81 | resetButtons KEYWORD2 82 | setBatteryLevel KEYWORD2 83 | setPowerStateAll KEYWORD2 84 | setBatteryPowerInformation KEYWORD2 85 | setDischargingState KEYWORD2 86 | setChargingState KEYWORD2 87 | setPowerLevel KEYWORD2 88 | pressSpecialButton KEYWORD2 89 | releaseSpecialButton KEYWORD2 90 | pressStart KEYWORD2 91 | releaseStart KEYWORD2 92 | pressSelect KEYWORD2 93 | releaseSelect KEYWORD2 94 | pressMenu KEYWORD2 95 | releaseMenu KEYWORD2 96 | pressHome KEYWORD2 97 | releaseHome KEYWORD2 98 | pressBack KEYWORD2 99 | releaseBack KEYWORD2 100 | pressVolumeInc KEYWORD2 101 | releaseVolumeInc KEYWORD2 102 | pressVolumeDec KEYWORD2 103 | releaseVolumeDec KEYWORD2 104 | pressVolumeMute KEYWORD2 105 | releaseVolumeMute KEYWORD2 106 | getControllerType KEYWORD2 107 | getHidReportId KEYWORD2 108 | getButtonCount KEYWORD2 109 | getTotalSpecialButtonCount KEYWORD2 110 | getDesktopSpecialButtonCount KEYWORD2 111 | getConsumerSpecialButtonCount KEYWORD2 112 | getHatSwitchCount KEYWORD2 113 | getAxisCount KEYWORD2 114 | getSimulationCount KEYWORD2 115 | getAutoReport KEYWORD2 116 | getIncludeStart KEYWORD2 117 | getIncludeSelect KEYWORD2 118 | getIncludeMenu KEYWORD2 119 | getIncludeHome KEYWORD2 120 | getIncludeBack KEYWORD2 121 | getIncludeVolumeInc KEYWORD2 122 | getIncludeVolumeDec KEYWORD2 123 | getIncludeVolumeMute KEYWORD2 124 | getWhichSpecialButtons KEYWORD2 125 | getIncludeXAxis KEYWORD2 126 | getIncludeYAxis KEYWORD2 127 | getIncludeZAxis KEYWORD2 128 | getIncludeRxAxis KEYWORD2 129 | getIncludeRyAxis KEYWORD2 130 | getIncludeRzAxis KEYWORD2 131 | getIncludeSlider1 KEYWORD2 132 | getIncludeSlider2 KEYWORD2 133 | getWhichAxes KEYWORD2 134 | getIncludeRudder KEYWORD2 135 | getIncludeThrottle KEYWORD2 136 | getIncludeAccelerator KEYWORD2 137 | getIncludeBrake KEYWORD2 138 | getIncludeSteering KEYWORD2 139 | getIncludeAccelerometer KEYWORD2 140 | getIncludeGyroscope KEYWORD2 141 | getWhichSimulationControls KEYWORD2 142 | getVid KEYWORD2 143 | getPid KEYWORD2 144 | getGuidVersion KEYWORD2 145 | getAxesMin KEYWORD2 146 | getAxesMax KEYWORD2 147 | getSimulationMin KEYWORD2 148 | getSimulationMax KEYWORD2 149 | getMotionMin KEYWORD2 150 | getMotionMax KEYWORD2 151 | getModelNumber KEYWORD2 152 | getSoftwareRevision KEYWORD2 153 | getSerialNumber KEYWORD2 154 | getFirmwareRevision KEYWORD2 155 | getHardwareRevision KEYWORD2 156 | setVid KEYWORD2 157 | setPid KEYWORD2 158 | setGuidVersion KEYWORD2 159 | setAxesMin KEYWORD2 160 | setAxesMax KEYWORD2 161 | setSimulationMin KEYWORD2 162 | setSimulationMax KEYWORD2 163 | setMotionMin KEYWORD2 164 | setMotionMax KEYWORD2 165 | setModelNumber KEYWORD2 166 | setSoftwareRevision KEYWORD2 167 | setSerialNumber KEYWORD2 168 | setFirmwareRevision KEYWORD2 169 | setHardwareRevision KEYWORD2 170 | deleteBond KEYWORD2 171 | deleteAllBonds KEYWORD2 172 | enterPairingMode KEYWORD2 173 | getPeerInfo KEYWORD2 174 | getDeviceName KEYWORD2 175 | getDeviceManufacturer KEYWORD2 176 | getTXPowerLevel KEYWORD2 177 | setTXPowerLevel KEYWORD2 178 | setGyroscope KEYWORD2 179 | setAccelerometer KEYWORD2 180 | setMotionControls KEYWORD2 181 | beginNUS KEYWORD2 182 | sendDataOverNUS KEYWORD2 183 | setNUSDataReceivedCallback KEYWORD2 184 | getNUS KEYWORD2 185 | 186 | ####################################### 187 | # Constants 188 | ####################################### 189 | 190 | CONTROLLER_TYPE_JOYSTICK LITERAL1 191 | CONTROLLER_TYPE_GAMEPAD LITERAL1 192 | CONTROLLER_TYPE_MULTI_AXIS LITERAL1 193 | 194 | BUTTON_1 LITERAL1 195 | BUTTON_2 LITERAL1 196 | BUTTON_3 LITERAL1 197 | BUTTON_4 LITERAL1 198 | BUTTON_5 LITERAL1 199 | BUTTON_6 LITERAL1 200 | BUTTON_7 LITERAL1 201 | BUTTON_8 LITERAL1 202 | BUTTON_9 LITERAL1 203 | BUTTON_10 LITERAL1 204 | BUTTON_11 LITERAL1 205 | BUTTON_12 LITERAL1 206 | BUTTON_13 LITERAL1 207 | BUTTON_14 LITERAL1 208 | BUTTON_15 LITERAL1 209 | BUTTON_16 LITERAL1 210 | BUTTON_17 LITERAL1 211 | BUTTON_18 LITERAL1 212 | BUTTON_19 LITERAL1 213 | BUTTON_20 LITERAL1 214 | BUTTON_21 LITERAL1 215 | BUTTON_22 LITERAL1 216 | BUTTON_23 LITERAL1 217 | BUTTON_24 LITERAL1 218 | BUTTON_25 LITERAL1 219 | BUTTON_26 LITERAL1 220 | BUTTON_27 LITERAL1 221 | BUTTON_28 LITERAL1 222 | BUTTON_29 LITERAL1 223 | BUTTON_30 LITERAL1 224 | BUTTON_31 LITERAL1 225 | BUTTON_32 LITERAL1 226 | BUTTON_33 LITERAL1 227 | BUTTON_34 LITERAL1 228 | BUTTON_35 LITERAL1 229 | BUTTON_36 LITERAL1 230 | BUTTON_37 LITERAL1 231 | BUTTON_38 LITERAL1 232 | BUTTON_39 LITERAL1 233 | BUTTON_40 LITERAL1 234 | BUTTON_41 LITERAL1 235 | BUTTON_42 LITERAL1 236 | BUTTON_43 LITERAL1 237 | BUTTON_44 LITERAL1 238 | BUTTON_45 LITERAL1 239 | BUTTON_46 LITERAL1 240 | BUTTON_47 LITERAL1 241 | BUTTON_48 LITERAL1 242 | BUTTON_49 LITERAL1 243 | BUTTON_50 LITERAL1 244 | BUTTON_51 LITERAL1 245 | BUTTON_52 LITERAL1 246 | BUTTON_53 LITERAL1 247 | BUTTON_54 LITERAL1 248 | BUTTON_55 LITERAL1 249 | BUTTON_56 LITERAL1 250 | BUTTON_57 LITERAL1 251 | BUTTON_58 LITERAL1 252 | BUTTON_59 LITERAL1 253 | BUTTON_60 LITERAL1 254 | BUTTON_61 LITERAL1 255 | BUTTON_62 LITERAL1 256 | BUTTON_63 LITERAL1 257 | BUTTON_64 LITERAL1 258 | BUTTON_65 LITERAL1 259 | BUTTON_66 LITERAL1 260 | BUTTON_67 LITERAL1 261 | BUTTON_68 LITERAL1 262 | BUTTON_69 LITERAL1 263 | BUTTON_70 LITERAL1 264 | BUTTON_71 LITERAL1 265 | BUTTON_72 LITERAL1 266 | BUTTON_73 LITERAL1 267 | BUTTON_74 LITERAL1 268 | BUTTON_75 LITERAL1 269 | BUTTON_76 LITERAL1 270 | BUTTON_77 LITERAL1 271 | BUTTON_78 LITERAL1 272 | BUTTON_79 LITERAL1 273 | BUTTON_80 LITERAL1 274 | BUTTON_81 LITERAL1 275 | BUTTON_82 LITERAL1 276 | BUTTON_83 LITERAL1 277 | BUTTON_84 LITERAL1 278 | BUTTON_85 LITERAL1 279 | BUTTON_86 LITERAL1 280 | BUTTON_87 LITERAL1 281 | BUTTON_88 LITERAL1 282 | BUTTON_89 LITERAL1 283 | BUTTON_90 LITERAL1 284 | BUTTON_91 LITERAL1 285 | BUTTON_92 LITERAL1 286 | BUTTON_93 LITERAL1 287 | BUTTON_94 LITERAL1 288 | BUTTON_95 LITERAL1 289 | BUTTON_96 LITERAL1 290 | BUTTON_97 LITERAL1 291 | BUTTON_98 LITERAL1 292 | BUTTON_99 LITERAL1 293 | BUTTON_100 LITERAL1 294 | BUTTON_101 LITERAL1 295 | BUTTON_102 LITERAL1 296 | BUTTON_103 LITERAL1 297 | BUTTON_104 LITERAL1 298 | BUTTON_105 LITERAL1 299 | BUTTON_106 LITERAL1 300 | BUTTON_107 LITERAL1 301 | BUTTON_108 LITERAL1 302 | BUTTON_109 LITERAL1 303 | BUTTON_110 LITERAL1 304 | BUTTON_111 LITERAL1 305 | BUTTON_112 LITERAL1 306 | BUTTON_113 LITERAL1 307 | BUTTON_114 LITERAL1 308 | BUTTON_115 LITERAL1 309 | BUTTON_116 LITERAL1 310 | BUTTON_117 LITERAL1 311 | BUTTON_118 LITERAL1 312 | BUTTON_119 LITERAL1 313 | BUTTON_120 LITERAL1 314 | BUTTON_121 LITERAL1 315 | BUTTON_122 LITERAL1 316 | BUTTON_123 LITERAL1 317 | BUTTON_124 LITERAL1 318 | BUTTON_125 LITERAL1 319 | BUTTON_126 LITERAL1 320 | BUTTON_127 LITERAL1 321 | BUTTON_128 LITERAL1 322 | 323 | DPAD_CENTERED LITERAL1 324 | DPAD_UP LITERAL1 325 | DPAD_UP_RIGHT LITERAL1 326 | DPAD_RIGHT LITERAL1 327 | DPAD_DOWN_RIGHT LITERAL1 328 | DPAD_DOWN LITERAL1 329 | DPAD_DOWN_LEFT LITERAL1 330 | DPAD_LEFT LITERAL1 331 | DPAD_UP_LEFT LITERAL1 332 | 333 | HAT_CENTERED LITERAL1 334 | HAT_UP LITERAL1 335 | HAT_UP_RIGHT LITERAL1 336 | HAT_RIGHT LITERAL1 337 | HAT_DOWN_RIGHT LITERAL1 338 | HAT_DOWN LITERAL1 339 | HAT_DOWN_LEFT LITERAL1 340 | HAT_LEFT LITERAL1 341 | HAT_UP_LEFT LITERAL1 342 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=ESP32-BLE-Gamepad 2 | version=0.7.4 3 | author=lemmingDev 4 | maintainer=lemmingDev 5 | sentence=Bluetooth LE Gamepad library for the ESP32. 6 | paragraph=Bluetooth LE Gamepad library for the ESP32. 7 | category=Communication 8 | url=https://github.com/lemmingDev/ESP32-BLE-Gamepad 9 | architectures=esp32 10 | depends=NimBLE-Arduino 11 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Software License Agreement (MIT License) 2 | 3 | Copyright (c) 2021 lemmingDev - https://github.com/lemmingDev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. --------------------------------------------------------------------------------