|
1 | 1 | #include <Bluepad32.h>
|
2 | 2 |
|
3 |
| -GamepadPtr myGamepads[BP32_MAX_GAMEPADS]; |
| 3 | +ControllerPtr myControllers[BP32_MAX_GAMEPADS]; |
4 | 4 |
|
5 | 5 | // This callback gets called any time a new gamepad is connected.
|
6 | 6 | // Up to 4 gamepads can be connected at the same time.
|
7 |
| -void onConnectedGamepad(GamepadPtr gp) { |
8 |
| - bool foundEmptySlot = false; |
9 |
| - for (int i = 0; i < BP32_MAX_GAMEPADS; i++) { |
10 |
| - if (myGamepads[i] == nullptr) { |
11 |
| - Serial.printf("CALLBACK: Gamepad is connected, index=%d\n", i); |
12 |
| - // Additionally, you can get certain gamepad properties like: |
13 |
| - // Model, VID, PID, BTAddr, flags, etc. |
14 |
| - GamepadProperties properties = gp->getProperties(); |
15 |
| - Serial.printf("Gamepad model: %s, VID=0x%04x, PID=0x%04x\n", |
16 |
| - gp->getModelName().c_str(), properties.vendor_id, |
17 |
| - properties.product_id); |
18 |
| - myGamepads[i] = gp; |
19 |
| - foundEmptySlot = true; |
20 |
| - break; |
| 7 | +void onConnectedController(ControllerPtr ctl) { |
| 8 | + bool foundEmptySlot = false; |
| 9 | + for (int i = 0; i < BP32_MAX_GAMEPADS; i++) { |
| 10 | + if (myControllers[i] == nullptr) { |
| 11 | + Serial.printf("CALLBACK: Controller is connected, index=%d\n", i); |
| 12 | + // Additionally, you can get certain gamepad properties like: |
| 13 | + // Model, VID, PID, BTAddr, flags, etc. |
| 14 | + ControllerProperties properties = ctl->getProperties(); |
| 15 | + Serial.printf("Controller model: %s, VID=0x%04x, PID=0x%04x\n", ctl->getModelName().c_str(), properties.vendor_id, |
| 16 | + properties.product_id); |
| 17 | + myControllers[i] = ctl; |
| 18 | + foundEmptySlot = true; |
| 19 | + break; |
| 20 | + } |
| 21 | + } |
| 22 | + if (!foundEmptySlot) { |
| 23 | + Serial.println("CALLBACK: Controller connected, but could not found empty slot"); |
21 | 24 | }
|
22 |
| - } |
23 |
| - if (!foundEmptySlot) { |
24 |
| - Serial.println( |
25 |
| - "CALLBACK: Gamepad connected, but could not found empty slot"); |
26 |
| - } |
27 | 25 | }
|
28 | 26 |
|
29 |
| -void onDisconnectedGamepad(GamepadPtr gp) { |
30 |
| - bool foundGamepad = false; |
| 27 | +void onDisconnectedController(ControllerPtr ctl) { |
| 28 | + bool foundController = false; |
31 | 29 |
|
32 |
| - for (int i = 0; i < BP32_MAX_GAMEPADS; i++) { |
33 |
| - if (myGamepads[i] == gp) { |
34 |
| - Serial.printf("CALLBACK: Gamepad is disconnected from index=%d\n", i); |
35 |
| - myGamepads[i] = nullptr; |
36 |
| - foundGamepad = true; |
37 |
| - break; |
| 30 | + for (int i = 0; i < BP32_MAX_GAMEPADS; i++) { |
| 31 | + if (myControllers[i] == ctl) { |
| 32 | + Serial.printf("CALLBACK: Controller disconnected from index=%d\n", i); |
| 33 | + myControllers[i] = nullptr; |
| 34 | + foundController = true; |
| 35 | + break; |
| 36 | + } |
38 | 37 | }
|
39 |
| - } |
40 | 38 |
|
41 |
| - if (!foundGamepad) { |
42 |
| - Serial.println( |
43 |
| - "CALLBACK: Gamepad disconnected, but not found in myGamepads"); |
44 |
| - } |
| 39 | + if (!foundController) { |
| 40 | + Serial.println("CALLBACK: Controller disconnected, but not found in myControllers"); |
| 41 | + } |
45 | 42 | }
|
46 | 43 |
|
47 |
| -// Arduino setup function. Runs in CPU 1 |
48 |
| -void setup() { |
49 |
| - Serial.begin(115200); |
50 |
| - Serial.printf("Firmware: %s\n", BP32.firmwareVersion()); |
51 |
| - const uint8_t *addr = BP32.localBdAddress(); |
52 |
| - Serial.printf("BD Addr: %2X:%2X:%2X:%2X:%2X:%2X\n", addr[0], addr[1], addr[2], |
53 |
| - addr[3], addr[4], addr[5]); |
54 |
| - |
55 |
| - // Setup the Bluepad32 callbacks |
56 |
| - BP32.setup(&onConnectedGamepad, &onDisconnectedGamepad); |
57 |
| - |
58 |
| - // "forgetBluetoothKeys()" should be called when the user performs |
59 |
| - // a "device factory reset", or similar. |
60 |
| - // Calling "forgetBluetoothKeys" in setup() just as an example. |
61 |
| - // Forgetting Bluetooth keys prevents "paired" gamepads to reconnect. |
62 |
| - // But might also fix some connection / re-connection issues. |
63 |
| - BP32.forgetBluetoothKeys(); |
| 44 | +void dumpGamepad(ControllerPtr ctl) { |
| 45 | + Serial.printf( |
| 46 | + "idx=%d, dpad: 0x%02x, buttons: 0x%04x, axis L: %4d, %4d, axis R: %4d, %4d, brake: %4d, throttle: %4d, " |
| 47 | + "misc: 0x%02x, gyro x:%6d y:%6d z:%6d, accel x:%6d y:%6d z:%6d\n", |
| 48 | + ctl->index(), // Controller Index |
| 49 | + ctl->dpad(), // DPAD |
| 50 | + ctl->buttons(), // bitmask of pressed buttons |
| 51 | + ctl->axisX(), // (-511 - 512) left X Axis |
| 52 | + ctl->axisY(), // (-511 - 512) left Y axis |
| 53 | + ctl->axisRX(), // (-511 - 512) right X axis |
| 54 | + ctl->axisRY(), // (-511 - 512) right Y axis |
| 55 | + ctl->brake(), // (0 - 1023): brake button |
| 56 | + ctl->throttle(), // (0 - 1023): throttle (AKA gas) button |
| 57 | + ctl->miscButtons(), // bitmak of pressed "misc" buttons |
| 58 | + ctl->gyroX(), // Gyro X |
| 59 | + ctl->gyroY(), // Gyro Y |
| 60 | + ctl->gyroZ(), // Gyro Z |
| 61 | + ctl->accelX(), // Accelerometer X |
| 62 | + ctl->accelY(), // Accelerometer Y |
| 63 | + ctl->accelZ() // Accelerometer Z |
| 64 | + ); |
64 | 65 | }
|
65 | 66 |
|
66 |
| -// Arduino loop function. Runs in CPU 1 |
67 |
| -void loop() { |
68 |
| - // This call fetches all the gamepad info from the NINA (ESP32) module. |
69 |
| - // Just call this function in your main loop. |
70 |
| - // The gamepads pointer (the ones received in the callbacks) gets updated |
71 |
| - // automatically. |
72 |
| - BP32.update(); |
73 |
| - |
74 |
| - // It is safe to always do this before using the gamepad API. |
75 |
| - // This guarantees that the gamepad is valid and connected. |
76 |
| - for (int i = 0; i < BP32_MAX_GAMEPADS; i++) { |
77 |
| - GamepadPtr myGamepad = myGamepads[i]; |
78 |
| - |
79 |
| - if (myGamepad && myGamepad->isConnected()) { |
80 |
| - // There are different ways to query whether a button is pressed. |
81 |
| - // By query each button individually: |
82 |
| - // a(), b(), x(), y(), l1(), etc... |
83 |
| - if (myGamepad->a()) { |
| 67 | +void dumpMouse(ControllerPtr ctl) { |
| 68 | + Serial.printf("idx=%d, buttons: 0x%04x, scrollWheel=0x%04x, delta X: %4d, delta Y: %4d\n", |
| 69 | + ctl->index(), // Controller Index |
| 70 | + ctl->buttons(), // bitmask of pressed buttons |
| 71 | + ctl->scrollWheel(), // Scroll Wheel |
| 72 | + ctl->deltaX(), // (-511 - 512) left X Axis |
| 73 | + ctl->deltaY() // (-511 - 512) left Y axis |
| 74 | + ); |
| 75 | +} |
| 76 | + |
| 77 | +void dumpKeyboard(ControllerPtr ctl) { |
| 78 | + // TODO: Print pressed keys |
| 79 | + Serial.printf("idx=%d\n", ctl->index()); |
| 80 | +} |
| 81 | + |
| 82 | +void dumpBalanceBoard(ControllerPtr ctl) { |
| 83 | + Serial.printf("idx=%d, TL=%u, TR=%u, BL=%u, BR=%u, temperature=%d\n", |
| 84 | + ctl->index(), // Controller Index |
| 85 | + ctl->topLeft(), // top-left scale |
| 86 | + ctl->topRight(), // top-right scale |
| 87 | + ctl->bottomLeft(), // bottom-left scale |
| 88 | + ctl->bottomRight(), // bottom-right scale |
| 89 | + ctl->temperature() // temperature: used to adjust the scale value's precision |
| 90 | + ); |
| 91 | +} |
| 92 | + |
| 93 | +void processGamepad(ControllerPtr ctl) { |
| 94 | + // There are different ways to query whether a button is pressed. |
| 95 | + // By query each button individually: |
| 96 | + // a(), b(), x(), y(), l1(), etc... |
| 97 | + if (ctl->a()) { |
84 | 98 | static int colorIdx = 0;
|
85 | 99 | // Some gamepads like DS4 and DualSense support changing the color LED.
|
86 | 100 | // It is possible to change it by calling:
|
87 | 101 | switch (colorIdx % 3) {
|
88 |
| - case 0: |
89 |
| - // Red |
90 |
| - myGamepad->setColorLED(255, 0, 0); |
91 |
| - break; |
92 |
| - case 1: |
93 |
| - // Green |
94 |
| - myGamepad->setColorLED(0, 255, 0); |
95 |
| - break; |
96 |
| - case 2: |
97 |
| - // Blue |
98 |
| - myGamepad->setColorLED(0, 0, 255); |
99 |
| - break; |
| 102 | + case 0: |
| 103 | + // Red |
| 104 | + ctl->setColorLED(255, 0, 0); |
| 105 | + break; |
| 106 | + case 1: |
| 107 | + // Green |
| 108 | + ctl->setColorLED(0, 255, 0); |
| 109 | + break; |
| 110 | + case 2: |
| 111 | + // Blue |
| 112 | + ctl->setColorLED(0, 0, 255); |
| 113 | + break; |
100 | 114 | }
|
101 | 115 | colorIdx++;
|
102 |
| - } |
| 116 | + } |
103 | 117 |
|
104 |
| - if (myGamepad->b()) { |
| 118 | + if (ctl->b()) { |
105 | 119 | // Turn on the 4 LED. Each bit represents one LED.
|
106 | 120 | static int led = 0;
|
107 | 121 | led++;
|
108 | 122 | // Some gamepads like the DS3, DualSense, Nintendo Wii, Nintendo Switch
|
109 |
| - // support changing the "Player LEDs": those 4 LEDs that usually |
110 |
| - // indicate the "gamepad seat". It is possible to change them by |
111 |
| - // calling: |
112 |
| - myGamepad->setPlayerLEDs(led & 0x0f); |
113 |
| - } |
| 123 | + // support changing the "Player LEDs": those 4 LEDs that usually indicate |
| 124 | + // the "gamepad seat". |
| 125 | + // It is possible to change them by calling: |
| 126 | + ctl->setPlayerLEDs(led & 0x0f); |
| 127 | + } |
114 | 128 |
|
115 |
| - if (myGamepad->x()) { |
| 129 | + if (ctl->x()) { |
116 | 130 | // Duration: 255 is ~2 seconds
|
117 | 131 | // force: intensity
|
118 | 132 | // Some gamepads like DS3, DS4, DualSense, Switch, Xbox One S support
|
119 | 133 | // rumble.
|
120 | 134 | // It is possible to set it by calling:
|
121 |
| - myGamepad->setRumble(0xc0 /* force */, 0xc0 /* duration */); |
122 |
| - } |
123 |
| - |
124 |
| - // Another way to query the buttons, is by calling buttons(), or |
125 |
| - // miscButtons() which return a bitmask. |
126 |
| - // Some gamepads also have DPAD, axis and more. |
127 |
| - Serial.printf( |
128 |
| - "idx=%d, dpad: 0x%02x, buttons: 0x%04x, axis L: %4d, %4d, axis R: " |
129 |
| - "%4d, %4d, brake: %4d, throttle: %4d, misc: 0x%02x, gyro x:%6d y:%6d " |
130 |
| - "z:%6d, accel x:%6d y:%6d z:%6d\n", |
131 |
| - i, // Gamepad Index |
132 |
| - myGamepad->dpad(), // DPAD |
133 |
| - myGamepad->buttons(), // bitmask of pressed buttons |
134 |
| - myGamepad->axisX(), // (-511 - 512) left X Axis |
135 |
| - myGamepad->axisY(), // (-511 - 512) left Y axis |
136 |
| - myGamepad->axisRX(), // (-511 - 512) right X axis |
137 |
| - myGamepad->axisRY(), // (-511 - 512) right Y axis |
138 |
| - myGamepad->brake(), // (0 - 1023): brake button |
139 |
| - myGamepad->throttle(), // (0 - 1023): throttle (AKA gas) button |
140 |
| - myGamepad->miscButtons(), // bitmak of pressed "misc" buttons |
141 |
| - myGamepad->gyroX(), // Gyro X |
142 |
| - myGamepad->gyroY(), // Gyro Y |
143 |
| - myGamepad->gyroZ(), // Gyro Z |
144 |
| - myGamepad->accelX(), // Accelerometer X |
145 |
| - myGamepad->accelY(), // Accelerometer Y |
146 |
| - myGamepad->accelZ() // Accelerometer Z |
147 |
| - ); |
148 |
| - |
149 |
| - // You can query the axis and other properties as well. See Gamepad.h |
150 |
| - // For all the available functions. |
| 135 | + ctl->setRumble(0xc0 /* force */, 0xc0 /* duration */); |
151 | 136 | }
|
152 |
| - } |
153 | 137 |
|
154 |
| - // The main loop must have some kind of "yield to lower priority task" event. |
155 |
| - // Otherwise the watchdog will get triggered. |
156 |
| - // If your main loop doesn't have one, just add a simple `vTaskDelay(1)`. |
157 |
| - // Detailed info here: |
158 |
| - // https://stackoverflow.com/questions/66278271/task-watchdog-got-triggered-the-tasks-did-not-reset-the-watchdog-in-time |
| 138 | + // Another way to query controller data is by getting the buttons() function. |
| 139 | + // See how the different "dump*" functions dump the Controller info. |
| 140 | + dumpGamepad(ctl); |
| 141 | +} |
| 142 | + |
| 143 | +void processMouse(ControllerPtr ctl) { |
| 144 | + // This is just an example. |
| 145 | + if (ctl->scrollWheel() > 0) { |
| 146 | + // Do Something |
| 147 | + } else if (ctl->scrollWheel() < 0) { |
| 148 | + // Do something else |
| 149 | + } |
| 150 | + |
| 151 | + // See "dumpMouse" for possible things to query. |
| 152 | + dumpMouse(ctl); |
| 153 | +} |
159 | 154 |
|
160 |
| - // vTaskDelay(1); |
161 |
| - delay(150); |
| 155 | +void processKeyboard(ControllerPtr ctl) { |
| 156 | + // This is just an example. |
| 157 | + if (ctl->isKeyPressed(Keyboard_A)) { |
| 158 | + // Do Something |
| 159 | + Serial.println("Key 'A' pressed"); |
| 160 | + } |
| 161 | + |
| 162 | + // Don't do "else" here. |
| 163 | + // Multiple keys can be pressed at the same time. |
| 164 | + if (ctl->isKeyPressed(Keyboard_LeftShift)) { |
| 165 | + // Do something else |
| 166 | + Serial.println("Key 'LEFT SHIFT' pressed"); |
| 167 | + } |
| 168 | + |
| 169 | + // Don't do "else" here. |
| 170 | + // Multiple keys can be pressed at the same time. |
| 171 | + if (ctl->isKeyPressed(Keyboard_LeftArrow)) { |
| 172 | + // Do something else |
| 173 | + Serial.println("Key 'Left Arrow' pressed"); |
| 174 | + } |
| 175 | + |
| 176 | + // See "dumpKeyboard" for possible things to query. |
| 177 | + dumpKeyboard(ctl); |
| 178 | +} |
| 179 | + |
| 180 | +void processBalanceBoard(ControllerPtr ctl) { |
| 181 | + // This is just an example. |
| 182 | + if (ctl->topLeft() > 10000) { |
| 183 | + // Do Something |
| 184 | + } |
| 185 | + |
| 186 | + // See "dumpBalanceBoard" for possible things to query. |
| 187 | + dumpBalanceBoard(ctl); |
| 188 | +} |
| 189 | + |
| 190 | +// Arduino setup function. Runs in CPU 1 |
| 191 | +void setup() { |
| 192 | + Serial.begin(115200); |
| 193 | + Serial.printf("Firmware: %s\n", BP32.firmwareVersion()); |
| 194 | + const uint8_t* addr = BP32.localBdAddress(); |
| 195 | + Serial.printf("BD Addr: %2X:%2X:%2X:%2X:%2X:%2X\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); |
| 196 | + |
| 197 | + // Setup the Bluepad32 callbacks |
| 198 | + BP32.setup(&onConnectedController, &onDisconnectedController); |
| 199 | + |
| 200 | + // "forgetBluetoothKeys()" should be called when the user performs |
| 201 | + // a "device factory reset", or similar. |
| 202 | + // Calling "forgetBluetoothKeys" in setup() just as an example. |
| 203 | + // Forgetting Bluetooth keys prevents "paired" gamepads to reconnect. |
| 204 | + // But might also fix some connection / re-connection issues. |
| 205 | + BP32.forgetBluetoothKeys(); |
| 206 | + |
| 207 | + // Enables mouse / touchpad support for gamepads that support them. |
| 208 | + // When enabled controllers like DualSense and DualShock4 generate two connected devices: |
| 209 | + // - First one: the gamepad |
| 210 | + // - Second one, which is a "vritual device", is a mouse |
| 211 | + // By default it is disabled. |
| 212 | + BP32.enableVirtualDevice(false); |
| 213 | +} |
| 214 | + |
| 215 | +// Arduino loop function. Runs in CPU 1 |
| 216 | +void loop() { |
| 217 | + // This call fetches all the gamepad info from the NINA (ESP32) module. |
| 218 | + // Just call this function in your main loop. |
| 219 | + // The gamepads pointer (the ones received in the callbacks) gets updated |
| 220 | + // automatically. |
| 221 | + BP32.update(); |
| 222 | + |
| 223 | + // It is safe to always do this before using the gamepad API. |
| 224 | + // This guarantees that the gamepad is valid and connected. |
| 225 | + for (int i = 0; i < BP32_MAX_GAMEPADS; i++) { |
| 226 | + ControllerPtr myController = myControllers[i]; |
| 227 | + |
| 228 | + if (myController && myController->isConnected()) { |
| 229 | + if (myController->isGamepad()) { |
| 230 | + processGamepad(myController); |
| 231 | + } else if (myController->isMouse()) { |
| 232 | + processMouse(myController); |
| 233 | + } else if (myController->isKeyboard()) { |
| 234 | + processKeyboard(myController); |
| 235 | + } else if (myController->isBalanceBoard()) { |
| 236 | + processBalanceBoard(myController); |
| 237 | + } else { |
| 238 | + Serial.printf("Data not available yet\n"); |
| 239 | + continue; |
| 240 | + } |
| 241 | + // See ArduinoController.h for all the available functions. |
| 242 | + } |
| 243 | + } |
| 244 | + // The main loop must have some kind of "yield to lower priority task" event. |
| 245 | + // Otherwise the watchdog will get triggered. |
| 246 | + // If your main loop doesn't have one, just add a simple `vTaskDelay(1)`. |
| 247 | + // Detailed info here: |
| 248 | + // https://stackoverflow.com/questions/66278271/task-watchdog-got-triggered-the-tasks-did-not-reset-the-watchdog-in-time |
| 249 | + |
| 250 | + // vTaskDelay(1); |
| 251 | + delay(150); |
162 | 252 | }
|
0 commit comments