Skip to content

Commit

Permalink
Add joypad HID master support (#2214)
Browse files Browse the repository at this point in the history
Play games on your Pico using Bluetooth gamepads!
  • Loading branch information
earlephilhower committed Jun 9, 2024
1 parent 151c52c commit 1fd66bf
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 11 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
# Features
* Adafruit TinyUSB Arduino (USB mouse, keyboard, flash drive, generic HID, CDC Serial, MIDI, WebUSB, others)
* Bluetooth on the PicoW (Classic and BLE) with Keyboard, Mouse, Joystick, and Virtual Serial
* Bluetooth Classic and BLE HID master mode (connect to BT keyboard or mouse)
* Bluetooth Classic and BLE HID master mode (connect to BT keyboard, mouse, or joystick)
* Generic Arduino USB Serial, Keyboard, Joystick, and Mouse emulation
* WiFi (Pico W, ESP32-based ESPHost, Atmel WINC1500)
* Ethernet (Wired W5500, W5100, ENC28J60)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ void ckb(void *cbdata, int key) {
}


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

void setup() {
Serial.begin();
delay(3000);
Expand Down Expand Up @@ -190,9 +202,11 @@ void setup() {
hid.onConsumerKeyDown(ckb, (void *)true);
hid.onConsumerKeyUp(ckb, (void *)false);

hid.onJoypad(joy);

hid.begin();

hid.connectKeyboard();
hid.connectAny();
// or hid.connectMouse();
}

Expand All @@ -204,6 +218,6 @@ void loop() {
hid.disconnect();
hid.clearPairing();
Serial.printf("Restarting HID master, put your device in pairing mode now.\n");
hid.connectKeyboard();
hid.connectAny();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,18 @@ void ckb(void *cbdata, int key) {
Serial.printf("Consumer: %02x %s\n", key, state ? "DOWN" : "UP");
}

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


void setup() {
Serial.begin();
Expand Down Expand Up @@ -190,10 +202,11 @@ void setup() {
hid.onConsumerKeyDown(ckb, (void *)true);
hid.onConsumerKeyUp(ckb, (void *)false);

hid.onJoypad(joy);

hid.begin(true);

hid.connectBLE(); //Keyboard();
// or hid.connectMouse();
hid.connectBLE();
}

void loop() {
Expand All @@ -204,6 +217,6 @@ void loop() {
hid.disconnect();
hid.clearPairing();
Serial.printf("Restarting HID master, put your device in pairing mode now.\n");
hid.connectBLE(); //Keyboard();
hid.connectBLE();
}
}
3 changes: 3 additions & 0 deletions libraries/BluetoothHIDMaster/keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ scanAsyncResult KEYWORD2

connectKeyboard KEYWORD2
connectMouse KEYWORD2
connectJoypad KEYWORD2
connectAny KEYWORD2

hidConnected KEYWORD2
onMouseMove KEYWORD2
Expand All @@ -29,6 +31,7 @@ onKeyDown KEYWORD2
onKeyUp KEYWORD2
onConsumerKeyDown KEYWORD2
onConsumerKeyUp KEYWORD2
onJoypad KEYWORD2

# BTDeviceInfo
deviceClass KEYWORD2
Expand Down
37 changes: 32 additions & 5 deletions libraries/BluetoothHIDMaster/src/BluetoothHIDMaster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,11 @@ void BluetoothHIDMaster::onConsumerKeyUp(void (*cb)(void *, int), void *cbData)
_consumerKeyUpData = cbData;
}

void BluetoothHIDMaster::onJoypad(void (*cb)(void *, int, int, int, int, uint8_t, uint32_t), void *cbData) {
_joypadCB = cb;
_joypadData = cbData;
}

std::list<BTDeviceInfo> BluetoothHIDMaster::scan(uint32_t mask, int scanTimeSec, bool async) {
return _hci.scan(mask, scanTimeSec, async);
}
Expand Down Expand Up @@ -252,6 +257,14 @@ bool BluetoothHIDMaster::connectMouse() {
return connectCOD(0x2580);
}

bool BluetoothHIDMaster::connectJoypad() {
return connectCOD(0x2508);
}

bool BluetoothHIDMaster::connectAny() {
return connectCOD(0x2500);
}

bool BluetoothHIDMaster::disconnect() {
BluetoothLock b;
if (!_running || !connected()) {
Expand Down Expand Up @@ -287,17 +300,21 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t *
int new_keys_count = 0;

uint16_t new_consumer_key = 0;
uint8_t newMB = 0;
uint32_t newMB = 0;
bool noMB = false;

bool updCons = false;
bool updKey = false;
bool updMB = false;
bool updJoy = false;

bool updMouse = false;
int dx = 0;
int dy = 0;
int dz = 0;
int rz = 0;
int dwheel = 0;
uint8_t hat = 0;

while (btstack_hid_parser_has_more(parser)) {
uint16_t usage_page;
Expand All @@ -306,19 +323,26 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t *
btstack_hid_parser_get_field(parser, &usage_page, &usage, &value);
if (usage_page == 0x01) {
updMouse = true;
updJoy = true;
if (usage == 0x30) {
dx = value;
} else if (usage == 0x31) {
dy = value;
} else if (usage == 0x32) {
dz = value;
} else if (usage == 0x35) {
rz = value;
} else if (usage == 0x38) {
dwheel = value;
} else if (usage == 0x39) {
hat = value & 0xff;
}
} else if (usage_page == 0x09) {
updMB = true;
if (usage == 0) {
noMB = true;
}
if (!noMB && value && (usage > 0) && (usage < 9)) {
if (!noMB && value && (usage > 0)) {
newMB |= 1 << (usage - 1);
}

Expand Down Expand Up @@ -386,13 +410,13 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t *
if (updCons) {
last_consumer_key = new_consumer_key;
}
if (updMB) {
if (updMB && _mouseButtonCB) {
if (lastMB != newMB) {
for (int i = 0; i < 8; i++) {
int mask = 1 << i;
if ((lastMB & mask) && !(newMB & mask) && _mouseButtonCB) {
if ((lastMB & mask) && !(newMB & mask)) {
_mouseButtonCB(_mouseButtonData, i, false);
} else if (!(lastMB & mask) && (newMB & mask) && _mouseButtonCB) {
} else if (!(lastMB & mask) && (newMB & mask)) {
_mouseButtonCB(_mouseButtonData, i, true);
}
}
Expand All @@ -403,6 +427,9 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t *
if (updMouse && _mouseMoveCB) {
_mouseMoveCB(_mouseMoveData, dx, dy, dwheel);
}
if (updJoy && _joypadCB) {
_joypadCB(_joypadData, dx, dy, dz, rz, hat, newMB);
}
}


Expand Down
8 changes: 8 additions & 0 deletions libraries/BluetoothHIDMaster/src/BluetoothHIDMaster.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class BluetoothHIDMaster {

static const uint32_t keyboard_cod = 0x2540;
static const uint32_t mouse_cod = 0x2540;
static const uint32_t joypad_cod = 0x2508;
static const uint32_t any_cod = 0;
std::list<BTDeviceInfo> scan(uint32_t mask, int scanTimeSec = 5, bool async = false);
bool scanAsyncDone();
Expand All @@ -86,6 +87,8 @@ class BluetoothHIDMaster {
bool connect(const uint8_t *addr);
bool connectKeyboard();
bool connectMouse();
bool connectJoypad();
bool connectAny();

bool connectBLE(const uint8_t *addr, int addrType);
bool connectBLE();
Expand All @@ -99,6 +102,7 @@ class BluetoothHIDMaster {
void onKeyUp(void (*)(void *, int), void *cbData = nullptr);
void onConsumerKeyDown(void (*)(void *, int), void *cbData = nullptr);
void onConsumerKeyUp(void (*)(void *, int), void *cbData = nullptr);
void onJoypad(void (*)(void *, int, int, int, int, uint8_t, uint32_t), void *cbData = nullptr);

private:
bool _ble = false;
Expand Down Expand Up @@ -131,6 +135,10 @@ class BluetoothHIDMaster {
void (*_consumerKeyUpCB)(void *, int) = nullptr;
void *_consumerKeyUpData;

void (*_joypadCB)(void *, int, int, int, int, uint8_t, uint32_t) = nullptr;
void *_joypadData;


btstack_packet_callback_registration_t _sm_event_callback_registration;
void sm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
Expand Down

0 comments on commit 1fd66bf

Please sign in to comment.