BLE HID keyboard with gesture

Dependencies:   BLE_API BLE_HID PAJ7620U2 mbed nRF51822

https://mtmtechblog.files.wordpress.com/2017/01/mtsense04-pi-on-mbed.jpeg We have full tutorial, please visit our blog

Revision:
3:656f8fb89f05
Parent:
2:6109e375f9a7
--- a/main.cpp	Fri Dec 30 08:36:50 2016 +0000
+++ b/main.cpp	Tue Jan 17 03:53:35 2017 +0000
@@ -17,159 +17,220 @@
  */
 #include "mbed.h"
 #include "ble/BLE.h"
-#include "ble/services/BatteryService.h"
-#include "ble/services/DeviceInformationService.h"
 
+#include "debug.h"
 #include "PAJ7620U2.h"
 
-#include "HIDServiceBase.h"
-//#include "MouseService.h"
-//#include "JoystickService.h"
-#include "KeyboardService.h"
+#include "BLE_HID/DeviceInformationService.h"
+#include "BLE_HID/BatteryService.h"
+#include "BLE_HID/HIDServiceBase.h"
+#include "BLE_HID/KeyboardService.h"
 
 
-/**
- * IO capabilities of the device. During development, you most likely want "JustWorks", which means
- * no IO capabilities.
- * It is also possible to use IO_CAPS_DISPLAY_ONLY to generate and show a pincode on the serial
- * output.
- */
-#ifndef HID_SECURITY_IOCAPS
-#define HID_SECURITY_IOCAPS (SecurityManager::IO_CAPS_NONE)
+#define DEVICE_NAME     ("MtM_gHID")
+
+#define TX_POWER_dBm    (0) // -40, -20, -16, -12, -8, -4, 0, 4
+
+#define BD_ADDRESS_TYPE (BLEProtocol::AddressType::RANDOM_STATIC)
+#define BD_ADDRESS      ((Gap::Address_t){0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC})
+
+#define ENABLE_BONDING  (true)
+#define REQUIRE_MITM    (false)
+#define IOCAPS          (SecurityManager::IO_CAPS_NONE)
+#define PASSKEY         ((SecurityManager::Passkey_t){'1', '2', '3', '4', '5', '6'})
+
+#define MANUFACTURERS_NAME  ("MtM")
+#define MODEL_NAME          (NULL)
+#define SERIAL_NUMBER       (NULL)
+#define HW_VERSION          (NULL)
+#define FW_VERSION          (NULL)
+#define SW_VERSION          (NULL)
+#if 0
+#define PNP_ID  &((PnPID_t){0x01, 0xFFFE, 0x0001, 0x0001})  // From Bluetooth SIG
+#else
+#define PNP_ID  &((PnPID_t){0x02, 0x1915, 0xEEEE, 0x0001})  // From USB-IF
 #endif
 
-/**
- * Security level. MITM disabled forces "Just Works". If you require MITM, HID_SECURITY_IOCAPS must
- * be at least IO_CAPS_DISPLAY_ONLY.
- */
-#ifndef HID_SECURITY_REQUIRE_MITM
-#define HID_SECURITY_REQUIRE_MITM false
-#endif
+#define MIN_CONN_INTERVAL   (Gap::MSEC_TO_GAP_DURATION_UNITS(10));  // 10ms
+#define MAX_CONN_INTERVAL   (Gap::MSEC_TO_GAP_DURATION_UNITS(30));  // 30ms
+#define SLAVE_LATENCY       (6)
+#define CONN_SUP_TIMEOUT    (43)    // 430ms
+
+#define ADV_INTERVAL        (50)    // 50ms
+#define ADV_TIMEOUT         (60)    // 60sec
 
 
-/* UART printf */
-Serial pc(p5, p4);
+/* UART for debug (log, assert) */
+Serial dbg(p5, p4);
 
 /* Sensor */
 PAJ7620U2 gesture(p3, p2, p0);
 volatile bool gestureHasIntEvent = false;
 
 /* HID service */
-//MouseService    *msService = NULL;
-//JoystickService *jsService = NULL;
 KeyboardService *kbService = NULL;
 
-/* Device name */
-static const char DEVICE_NAME[] = "MtM Gesture";
-static const char SHORT_DEVICE_NAME[] = "gHID";
+
+static void connectionCallback(const Gap::ConnectionCallbackParams_t *params)
+{
+    log("connection\n");
 
+    log("role(%d)\n", params->role);
+    log("peerAddrType(%d), peerAddr(%02X:%02X:%02X:%02X:%02X:%02X)\n", 
+        params->peerAddrType,
+        params->peerAddr[5], params->peerAddr[4], params->peerAddr[3], params->peerAddr[2], params->peerAddr[1], params->peerAddr[0]);
+    log("ownAddrType(%d), ownAddr(%02X:%02X:%02X:%02X:%02X:%02X)\n", 
+        params->ownAddrType,
+        params->ownAddr[5], params->ownAddr[4], params->ownAddr[3], params->ownAddr[2], params->ownAddr[1], params->ownAddr[0]);
 
-void connectionCallback(const Gap::ConnectionCallbackParams_t *params)
-{
-    pc.printf("connection\n");
+    log("connectionParams(%d, %d, %d, %d)\n", 
+        params->connectionParams->minConnectionInterval,
+        params->connectionParams->maxConnectionInterval,
+        params->connectionParams->slaveLatency,
+        params->connectionParams->connectionSupervisionTimeout);
 }
 
-void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
+static void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
 {    
     BLE::Instance().gap().startAdvertising(); // restart advertising
     
-    pc.printf("disconnection\n");
+    log("disconnection\n");
 }
 
-static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey)
+static void timeoutCallback(const Gap::TimeoutSource_t source)
 {
-    pc.printf("Input passKey: ");
-    for (unsigned i = 0; i < Gap::ADDR_LEN; i++) {
-        pc.printf("%c", passkey[i]);
-    }
-    pc.printf("\r\n");
+    log("timeout(%d)\n", source);
+}
+
+static void securitySetupInitiatedCallback(Gap::Handle_t, bool allowBonding, bool requireMITM, SecurityManager::SecurityIOCapabilities_t iocaps)
+{
+    log("Security setup initiated\r\n");
 }
 
 static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status)
 {
     if (status == SecurityManager::SEC_STATUS_SUCCESS) {
-        pc.printf("Security success %d\r\n", status);
+        log("Security success %d\r\n", status);
     } else {
-        pc.printf("Security failed %d\r\n", status);
+        log("Security failed %d\r\n", status);
     }
 }
 
-static void securitySetupInitiatedCallback(Gap::Handle_t, bool allowBonding, bool requireMITM, SecurityManager::SecurityIOCapabilities_t iocaps)
-{
-    pc.printf("Security setup initiated\r\n");
-}
-
-void initializeSecurity(BLE &ble)
+static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey)
 {
-    bool enableBonding = true;
-    bool requireMITM = HID_SECURITY_REQUIRE_MITM;
-
-    ble.securityManager().onSecuritySetupInitiated(securitySetupInitiatedCallback);
-    ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback);
-    ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
-
-    ble.securityManager().init(enableBonding, requireMITM, HID_SECURITY_IOCAPS);
+    log("Input passKey: ");
+    for (unsigned i = 0; i < Gap::ADDR_LEN; i++) {
+        log("%c", passkey[i]);
+    }
+    log("\r\n");
 }
 
-void initializeHOGP(BLE &ble)
+static void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
 {
-    static const uint16_t uuid16_list[] =  {GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE,
-        GattService::UUID_DEVICE_INFORMATION_SERVICE,
-        GattService::UUID_BATTERY_SERVICE};
-
-    DeviceInformationService deviceInfo(ble, "ARM", "m1", "abc", "def", "ghi", "jkl");
-
-    BatteryService batteryInfo(ble, 80);
+    log("InitComplete\n");
 
-    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));
-
-    // see 5.1.2: HID over GATT Specification (pg. 25)
-    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
-    // 30ms to 50ms is recommended (5.1.2)
-    ble.gap().setAdvertisingInterval(50);
-}
-
-void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
-{
     BLE &ble          = params->ble;
     ble_error_t error = params->error;
 
-    if (error != BLE_ERROR_NONE) {
-        return;
+    assert(error == BLE_ERROR_NONE);
+
+
+    /* Set Tx power */
+    error = ble.gap().setTxPower(TX_POWER_dBm);
+    assert(error == BLE_ERROR_NONE);
+
+    /* Set address */
+    error = ble.gap().setAddress(BD_ADDRESS_TYPE, BD_ADDRESS);
+    assert(error == BLE_ERROR_NONE);
+
+    /* Initialize BLE security */
+    error = ble.securityManager().init(ENABLE_BONDING, REQUIRE_MITM, IOCAPS, PASSKEY);
+    assert(error == BLE_ERROR_NONE);
+
+#if 1   // After reset, clear bonding state
+    /* Purge all bonding state */
+    error = ble.securityManager().purgeAllBondingState();
+    assert(error == BLE_ERROR_NONE);
+#endif
+
+#if 0   // Disable white-list
+    /* Set white-list */
+    BLEProtocol::Address_t addresses[2];
+    Gap::Whitelist_t whitelist;
+    whitelist.addresses = addresses;
+    whitelist.size = 0;
+    whitelist.capacity = 2;
+    error = ble.securityManager().getAddressesFromBondTable(whitelist);
+    assert(error == BLE_ERROR_NONE);
+    
+    log("getAddressesFromBondTable (%d)\n", whitelist.size);
+    for(int i=0; i<whitelist.size; i++) {
+        int type = (int)whitelist.addresses[i].type;
+        uint8_t *addr = (uint8_t *)whitelist.addresses[i].address;
+        log("type(%d), addr(%02X:%02X:%02X:%02X:%02X:%02X)\n",
+            type, addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);            
     }
+    error = ble.gap().setWhitelist(whitelist);
+    assert(error == BLE_ERROR_NONE);
+    error = ble.gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST);   // Allow scan requests and connect requests from any device
+    assert(error == BLE_ERROR_NONE);
+#endif
 
+    /* Set callback functions */
     ble.gap().onConnection(connectionCallback);
     ble.gap().onDisconnection(disconnectionCallback);
+    ble.gap().onTimeout(timeoutCallback);
+    ble.securityManager().onSecuritySetupInitiated(securitySetupInitiatedCallback);
+    ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
+    ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback);
 
-    /* Setup primary service. */
-    initializeSecurity(ble);
-//  msService = new MouseService(ble);
-//  jsService = new JoystickService(ble);
+    /* Setup primary service */
+    DeviceInformationService deviceInfo(ble, MANUFACTURERS_NAME, MODEL_NAME, SERIAL_NUMBER, HW_VERSION, FW_VERSION, SW_VERSION, PNP_ID);
+    BatteryService batteryInfo(ble, 80);
     kbService = new KeyboardService(ble);
-    initializeHOGP(ble);
+
+    /* Connection parameters */
+    Gap::ConnectionParams_t conn_params;
+    conn_params.minConnectionInterval = MIN_CONN_INTERVAL;
+    conn_params.maxConnectionInterval = MAX_CONN_INTERVAL;
+    conn_params.slaveLatency = SLAVE_LATENCY;
+    conn_params.connectionSupervisionTimeout = CONN_SUP_TIMEOUT;
+    ble.gap().setPreferredConnectionParams(&conn_params);
 
     /* Setup advertising. */
-    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD/*MOUSE*/);
-    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (const uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
-    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME, (const uint8_t *)SHORT_DEVICE_NAME, sizeof(SHORT_DEVICE_NAME));
-    ble.gap().setDeviceName((const uint8_t *)DEVICE_NAME);
-    ble.gap().startAdvertising();
+    static const uint16_t uuid16_list[] = {
+        GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE
+    };
+    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+    ble.gap().setAdvertisingInterval(ADV_INTERVAL);
+    ble.gap().setAdvertisingTimeout(ADV_TIMEOUT);
+    error = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_LIMITED_DISCOVERABLE);
+    assert(error == BLE_ERROR_NONE);
+    error = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
+    assert(error == BLE_ERROR_NONE);
+    error = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD);
+    assert(error == BLE_ERROR_NONE);
+    error = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (const uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
+    assert(error == BLE_ERROR_NONE);
+    error = ble.gap().setDeviceName((const uint8_t *)DEVICE_NAME);
+    assert(error == BLE_ERROR_NONE);
+    error = ble.gap().startAdvertising();
+    assert(error == BLE_ERROR_NONE);
 }
 
-void gestureIntEventCallback()
+static void gestureIntEventCallback()
 {
-    pc.printf("gestureIntEventCallback\n");
+    log("gestureIntEventCallback\n");
     gestureHasIntEvent = true;
 }
 
 int main(void)
 {
-    /* Force to disable the hardware flow control of Serial */
+    /* Disable the hardware flow control of Serial */
     *((uint32_t *)(0x40002000+0x56C)) = 0;
-    pc.printf("~ Hell World ~\n");
+    log("\n");
+    log("~ Hell World ~\n");
+    log("\n");
 
     /* Init BLE */
     BLE& ble = BLE::Instance();
@@ -177,7 +238,7 @@
     while(ble.hasInitialized() == false) { /* spin loop */ }
 
     /* Config sensor */
-    pc.printf("PID(0x%04X), VID(0x%02X)\n", gesture.PID, gesture.VID);
+    log("PID(0x%04X), VID(0x%02X)\n", gesture.PID, gesture.VID);
     gesture.IntEvent(&gestureIntEventCallback);
 
     /* Main loop */
@@ -188,45 +249,45 @@
 
             /* Get sensor data */
             uint16_t int_flag = gesture.ReadIntFlag();
-            pc.printf("(0x%04X)\n", int_flag);
+            log("(0x%04X)\n", int_flag);
 
             /* Send HID code */
             if(kbService!=NULL && kbService->isConnected()){
                 switch(int_flag){
                 case 0x0001:    // Up
-                    pc.printf("UpArrow\n");
+                    log("UpArrow\n");
                     kbService->printf("%c", UP_ARROW);
                     break;
                 case 0x0002:    // Down
-                    pc.printf("DownArrow\n");
+                    log("DownArrow\n");
                     kbService->printf("%c", DOWN_ARROW);
                     break;
                 case 0x0004:    // Left
-                    pc.printf("LeftArrow\n");
+                    log("LeftArrow\n");
                     kbService->printf("%c", LEFT_ARROW);
                     break;
                 case 0x0008:    // Right
-                    pc.printf("RightArrow\n");
+                    log("RightArrow\n");
                     kbService->printf("%c", RIGHT_ARROW);
                     break;
                 case 0x0010:    // Forward
-                    pc.printf("PageDown\n");
+                    log("PageDown\n");
                     kbService->printf("%c", KEY_PAGE_DOWN);
                     break;
                 case 0x0020:    // Backword
-                    pc.printf("PageUp\n");
+                    log("PageUp\n");
                     kbService->printf("%c", KEY_PAGE_UP);
                     break;
                 case 0x0040:    // Clockwise
-                    pc.printf("F5\n");
+                    log("F5\n");
                     kbService->printf("%c", KEY_F5);
                     break;
                 case 0x0080:    // Counter-Clockwise
-                    pc.printf("ESC\n");
+                    log("ESC\n");
                     kbService->printf("%c", 27);
                     break;
                 case 0x0100:    // Wave
-                    pc.printf("Bye Bye\n");
+                    log("Bye Bye\n");
                     kbService->printf("Bye Bye\n");
                     break;
                 }// End of switch
@@ -234,9 +295,10 @@
         }
 
         /* low power wait for event */
-//      pc.printf("sleep\n");
+//      log("sleep\n");
         ble.waitForEvent();
-//      pc.printf("wakeup\n");
+//      log("wakeup\n");
 
     }// End of while
 }
+