Example for BLE HID scanner

Fork of BLE_HIDScanner_DELTA by Silvia Chen

Files at this revision

API Documentation at this revision

Comitter:
silviaChen
Date:
Tue Feb 07 02:51:56 2017 +0000
Parent:
0:c7bcc0b36b5e
Child:
2:9f46fa6237dd
Commit message:
BLE HID Keyboard demo

Changed in this revision

HIDService.cpp Show annotated file 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HIDService.cpp	Tue Feb 07 02:51:56 2017 +0000
@@ -0,0 +1,46 @@
+#include "HIDService.h"
+
+const uint8_t KeyboardReportMap[] =
+    {   0x05, 0x01,                 // Usage Page (Generic Desktop)
+        0x09, 0x06,                 // Usage (Keyboard)
+        0xA1, 0x01,                 // Collection (Application)
+        0x05, 0x07,                 //     Usage Page (Key Codes)
+        0x19, 0xe0,                 //     Usage Minimum (224)
+        0x29, 0xe7,                 //     Usage Maximum (231)
+        0x15, 0x00,                 //     Logicagl Minimum (0)
+        0x25, 0x01,                 //     Logical Maximum (1)
+        0x75, 0x01,                 //     Report Size (1)
+        0x95, 0x08,                 //     Report Count (8)
+        0x81, 0x02,                 //     Input (Data, Variable, Absolute)
+        
+        0x95, 0x01,                 //     Report Count (1)
+        0x75, 0x08,                 //     Report Size (8)
+        0x81, 0x01,                 //     Input (Constant) reserved byte(1)
+
+        0x95, 0x05,                 //     Report Count (5)
+        0x75, 0x01,                 //     Report Size (1)
+        0x05, 0x08,                 //     Usage Page (Page# for LEDs)
+        0x19, 0x01,                 //     Usage Minimum (1)
+        0x29, 0x05,                 //     Usage Maximum (5)
+        0x91, 0x02,                 //     Output (Data, Variable, Absolute), Led report
+        0x95, 0x01,                 //     Report Count (1)
+        0x75, 0x03,                 //     Report Size (3)
+        0x91, 0x01,                 //     Output (Data, Variable, Absolute), Led report padding
+
+        0x95, 0x06,                 //     Report Count (6)
+        0x75, 0x08,                 //     Report Size (8)
+        0x15, 0x00,                 //     Logical Minimum (0)
+        0x25, 0x65,                 //     Logical Maximum (101)
+        0x05, 0x07,                 //     Usage Page (Key codes)
+        0x19, 0x00,                 //     Usage Minimum (0)
+        0x29, 0x65,                 //     Usage Maximum (101)
+        0x81, 0x00,                 //     Input (Data, Array) Key array(6 bytes)
+
+        0x09, 0x05,                 //     Usage (Vendor Defined)
+        0x15, 0x00,                 //     Logical Minimum (0)
+        0x26, 0xFF, 0x00,           //     Logical Maximum (255)
+        0x75, 0x08,                 //     Report Count (2)
+        0x95, 0x02,                 //     Report Size (8 bit)
+        0xB1, 0x02,                 //     Feature (Data, Variable, Absolute)
+        0xC0                        // End Collection (Application)
+    };
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HIDService.h	Tue Feb 07 02:51:56 2017 +0000
@@ -0,0 +1,132 @@
+#ifndef __BLE_HID_SERVICE_H__
+#define __BLE_HID_SERVICE_H__
+
+#include "BLE.h"
+/**
+* @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>
+* @Author: Marco.Hsu  
+* @Email: marco.missyou@gmail.com  
+*/
+
+extern const uint8_t KeyboardReportMap[76];
+        
+class HIDService {
+public:
+    HIDService(BLEDevice &_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),
+        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)
+        {
+            static bool serviceAdded = false; /* We should only ever need to add the heart rate service once. */
+            if (serviceAdded) {
+            return;
+            }
+            
+            //SecurityManager::SecurityMode_t securityMode = SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM;
+            Protocol_Mode.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM);
+            ReportMap.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM);
+            Report.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM);
+            HID_Information.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM);
+            HID_Control_Point.requireSecurity(SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM);
+            GattCharacteristic *charTable[] = {&Protocol_Mode, &ReportMap, &Report, &HID_Information, &HID_Control_Point};
+            GattService         HIDGattService(GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
+            ble.addService(HIDGattService);
+            serviceAdded = true;
+            ble.onDataWritten(this, &HIDService::onDataWritten);
+        }
+public:
+    void updateReport(uint8_t modifydata, uint8_t data) {
+        reportValue.updateReportValue(modifydata, data);
+        
+        //ble.updateCharacteristicValue(Report.getValueAttribute().getHandle(), reportValue.getPointer(), 8);
+        ble.gattServer().write(Report.getValueAttribute().getHandle(), reportValue.getPointer(), 8);
+    }
+    
+    virtual void onDataWritten(const GattWriteCallbackParams *params) {
+        if (params->handle == HID_Control_Point.getValueAttribute().getHandle()) {
+            uint16_t bytesRead = params->len;
+            if (bytesRead == 1) {
+                memcpy(&hidcontrolPointer, params->data, bytesRead);
+            }
+        }
+        if (params->handle == Report.getValueAttribute().getHandle()) {
+            uint16_t bytesRead = params->len;
+            if (bytesRead <= 4) {
+                memcpy(&reportValue, params->data, bytesRead);
+            }
+        }
+    }
+
+private:
+    struct ReportMapStructure{
+            uint8_t KeyboardMap[76];
+            ReportMapStructure(const uint8_t* data): KeyboardMap() {
+            memcpy(&KeyboardMap[0], data, 76);
+            }
+            uint8_t     *getPointer(void) {
+            return      KeyboardMap;
+            }
+    };
+
+private:
+   struct ReportStructure {
+            // Initial setting report value
+            ReportStructure(): reportValue() {
+                uint8_t data= 0x00;
+                updateReportValue(data, data);
+            }
+            
+            void updateReportValue(uint8_t modifyKey, uint8_t data){
+                memset(&reportValue[0], 0 ,8);
+                memcpy(&reportValue[0], &modifyKey, 1);
+                memcpy(&reportValue[2], &data, 1);
+            }
+        
+            uint8_t     *getPointer(void) {
+            return      reportValue;
+            }
+
+            uint8_t reportValue[8];
+        };
+        
+private:
+    struct HIDInforStructure{
+            uint16_t    bcdHID;
+            uint8_t     bCountryCode;
+            uint8_t     Flags;
+            
+            HIDInforStructure():bcdHID(0),bCountryCode(0),Flags(0){
+                    memcpy(&hidInformation[0], &bcdHID, 2);
+                    memcpy(&hidInformation[2], &bCountryCode, 1);
+                    memcpy(&hidInformation[3], &Flags, 1);
+                }
+            uint8_t     *getPointer(void) {
+            return      hidInformation;
+            }
+            
+            uint8_t hidInformation[4];
+        };
+        
+private:
+    BLEDevice           &ble;
+    uint8_t             protocol_modeValue;
+    ReportStructure     reportValue;
+    uint8_t             hidcontrolPointer;
+    ReportMapStructure  KeyboardMap;
+    HIDInforStructure   hidInformation;
+    GattCharacteristic      Protocol_Mode;
+    GattCharacteristic      ReportMap;
+    GattCharacteristic      Report;
+//    ReadOnlyGattCharacteristic         Boot_Keyboard_Input_Report;
+//    ReadWriteGattCharacteristic        Boot_Keyboard_Output_Report;
+//    ReadOnlyGattCharacteristic         Boot_Mouse_Input_Report;
+    GattCharacteristic      HID_Information;
+    GattCharacteristic      HID_Control_Point;
+};
+#endif /* #ifndef __BLE_GLUCOSE_SERVICE_H__*/
\ No newline at end of file
--- a/main.cpp	Thu Oct 13 07:34:53 2016 +0000
+++ b/main.cpp	Tue Feb 07 02:51:56 2017 +0000
@@ -16,30 +16,81 @@
 
 #include "mbed.h"
 #include "ble/BLE.h"
-#include "ble/services/HeartRateService.h"
 #include "ble/services/BatteryService.h"
 #include "ble/services/DeviceInformationService.h"
+#include "HIDService.h"
 
-DigitalOut led1(LED1);
+//DigitalOut led1(LED1);
+Serial uart(USBTX, USBRX);
 
-const static char     DEVICE_NAME[]        = "DELTA_HRM1";
-static const uint16_t uuid16_list[]        = {GattService::UUID_HEART_RATE_SERVICE,
+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_DEVICE_INFORMATION_SERVICE};
 static volatile bool  triggerSensorPolling = false;
+bool isConnectionSecured = false;
 
-uint8_t hrmCounter = 100; // init HRM to 100bps
+DeviceInformationService *deviceInfo;
 
-HeartRateService         *hrService;
-DeviceInformationService *deviceInfo;
+void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey)
+{
+    uart.printf("Input passKey: ");
+    for (unsigned i = 0; i < Gap::ADDR_LEN; i++) {
+        uart.printf("%c ", passkey[i]);
+    }
+    uart.printf("\r\n");
+}
+ 
+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;
+    } else {
+        uart.printf("Security failed\r\n", status);
+    }
+}
+
+void linkSecuredCallback(Gap::Handle_t handle, SecurityManager::SecurityMode_t securityMode)
+{
+    uart.printf("linkSecuredCallback\r\n");
+    isConnectionSecured = true;
+}
+
+void onConnectionCallback(const Gap::ConnectionCallbackParams_t *params)
+{
+    uart.printf("Connected\r\n");
+}
 
 void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
 {
+    uart.printf("Disconnected\r\n");
+    isConnectionSecured = false;
     BLE::Instance(BLE::DEFAULT_INSTANCE).gap().startAdvertising(); // restart advertising
 }
 
+void onTimeoutCallback(Gap::TimeoutSource_t source)
+{
+    switch (source) {
+        case Gap::TIMEOUT_SRC_ADVERTISING:
+            uart.printf("Advertising timeout\r\n");
+            break;  
+        case Gap::TIMEOUT_SRC_SECURITY_REQUEST:
+            uart.printf("Security request timeout\r\n");
+            break;
+        case Gap::TIMEOUT_SRC_SCAN:
+            uart.printf("Scanning timeout\r\n");
+            break;
+        case Gap::TIMEOUT_SRC_CONN:
+            uart.printf("Connection timeout\r\n");
+            break;
+    }
+}
+
 void periodicCallback(void)
 {
-    led1 = !led1; /* Do blinky on LED1 while we're waiting for BLE events */
+    //led1 = !led1; /* Do blinky on LED1 while we're waiting for BLE events */
 
     /* Note that the periodicCallback() executes in interrupt context, so it is safer to do
      * heavy-weight sensor polling from the main thread. */
@@ -54,11 +105,22 @@
     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. */
-    hrService = new HeartRateService(ble, hrmCounter, HeartRateService::LOCATION_FINGER);
+    hidService = new HIDService(ble);
 
     /* Setup auxiliary service. */
     deviceInfo = new DeviceInformationService(ble, "DELTA", "NQ620", "SN1", "hw-rev1", "fw-rev1", "soft-rev1");
@@ -66,18 +128,28 @@
     /* 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::GENERIC_HEART_RATE_SENSOR);
+    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(1000); /* 1000ms */
+    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, 1); // blink LED every second
+    //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);
@@ -88,20 +160,63 @@
 
     // infinite loop
     while (1) {
-        // check for trigger from periodicCallback()
-        if (triggerSensorPolling && ble.getGapState().connected) {
-            triggerSensorPolling = false;
-
-            // Do blocking calls or whatever is necessary for sensor polling.
-            // In our case, we simply update the HRM measurement.
-            hrmCounter++;
-            if (hrmCounter == 175) { //  100 <= HRM bps <=175
-                hrmCounter = 100;
+        if (isConnectionSecured) {
+            if (uart.readable() == 1) {
+                keyData = uart.getc();
+                uart.putc(keyData);
+                msg[index_w++] = keyData;
+                if(keyData <= 0x39 && keyData >= 0x30){             //number
+                    if(keyData == 0x30){
+                        modifyKey[index_b] = 0x00;
+                        key_press_scan_buff[index_b] = 0x27;
+                        index_b++;
+                        key_press_scan_buff[index_b] = 0x73;
+                    } else {
+                        modifyKey[index_b] = 0x00;
+                        key_press_scan_buff[index_b] = keyData-0x13;
+                        index_b++;
+                        key_press_scan_buff[index_b] = 0x73;
+                        }
+                } else if(keyData <= 0x7a && keyData >= 0x61 ){ //lowercase letters
+                    modifyKey[index_b] = 0x00;
+                    key_press_scan_buff[index_b] = keyData-0x5d;
+                    index_b++;
+                    key_press_scan_buff[index_b] = 0x73;
+                } else if(keyData <= 0x5a && keyData >= 0x41){  //uppercase letters
+                    modifyKey[index_b] = 0x02;
+                    key_press_scan_buff[index_b] = keyData-0x3d;
+                    index_b++;
+                    key_press_scan_buff[index_b] = 0x73;
+                } else if (keyData == 0x20) {                   //space
+                    modifyKey[index_b] = 0x00;
+                    key_press_scan_buff[index_b] = 0x2c;
+                    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);
+                    }
+            
+                    index_b = 0;
+                    index_w = 0;
+                    memset(modifyKey, 0, 50);
+                    memset(msg, 0, 25);
+                    memset(key_press_scan_buff, 0, 50);
+                }
             }
-
-            hrService->updateHeartRate(hrmCounter);
         } else {
             ble.waitForEvent(); // low power wait for event
+            wait(1);
         }
+        
     }
 }