Skip to content

Commit 1fd66bf

Browse files
Add joypad HID master support (#2214)
Play games on your Pico using Bluetooth gamepads!
1 parent 151c52c commit 1fd66bf

File tree

6 files changed

+76
-11
lines changed

6 files changed

+76
-11
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
8787
# Features
8888
* Adafruit TinyUSB Arduino (USB mouse, keyboard, flash drive, generic HID, CDC Serial, MIDI, WebUSB, others)
8989
* Bluetooth on the PicoW (Classic and BLE) with Keyboard, Mouse, Joystick, and Virtual Serial
90-
* Bluetooth Classic and BLE HID master mode (connect to BT keyboard or mouse)
90+
* Bluetooth Classic and BLE HID master mode (connect to BT keyboard, mouse, or joystick)
9191
* Generic Arduino USB Serial, Keyboard, Joystick, and Mouse emulation
9292
* WiFi (Pico W, ESP32-based ESPHost, Atmel WINC1500)
9393
* Ethernet (Wired W5500, W5100, ENC28J60)

libraries/BluetoothHIDMaster/examples/KeyboardPiano/KeyboardPiano.ino

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,18 @@ void ckb(void *cbdata, int key) {
158158
}
159159

160160

161+
// Joystick can get reports of 4 analog axes, 1 d-pad bitfield, and up to 32 buttons
162+
// Axes and hats that aren't reported by the pad are read as 0
163+
void joy(void *cbdata, int x, int y, int z, int rz, uint8_t hat, uint32_t buttons) {
164+
(void) cbdata;
165+
const char *hats[16] = { "U", "UR", "R", "DR", "D", "DL", "L", "UL", "", "", "", "", "", "", "", "." };
166+
Serial.printf("Joystick: (%4d, %4d) (%4d, %4d), Hat: %-2s, Buttons:", x, y, z, rz, hats[hat & 15]);
167+
for (int i = 0; i < 32; i++) {
168+
Serial.printf(" %c", (buttons & 1 << i) ? '*' : '.');
169+
}
170+
Serial.println();
171+
}
172+
161173
void setup() {
162174
Serial.begin();
163175
delay(3000);
@@ -190,9 +202,11 @@ void setup() {
190202
hid.onConsumerKeyDown(ckb, (void *)true);
191203
hid.onConsumerKeyUp(ckb, (void *)false);
192204

205+
hid.onJoypad(joy);
206+
193207
hid.begin();
194208

195-
hid.connectKeyboard();
209+
hid.connectAny();
196210
// or hid.connectMouse();
197211
}
198212

@@ -204,6 +218,6 @@ void loop() {
204218
hid.disconnect();
205219
hid.clearPairing();
206220
Serial.printf("Restarting HID master, put your device in pairing mode now.\n");
207-
hid.connectKeyboard();
221+
hid.connectAny();
208222
}
209223
}

libraries/BluetoothHIDMaster/examples/KeyboardPianoBLE/KeyboardPianoBLE.ino

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,18 @@ void ckb(void *cbdata, int key) {
157157
Serial.printf("Consumer: %02x %s\n", key, state ? "DOWN" : "UP");
158158
}
159159

160+
// Joystick can get reports of 4 analog axes, 1 d-pad bitfield, and up to 32 buttons
161+
// Axes and hats that aren't reported by the pad are read as 0
162+
void joy(void *cbdata, int x, int y, int z, int rz, uint8_t hat, uint32_t buttons) {
163+
(void) cbdata;
164+
const char *hats[16] = { "U", "UR", "R", "DR", "D", "DL", "L", "UL", "", "", "", "", "", "", "", "." };
165+
Serial.printf("Joystick: (%4d, %4d) (%4d, %4d), Hat: %-2s, Buttons:", x, y, z, rz, hats[hat & 15]);
166+
for (int i = 0; i < 32; i++) {
167+
Serial.printf(" %c", (buttons & 1 << i) ? '*' : '.');
168+
}
169+
Serial.println();
170+
}
171+
160172

161173
void setup() {
162174
Serial.begin();
@@ -190,10 +202,11 @@ void setup() {
190202
hid.onConsumerKeyDown(ckb, (void *)true);
191203
hid.onConsumerKeyUp(ckb, (void *)false);
192204

205+
hid.onJoypad(joy);
206+
193207
hid.begin(true);
194208

195-
hid.connectBLE(); //Keyboard();
196-
// or hid.connectMouse();
209+
hid.connectBLE();
197210
}
198211

199212
void loop() {
@@ -204,6 +217,6 @@ void loop() {
204217
hid.disconnect();
205218
hid.clearPairing();
206219
Serial.printf("Restarting HID master, put your device in pairing mode now.\n");
207-
hid.connectBLE(); //Keyboard();
220+
hid.connectBLE();
208221
}
209222
}

libraries/BluetoothHIDMaster/keywords.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ scanAsyncResult KEYWORD2
2121

2222
connectKeyboard KEYWORD2
2323
connectMouse KEYWORD2
24+
connectJoypad KEYWORD2
25+
connectAny KEYWORD2
2426

2527
hidConnected KEYWORD2
2628
onMouseMove KEYWORD2
@@ -29,6 +31,7 @@ onKeyDown KEYWORD2
2931
onKeyUp KEYWORD2
3032
onConsumerKeyDown KEYWORD2
3133
onConsumerKeyUp KEYWORD2
34+
onJoypad KEYWORD2
3235

3336
# BTDeviceInfo
3437
deviceClass KEYWORD2

libraries/BluetoothHIDMaster/src/BluetoothHIDMaster.cpp

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ void BluetoothHIDMaster::onConsumerKeyUp(void (*cb)(void *, int), void *cbData)
161161
_consumerKeyUpData = cbData;
162162
}
163163

164+
void BluetoothHIDMaster::onJoypad(void (*cb)(void *, int, int, int, int, uint8_t, uint32_t), void *cbData) {
165+
_joypadCB = cb;
166+
_joypadData = cbData;
167+
}
168+
164169
std::list<BTDeviceInfo> BluetoothHIDMaster::scan(uint32_t mask, int scanTimeSec, bool async) {
165170
return _hci.scan(mask, scanTimeSec, async);
166171
}
@@ -252,6 +257,14 @@ bool BluetoothHIDMaster::connectMouse() {
252257
return connectCOD(0x2580);
253258
}
254259

260+
bool BluetoothHIDMaster::connectJoypad() {
261+
return connectCOD(0x2508);
262+
}
263+
264+
bool BluetoothHIDMaster::connectAny() {
265+
return connectCOD(0x2500);
266+
}
267+
255268
bool BluetoothHIDMaster::disconnect() {
256269
BluetoothLock b;
257270
if (!_running || !connected()) {
@@ -287,17 +300,21 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t *
287300
int new_keys_count = 0;
288301

289302
uint16_t new_consumer_key = 0;
290-
uint8_t newMB = 0;
303+
uint32_t newMB = 0;
291304
bool noMB = false;
292305

293306
bool updCons = false;
294307
bool updKey = false;
295308
bool updMB = false;
309+
bool updJoy = false;
296310

297311
bool updMouse = false;
298312
int dx = 0;
299313
int dy = 0;
314+
int dz = 0;
315+
int rz = 0;
300316
int dwheel = 0;
317+
uint8_t hat = 0;
301318

302319
while (btstack_hid_parser_has_more(parser)) {
303320
uint16_t usage_page;
@@ -306,19 +323,26 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t *
306323
btstack_hid_parser_get_field(parser, &usage_page, &usage, &value);
307324
if (usage_page == 0x01) {
308325
updMouse = true;
326+
updJoy = true;
309327
if (usage == 0x30) {
310328
dx = value;
311329
} else if (usage == 0x31) {
312330
dy = value;
331+
} else if (usage == 0x32) {
332+
dz = value;
333+
} else if (usage == 0x35) {
334+
rz = value;
313335
} else if (usage == 0x38) {
314336
dwheel = value;
337+
} else if (usage == 0x39) {
338+
hat = value & 0xff;
315339
}
316340
} else if (usage_page == 0x09) {
317341
updMB = true;
318342
if (usage == 0) {
319343
noMB = true;
320344
}
321-
if (!noMB && value && (usage > 0) && (usage < 9)) {
345+
if (!noMB && value && (usage > 0)) {
322346
newMB |= 1 << (usage - 1);
323347
}
324348

@@ -386,13 +410,13 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t *
386410
if (updCons) {
387411
last_consumer_key = new_consumer_key;
388412
}
389-
if (updMB) {
413+
if (updMB && _mouseButtonCB) {
390414
if (lastMB != newMB) {
391415
for (int i = 0; i < 8; i++) {
392416
int mask = 1 << i;
393-
if ((lastMB & mask) && !(newMB & mask) && _mouseButtonCB) {
417+
if ((lastMB & mask) && !(newMB & mask)) {
394418
_mouseButtonCB(_mouseButtonData, i, false);
395-
} else if (!(lastMB & mask) && (newMB & mask) && _mouseButtonCB) {
419+
} else if (!(lastMB & mask) && (newMB & mask)) {
396420
_mouseButtonCB(_mouseButtonData, i, true);
397421
}
398422
}
@@ -403,6 +427,9 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t *
403427
if (updMouse && _mouseMoveCB) {
404428
_mouseMoveCB(_mouseMoveData, dx, dy, dwheel);
405429
}
430+
if (updJoy && _joypadCB) {
431+
_joypadCB(_joypadData, dx, dy, dz, rz, hat, newMB);
432+
}
406433
}
407434

408435

libraries/BluetoothHIDMaster/src/BluetoothHIDMaster.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class BluetoothHIDMaster {
7878

7979
static const uint32_t keyboard_cod = 0x2540;
8080
static const uint32_t mouse_cod = 0x2540;
81+
static const uint32_t joypad_cod = 0x2508;
8182
static const uint32_t any_cod = 0;
8283
std::list<BTDeviceInfo> scan(uint32_t mask, int scanTimeSec = 5, bool async = false);
8384
bool scanAsyncDone();
@@ -86,6 +87,8 @@ class BluetoothHIDMaster {
8687
bool connect(const uint8_t *addr);
8788
bool connectKeyboard();
8889
bool connectMouse();
90+
bool connectJoypad();
91+
bool connectAny();
8992

9093
bool connectBLE(const uint8_t *addr, int addrType);
9194
bool connectBLE();
@@ -99,6 +102,7 @@ class BluetoothHIDMaster {
99102
void onKeyUp(void (*)(void *, int), void *cbData = nullptr);
100103
void onConsumerKeyDown(void (*)(void *, int), void *cbData = nullptr);
101104
void onConsumerKeyUp(void (*)(void *, int), void *cbData = nullptr);
105+
void onJoypad(void (*)(void *, int, int, int, int, uint8_t, uint32_t), void *cbData = nullptr);
102106

103107
private:
104108
bool _ble = false;
@@ -131,6 +135,10 @@ class BluetoothHIDMaster {
131135
void (*_consumerKeyUpCB)(void *, int) = nullptr;
132136
void *_consumerKeyUpData;
133137

138+
void (*_joypadCB)(void *, int, int, int, int, uint8_t, uint32_t) = nullptr;
139+
void *_joypadData;
140+
141+
134142
btstack_packet_callback_registration_t _sm_event_callback_registration;
135143
void sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
136144
void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);

0 commit comments

Comments
 (0)