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
# 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)
......
......@@ -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);
......@@ -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();
}
......@@ -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();
}
}
......@@ -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();
......@@ -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() {
......@@ -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();
}
}
......@@ -21,6 +21,8 @@ scanAsyncResult KEYWORD2
connectKeyboard KEYWORD2
connectMouse KEYWORD2
connectJoypad KEYWORD2
connectAny KEYWORD2
hidConnected KEYWORD2
onMouseMove KEYWORD2
......@@ -29,6 +31,7 @@ onKeyDown KEYWORD2
onKeyUp KEYWORD2
onConsumerKeyDown KEYWORD2
onConsumerKeyUp KEYWORD2
onJoypad KEYWORD2
# BTDeviceInfo
deviceClass KEYWORD2
......
......@@ -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);
}
......@@ -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()) {
......@@ -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;
......@@ -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);
}
......@@ -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);
}
}
......@@ -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);
}
}
......
......@@ -78,6 +78,7 @@ public:
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();
......@@ -86,6 +87,8 @@ public:
bool connect(const uint8_t *addr);
bool connectKeyboard();
bool connectMouse();
bool connectJoypad();
bool connectAny();
bool connectBLE(const uint8_t *addr, int addrType);
bool connectBLE();
......@@ -99,6 +102,7 @@ public:
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;
......@@ -131,6 +135,10 @@ private:
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);
......
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