BLE HID Keyboard example for Delta BLE platform

Fork of BLE_HeartRate_DELTA by Delta

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

Files at this revision

API Documentation at this revision

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

BLE_API.lib Show diff for this revision Revisions of this file
HIDService.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show diff for this revision Revisions of this file
mbed_app.json Show annotated file Show diff for this revision Revisions of this file
nRF51822.lib Show diff for this revision Revisions of this file
shields/TARGET_ST_BLUENRG.lib Show diff for this revision Revisions of this file
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