Skip to content

Commit f16576b

Browse files
committed
central keyboard work with polling method
- central bonding still needs some more work for reconnection.
1 parent 1fd9cbe commit f16576b

File tree

4 files changed

+225
-46
lines changed

4 files changed

+225
-46
lines changed

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

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

21+
// Polling or callback implementation
22+
#define POLLING 1
23+
2124
BLEClientHidAdafruit hid;
2225

26+
// Last checked report, to detect if we receive a new one with polling method
27+
hid_keyboard_report_t last_kbd_report = { 0 };
28+
hid_mouse_report_t last_mse_report = { 0 };
29+
2330
void setup()
2431
{
2532
Serial.begin(115200);
2633

2734
Serial.println("Bluefruit52 Central HID (Keyboard + Mouse) Example");
2835
Serial.println("--------------------------------------------------\n");
36+
37+
Bluefruit.configCentralConn(23, 3, 1, 3);
2938

3039
// Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
3140
// SRAM usage required by SoftDevice will increase dramatically with number of connections
@@ -83,18 +92,31 @@ void connect_callback(uint16_t conn_handle)
8392
{
8493
Serial.println("Found it");
8594

95+
// HID device mostly require pairing/bonding
96+
if ( !Bluefruit.Gap.requestPairing(conn_handle) )
97+
{
98+
Serial.print("Failed to paired");
99+
return;
100+
}
101+
86102
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.hid_information.xml
87103
uint8_t hidInfo[4];
88104
hid.getHidInfo(hidInfo);
89105

90106
Serial.printf("HID version: %d.%d\n", hidInfo[0], hidInfo[1]);
91107
Serial.print("Country code: "); Serial.println(hidInfo[2]);
92108
Serial.printf("HID Flags : 0x%02X\n", hidInfo[3]);
93-
94109

95-
// Serial.println("Enable TXD's notify");
96-
// clientUart.enableTXD();
110+
// BLEClientHidAdafruit currently only suports Boot Protocol Mode
111+
// for Keyboard and Mouse. Let's set the protocol mode on prph to Boot Mode
112+
hid.setProtocolMode(HID_PROTOCOL_MODE_BOOT);
97113

114+
// Enable Keyboard report notification if present on prph
115+
if ( hid.keyboardPresent() ) hid.enableKeyboard();
116+
117+
// Enable Mouse report notification if present on prph
118+
if ( hid.mousePresent() ) hid.enableMouse();
119+
98120
Serial.println("Ready to receive from peripheral");
99121
}else
100122
{
@@ -118,27 +140,72 @@ void disconnect_callback(uint16_t conn_handle, uint8_t reason)
118140
Serial.println("Disconnected");
119141
}
120142

121-
/**
122-
* Callback invoked when uart received data
123-
* @param uart_svc Reference object to the service where the data
124-
* arrived. In this example it is clientUart
125-
*/
126-
//void bleuart_rx_callback(BLEClientUart& uart_svc)
127-
//{
128-
// Serial.print("[RX]: ");
129-
//
130-
// while ( uart_svc.available() )
131-
// {
132-
// Serial.print( (char) uart_svc.read() );
133-
// }
134-
//
135-
// Serial.println();
136-
//}
137-
138143
void loop()
139144
{
140-
// nothing to do
145+
146+
#if POLLING
147+
// nothing to do if hid not discovered
141148
if ( !hid.discovered() ) return;
149+
150+
/*------------- Polling Keyboard -------------*/
151+
hid_keyboard_report_t kbd_report;
152+
153+
// Get latest report
154+
hid.getKeyboardReport(&kbd_report);
142155

156+
// Check with last report to see if there is new arrival report
157+
if ( memcmp(&last_kbd_report, &kbd_report, sizeof(kbd_report)) )
158+
{
159+
bool shifted = false;
160+
161+
if ( kbd_report.modifier )
162+
{
163+
if ( kbd_report.modifier & (KEYBOARD_MODIFIER_LEFTCTRL | KEYBOARD_MODIFIER_RIGHTCTRL) )
164+
{
165+
Serial.print("Ctrl ");
166+
}
167+
168+
if ( kbd_report.modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT) )
169+
{
170+
Serial.print("Shift ");
171+
172+
shifted = true;
173+
}
174+
175+
if ( kbd_report.modifier & (KEYBOARD_MODIFIER_LEFTALT | KEYBOARD_MODIFIER_RIGHTALT) )
176+
{
177+
Serial.print("Alt ");
178+
}
179+
}
180+
181+
for(uint8_t i=0; i<6; i++)
182+
{
183+
uint8_t kc = kbd_report.keycode[i];
184+
char ch = 0;
185+
186+
if ( kc < 128 )
187+
{
188+
ch = shifted ? HID_KEYCODE_TO_ASCII[kc].shifted : HID_KEYCODE_TO_ASCII[kc].ascii;
189+
}else
190+
{
191+
// non-US keyboard !!??
192+
}
193+
194+
// Printable
195+
if (ch)
196+
{
197+
Serial.print(ch);
198+
}
199+
}
200+
}
201+
202+
// update last report
203+
memcpy(&last_kbd_report, &kbd_report, sizeof(kbd_report));
204+
205+
206+
// polling interval is 5 ms
207+
delay(5);
208+
#endif
209+
143210
}
144211

libraries/Bluefruit52Lib/keywords.txt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,35 @@ SECMODE_ENC_WITH_MITM LITERAL1
470470
SECMODE_SIGNED_NO_MITM LITERAL1
471471
SECMODE_SIGNED_WITH_MITM LITERAL1
472472

473+
#######################################
474+
# HID Constants
475+
#######################################
476+
477+
HID_PROTOCOL_MODE_BOOT LITERAL1
478+
HID_PROTOCOL_MODE_REPORT LITERAL1
479+
480+
MOUSE_BUTTON_LEFT LITERAL1
481+
MOUSE_BUTTON_RIGHT LITERAL1
482+
MOUSE_BUTTON_MIDDLE LITERAL1
483+
MOUSE_BUTTON_BACKWARD LITERAL1
484+
MOUSE_BUTTON_FORWARD LITERAL1
485+
486+
KEYBOARD_MODIFIER_LEFTCTRL LITERAL1
487+
KEYBOARD_MODIFIER_LEFTSHIFT LITERAL1
488+
KEYBOARD_MODIFIER_LEFTALT LITERAL1
489+
KEYBOARD_MODIFIER_LEFTGUI LITERAL1
490+
KEYBOARD_MODIFIER_RIGHTCTRL LITERAL1
491+
KEYBOARD_MODIFIER_RIGHTSHIFT LITERAL1
492+
KEYBOARD_MODIFIER_RIGHTALT LITERAL1
493+
KEYBOARD_MODIFIER_RIGHTGUI LITERAL1
494+
495+
KEYBOARD_LED_NUMLOCK LITERAL1
496+
KEYBOARD_LED_CAPSLOCK LITERAL1
497+
KEYBOARD_LED_SCROLLLOCK LITERAL1
498+
KEYBOARD_LED_COMPOSE LITERAL1
499+
KEYBOARD_LED_KANA LITERAL1
500+
501+
473502
#######################################
474503
# ANCS Constants
475504
#######################################

libraries/Bluefruit52Lib/src/clients/BLEClientHidAdafruit.cpp

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,29 @@ BLEClientHidAdafruit::BLEClientHidAdafruit(void)
4040
: BLEClientService(UUID16_SVC_HUMAN_INTERFACE_DEVICE),
4141
_protcol_mode(UUID16_CHR_PROTOCOL_MODE),
4242
_kbd_boot_input(UUID16_CHR_BOOT_KEYBOARD_INPUT_REPORT), _kbd_boot_output(UUID16_CHR_BOOT_KEYBOARD_OUTPUT_REPORT),
43-
_mouse_boot_input(UUID16_CHR_BOOT_MOUSE_INPUT_REPORT),
43+
_mse_boot_input(UUID16_CHR_BOOT_MOUSE_INPUT_REPORT),
4444
_hid_info(UUID16_CHR_HID_INFORMATION), _hid_control(UUID16_CHR_HID_CONTROL_POINT)
4545
{
46+
_kbd_cb = NULL;
47+
_mse_cb = NULL;
48+
varclr(&_last_kbd_report);
49+
varclr(&_last_mse_report);
4650
}
4751

52+
53+
void kbd_client_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
54+
{
55+
BLEClientHidAdafruit& svc = (BLEClientHidAdafruit&) chr->parentService();
56+
svc._handle_kbd_input(data, len);
57+
}
58+
59+
void mse_client_notify_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
60+
{
61+
BLEClientHidAdafruit& svc = (BLEClientHidAdafruit&) chr->parentService();
62+
svc._handle_mse_input(data, len);
63+
}
64+
65+
4866
bool BLEClientHidAdafruit::begin(void)
4967
{
5068
// Invoke base class begin()
@@ -57,12 +75,22 @@ bool BLEClientHidAdafruit::begin(void)
5775
_kbd_boot_input.begin(this);
5876
_kbd_boot_output.begin(this);
5977

60-
_mouse_boot_input.begin(this);
78+
_mse_boot_input.begin(this);
6179

6280

6381
// set notify callback
64-
// _kbd_boot_input.setNotifyCallback(kbd_client_notify_cb);
65-
// _mouse_boot_input.setNotifyCallback(mouse_client_notify_cb);
82+
_kbd_boot_input.setNotifyCallback(kbd_client_notify_cb);
83+
_mse_boot_input.setNotifyCallback(mse_client_notify_cb);
84+
}
85+
86+
void BLEClientHidAdafruit::setKeyboardReportCallback(kbd_callback_t fp)
87+
{
88+
_kbd_cb = fp;
89+
}
90+
91+
void BLEClientHidAdafruit::setMouseReportCallback(mse_callback_t fp)
92+
{
93+
_mse_cb = fp;
6694
}
6795

6896
bool BLEClientHidAdafruit::discover(uint16_t conn_handle)
@@ -72,7 +100,7 @@ bool BLEClientHidAdafruit::discover(uint16_t conn_handle)
72100
_conn_hdl = BLE_CONN_HANDLE_INVALID; // make as invalid until we found all chars
73101

74102
// Discover all characteristics
75-
Bluefruit.Discovery.discoverCharacteristic(conn_handle, _protcol_mode, _kbd_boot_input, _kbd_boot_output, _mouse_boot_input, _hid_info, _hid_control);
103+
Bluefruit.Discovery.discoverCharacteristic(conn_handle, _protcol_mode, _kbd_boot_input, _kbd_boot_output, _mse_boot_input, _hid_info, _hid_control);
76104

77105
VERIFY( _protcol_mode.discovered() && _hid_info.discovered() && _hid_control.discovered() );
78106
VERIFY ( keyboardPresent() || mousePresent() );
@@ -81,6 +109,9 @@ bool BLEClientHidAdafruit::discover(uint16_t conn_handle)
81109
return true;
82110
}
83111

112+
/*------------------------------------------------------------------*/
113+
/* Info
114+
*------------------------------------------------------------------*/
84115
bool BLEClientHidAdafruit::getHidInfo(uint8_t info[4])
85116
{
86117
return 4 == _hid_info.read(info, 4);
@@ -94,32 +125,70 @@ uint8_t BLEClientHidAdafruit::getCountryCode(void)
94125
return info[2];
95126
}
96127

97-
bool BLEClientHidAdafruit::keyboardPresent(void)
128+
bool BLEClientHidAdafruit::setProtocolMode(uint8_t mode)
98129
{
99-
return _kbd_boot_input.discovered() && _kbd_boot_output.discovered();
130+
return _protcol_mode.write8(mode);
100131
}
101132

102-
bool BLEClientHidAdafruit::mousePresent(void)
133+
/*------------------------------------------------------------------*/
134+
/* Keyboard
135+
*------------------------------------------------------------------*/
136+
bool BLEClientHidAdafruit::keyboardPresent(void)
103137
{
104-
return _mouse_boot_input.discovered();
138+
return _kbd_boot_input.discovered() && _kbd_boot_output.discovered();
105139
}
106140

107141
bool BLEClientHidAdafruit::enableKeyboard(void)
108142
{
109143
_kbd_boot_input.enableNotify();
110144
}
111145

112-
bool BLEClientHidAdafruit::enableMouse(void)
146+
bool BLEClientHidAdafruit::disableKeyboard(void)
147+
{
148+
_kbd_boot_input.disableNotify();
149+
}
150+
151+
void BLEClientHidAdafruit::_handle_kbd_input(uint8_t* data, uint16_t len)
113152
{
114-
_mouse_boot_input.enableNotify();
153+
varclr(&_last_kbd_report);
154+
memcpy(&_last_kbd_report, data, len);
155+
156+
if ( _kbd_cb ) _kbd_cb(&_last_kbd_report);
115157
}
116158

117-
bool BLEClientHidAdafruit::disableKeyboard(void)
159+
void BLEClientHidAdafruit::getKeyboardReport(hid_keyboard_report_t* report)
118160
{
119-
_kbd_boot_input.disableNotify();
161+
memcpy(report, &_last_kbd_report, sizeof(hid_keyboard_report_t));
162+
}
163+
164+
/*------------------------------------------------------------------*/
165+
/* Mouse
166+
*------------------------------------------------------------------*/
167+
bool BLEClientHidAdafruit::mousePresent(void)
168+
{
169+
return _mse_boot_input.discovered();
170+
}
171+
172+
bool BLEClientHidAdafruit::enableMouse(void)
173+
{
174+
_mse_boot_input.enableNotify();
120175
}
121176

122177
bool BLEClientHidAdafruit::disableMouse(void)
123178
{
124-
_mouse_boot_input.disableNotify();
179+
_mse_boot_input.disableNotify();
125180
}
181+
182+
void BLEClientHidAdafruit::_handle_mse_input(uint8_t* data, uint16_t len)
183+
{
184+
varclr(&_last_mse_report);
185+
memcpy(&_last_mse_report, data, len);
186+
187+
if ( _mse_cb ) _mse_cb(&_last_mse_report);
188+
}
189+
190+
void BLEClientHidAdafruit::getMouseReport(hid_mouse_report_t* report)
191+
{
192+
memcpy(report, &_last_mse_report, sizeof(hid_mouse_report_t));
193+
}
194+

0 commit comments

Comments
 (0)