/********************************************************************* This is an example for our nRF52 based Bluefruit LE modules Pick one up today in the adafruit shop! Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! MIT license, check LICENSE for more information All text above, and the splash screen below must be included in any redistribution *********************************************************************/ /* * This sketch demonstrate the central API(). An additional bluefruit * that has blehid as peripheral is required for the demo. */ #include // Polling or callback implementation #define POLLING 1 BLEClientHidAdafruit hid; // Last checked report, to detect if there is changes between reports hid_keyboard_report_t last_kbd_report = { 0 }; hid_mouse_report_t last_mse_report = { 0 }; void setup() { Serial.begin(115200); while ( !Serial ) delay(10); // for nrf52840 with native usb Serial.println("Bluefruit52 Central HID (Keyboard + Mouse) Example"); Serial.println("--------------------------------------------------\n"); // Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1 // SRAM usage required by SoftDevice will increase dramatically with number of connections Bluefruit.begin(0, 1); Bluefruit.setName("Bluefruit52 Central"); // Init BLE Central Uart Serivce hid.begin(); #if POLLING == 0 hid.setKeyboardReportCallback(keyboard_report_callback); #endif // Increase Blink rate to different from PrPh advertising mode Bluefruit.setConnLedInterval(250); // Callbacks for Central Bluefruit.Central.setConnectCallback(connect_callback); Bluefruit.Central.setDisconnectCallback(disconnect_callback); /* Start Central Scanning * - Enable auto scan if disconnected * - Interval = 100 ms, window = 80 ms * - Don't use active scan * - Filter only accept HID service in advertising * - Start(timeout) with timeout = 0 will scan forever (until connected) */ Bluefruit.Scanner.setRxCallback(scan_callback); Bluefruit.Scanner.restartOnDisconnect(true); Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms Bluefruit.Scanner.filterService(hid); // only report HID service Bluefruit.Scanner.useActiveScan(false); Bluefruit.Scanner.start(0); // 0 = Don't stop scanning after n seconds } /** * Callback invoked when scanner pick up an advertising data * @param report Structural advertising data */ void scan_callback(ble_gap_evt_adv_report_t* report) { // Since we configure the scanner with filterUuid() // Scan callback only invoked for device with hid service advertised // Connect to the device with hid service in advertising packet Bluefruit.Central.connect(report); } /** * Callback invoked when an connection is established * @param conn_handle */ void connect_callback(uint16_t conn_handle) { Serial.println("Connected"); Serial.print("Discovering HID Service ... "); if ( hid.discover(conn_handle) ) { Serial.println("Found it"); // HID device mostly require pairing/bonding if ( !Bluefruit.Gap.requestPairing(conn_handle) ) { Serial.print("Failed to paired"); return; } // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.hid_information.xml uint8_t hidInfo[4]; hid.getHidInfo(hidInfo); Serial.printf("HID version: %d.%d\n", hidInfo[0], hidInfo[1]); Serial.print("Country code: "); Serial.println(hidInfo[2]); Serial.printf("HID Flags : 0x%02X\n", hidInfo[3]); // BLEClientHidAdafruit currently only suports Boot Protocol Mode // for Keyboard and Mouse. Let's set the protocol mode on prph to Boot Mode hid.setProtocolMode(HID_PROTOCOL_MODE_BOOT); // Enable Keyboard report notification if present on prph if ( hid.keyboardPresent() ) hid.enableKeyboard(); // Enable Mouse report notification if present on prph if ( hid.mousePresent() ) hid.enableMouse(); Serial.println("Ready to receive from peripheral"); }else { Serial.println("Found NONE"); // disconect since we couldn't find bleuart service Bluefruit.Central.disconnect(conn_handle); } } /** * Callback invoked when a connection is dropped * @param conn_handle * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h * https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/master/cores/nRF5/nordic/softdevice/s140_nrf52_6.1.1_API/include/ble_hci.h */ void disconnect_callback(uint16_t conn_handle, uint8_t reason) { (void) conn_handle; (void) reason; Serial.println("Disconnected"); } void loop() { #if POLLING == 1 // nothing to do if hid not discovered if ( !hid.discovered() ) return; /*------------- Polling Keyboard -------------*/ hid_keyboard_report_t kbd_report; // Get latest report hid.getKeyboardReport(&kbd_report); processKeyboardReport(&kbd_report); // polling interval is 5 ms delay(5); #endif } void keyboard_report_callback(hid_keyboard_report_t* report) { processKeyboardReport(report); } void processKeyboardReport(hid_keyboard_report_t* report) { // Check with last report to see if there is any changes if ( memcmp(&last_kbd_report, report, sizeof(hid_keyboard_report_t)) ) { bool shifted = false; if ( report->modifier ) { if ( report->modifier & (KEYBOARD_MODIFIER_LEFTCTRL | KEYBOARD_MODIFIER_RIGHTCTRL) ) { Serial.print("Ctrl "); } if ( report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT) ) { Serial.print("Shift "); shifted = true; } if ( report->modifier & (KEYBOARD_MODIFIER_LEFTALT | KEYBOARD_MODIFIER_RIGHTALT) ) { Serial.print("Alt "); } } for(uint8_t i=0; i<6; i++) { uint8_t kc = report->keycode[i]; char ch = 0; if ( kc < 128 ) { ch = shifted ? HID_KEYCODE_TO_ASCII[kc].shifted : HID_KEYCODE_TO_ASCII[kc].ascii; }else { // non-US keyboard !!?? } // Printable if (ch) { Serial.print(ch); } } } // update last report memcpy(&last_kbd_report, report, sizeof(hid_keyboard_report_t)); }