BLE HID Keyboard example for Delta BLE platform
Fork of BLE_HeartRate_DELTA by
This example demonstrates the HID over GATT profile for keyboard.
1. Running this application on Delta BLE platform 2. To connect and pair with device named "HID_Keyboard" in your mobile (iOS/Android) Settings>Bluetooth page 3. Open a text editing application on your mobile phone 4. On the PC, open the terminal tool (Putty or TeraTerm) and choose the correct COM/BaudRate 5. Enter any character from your PC, and it will be displayed in your mobile phone
Revision 2:9f46fa6237dd, committed 2017-03-27
- Comitter:
- silviaChen
- Date:
- Mon Mar 27 09:58:38 2017 +0000
- Parent:
- 1:8bca989a70be
- Commit message:
- update mbed-os so both NNN50 and NQ620 (as well as NQ624 module) platform are supported; Add config in mbed_app.json to fix NQ620 use internal RC issue ; remove the unused shield folder; support both iOS and Android
Changed in this revision
diff -r 8bca989a70be -r 9f46fa6237dd BLE_API.lib --- a/BLE_API.lib Tue Feb 07 02:51:56 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/#65474dc93927
diff -r 8bca989a70be -r 9f46fa6237dd HIDService.h --- a/HIDService.h Tue Feb 07 02:51:56 2017 +0000 +++ b/HIDService.h Mon Mar 27 09:58:38 2017 +0000 @@ -2,6 +2,9 @@ #define __BLE_HID_SERVICE_H__ #include "BLE.h" + +#define BLE_UUID_DESCRIPTOR_REPORT_REFERENCE 0x2908 + /** * @class Human Interface Device Service * @brief BLE Human Interface Device Service. This service displays the Glucose measurement value represented as a 16bit Float format.<br> @@ -9,19 +12,31 @@ * @Email: marco.missyou@gmail.com */ +typedef struct { + uint8_t ID; + uint8_t type; +} report_reference_t; + +enum ReportType { + INPUT_REPORT = 0x1, + OUTPUT_REPORT = 0x2, + FEATURE_REPORT = 0x3, +}; + extern const uint8_t KeyboardReportMap[76]; class HIDService { public: - HIDService(BLEDevice &_ble, const uint8_t* key = &KeyboardReportMap[0]): + HIDService(BLE &_ble, const uint8_t* key = &KeyboardReportMap[0]): ble(_ble), protocol_modeValue(1), // Report Protocol Mode(1), Boot Protocol Mode(0) KeyboardMap(key), Protocol_Mode(GattCharacteristic::UUID_PROTOCOL_MODE_CHAR, &protocol_modeValue, 1, 1, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE|GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), - ReportMap(GattCharacteristic::UUID_REPORT_MAP_CHAR, KeyboardMap.getPointer(), 76, sizeof(KeyboardMap), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ |GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY), - Report(GattCharacteristic::UUID_REPORT_CHAR, reportValue.getPointer(), 8, 8, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY|GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), + ReportMap(GattCharacteristic::UUID_REPORT_MAP_CHAR, KeyboardMap.getPointer(), 76, sizeof(KeyboardMap), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), + Report(GattCharacteristic::UUID_REPORT_CHAR, reportValue.getPointer(), 8, 8, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY|GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ|GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE, inputReportDescriptors(), 1), HID_Information(GattCharacteristic::UUID_HID_INFORMATION_CHAR, hidInformation.getPointer(), 4, 4, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), - HID_Control_Point(GattCharacteristic::UUID_HID_CONTROL_POINT_CHAR, &hidcontrolPointer, 1, 1, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE) + HID_Control_Point(GattCharacteristic::UUID_HID_CONTROL_POINT_CHAR, &hidcontrolPointer, 1, 1, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE), + inputReportReferenceDescriptor(BLE_UUID_DESCRIPTOR_REPORT_REFERENCE,(uint8_t *)&inputReportReferenceData, 2, 2) { static bool serviceAdded = false; /* We should only ever need to add the heart rate service once. */ if (serviceAdded) { @@ -73,6 +88,16 @@ return KeyboardMap; } }; + + GattAttribute** HIDService::inputReportDescriptors() { + inputReportReferenceData.ID = 0; + inputReportReferenceData.type = INPUT_REPORT; + + static GattAttribute *descs[] = { + &inputReportReferenceDescriptor, + }; + return descs; + } private: struct ReportStructure { @@ -114,7 +139,7 @@ }; private: - BLEDevice &ble; + BLE &ble; uint8_t protocol_modeValue; ReportStructure reportValue; uint8_t hidcontrolPointer; @@ -128,5 +153,7 @@ // ReadOnlyGattCharacteristic Boot_Mouse_Input_Report; GattCharacteristic HID_Information; GattCharacteristic HID_Control_Point; + GattAttribute inputReportReferenceDescriptor; + report_reference_t inputReportReferenceData; }; #endif /* #ifndef __BLE_GLUCOSE_SERVICE_H__*/ \ No newline at end of file
diff -r 8bca989a70be -r 9f46fa6237dd main.cpp --- a/main.cpp Tue Feb 07 02:51:56 2017 +0000 +++ b/main.cpp Mon Mar 27 09:58:38 2017 +0000 @@ -21,17 +21,28 @@ #include "HIDService.h" //DigitalOut led1(LED1); -Serial uart(USBTX, USBRX); +Serial uart(USBTX, USBRX, 115200); +BLE deltaBLE; HIDService *hidService; unsigned char keyData; const static char DEVICE_NAME[] = "HID_Keyboard"; static const uint16_t uuid16_list[] = {GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE, + GattService::UUID_BATTERY_SERVICE, GattService::UUID_DEVICE_INFORMATION_SERVICE}; static volatile bool triggerSensorPolling = false; bool isConnectionSecured = false; +bool isSecuritySetup = false; +static uint8_t key_press_scan_buff[50]; +static uint8_t modifyKey[50]; +char msg[25] = ""; +int index_b = 0; +int index_w = 0; +unsigned char uart_buf[64]; +unsigned int i = 0; DeviceInformationService *deviceInfo; +BatteryService *batteryService; void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey) { @@ -41,12 +52,18 @@ } uart.printf("\r\n"); } + +void securitySetupInitiatedCallback(Gap::Handle_t, bool allowBonding, bool requireMITM, SecurityManager::SecurityIOCapabilities_t iocaps) +{ + uart.printf("securitySetupInitiatedCallback\r\n"); + isSecuritySetup = true; +} void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status) { if (status == SecurityManager::SEC_STATUS_SUCCESS) { uart.printf("Security success\r\n", status); - isConnectionSecured = true; + //isConnectionSecured = true; } else { uart.printf("Security failed\r\n", status); } @@ -55,7 +72,15 @@ void linkSecuredCallback(Gap::Handle_t handle, SecurityManager::SecurityMode_t securityMode) { uart.printf("linkSecuredCallback\r\n"); + if (!isSecuritySetup) { + isConnectionSecured = true; + } +} + +void securityContextStoredCallback(Gap::Handle_t handle) { + uart.printf("securityContextStoredCallback\r\n"); isConnectionSecured = true; + isSecuritySetup = false; } void onConnectionCallback(const Gap::ConnectionCallbackParams_t *params) @@ -67,7 +92,7 @@ { uart.printf("Disconnected\r\n"); isConnectionSecured = false; - BLE::Instance(BLE::DEFAULT_INSTANCE).gap().startAdvertising(); // restart advertising + deltaBLE.gap().startAdvertising(); // restart advertising } void onTimeoutCallback(Gap::TimeoutSource_t source) @@ -94,77 +119,18 @@ /* Note that the periodicCallback() executes in interrupt context, so it is safer to do * heavy-weight sensor polling from the main thread. */ - triggerSensorPolling = true; + //triggerSensorPolling = true; } -void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) -{ - BLE &ble = params->ble; - ble_error_t error = params->error; - - if (error != BLE_ERROR_NONE) { - return; - } - - bool enableBonding = true; - bool requireMITM = false; - - //const uint8_t passkeyValue[6] = {0x00,0x00,0x00,0x00,0x00,0x00}; - ble.securityManager().init(enableBonding, requireMITM, SecurityManager::IO_CAPS_NONE); - ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback); - ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback); - ble.securityManager().onLinkSecured(linkSecuredCallback); - ble.gap().onDisconnection(disconnectionCallback); - ble.gap().onConnection(onConnectionCallback); - ble.gap().onTimeout(onTimeoutCallback); - //ble.gattServer().onDataRead(onDataReadCallback); - - /* Setup primary service. */ - hidService = new HIDService(ble); - - /* Setup auxiliary service. */ - deviceInfo = new DeviceInformationService(ble, "DELTA", "NQ620", "SN1", "hw-rev1", "fw-rev1", "soft-rev1"); - - /* Setup advertising. */ - ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); - ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); - ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD); - ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); - ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); - ble.gap().setAdvertisingInterval(50); /* 50ms */ - ble.gap().startAdvertising(); - uart.printf("Start advertising\r\n"); -} - -static uint8_t key_press_scan_buff[50]; -static uint8_t modifyKey[50]; -char msg[25] = ""; -int index_b = 0; -int index_w = 0; - -int main(void) -{ - //led1 = 1; - // Ticker ticker; -// ticker.attach(periodicCallback, 0.1); // blink LED every second - - uart.baud(115200); - uart.printf("Srarting HID Service\r\n"); - - BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE); - ble.init(bleInitComplete); - - /* SpinWait for initialization to complete. This is necessary because the - * BLE object is used in the main loop below. */ - while (ble.hasInitialized() == false) { /* spin loop */ } - - // infinite loop - while (1) { +void CLI_execute() { +// if (i>=2) +// if (uart_buf[i-2] == 0x0D && uart_buf[i-1] == 0x0A){//detecting CR LF + if (uart.readable()) return;//retrun if it is not the end of packet + if (isConnectionSecured) { - if (uart.readable() == 1) { - keyData = uart.getc(); - uart.putc(keyData); - msg[index_w++] = keyData; + for (int j=0; j<i; j++) { + keyData = uart_buf[j]; + if(keyData <= 0x39 && keyData >= 0x30){ //number if(keyData == 0x30){ modifyKey[index_b] = 0x00; @@ -192,31 +158,110 @@ key_press_scan_buff[index_b] = 0x2c; index_b++; key_press_scan_buff[index_b] = 0x73; + } else if (keyData == 0x08) { //backspace + modifyKey[index_b] = 0x00; + key_press_scan_buff[index_b] = 0x2a; + index_b++; + key_press_scan_buff[index_b] = 0x73; + } else if (keyData == 0x0d) { //return + modifyKey[index_b] = 0x00; + key_press_scan_buff[index_b] = 0x28; + index_b++; + key_press_scan_buff[index_b] = 0x73; } else { modifyKey[index_b] = 0x00; //key_press_scan_buff[index_b] = 0x73; //this is dummy data. //msg[index_w+1] = '\0'; } index_b++; - if(keyData == 0x0a && ble.getGapState().connected){ - for(int i = 0; i < index_b ; i++){ - uart.printf("m[%x] k[%x] ", modifyKey[i], key_press_scan_buff[i]); - hidService->updateReport(modifyKey[i], key_press_scan_buff[i]); - ble.waitForEvent(); - wait(0.03); - } + } + + i=0; - index_b = 0; - index_w = 0; - memset(modifyKey, 0, 50); - memset(msg, 0, 25); - memset(key_press_scan_buff, 0, 50); - } + for(int i = 0; i < index_b ; i++){ + uart.printf("m[%x] k[%x] ", modifyKey[i], key_press_scan_buff[i]); + hidService->updateReport(modifyKey[i], key_press_scan_buff[i]); + //BLE::Instance(BLE::DEFAULT_INSTANCE).waitForEvent(); + //wait(0.03); } - } else { - ble.waitForEvent(); // low power wait for event - wait(1); + + index_b = 0; + index_w = 0; + memset(modifyKey, 0, 50); + memset(msg, 0, 25); + memset(key_press_scan_buff, 0, 50); + } +// } +} + +void uart_interrupt() { + uart.printf("uart_interrupt\r\n"); + uart_buf[i++] = uart.getc(); + CLI_execute(); +} + +void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) +{ + BLE &ble = params->ble; + ble_error_t error = params->error; + + if (error != BLE_ERROR_NONE) { + return; + } + + bool enableBonding = true; + bool requireMITM = false; + + //uint8_t pKey[6] = {'1', '1', '1', '1', '1', '1'}; //set the passkey + ble.securityManager().init(enableBonding, requireMITM, SecurityManager::IO_CAPS_NONE); + ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback); + ble.securityManager().onSecuritySetupInitiated(securitySetupInitiatedCallback); + ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback); + ble.securityManager().onLinkSecured(linkSecuredCallback); + ble.securityManager().onSecurityContextStored(securityContextStoredCallback); + ble.gap().onDisconnection(disconnectionCallback); + ble.gap().onConnection(onConnectionCallback); + ble.gap().onTimeout(onTimeoutCallback); + //ble.gattServer().onDataRead(onDataReadCallback); + + /* Setup primary service. */ + hidService = new HIDService(ble); + + /* Setup auxiliary service. */ + batteryService = new BatteryService(ble, 100); + deviceInfo = new DeviceInformationService(ble, "DELTA", "NQ620", "SN1", "hw-rev1", "fw-rev1", "soft-rev1"); + + /* Setup advertising. */ + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD); + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); + ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); + ble.gap().setAdvertisingInterval(50); /* 50ms */ + ble.gap().startAdvertising(); + uart.printf("Start advertising\r\n"); +} + +int main(void) +{ + //led1 = 1; + //Ticker ticker; + //ticker.attach(periodicCallback, 1); // blink LED every second + + uart.attach(&uart_interrupt); + uart.printf("Srarting HID Service\r\n"); + + BLE &deltaBLE = BLE::Instance(BLE::DEFAULT_INSTANCE); + deltaBLE.init(bleInitComplete); + + /* SpinWait for initialization to complete. This is necessary because the + * BLE object is used in the main loop below. */ + while (deltaBLE.hasInitialized() == false) { /* spin loop */ } + + // infinite loop + while (1) { + deltaBLE.waitForEvent(); } }
diff -r 8bca989a70be -r 9f46fa6237dd mbed-os.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-os.lib Mon Mar 27 09:58:38 2017 +0000 @@ -0,0 +1,1 @@ +https://github.com/ARMmbed/mbed-os/#f4864dc6429e1ff5474111d4e0f6bee36a759b1c
diff -r 8bca989a70be -r 9f46fa6237dd mbed.bld --- a/mbed.bld Tue Feb 07 02:51:56 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://mbed.org/users/mbed_official/code/mbed/builds/25aea2a3f4e3 \ No newline at end of file
diff -r 8bca989a70be -r 9f46fa6237dd mbed_app.json --- a/mbed_app.json Tue Feb 07 02:51:56 2017 +0000 +++ b/mbed_app.json Mon Mar 27 09:58:38 2017 +0000 @@ -1,13 +1,12 @@ { - "target_overrides": { - "K64F": { - "target.features_add": ["BLE"], - "target.extra_labels_add": ["ST_BLUENRG"], - "target.macros_add": ["IDB0XA1_D13_PATCH"] + "config": { + "lf_clock_rc_calib_timer_interval": { + "value": 16, + "macro_name": "MBED_CONF_NORDIC_NRF_LF_CLOCK_CALIB_TIMER_INTERVAL" }, - "NUCLEO_F401RE": { - "target.features_add": ["BLE"], - "target.extra_labels_add": ["ST_BLUENRG"] + "lf_clock_rc_calib_mode_config": { + "value": 0, + "macro_name": "MBED_CONF_NORDIC_NRF_LF_CLOCK_CALIB_MODE_CONFIG" } } } \ No newline at end of file
diff -r 8bca989a70be -r 9f46fa6237dd nRF51822.lib --- a/nRF51822.lib Tue Feb 07 02:51:56 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://mbed.org/teams/Nordic-Semiconductor/code/nRF51822/#c90ae1400bf2
diff -r 8bca989a70be -r 9f46fa6237dd shields/TARGET_ST_BLUENRG.lib --- a/shields/TARGET_ST_BLUENRG.lib Tue Feb 07 02:51:56 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://developer.mbed.org/teams/ST/code/X_NUCLEO_IDB0XA1/#fa98703ece8e