ble nano hid over gatt
Dependencies: BLE_API mbed-dev nRF51822
HIDController_BLE.cpp
- Committer:
- cho45
- Date:
- 2016-08-30
- Revision:
- 55:f01a31103685
- Parent:
- 54:899fc2b0a76b
- Child:
- 56:e1d90e2c7402
File content as of revision 55:f01a31103685:
#include "mbed.h" #include "BLE.h" #include "config.h" #include "KeyboardService.h" #include "BatteryService.h" #include "DeviceInformationService.h" #include "DFUService.h" #include "HIDController_BLE.h" static const char MODEL_NAME[] = "keble"; static const char SERIAL_NUMBER[] = "X00000"; static const char HARDWARE_REVISION[] = "0.1"; static const char FIRMWARE_REVISION[] = "0.1"; static const char SOFTWARE_REVISION[] = "0.0"; static const PnPID_t PNPID = { 2, 0x05ac, 0x0267, 0x50 }; static const uint8_t DEVICE_NAME[] = "Keble"; // static const uint8_t SHORT_DEVICE_NAME[] = "Keble"; static const bool ENABLE_BONDING = true; static const bool REQUIRE_MITM = true; static const uint8_t PASSKEY[6] = {'1','2','3','4','5','6'}; // must be 6-digits number static const uint16_t uuid16_list[] = { GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE, GattService::UUID_DEVICE_INFORMATION_SERVICE, GattService::UUID_BATTERY_SERVICE }; static KeyboardService* keyboardService; static BatteryService* batteryService; static DeviceInformationService* deviceInformationService; // static DFUService* dfuService; static BLEProtocol::Address_t peerAddress; static volatile Status_t controllerStatus; static void onConnect(const Gap::ConnectionCallbackParams_t *params) { peerAddress.type = params->peerAddrType; memcpy(peerAddress.address, params->peerAddr, Gap::ADDR_LEN); // TODO whitelist に peerAddr が含まれていない場合 securitySetupCompletedCallback を待ってから CONNECTED にすべき controllerStatus = CONNECTED; } static void onDisconnect(const Gap::DisconnectionCallbackParams_t *params) { controllerStatus = DISCONNECTED; DEBUG_PRINTF_BLE_INTERRUPT("onDisconnect\r\n"); } static void onTimeout(const Gap::TimeoutSource_t source) { controllerStatus = TIMEOUT; DEBUG_PRINTF_BLE_INTERRUPT("onTimeout\r\n"); } static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey) { DEBUG_PRINTF_BLE_INTERRUPT("Input passKey: "); for (unsigned i = 0; i < Gap::ADDR_LEN; i++) { DEBUG_PRINTF_BLE_INTERRUPT("%c", passkey[i]); } DEBUG_PRINTF_BLE_INTERRUPT("\r\n"); } static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status) { if (status == SecurityManager::SEC_STATUS_SUCCESS) { DEBUG_PRINTF_BLE_INTERRUPT("Security success %d\r\n", status); DEBUG_PRINTF_BLE_INTERRUPT("Set whitelist\r\n"); Gap::Whitelist_t whitelist; whitelist.size = 1; whitelist.capacity = 1; whitelist.addresses = &peerAddress; BLE::Instance(BLE::DEFAULT_INSTANCE).gap().setWhitelist(whitelist); DEBUG_PRINTF_BLE_INTERRUPT("Set Advertising Policy Mode\r\n"); // BLE::Instance(BLE::DEFAULT_INSTANCE).gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_SCAN_REQS); // BLE::Instance(BLE::DEFAULT_INSTANCE).gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_CONN_REQS); BLE::Instance(BLE::DEFAULT_INSTANCE).gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_ALL_REQS); } else { DEBUG_PRINTF_BLE_INTERRUPT("Security failed %d\r\n", status); } } static void securitySetupInitiatedCallback(Gap::Handle_t, bool allowBonding, bool requireMITM, SecurityManager::SecurityIOCapabilities_t iocaps) { DEBUG_PRINTF_BLE_INTERRUPT("Security setup initiated\r\n"); } static void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) { // https://developer.mbed.org/compiler/#nav:/keyboard/BLE_API/ble/blecommon.h; ble_error_t error; BLE &ble = params->ble; controllerStatus = DISCONNECTED; /**< Minimum Connection Interval in 1.25 ms units, see BLE_GAP_CP_LIMITS.*/ uint16_t minConnectionInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(20); /**< Maximum Connection Interval in 1.25 ms units, see BLE_GAP_CP_LIMITS.*/ uint16_t maxConnectionInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(40); /**< Slave Latency in number of connection events, see BLE_GAP_CP_LIMITS.*/ uint16_t slaveLatency = 10; /**< Connection Supervision Timeout in 10 ms units, see BLE_GAP_CP_LIMITS.*/ uint16_t connectionSupervisionTimeout = 32 * 100; Gap::ConnectionParams_t connectionParams = { minConnectionInterval, maxConnectionInterval, slaveLatency, connectionSupervisionTimeout }; error = params->error; if (error != BLE_ERROR_NONE) { DEBUG_PRINTF_BLE("error on ble.init() \r\n"); goto return_error; } ble.gap().onDisconnection(onDisconnect); ble.gap().onConnection(onConnect); ble.gap().onTimeout(onTimeout); // DEBUG_PRINTF_BLE("setup ble security manager\r\n"); ble.securityManager().onSecuritySetupInitiated(securitySetupInitiatedCallback); ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback); ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback); // bonding with hard-coded passkey. error = ble.securityManager().init(ENABLE_BONDING, REQUIRE_MITM, SecurityManager::IO_CAPS_DISPLAY_YESNO, PASSKEY); // error = ble.securityManager().init(ENABLE_BONDING, REQUIRE_MITM, SecurityManager::IO_CAPS_DISPLAY_ONLY, PASSKEY); if (error != BLE_ERROR_NONE) { DEBUG_PRINTF_BLE("error on ble.securityManager().init()"); goto return_error; } // DEBUG_PRINTF_BLE("new KeyboardService\r\n"); keyboardService = new KeyboardService(ble); // DEBUG_PRINTF_BLE("new DeviceInformationService\r\n"); deviceInformationService = new DeviceInformationService(ble, "lowreal.net", MODEL_NAME, SERIAL_NUMBER, HARDWARE_REVISION, FIRMWARE_REVISION, SOFTWARE_REVISION, &PNPID); // DEBUG_PRINTF_BLE("new BatteryService\r\n"); batteryService = new BatteryService(ble, 100); /** TODO DEBUG_PRINTF_BLE("new DFUService\r\n"); dfuService = new DFUService(ble); */ //DEBUG_PRINTF_BLE("setup connection params\r\n"); ble.gap().setPreferredConnectionParams(&connectionParams); // DEBUG_PRINTF_BLE("general setup\r\n"); error = ble.gap().accumulateAdvertisingPayload( GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE ); // shoud be LE_LIMITED_DISCOVERABLE // error = ble.gap().accumulateAdvertisingPayload( // GapAdvertisingData::BREDR_NOT_SUPPORTED | // GapAdvertisingData::LE_LIMITED_DISCOVERABLE // ); if (error != BLE_ERROR_NONE) goto return_error; // DEBUG_PRINTF_BLE("set COMPLETE_LIST_16BIT_SERVICE_IDS\r\n"); error = ble.gap().accumulateAdvertisingPayload( GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t*)uuid16_list, sizeof(uuid16_list) ); if (error != BLE_ERROR_NONE) goto return_error; // DEBUG_PRINTF_BLE("set advertising\r\n"); // see 5.1.2: HID over GATT Specification (pg. 25) ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); // DEBUG_PRINTF_BLE("set advertising interval\r\n"); ble.gap().setAdvertisingInterval(20); // DEBUG_PRINTF_BLE("set advertising timeout\r\n"); ble.gap().setAdvertisingTimeout(30); /* ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_DIRECTED); ble.gap().setAdvertisingTimeout(1.28); */ // DEBUG_PRINTF_BLE("set keyboard\r\n"); error = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD); if (error != BLE_ERROR_NONE) goto return_error; // DEBUG_PRINTF_BLE("set complete local name\r\n"); error = ble.gap().accumulateAdvertisingPayload( GapAdvertisingData::COMPLETE_LOCAL_NAME, DEVICE_NAME, sizeof(DEVICE_NAME) ); if (error != BLE_ERROR_NONE) goto return_error; // DEBUG_PRINTF_BLE("set device name\r\n"); error = ble.gap().setDeviceName(DEVICE_NAME); if (error != BLE_ERROR_NONE) goto return_error; /* (Valid values are -40, -20, -16, -12, -8, -4, 0, 4) */ ble.gap().setTxPower(0); ble.gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST); // DEBUG_PRINTF_BLE("advertising\r\n"); error = ble.gap().startAdvertising(); if (error != BLE_ERROR_NONE) goto return_error; controllerStatus = ADVERTISING; return; return_error: DEBUG_PRINTF_BLE("error with %d\r\n", error); return; } bool HIDController::connected() { return controllerStatus == CONNECTED; } Status_t HIDController::status() { return controllerStatus; } const char* HIDController::statusString() { static const char* disconnected = "disconnected"; static const char* connecting = "connecting"; static const char* connected = "connected"; static const char* timeout = "timeout"; static const char* advertising = "advertising"; static const char* unknown = "unknown"; return controllerStatus == DISCONNECTED ? disconnected: controllerStatus == CONNECTING ? connecting: controllerStatus == CONNECTED ? connected: controllerStatus == TIMEOUT ? timeout: controllerStatus == ADVERTISING ? advertising: unknown; } void HIDController::init() { // https://github.com/jpbrucker/BLE_HID/blob/master/examples/examples_common.cpp DEBUG_PRINTF_BLE("ble.init\r\n"); BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE); ble.init(bleInitComplete); // copied from https://github.com/lancaster-university/microbit-dal/commit/3c314794f07e5ac91331c9e9849475375708ec89 // configure the stack to hold on to CPU during critical timing events. // mbed-classic performs __disabe_irq calls in its timers, which can cause MIC failures // on secure BLE channels. ble_common_opt_radio_cpu_mutex_t opt; opt.enable = 1; sd_ble_opt_set(BLE_COMMON_OPT_RADIO_CPU_MUTEX, (const ble_opt_t *)&opt); while (!ble.hasInitialized()) { } DEBUG_PRINTF_BLE("ble.hasIntialized\r\n"); } void HIDController::waitForEvent() { BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE); keyboardService->processSend(); ble.waitForEvent(); } void HIDController::appendReportData(const uint8_t key) { if (keyboardService) { keyboardService->appendReportData(key); } } void HIDController::deleteReportData(const uint8_t key) { if (keyboardService) { keyboardService->deleteReportData(key); } } void HIDController::queueCurrentReportData() { if (!connected()) return; if (keyboardService) { DEBUG_PRINTF_BLE("Q\r\n"); keyboardService->queueCurrentReportData(); } } void HIDController::updateBatteryLevel(const uint8_t percentage) { if (!batteryService) return; batteryService->updateBatteryLevel(percentage); } void HIDController::initializeConnection(const bool ignoreWhiteList = false) { ble_error_t error; BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE); ble.gap().setAdvertisingInterval(20); ble.gap().setAdvertisingTimeout(30); if (ignoreWhiteList) { ble.gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST); } // DEBUG_PRINTF_BLE("advertising\r\n"); error = ble.gap().startAdvertising(); if (error != BLE_ERROR_NONE) goto return_error; controllerStatus = ADVERTISING; return; return_error: DEBUG_PRINTF_BLE("error with %d\r\n", error); return; }