ble nano hid over gatt

Dependencies:   BLE_API mbed-dev nRF51822

Committer:
cho45
Date:
Thu Jul 21 01:25:31 2016 +0900
Revision:
6:f1c3ea8bc850
Child:
9:d1daefbf1fbd
act as keyboard

Who changed what in which revision?

UserRevisionLine numberNew contents of line
cho45 6:f1c3ea8bc850 1 #include "mbed.h"
cho45 6:f1c3ea8bc850 2 #include "BLE.h"
cho45 6:f1c3ea8bc850 3 #include "KeyboardService.h"
cho45 6:f1c3ea8bc850 4 #include "BatteryService.h"
cho45 6:f1c3ea8bc850 5 #include "DeviceInformationService.h"
cho45 6:f1c3ea8bc850 6 #include "HIDController_BLE.h"
cho45 6:f1c3ea8bc850 7
cho45 6:f1c3ea8bc850 8 static const char MODEL_NAME[] = "keyboard";
cho45 6:f1c3ea8bc850 9 static const char SERIAL_NUMBER[] = "X00000";
cho45 6:f1c3ea8bc850 10 static const char HARDWARE_REVISION[] = "0.1";
cho45 6:f1c3ea8bc850 11 static const char FIRMWARE_REVISION[] = "0.1";
cho45 6:f1c3ea8bc850 12 static const char SOFTWARE_REVISION[] = "0.0";
cho45 6:f1c3ea8bc850 13
cho45 6:f1c3ea8bc850 14 static const uint8_t DEVICE_NAME[] = "my keyboard";
cho45 6:f1c3ea8bc850 15 static const uint8_t SHORT_DEVICE_NAME[] = "kbd1";
cho45 6:f1c3ea8bc850 16
cho45 6:f1c3ea8bc850 17 static const bool ENABLE_BONDING = true;
cho45 6:f1c3ea8bc850 18 static const bool REQUIRE_MITM = true;
cho45 6:f1c3ea8bc850 19 static const uint8_t PASSKEY[6] = {'1','2','3','4','5','6'}; // must be 6-digits number
cho45 6:f1c3ea8bc850 20
cho45 6:f1c3ea8bc850 21 static const uint16_t uuid16_list[] = {
cho45 6:f1c3ea8bc850 22 GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE,
cho45 6:f1c3ea8bc850 23 GattService::UUID_DEVICE_INFORMATION_SERVICE,
cho45 6:f1c3ea8bc850 24 GattService::UUID_BATTERY_SERVICE
cho45 6:f1c3ea8bc850 25 };
cho45 6:f1c3ea8bc850 26
cho45 6:f1c3ea8bc850 27 static KeyboardService* keyboardService;
cho45 6:f1c3ea8bc850 28 static BatteryService* batteryService;
cho45 6:f1c3ea8bc850 29 static DeviceInformationService* deviceInformationService;
cho45 6:f1c3ea8bc850 30
cho45 6:f1c3ea8bc850 31 static void updateBatteryLevel() {
cho45 6:f1c3ea8bc850 32 if (!batteryService) return;
cho45 6:f1c3ea8bc850 33 static const float BATTERY_MAX = 2.4;
cho45 6:f1c3ea8bc850 34 static const float REFERNECE = 1.2;
cho45 6:f1c3ea8bc850 35 static const float PRESCALE = 3;
cho45 6:f1c3ea8bc850 36
cho45 6:f1c3ea8bc850 37 NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Enabled;
cho45 6:f1c3ea8bc850 38
cho45 6:f1c3ea8bc850 39 // Use internal 1.2V reference for batteryInput
cho45 6:f1c3ea8bc850 40 // 1/3 pre-scaled input and 1.2V internal band gap reference
cho45 6:f1c3ea8bc850 41 // ref. mbed-src/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/analogin_api.c
cho45 6:f1c3ea8bc850 42 NRF_ADC->CONFIG =
cho45 6:f1c3ea8bc850 43 (ADC_CONFIG_RES_10bit << ADC_CONFIG_RES_Pos) |
cho45 6:f1c3ea8bc850 44 // Use VDD 1/3 for input
cho45 6:f1c3ea8bc850 45 (ADC_CONFIG_INPSEL_SupplyOneThirdPrescaling << ADC_CONFIG_INPSEL_Pos) |
cho45 6:f1c3ea8bc850 46 // Use internal band gap for reference
cho45 6:f1c3ea8bc850 47 (ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) |
cho45 6:f1c3ea8bc850 48 (ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos);
cho45 6:f1c3ea8bc850 49
cho45 6:f1c3ea8bc850 50 // Start ADC
cho45 6:f1c3ea8bc850 51 NRF_ADC->TASKS_START = 1;
cho45 6:f1c3ea8bc850 52 while (((NRF_ADC->BUSY & ADC_BUSY_BUSY_Msk) >> ADC_BUSY_BUSY_Pos) == ADC_BUSY_BUSY_Busy) {
cho45 6:f1c3ea8bc850 53 // busy loop
cho45 6:f1c3ea8bc850 54 }
cho45 6:f1c3ea8bc850 55
cho45 6:f1c3ea8bc850 56 // Read ADC result
cho45 6:f1c3ea8bc850 57 uint16_t raw10bit = static_cast<uint16_t>(NRF_ADC->RESULT);
cho45 6:f1c3ea8bc850 58 float ratio = raw10bit / static_cast<float>(1<<10);
cho45 6:f1c3ea8bc850 59
cho45 6:f1c3ea8bc850 60 float batteryVoltage = ratio * (REFERNECE * PRESCALE);
cho45 6:f1c3ea8bc850 61 float percentage = (batteryVoltage / BATTERY_MAX) * 100;
cho45 6:f1c3ea8bc850 62 if (percentage > 100) {
cho45 6:f1c3ea8bc850 63 percentage = 100;
cho45 6:f1c3ea8bc850 64 }
cho45 6:f1c3ea8bc850 65 printf("updateBatteryLevel %f V : %d/100\r\n", batteryVoltage, static_cast<uint8_t>(percentage));
cho45 6:f1c3ea8bc850 66 batteryService->updateBatteryLevel(static_cast<uint8_t>(percentage));
cho45 6:f1c3ea8bc850 67 }
cho45 6:f1c3ea8bc850 68
cho45 6:f1c3ea8bc850 69
cho45 6:f1c3ea8bc850 70 static void onConnect(const Gap::ConnectionCallbackParams_t *params) {
cho45 6:f1c3ea8bc850 71 printf("onConnect\r\n");
cho45 6:f1c3ea8bc850 72 }
cho45 6:f1c3ea8bc850 73
cho45 6:f1c3ea8bc850 74 static void onDisconnect(const Gap::DisconnectionCallbackParams_t *params) {
cho45 6:f1c3ea8bc850 75 printf("onDisconnect\r\n");
cho45 6:f1c3ea8bc850 76 BLE::Instance(BLE::DEFAULT_INSTANCE).gap().startAdvertising();
cho45 6:f1c3ea8bc850 77 }
cho45 6:f1c3ea8bc850 78
cho45 6:f1c3ea8bc850 79 static void onTimeout(const Gap::TimeoutSource_t source) {
cho45 6:f1c3ea8bc850 80 printf("onTimeout\r\n");
cho45 6:f1c3ea8bc850 81 BLE::Instance(BLE::DEFAULT_INSTANCE).gap().startAdvertising();
cho45 6:f1c3ea8bc850 82 }
cho45 6:f1c3ea8bc850 83
cho45 6:f1c3ea8bc850 84 static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey) {
cho45 6:f1c3ea8bc850 85 printf("Input passKey: ");
cho45 6:f1c3ea8bc850 86 for (unsigned i = 0; i < Gap::ADDR_LEN; i++) {
cho45 6:f1c3ea8bc850 87 printf("%c", passkey[i]);
cho45 6:f1c3ea8bc850 88 }
cho45 6:f1c3ea8bc850 89 printf("\r\n");
cho45 6:f1c3ea8bc850 90 }
cho45 6:f1c3ea8bc850 91
cho45 6:f1c3ea8bc850 92 static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status) {
cho45 6:f1c3ea8bc850 93 if (status == SecurityManager::SEC_STATUS_SUCCESS) {
cho45 6:f1c3ea8bc850 94 printf("Security success %d\r\n", status);
cho45 6:f1c3ea8bc850 95 } else {
cho45 6:f1c3ea8bc850 96 printf("Security failed %d\r\n", status);
cho45 6:f1c3ea8bc850 97 }
cho45 6:f1c3ea8bc850 98 }
cho45 6:f1c3ea8bc850 99
cho45 6:f1c3ea8bc850 100 static void securitySetupInitiatedCallback(Gap::Handle_t, bool allowBonding, bool requireMITM, SecurityManager::SecurityIOCapabilities_t iocaps) {
cho45 6:f1c3ea8bc850 101 printf("Security setup initiated\r\n");
cho45 6:f1c3ea8bc850 102 }
cho45 6:f1c3ea8bc850 103
cho45 6:f1c3ea8bc850 104 static void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) {
cho45 6:f1c3ea8bc850 105 // https://developer.mbed.org/compiler/#nav:/keyboard/BLE_API/ble/blecommon.h;
cho45 6:f1c3ea8bc850 106 ble_error_t error;
cho45 6:f1c3ea8bc850 107 BLE &ble = params->ble;
cho45 6:f1c3ea8bc850 108
cho45 6:f1c3ea8bc850 109 error = params->error;
cho45 6:f1c3ea8bc850 110 if (error != BLE_ERROR_NONE) {
cho45 6:f1c3ea8bc850 111 printf("error on ble.init() \r\n");
cho45 6:f1c3ea8bc850 112 goto return_error;
cho45 6:f1c3ea8bc850 113 }
cho45 6:f1c3ea8bc850 114
cho45 6:f1c3ea8bc850 115 ble.gap().onDisconnection(onDisconnect);
cho45 6:f1c3ea8bc850 116 ble.gap().onConnection(onConnect);
cho45 6:f1c3ea8bc850 117 ble.gap().onTimeout(onTimeout);
cho45 6:f1c3ea8bc850 118
cho45 6:f1c3ea8bc850 119 printf("setup ble security manager\r\n");
cho45 6:f1c3ea8bc850 120 ble.securityManager().onSecuritySetupInitiated(securitySetupInitiatedCallback);
cho45 6:f1c3ea8bc850 121 ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback);
cho45 6:f1c3ea8bc850 122 ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
cho45 6:f1c3ea8bc850 123 // bonding with hard-coded passkey.
cho45 6:f1c3ea8bc850 124 error = ble.securityManager().init(ENABLE_BONDING, REQUIRE_MITM, SecurityManager::IO_CAPS_DISPLAY_ONLY, PASSKEY);
cho45 6:f1c3ea8bc850 125 if (error != BLE_ERROR_NONE) {
cho45 6:f1c3ea8bc850 126 printf("error on ble.securityManager().init()");
cho45 6:f1c3ea8bc850 127 goto return_error;
cho45 6:f1c3ea8bc850 128 }
cho45 6:f1c3ea8bc850 129
cho45 6:f1c3ea8bc850 130 keyboardService = new KeyboardService(ble);
cho45 6:f1c3ea8bc850 131 deviceInformationService = new DeviceInformationService(ble, "lowreal.net", MODEL_NAME, SERIAL_NUMBER, HARDWARE_REVISION, FIRMWARE_REVISION, SOFTWARE_REVISION);
cho45 6:f1c3ea8bc850 132 batteryService = new BatteryService(ble, 100);
cho45 6:f1c3ea8bc850 133 updateBatteryLevel();
cho45 6:f1c3ea8bc850 134
cho45 6:f1c3ea8bc850 135 printf("general setup\r\n");
cho45 6:f1c3ea8bc850 136 error = ble.gap().accumulateAdvertisingPayload(
cho45 6:f1c3ea8bc850 137 GapAdvertisingData::BREDR_NOT_SUPPORTED |
cho45 6:f1c3ea8bc850 138 GapAdvertisingData::LE_GENERAL_DISCOVERABLE
cho45 6:f1c3ea8bc850 139 );
cho45 6:f1c3ea8bc850 140 if (error != BLE_ERROR_NONE) goto return_error;
cho45 6:f1c3ea8bc850 141
cho45 6:f1c3ea8bc850 142 printf("set COMPLETE_LIST_16BIT_SERVICE_IDS\r\n");
cho45 6:f1c3ea8bc850 143 error = ble.gap().accumulateAdvertisingPayload(
cho45 6:f1c3ea8bc850 144 GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS,
cho45 6:f1c3ea8bc850 145 (uint8_t*)uuid16_list, sizeof(uuid16_list)
cho45 6:f1c3ea8bc850 146 );
cho45 6:f1c3ea8bc850 147 if (error != BLE_ERROR_NONE) goto return_error;
cho45 6:f1c3ea8bc850 148
cho45 6:f1c3ea8bc850 149 printf("set advertising\r\n");
cho45 6:f1c3ea8bc850 150 // see 5.1.2: HID over GATT Specification (pg. 25)
cho45 6:f1c3ea8bc850 151 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
cho45 6:f1c3ea8bc850 152
cho45 6:f1c3ea8bc850 153 printf("set advertising interval\r\n");
cho45 6:f1c3ea8bc850 154 ble.gap().setAdvertisingInterval(50);
cho45 6:f1c3ea8bc850 155 // XXX
cho45 6:f1c3ea8bc850 156 // ble.gap().setAdvertisingTimeout(0);
cho45 6:f1c3ea8bc850 157
cho45 6:f1c3ea8bc850 158 printf("set keyboard\r\n");
cho45 6:f1c3ea8bc850 159 error = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD);
cho45 6:f1c3ea8bc850 160 if (error != BLE_ERROR_NONE) goto return_error;
cho45 6:f1c3ea8bc850 161
cho45 6:f1c3ea8bc850 162 printf("set complete local name\r\n");
cho45 6:f1c3ea8bc850 163 error = ble.gap().accumulateAdvertisingPayload(
cho45 6:f1c3ea8bc850 164 GapAdvertisingData::COMPLETE_LOCAL_NAME,
cho45 6:f1c3ea8bc850 165 DEVICE_NAME, sizeof(DEVICE_NAME)
cho45 6:f1c3ea8bc850 166 );
cho45 6:f1c3ea8bc850 167 if (error != BLE_ERROR_NONE) goto return_error;
cho45 6:f1c3ea8bc850 168
cho45 6:f1c3ea8bc850 169 printf("set device name\r\n");
cho45 6:f1c3ea8bc850 170 error = ble.gap().setDeviceName(DEVICE_NAME);
cho45 6:f1c3ea8bc850 171 if (error != BLE_ERROR_NONE) goto return_error;
cho45 6:f1c3ea8bc850 172 // ble.gap().setTxPower(-12);
cho45 6:f1c3ea8bc850 173
cho45 6:f1c3ea8bc850 174
cho45 6:f1c3ea8bc850 175 printf("advertising\r\n");
cho45 6:f1c3ea8bc850 176 error = ble.gap().startAdvertising();
cho45 6:f1c3ea8bc850 177 if (error != BLE_ERROR_NONE) goto return_error;
cho45 6:f1c3ea8bc850 178 return;
cho45 6:f1c3ea8bc850 179
cho45 6:f1c3ea8bc850 180 return_error:
cho45 6:f1c3ea8bc850 181 printf("error with %d\r\n", error);
cho45 6:f1c3ea8bc850 182 return;
cho45 6:f1c3ea8bc850 183 }
cho45 6:f1c3ea8bc850 184
cho45 6:f1c3ea8bc850 185 void HIDController::init() {
cho45 6:f1c3ea8bc850 186 // https://github.com/jpbrucker/BLE_HID/blob/master/examples/examples_common.cpp
cho45 6:f1c3ea8bc850 187 printf("ble.init\r\n");
cho45 6:f1c3ea8bc850 188
cho45 6:f1c3ea8bc850 189 BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
cho45 6:f1c3ea8bc850 190 ble.init(bleInitComplete);
cho45 6:f1c3ea8bc850 191
cho45 6:f1c3ea8bc850 192 while (!ble.hasInitialized()) { }
cho45 6:f1c3ea8bc850 193
cho45 6:f1c3ea8bc850 194 printf("ble.hasIntialized\r\n");
cho45 6:f1c3ea8bc850 195 }
cho45 6:f1c3ea8bc850 196
cho45 6:f1c3ea8bc850 197 void HIDController::process() {
cho45 6:f1c3ea8bc850 198 BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
cho45 6:f1c3ea8bc850 199 ble.waitForEvent();
cho45 6:f1c3ea8bc850 200 }
cho45 6:f1c3ea8bc850 201
cho45 6:f1c3ea8bc850 202 void HIDController::appendReportData(uint8_t key) {
cho45 6:f1c3ea8bc850 203 if (keyboardService) {
cho45 6:f1c3ea8bc850 204 keyboardService->appendReportData(key);
cho45 6:f1c3ea8bc850 205 }
cho45 6:f1c3ea8bc850 206 }
cho45 6:f1c3ea8bc850 207
cho45 6:f1c3ea8bc850 208 void HIDController::deleteReportData(uint8_t key) {
cho45 6:f1c3ea8bc850 209 if (keyboardService) {
cho45 6:f1c3ea8bc850 210 keyboardService->deleteReportData(key);
cho45 6:f1c3ea8bc850 211 }
cho45 6:f1c3ea8bc850 212 }