Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: BLE_API mbed-dev nRF51822
HIDController_BLE.cpp
- Committer:
- cho45
- Date:
- 2016-08-27
- Revision:
- 42:2c3be8694896
- Parent:
- 40:364deaa190fe
- Child:
- 45:f4be69c936f6
File content as of revision 42:2c3be8694896:
#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[] = "keyboard";
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 uint8_t DEVICE_NAME[] = "my keyboard";
static const uint8_t SHORT_DEVICE_NAME[] = "kbd1";
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");
BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
ble.gap().setAdvertisingInterval(20);
ble.gap().setAdvertisingTimeout(30);
ble.gap().startAdvertising();
}
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 = 4;
/**< 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_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);
// 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);
while (!ble.hasInitialized()) { }
DEBUG_PRINTF_BLE("ble.hasIntialized\r\n");
}
void HIDController::waitForEvent() {
BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
keyboardService->stopReportTicker();
ble.waitForEvent();
}
void HIDController::appendReportData(uint8_t key) {
if (keyboardService) {
keyboardService->appendReportData(key);
}
}
void HIDController::deleteReportData(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(uint8_t percentage) {
if (!batteryService) return;
batteryService->updateBatteryLevel(percentage);
}
void HIDController::initializeConnection(bool ignoreWhiteList) {
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;
}