A BLE HID controller implementation with communication over SPI

Dependencies:   BLE_API BLE_HID mbed nRF51822

Files at this revision

API Documentation at this revision

Comitter:
mrhannah
Date:
Wed Dec 13 05:34:15 2017 +0000
Commit message:
Initial/(final) changes for project

Changed in this revision

BLE_API.lib Show annotated file Show diff for this revision Revisions of this file
BLE_HID.lib Show annotated file Show diff for this revision Revisions of this file
ControllerService.h Show annotated file Show diff for this revision Revisions of this file
common.cpp Show annotated file Show diff for this revision Revisions of this file
common.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.bld Show annotated file Show diff for this revision Revisions of this file
nRF51822.lib Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BLE_API.lib	Wed Dec 13 05:34:15 2017 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/#65474dc93927
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BLE_HID.lib	Wed Dec 13 05:34:15 2017 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/jbru/code/BLE_HID/#4f8429a1905b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ControllerService.h	Wed Dec 13 05:34:15 2017 +0000
@@ -0,0 +1,116 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mbed.h"
+
+#include "HIDServiceBase.h"
+
+/**
+ * Report descriptor for a standard 3 buttons + wheel mouse with relative X/Y
+ * moves
+ */
+report_map_t CONTROLLER_REPORT_MAP = {
+        USAGE_PAGE(1),      0x01,       // Genric Desktop
+        USAGE(1),           0x05,       // Gamepad
+        COLLECTION(1),      0x01,       // Application
+        COLLECTION(1),      0x00,       // Physical
+        
+        REPORT_ID(1),       0x03,       // Report ID 3
+        USAGE_PAGE(1),      0x09,       // Buttons
+        USAGE_MINIMUM(1),       0x01,   // Minimum of 1
+        USAGE_MAXIMUM(1),       0x10,   // Maximum of 16
+        LOGICAL_MINIMUM(1),     0x00,   // Logical Minimum of 0
+        LOGICAL_MAXIMUM(1),     0x01,   // Logical Maximum of 1
+        REPORT_SIZE(1),     0x01,       // Each report is 1 bit
+        REPORT_COUNT(1),    0x10,       // All in all 16 * 1 bit
+        INPUT(1),           0x02,       // Input (Data, Var, Abs)
+        
+        USAGE_PAGE(1),      0x01,       // Generic Desktop
+        USAGE(1),           0x30,       // Usage X
+        USAGE(1),           0x31,       // Usage Y
+        USAGE(1),           0x32,       // Usage Z
+        USAGE(1),           0x33,       // Usage Rx
+        LOGICAL_MINIMUM(1), 0x00,       // Logical Minimum: 0
+        LOGICAL_MAXIMUM(1), 0xFE,       // Logical maximum: 254
+        REPORT_SIZE(1),     0x08,       // Each report is 8 bits
+        REPORT_COUNT(1),    0x04,       // 4 reports * 8 bits
+        INPUT(1),           0x02,        // Input (Data, Var, Abs)
+        END_COLLECTION(0),
+        END_COLLECTION(0)
+};
+
+uint8_t report[7] = {0x03, 0, 0, 0, 0, 0, 0};
+
+//bool toggle = true;
+//uint8_t onReport[7] = {0x03, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80};
+//uint8_t offReport[7] = {0x03, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20};
+
+class ControllerService: public HIDServiceBase
+{
+public:
+    ControllerService(BLE &_ble) :
+        HIDServiceBase(_ble,
+                       CONTROLLER_REPORT_MAP, sizeof(CONTROLLER_REPORT_MAP),
+                       inputReport          = report,
+                       outputReport         = NULL,
+                       featureReport        = NULL,
+                       inputReportLength    = 7,
+                       outputReportLength   = 0,
+                       featureReportLength  = 0,
+                       reportTickerDelay    = 250)
+    {
+        failedReports = 0;
+        startReportTicker();
+    }
+
+    virtual void onConnection(const Gap::ConnectionCallbackParams_t *params)
+    {
+        HIDServiceBase::onConnection(params);
+        startReportTicker();
+    }
+
+    virtual void onDisconnection(const Gap::DisconnectionCallbackParams_t *params)
+    {
+        stopReportTicker();
+        HIDServiceBase::onDisconnection(params);
+    }
+    
+    void setReport(uint8_t *r) {
+//        for (int i = 0; i < 6; i++) {
+//            report[i+1] = r[i];
+//        }
+        memcpy(&report[1], r, 6);
+    }
+
+    /**
+     * Called by the report ticker
+     */
+
+    virtual void sendCallback(void) {
+//        if (toggle) {
+//            send(onReport);
+//        } else {
+//            send(offReport);
+//        }
+//        toggle = !toggle;
+        
+        if (send(report)) {
+            failedReports++;
+        }   
+    }
+    
+    uint32_t failedReports;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common.cpp	Wed Dec 13 05:34:15 2017 +0000
@@ -0,0 +1,76 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ble/services/BatteryService.h"
+#include "ble/services/DeviceInformationService.h"
+
+#include "common.h"
+
+static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey)
+{
+    printf("Input passKey: ");
+    for (unsigned i = 0; i < Gap::ADDR_LEN; i++) {
+        printf("%c", passkey[i]);
+    }
+    printf("\r\n");
+}
+
+static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status)
+{
+    if (status == SecurityManager::SEC_STATUS_SUCCESS) {
+        printf("Security success %d\r\n", status);
+    } else {
+        printf("Security failed %d\r\n", status);
+    }
+}
+
+static void securitySetupInitiatedCallback(Gap::Handle_t, bool allowBonding, bool requireMITM, SecurityManager::SecurityIOCapabilities_t iocaps)
+{
+    printf("Security setup initiated\r\n");
+}
+
+void initializeSecurity(BLE &ble)
+{
+    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);
+}
+
+void initializeHOGP(BLE &ble)
+{
+    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);
+
+    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);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common.h	Wed Dec 13 05:34:15 2017 +0000
@@ -0,0 +1,69 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HID_EXAMPLES_COMMON_H_
+#define HID_EXAMPLES_COMMON_H_
+
+/**
+ * Functions and configuration common to all HID demos
+ */
+
+#include "ble/BLE.h"
+
+#include "HIDServiceBase.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)
+#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
+
+/**
+ * Disable debug messages by setting NDEBUG
+ */
+#ifndef NDEBUG
+#define HID_DEBUG(...) printf(__VA_ARGS__)
+#else
+#define HID_DEBUG(...)
+#endif
+
+/**
+ * Initialize security manager: set callback functions and required security level
+ */
+void initializeSecurity(BLE &ble);
+
+/**
+ * - Initialize auxiliary services required by the HID-over-GATT Profile.
+ * - Initialize common Gap advertisement.
+ *
+ * Demos only have to set a custom device name and appearance, and their HID
+ * service.
+ */
+void initializeHOGP(BLE &ble);
+
+#endif /* !BLE_HID_COMMON_H_ */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Dec 13 05:34:15 2017 +0000
@@ -0,0 +1,149 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mbed.h"
+
+#include "ble/BLE.h"
+#include "ControllerService.h"
+#include "common.h"
+
+/**
+ * This program implements a complete HID-over-Gatt Profile:
+ *  - HID is provided by ControllerServices
+ *  - Battery Service
+ *  - Device Information Service
+ *
+ */
+
+DigitalOut waiting_led(LED1);
+DigitalOut connected_led(LED2);
+
+
+SPISlave arduino(p23, p22, p21, p24);  // mosi, miso, sck, ssel
+DigitalOut arduino_int(p25);
+
+BLE ble;
+ControllerService *contServicePtr;
+
+static const char DEVICE_NAME[] = "puppeteerPro";
+static const char SHORT_DEVICE_NAME[] = "pptPro";
+
+static void onDisconnect(const Gap::DisconnectionCallbackParams_t *params)
+{
+    HID_DEBUG("disconnected\r\n");
+    connected_led = 0;
+
+    ble.gap().startAdvertising(); // restart advertising
+}
+
+static void onConnect(const Gap::ConnectionCallbackParams_t *params)
+{
+    HID_DEBUG("connected\r\n");
+    waiting_led = false;
+}
+
+static void waiting() {
+    if (!contServicePtr->isConnected()) {
+        //connected_led = !connected_led;
+    }
+    else {
+        //connected_led = 1;
+    }
+}
+
+// Current system
+// arduino does slave select & interrupt
+// arduino transfers
+// ble hits isr & records current report
+void spi_receive() {
+    static uint8_t byte_count = 0;
+    static uint8_t current_report[6] = {0};
+
+    if(arduino.receive()) {
+        waiting_led = !waiting_led;
+        uint8_t resp = arduino.read();
+    
+        if (resp == 0xFF) {
+            byte_count = 0;
+            memset(current_report, 0, sizeof(current_report));
+        } else {
+            current_report[byte_count] = resp;
+            byte_count++;
+        
+            if (byte_count == 6) {
+                connected_led = !connected_led;
+                contServicePtr->setReport(current_report);
+                byte_count = 0;
+            }
+        }
+        arduino.reply(0xAA);
+
+        // finally trigger interrupt to signal receiving
+        arduino_int = 0;
+        wait_us(100);
+        arduino_int = 1;
+        wait_us(100);
+        arduino_int = 0;
+    }
+}
+
+int main()
+{
+    Ticker heartbeat;
+
+    HID_DEBUG("initialising ticker\r\n");
+
+    heartbeat.attach(waiting, 1);
+
+    HID_DEBUG("initialising ble\r\n");
+    ble.init();
+
+    ble.gap().onDisconnection(onDisconnect);
+    ble.gap().onConnection(onConnect);
+
+    initializeSecurity(ble);
+
+    HID_DEBUG("adding hid service\r\n");
+    ControllerService contService(ble);
+    contServicePtr = &contService;
+
+    HID_DEBUG("adding device info and battery service\r\n");
+    initializeHOGP(ble);
+
+    HID_DEBUG("setting up gap\r\n");
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GAMEPAD);
+    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);
+
+    HID_DEBUG("advertising\r\n");
+    ble.gap().startAdvertising();
+    
+    // Start interrupt sequence for SPI with Arduino
+    Ticker spi_thread;
+    connected_led = 1;
+    arduino_int = 0;
+    arduino.reply(0xAA);
+    spi_thread.attach(spi_receive, 0.020); // 5ms ticker sequence
+
+    
+    while (true) {
+        ble.waitForEvent();
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Wed Dec 13 05:34:15 2017 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/e7ca05fa8600
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nRF51822.lib	Wed Dec 13 05:34:15 2017 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/teams/Nordic-Semiconductor/code/nRF51822/#c90ae1400bf2