Skip to content

Commit 54242a6

Browse files
authored
Merge pull request #730 from OstlerDev/feat_BLEClientHid_Gamepad
HID Gamepad Client & Central HID Gamepad example
2 parents ec8b49a + 0f30241 commit 54242a6

File tree

5 files changed

+146
-18
lines changed

5 files changed

+146
-18
lines changed

libraries/Bluefruit52Lib/examples/Central/central_hid/central_hid.ino

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,33 @@
1818
*/
1919
#include <bluefruit.h>
2020

21-
// Polling or callback implementation
22-
#define POLLING 1
21+
/*
22+
* Polling or callback implementation
23+
* 0 = Request data using a poll
24+
* 1 = Use callbacks
25+
*/
26+
#define POLLING 0
27+
/*
28+
* Device Type flag for use in the example
29+
* Change this if you want to demo the Gamepad!
30+
* 0 = Keyboard + Mouse
31+
* 1 = Gamepad
32+
*/
33+
#define DEVICE_TYPE 0
2334

2435
BLEClientHidAdafruit hid;
2536

2637
// Last checked report, to detect if there is changes between reports
2738
hid_keyboard_report_t last_kbd_report = { 0 };
2839
hid_mouse_report_t last_mse_report = { 0 };
40+
hid_gamepad_report_t last_gpd_report = { 0 };
2941

3042
void setup()
3143
{
3244
Serial.begin(115200);
3345
// while ( !Serial ) delay(10); // for nrf52840 with native usb
3446

35-
Serial.println("Bluefruit52 Central HID (Keyboard + Mouse) Example");
47+
Serial.println("Bluefruit52 Central HID (Keyboard + Mouse + Gamepad) Example");
3648
Serial.println("--------------------------------------------------\n");
3749

3850
// Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
@@ -46,6 +58,7 @@ void setup()
4658

4759
#if POLLING == 0
4860
hid.setKeyboardReportCallback(keyboard_report_callback);
61+
hid.setGamepadReportCallback(gamepad_report_callback);
4962
#endif
5063

5164
// Increase Blink rate to different from PrPh advertising mode
@@ -69,7 +82,7 @@ void setup()
6982
Bluefruit.Scanner.restartOnDisconnect(true);
7083
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
7184
Bluefruit.Scanner.filterService(hid); // only report HID service
72-
Bluefruit.Scanner.useActiveScan(false);
85+
Bluefruit.Scanner.useActiveScan(true);
7386
Bluefruit.Scanner.start(0); // 0 = Don't stop scanning after n seconds
7487
}
7588

@@ -137,15 +150,21 @@ void connection_secured_callback(uint16_t conn_handle)
137150
Serial.printf("HID Flags : 0x%02X\n", hidInfo[3]);
138151

139152
// BLEClientHidAdafruit currently only supports Boot Protocol Mode
140-
// for Keyboard and Mouse. Let's set the protocol mode on prph to Boot Mode
153+
// for Keyboard and Mouse. If we are using a Keyboard + Mouse,
154+
// let's set the protocol mode on prph to Boot Mode.
155+
#if DEVICE_TYPE == 0
141156
hid.setBootMode(true);
157+
#endif
142158

143159
// Enable Keyboard report notification if present on prph
144160
if ( hid.keyboardPresent() ) hid.enableKeyboard();
145161

146162
// Enable Mouse report notification if present on prph
147163
if ( hid.mousePresent() ) hid.enableMouse();
148164

165+
// Enable Gamepad report notification if present on prph
166+
if ( hid.gamepadPresent() ) hid.enableGamepad();
167+
149168
Serial.println("Ready to receive from peripheral");
150169
}
151170
}
@@ -169,15 +188,26 @@ void loop()
169188
#if POLLING == 1
170189
// nothing to do if hid not discovered
171190
if ( !hid.discovered() ) return;
172-
173-
/*------------- Polling Keyboard -------------*/
174-
hid_keyboard_report_t kbd_report;
175-
176-
// Get latest report
177-
hid.getKeyboardReport(&kbd_report);
178191

179-
processKeyboardReport(&kbd_report);
192+
if ( hid.keyboardPresent() ) {
193+
/*------------- Polling Keyboard -------------*/
194+
hid_keyboard_report_t kbd_report;
195+
196+
// Get latest report
197+
Serial.println("Get report from Keyboard");
198+
hid.getKeyboardReport(&kbd_report);
199+
processKeyboardReport(&kbd_report);
200+
}
180201

202+
if ( hid.gamepadPresent() ) {
203+
/*------------- Polling Gamepad -------------*/
204+
hid_gamepad_report_t gpd_report;
205+
206+
// Get latest report
207+
Serial.println("Get report from Gamepad");
208+
hid.getGamepadReport(&gpd_report);
209+
processGamepadReport(&gpd_report);
210+
}
181211

182212
// polling interval is 5 ms
183213
delay(5);
@@ -186,6 +216,24 @@ void loop()
186216
}
187217

188218

219+
void gamepad_report_callback(hid_gamepad_report_t* report)
220+
{
221+
Serial.println("Recieved report from Gamepad");
222+
processGamepadReport(report);
223+
}
224+
225+
void processGamepadReport(hid_gamepad_report_t* report)
226+
{
227+
memcpy(&last_gpd_report, report, sizeof(hid_gamepad_report_t));
228+
229+
Serial.print("Current Gamepad State: Buttons: ");
230+
Serial.print(report->buttons, BIN);
231+
Serial.print(" X: ");
232+
Serial.print(report->x);
233+
Serial.print(" Y: ");
234+
Serial.println(report->y);
235+
}
236+
189237
void keyboard_report_callback(hid_keyboard_report_t* report)
190238
{
191239
processKeyboardReport(report);

libraries/Bluefruit52Lib/src/BLEDiscovery.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ void BLEDiscovery::_eventHandler(ble_evt_t* evt)
240240

241241
LOG_LV2("DISC", "[CHR] Characteristic Count: %d", chr_rsp->count);
242242

243+
for (uint8_t i; i < chr_rsp->count; i++) {
244+
LOG_LV2("DISC", "[CHR] Characteristic #%d: 0x%04X", i, chr_rsp->chars[i].uuid.uuid);
245+
}
246+
243247
if (gattc->gatt_status == BLE_GATT_STATUS_SUCCESS)
244248
{
245249
if ( chr_rsp->count )

libraries/Bluefruit52Lib/src/BLEDiscovery.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,18 @@ class BLEDiscovery
101101
return discoverCharacteristic(conn_handle, chr_arr, arrcount(chr_arr));
102102
}
103103

104+
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2, BLEClientCharacteristic& chr3, BLEClientCharacteristic& chr4, BLEClientCharacteristic& chr5, BLEClientCharacteristic& chr6, BLEClientCharacteristic& chr7)
105+
{
106+
BLEClientCharacteristic* chr_arr[] = {&chr1, &chr2, &chr3, &chr4, &chr5, &chr6, &chr7};
107+
return discoverCharacteristic(conn_handle, chr_arr, arrcount(chr_arr));
108+
}
109+
110+
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2, BLEClientCharacteristic& chr3, BLEClientCharacteristic& chr4, BLEClientCharacteristic& chr5, BLEClientCharacteristic& chr6, BLEClientCharacteristic& chr7, BLEClientCharacteristic& chr8)
111+
{
112+
BLEClientCharacteristic* chr_arr[] = {&chr1, &chr2, &chr3, &chr4, &chr5, &chr6, &chr7, &chr8};
113+
return discoverCharacteristic(conn_handle, chr_arr, arrcount(chr_arr));
114+
}
115+
104116
/*------------------------------------------------------------------*/
105117
/* INTERNAL USAGE ONLY
106118
* Although declare as public, it is meant to be invoked by internal

libraries/Bluefruit52Lib/src/clients/BLEClientHidAdafruit.cpp

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,15 @@ BLEClientHidAdafruit::BLEClientHidAdafruit(void)
4141
_protcol_mode(UUID16_CHR_PROTOCOL_MODE),
4242
_hid_info(UUID16_CHR_HID_INFORMATION), _hid_control(UUID16_CHR_HID_CONTROL_POINT),
4343
_kbd_boot_input(UUID16_CHR_BOOT_KEYBOARD_INPUT_REPORT), _kbd_boot_output(UUID16_CHR_BOOT_KEYBOARD_OUTPUT_REPORT),
44-
_mse_boot_input(UUID16_CHR_BOOT_MOUSE_INPUT_REPORT)
44+
_mse_boot_input(UUID16_CHR_BOOT_MOUSE_INPUT_REPORT),
45+
_gpd_report(UUID16_CHR_REPORT)
4546
{
4647
_kbd_cb = NULL;
4748
_mse_cb = NULL;
49+
_gpd_cb = NULL;
4850
varclr(&_last_kbd_report);
4951
varclr(&_last_mse_report);
52+
varclr(&_last_gpd_report);
5053
}
5154

5255

@@ -62,6 +65,12 @@ void mse_client_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t
6265
svc._handle_mse_input(data, len);
6366
}
6467

68+
void gpd_client_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
69+
{
70+
BLEClientHidAdafruit& svc = (BLEClientHidAdafruit&) chr->parentService();
71+
svc._handle_gpd_input(data, len);
72+
}
73+
6574

6675
bool BLEClientHidAdafruit::begin(void)
6776
{
@@ -77,10 +86,13 @@ bool BLEClientHidAdafruit::begin(void)
7786

7887
_mse_boot_input.begin(this);
7988

89+
_gpd_report.begin(this);
90+
8091

8192
// set notify callback
8293
_kbd_boot_input.setNotifyCallback(kbd_client_notify_cb);
8394
_mse_boot_input.setNotifyCallback(mse_client_notify_cb);
95+
_gpd_report.setNotifyCallback(gpd_client_notify_cb);
8496

8597
return true;
8698
}
@@ -95,17 +107,23 @@ void BLEClientHidAdafruit::setMouseReportCallback(mse_callback_t fp)
95107
_mse_cb = fp;
96108
}
97109

110+
void BLEClientHidAdafruit::setGamepadReportCallback(gpd_callback_t fp)
111+
{
112+
_gpd_cb = fp;
113+
}
114+
115+
98116
bool BLEClientHidAdafruit::discover(uint16_t conn_handle)
99117
{
100118
// Call Base class discover
101119
VERIFY( BLEClientService::discover(conn_handle) );
102120
_conn_hdl = BLE_CONN_HANDLE_INVALID; // make as invalid until we found all chars
103121

104122
// Discover all characteristics
105-
Bluefruit.Discovery.discoverCharacteristic(conn_handle, _protcol_mode, _kbd_boot_input, _kbd_boot_output, _mse_boot_input, _hid_info, _hid_control);
123+
Bluefruit.Discovery.discoverCharacteristic(conn_handle, _protcol_mode, _kbd_boot_input, _kbd_boot_output, _mse_boot_input, _hid_info, _hid_control, _gpd_report);
106124

107-
VERIFY( _protcol_mode.discovered() && _hid_info.discovered() && _hid_control.discovered() );
108-
VERIFY ( keyboardPresent() || mousePresent() );
125+
VERIFY( _hid_info.discovered() && _hid_control.discovered() );
126+
VERIFY (keyboardPresent() || mousePresent() || gamepadPresent());
109127

110128
_conn_hdl = conn_handle;
111129
return true;
@@ -138,7 +156,7 @@ bool BLEClientHidAdafruit::setBootMode(bool boot)
138156
*------------------------------------------------------------------*/
139157
bool BLEClientHidAdafruit::keyboardPresent(void)
140158
{
141-
return _kbd_boot_input.discovered() && _kbd_boot_output.discovered();
159+
return _kbd_boot_input.discovered() && _kbd_boot_output.discovered() && _protcol_mode.discovered();
142160
}
143161

144162
bool BLEClientHidAdafruit::enableKeyboard(void)
@@ -169,7 +187,7 @@ void BLEClientHidAdafruit::getKeyboardReport(hid_keyboard_report_t* report)
169187
*------------------------------------------------------------------*/
170188
bool BLEClientHidAdafruit::mousePresent(void)
171189
{
172-
return _mse_boot_input.discovered();
190+
return _mse_boot_input.discovered() && _protcol_mode.discovered();
173191
}
174192

175193
bool BLEClientHidAdafruit::enableMouse(void)
@@ -195,3 +213,34 @@ void BLEClientHidAdafruit::getMouseReport(hid_mouse_report_t* report)
195213
memcpy(report, &_last_mse_report, sizeof(hid_mouse_report_t));
196214
}
197215

216+
/*------------------------------------------------------------------*/
217+
/* Gamepad
218+
*------------------------------------------------------------------*/
219+
bool BLEClientHidAdafruit::gamepadPresent(void)
220+
{
221+
return _gpd_report.discovered();
222+
}
223+
224+
bool BLEClientHidAdafruit::enableGamepad(void)
225+
{
226+
return _gpd_report.enableNotify();
227+
}
228+
229+
bool BLEClientHidAdafruit::disableGamepad(void)
230+
{
231+
return _gpd_report.disableNotify();
232+
}
233+
234+
void BLEClientHidAdafruit::_handle_gpd_input(uint8_t* data, uint16_t len)
235+
{
236+
varclr(&_last_gpd_report);
237+
memcpy(&_last_gpd_report, data, len);
238+
239+
if ( _gpd_cb ) _gpd_cb(&_last_gpd_report);
240+
}
241+
242+
void BLEClientHidAdafruit::getGamepadReport(hid_gamepad_report_t* report)
243+
{
244+
memcpy(report, &_last_gpd_report, sizeof(hid_gamepad_report_t));
245+
}
246+

libraries/Bluefruit52Lib/src/clients/BLEClientHidAdafruit.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class BLEClientHidAdafruit : public BLEClientService
4949
// Callback Signatures
5050
typedef void (*kbd_callback_t ) (hid_keyboard_report_t* report);
5151
typedef void (*mse_callback_t ) (hid_mouse_report_t* report);
52+
typedef void (*gpd_callback_t ) (hid_gamepad_report_t* report);
5253

5354
BLEClientHidAdafruit(void);
5455

@@ -74,16 +75,26 @@ class BLEClientHidAdafruit : public BLEClientService
7475

7576
void getMouseReport(hid_mouse_report_t* report);
7677

78+
// Gamepad API
79+
bool gamepadPresent(void);
80+
bool enableGamepad(void);
81+
bool disableGamepad(void);
82+
83+
void getGamepadReport(hid_gamepad_report_t* report);
84+
7785
// Report callback
7886
void setKeyboardReportCallback(kbd_callback_t fp);
7987
void setMouseReportCallback(mse_callback_t fp);
88+
void setGamepadReportCallback(gpd_callback_t fp);
8089

8190
protected:
8291
kbd_callback_t _kbd_cb;
8392
mse_callback_t _mse_cb;
93+
gpd_callback_t _gpd_cb;
8494

8595
hid_keyboard_report_t _last_kbd_report;
8696
hid_mouse_report_t _last_mse_report;
97+
hid_gamepad_report_t _last_gpd_report;
8798

8899
// Only support Boot protocol for keyboard and Mouse
89100
BLEClientCharacteristic _protcol_mode;
@@ -95,11 +106,15 @@ class BLEClientHidAdafruit : public BLEClientService
95106

96107
BLEClientCharacteristic _mse_boot_input;
97108

109+
BLEClientCharacteristic _gpd_report;
110+
98111
void _handle_kbd_input(uint8_t* data, uint16_t len);
99112
void _handle_mse_input(uint8_t* data, uint16_t len);
113+
void _handle_gpd_input(uint8_t* data, uint16_t len);
100114

101115
friend void kbd_client_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len);
102116
friend void mse_client_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len);
117+
friend void gpd_client_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len);
103118
};
104119

105120

0 commit comments

Comments
 (0)