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

Files at this revision

API Documentation at this revision

Comitter:
bcc6
Date:
Tue Jan 17 03:53:35 2017 +0000
Parent:
2:6109e375f9a7
Commit message:
After reset, purge all bonding state.

Changed in this revision

BLE_HID.lib Show annotated file Show diff for this revision Revisions of this file
debug.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
--- a/BLE_HID.lib	Fri Dec 30 08:36:50 2016 +0000
+++ b/BLE_HID.lib	Tue Jan 17 03:53:35 2017 +0000
@@ -1,1 +1,1 @@
-https://developer.mbed.org/users/bcc6/code/BLE_HID/#c02f0fe0db5f
+https://developer.mbed.org/users/bcc6/code/BLE_HID/#dc4e6dbcb79b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debug.h	Tue Jan 17 03:53:35 2017 +0000
@@ -0,0 +1,32 @@
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+#include "Serial.h"
+
+
+#define ENABLE_LOG      1
+#define ENABLE_ASSERT   1
+
+extern Serial dbg;
+
+
+#if ENABLE_LOG
+#define log(...) dbg.printf(__VA_ARGS__)
+#else
+#define log(...)
+#endif
+
+#if ENABLE_ASSERT
+#define assert(expr) { \
+    if (!(expr)) { \
+        dbg.printf("ASSERT: %s, file %s, line %d\n", #expr, __FILE__, __LINE__); \
+        while(1); \
+    } \
+}
+#else
+#define assert(expr)
+#endif
+
+
+#endif
+
--- 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
 }
+