Unverified Commit 1fd66bf9 authored by Earle F. Philhower, III's avatar Earle F. Philhower, III Committed by GitHub

Add joypad HID master support (#2214)

Play games on your Pico using Bluetooth gamepads!
parent 151c52c1
...@@ -87,7 +87,7 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo ...@@ -87,7 +87,7 @@ Read the [Contributing Guide](https://github.com/earlephilhower/arduino-pico/blo
# Features # Features
* Adafruit TinyUSB Arduino (USB mouse, keyboard, flash drive, generic HID, CDC Serial, MIDI, WebUSB, others) * 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 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 * Generic Arduino USB Serial, Keyboard, Joystick, and Mouse emulation
* WiFi (Pico W, ESP32-based ESPHost, Atmel WINC1500) * WiFi (Pico W, ESP32-based ESPHost, Atmel WINC1500)
* Ethernet (Wired W5500, W5100, ENC28J60) * Ethernet (Wired W5500, W5100, ENC28J60)
......
...@@ -158,6 +158,18 @@ void ckb(void *cbdata, int key) { ...@@ -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() { void setup() {
Serial.begin(); Serial.begin();
delay(3000); delay(3000);
...@@ -190,9 +202,11 @@ void setup() { ...@@ -190,9 +202,11 @@ void setup() {
hid.onConsumerKeyDown(ckb, (void *)true); hid.onConsumerKeyDown(ckb, (void *)true);
hid.onConsumerKeyUp(ckb, (void *)false); hid.onConsumerKeyUp(ckb, (void *)false);
hid.onJoypad(joy);
hid.begin(); hid.begin();
hid.connectKeyboard(); hid.connectAny();
// or hid.connectMouse(); // or hid.connectMouse();
} }
...@@ -204,6 +218,6 @@ void loop() { ...@@ -204,6 +218,6 @@ void loop() {
hid.disconnect(); hid.disconnect();
hid.clearPairing(); hid.clearPairing();
Serial.printf("Restarting HID master, put your device in pairing mode now.\n"); Serial.printf("Restarting HID master, put your device in pairing mode now.\n");
hid.connectKeyboard(); hid.connectAny();
} }
} }
...@@ -157,6 +157,18 @@ void ckb(void *cbdata, int key) { ...@@ -157,6 +157,18 @@ void ckb(void *cbdata, int key) {
Serial.printf("Consumer: %02x %s\n", key, state ? "DOWN" : "UP"); 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() { void setup() {
Serial.begin(); Serial.begin();
...@@ -190,10 +202,11 @@ void setup() { ...@@ -190,10 +202,11 @@ void setup() {
hid.onConsumerKeyDown(ckb, (void *)true); hid.onConsumerKeyDown(ckb, (void *)true);
hid.onConsumerKeyUp(ckb, (void *)false); hid.onConsumerKeyUp(ckb, (void *)false);
hid.onJoypad(joy);
hid.begin(true); hid.begin(true);
hid.connectBLE(); //Keyboard(); hid.connectBLE();
// or hid.connectMouse();
} }
void loop() { void loop() {
...@@ -204,6 +217,6 @@ void loop() { ...@@ -204,6 +217,6 @@ void loop() {
hid.disconnect(); hid.disconnect();
hid.clearPairing(); hid.clearPairing();
Serial.printf("Restarting HID master, put your device in pairing mode now.\n"); Serial.printf("Restarting HID master, put your device in pairing mode now.\n");
hid.connectBLE(); //Keyboard(); hid.connectBLE();
} }
} }
...@@ -21,6 +21,8 @@ scanAsyncResult KEYWORD2 ...@@ -21,6 +21,8 @@ scanAsyncResult KEYWORD2
connectKeyboard KEYWORD2 connectKeyboard KEYWORD2
connectMouse KEYWORD2 connectMouse KEYWORD2
connectJoypad KEYWORD2
connectAny KEYWORD2
hidConnected KEYWORD2 hidConnected KEYWORD2
onMouseMove KEYWORD2 onMouseMove KEYWORD2
...@@ -29,6 +31,7 @@ onKeyDown KEYWORD2 ...@@ -29,6 +31,7 @@ onKeyDown KEYWORD2
onKeyUp KEYWORD2 onKeyUp KEYWORD2
onConsumerKeyDown KEYWORD2 onConsumerKeyDown KEYWORD2
onConsumerKeyUp KEYWORD2 onConsumerKeyUp KEYWORD2
onJoypad KEYWORD2
# BTDeviceInfo # BTDeviceInfo
deviceClass KEYWORD2 deviceClass KEYWORD2
......
...@@ -161,6 +161,11 @@ void BluetoothHIDMaster::onConsumerKeyUp(void (*cb)(void *, int), void *cbData) ...@@ -161,6 +161,11 @@ void BluetoothHIDMaster::onConsumerKeyUp(void (*cb)(void *, int), void *cbData)
_consumerKeyUpData = 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) { std::list<BTDeviceInfo> BluetoothHIDMaster::scan(uint32_t mask, int scanTimeSec, bool async) {
return _hci.scan(mask, scanTimeSec, async); return _hci.scan(mask, scanTimeSec, async);
} }
...@@ -252,6 +257,14 @@ bool BluetoothHIDMaster::connectMouse() { ...@@ -252,6 +257,14 @@ bool BluetoothHIDMaster::connectMouse() {
return connectCOD(0x2580); return connectCOD(0x2580);
} }
bool BluetoothHIDMaster::connectJoypad() {
return connectCOD(0x2508);
}
bool BluetoothHIDMaster::connectAny() {
return connectCOD(0x2500);
}
bool BluetoothHIDMaster::disconnect() { bool BluetoothHIDMaster::disconnect() {
BluetoothLock b; BluetoothLock b;
if (!_running || !connected()) { if (!_running || !connected()) {
...@@ -287,17 +300,21 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t * ...@@ -287,17 +300,21 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t *
int new_keys_count = 0; int new_keys_count = 0;
uint16_t new_consumer_key = 0; uint16_t new_consumer_key = 0;
uint8_t newMB = 0; uint32_t newMB = 0;
bool noMB = false; bool noMB = false;
bool updCons = false; bool updCons = false;
bool updKey = false; bool updKey = false;
bool updMB = false; bool updMB = false;
bool updJoy = false;
bool updMouse = false; bool updMouse = false;
int dx = 0; int dx = 0;
int dy = 0; int dy = 0;
int dz = 0;
int rz = 0;
int dwheel = 0; int dwheel = 0;
uint8_t hat = 0;
while (btstack_hid_parser_has_more(parser)) { while (btstack_hid_parser_has_more(parser)) {
uint16_t usage_page; uint16_t usage_page;
...@@ -306,19 +323,26 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t * ...@@ -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); btstack_hid_parser_get_field(parser, &usage_page, &usage, &value);
if (usage_page == 0x01) { if (usage_page == 0x01) {
updMouse = true; updMouse = true;
updJoy = true;
if (usage == 0x30) { if (usage == 0x30) {
dx = value; dx = value;
} else if (usage == 0x31) { } else if (usage == 0x31) {
dy = value; dy = value;
} else if (usage == 0x32) {
dz = value;
} else if (usage == 0x35) {
rz = value;
} else if (usage == 0x38) { } else if (usage == 0x38) {
dwheel = value; dwheel = value;
} else if (usage == 0x39) {
hat = value & 0xff;
} }
} else if (usage_page == 0x09) { } else if (usage_page == 0x09) {
updMB = true; updMB = true;
if (usage == 0) { if (usage == 0) {
noMB = true; noMB = true;
} }
if (!noMB && value && (usage > 0) && (usage < 9)) { if (!noMB && value && (usage > 0)) {
newMB |= 1 << (usage - 1); newMB |= 1 << (usage - 1);
} }
...@@ -386,13 +410,13 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t * ...@@ -386,13 +410,13 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t *
if (updCons) { if (updCons) {
last_consumer_key = new_consumer_key; last_consumer_key = new_consumer_key;
} }
if (updMB) { if (updMB && _mouseButtonCB) {
if (lastMB != newMB) { if (lastMB != newMB) {
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
int mask = 1 << i; int mask = 1 << i;
if ((lastMB & mask) && !(newMB & mask) && _mouseButtonCB) { if ((lastMB & mask) && !(newMB & mask)) {
_mouseButtonCB(_mouseButtonData, i, false); _mouseButtonCB(_mouseButtonData, i, false);
} else if (!(lastMB & mask) && (newMB & mask) && _mouseButtonCB) { } else if (!(lastMB & mask) && (newMB & mask)) {
_mouseButtonCB(_mouseButtonData, i, true); _mouseButtonCB(_mouseButtonData, i, true);
} }
} }
...@@ -403,6 +427,9 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t * ...@@ -403,6 +427,9 @@ void BluetoothHIDMaster::hid_host_handle_interrupt_report(btstack_hid_parser_t *
if (updMouse && _mouseMoveCB) { if (updMouse && _mouseMoveCB) {
_mouseMoveCB(_mouseMoveData, dx, dy, dwheel); _mouseMoveCB(_mouseMoveData, dx, dy, dwheel);
} }
if (updJoy && _joypadCB) {
_joypadCB(_joypadData, dx, dy, dz, rz, hat, newMB);
}
} }
......
...@@ -78,6 +78,7 @@ public: ...@@ -78,6 +78,7 @@ public:
static const uint32_t keyboard_cod = 0x2540; static const uint32_t keyboard_cod = 0x2540;
static const uint32_t mouse_cod = 0x2540; static const uint32_t mouse_cod = 0x2540;
static const uint32_t joypad_cod = 0x2508;
static const uint32_t any_cod = 0; static const uint32_t any_cod = 0;
std::list<BTDeviceInfo> scan(uint32_t mask, int scanTimeSec = 5, bool async = false); std::list<BTDeviceInfo> scan(uint32_t mask, int scanTimeSec = 5, bool async = false);
bool scanAsyncDone(); bool scanAsyncDone();
...@@ -86,6 +87,8 @@ public: ...@@ -86,6 +87,8 @@ public:
bool connect(const uint8_t *addr); bool connect(const uint8_t *addr);
bool connectKeyboard(); bool connectKeyboard();
bool connectMouse(); bool connectMouse();
bool connectJoypad();
bool connectAny();
bool connectBLE(const uint8_t *addr, int addrType); bool connectBLE(const uint8_t *addr, int addrType);
bool connectBLE(); bool connectBLE();
...@@ -99,6 +102,7 @@ public: ...@@ -99,6 +102,7 @@ public:
void onKeyUp(void (*)(void *, int), void *cbData = nullptr); void onKeyUp(void (*)(void *, int), void *cbData = nullptr);
void onConsumerKeyDown(void (*)(void *, int), void *cbData = nullptr); void onConsumerKeyDown(void (*)(void *, int), void *cbData = nullptr);
void onConsumerKeyUp(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: private:
bool _ble = false; bool _ble = false;
...@@ -131,6 +135,10 @@ private: ...@@ -131,6 +135,10 @@ private:
void (*_consumerKeyUpCB)(void *, int) = nullptr; void (*_consumerKeyUpCB)(void *, int) = nullptr;
void *_consumerKeyUpData; 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; 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 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); void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment