ble nano hid over gatt

Dependencies:   BLE_API mbed-dev nRF51822

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HIDController_BLE.cpp Source File

HIDController_BLE.cpp

00001 #include "mbed.h"
00002 #include "BLE.h"
00003 
00004 #include "config.h"
00005 #include "WatchDog.h"
00006 #include "KeyboardService.h"
00007 #include "BatteryService.h"
00008 #include "DeviceInformationService.h"
00009 #include "ScanParametersService.h"
00010 #include "DFUService.h"
00011 #include "HIDController_BLE.h"
00012 
00013 static const char MANUFACTURERERS_NAME[] = "lowreal.net";
00014 static const char MODEL_NAME[] = "keble";
00015 static const char SERIAL_NUMBER[] = "X00000";
00016 static const char HARDWARE_REVISION[] = "0.1";
00017 static const char FIRMWARE_REVISION[] = "0.1";
00018 static const char SOFTWARE_REVISION[] = "0.0";
00019 static PnPID_t PNP_ID = {
00020     0x01,  // From Bluetooth SIG
00021     0xFFFE,
00022     0x0001,
00023     0x01,
00024 };
00025 
00026 static const uint8_t DEVICE_NAME[] = "Keble";
00027 
00028 static const bool ENABLE_BONDING = true;
00029 static const bool REQUIRE_MITM = true;
00030 static const uint8_t PASSKEY[6] = {'1','2','3','4','5','6'}; // must be 6-digits number
00031 
00032 static const uint16_t uuid16_list[] =  {
00033     GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE
00034 };
00035 
00036 static KeyboardService* keyboardService;
00037 static BatteryService* batteryService;
00038 static DeviceInformationService* deviceInformationService;
00039 static ScanParametersService* scanParametersService;
00040 // static DFUService* dfuService;
00041 
00042 
00043 static BLEProtocol::Address_t peerAddress;
00044 
00045 static volatile Status_t controllerStatus;
00046 
00047 static void onConnect(const Gap::ConnectionCallbackParams_t *params) {
00048     peerAddress.type = params->peerAddrType;
00049     memcpy(peerAddress.address, params->peerAddr, Gap::ADDR_LEN);
00050 
00051     BLEProtocol::Address_t peerAddresses[2];
00052     Gap::Whitelist_t whitelist;
00053     whitelist.size = 0;
00054     whitelist.capacity = 2;
00055     whitelist.addresses = peerAddresses;
00056     BLE::Instance(BLE::DEFAULT_INSTANCE).gap().getWhitelist(whitelist);
00057     DEBUG_PRINTF_BLE_INTERRUPT("getWhitelist %d\r\n", whitelist.size);
00058     for (int i = 0; i < whitelist.size; i++) {
00059         if (whitelist.addresses[i].type == params->peerAddrType &&
00060                 memcmp(whitelist.addresses[i].address, params->peerAddr, Gap::ADDR_LEN) == 0) {
00061 
00062             BLE::Instance(BLE::DEFAULT_INSTANCE).gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_ALL_REQS);
00063             controllerStatus = CONNECTED;
00064             DEBUG_PRINTF_BLE_INTERRUPT("peer is found in whitelist\r\n");
00065             return;
00066         }
00067     }
00068 
00069     controllerStatus = CONNECTING;
00070     DEBUG_PRINTF_BLE_INTERRUPT("peer is not found in whitelist\r\n");
00071 }
00072 
00073 static void onDisconnect(const Gap::DisconnectionCallbackParams_t *params) {
00074     controllerStatus = DISCONNECTED;
00075     DEBUG_PRINTF_BLE_INTERRUPT("onDisconnect\r\n");
00076 }
00077 
00078 static void onTimeout(const Gap::TimeoutSource_t source) {
00079     DEBUG_PRINTF_BLE_INTERRUPT("onTimeout %d\r\n", source);
00080     switch (source) {
00081         case Gap::TIMEOUT_SRC_ADVERTISING:
00082             controllerStatus = TIMEOUT;
00083             return;
00084         
00085         // treat following timeout as DISCONNECT (retry advertising)
00086         case Gap::TIMEOUT_SRC_SECURITY_REQUEST:
00087         case Gap::TIMEOUT_SRC_SCAN:
00088         case Gap::TIMEOUT_SRC_CONN:
00089             controllerStatus = DISCONNECTED;
00090             return;
00091     }
00092 }
00093 
00094 static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey) {
00095     DEBUG_PRINTF_BLE_INTERRUPT("Input passKey: ");
00096     for (unsigned i = 0; i < Gap::ADDR_LEN; i++) {
00097         DEBUG_PRINTF_BLE_INTERRUPT("%c", passkey[i]);
00098     }
00099     DEBUG_PRINTF_BLE_INTERRUPT("\r\n");
00100 }
00101 
00102 static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status) {
00103     if (status == SecurityManager::SEC_STATUS_SUCCESS) {
00104         DEBUG_PRINTF_BLE_INTERRUPT("Security success %d\r\n", status);
00105         DEBUG_PRINTF_BLE_INTERRUPT("Set whitelist\r\n");
00106         Gap::Whitelist_t whitelist;
00107         whitelist.size = 1;
00108         whitelist.capacity = 1;
00109         whitelist.addresses = &peerAddress;
00110 
00111         BLE::Instance(BLE::DEFAULT_INSTANCE).gap().setWhitelist(whitelist);
00112         DEBUG_PRINTF_BLE_INTERRUPT("Set Advertising Policy Mode\r\n");
00113         // BLE::Instance(BLE::DEFAULT_INSTANCE).gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_SCAN_REQS);
00114         // BLE::Instance(BLE::DEFAULT_INSTANCE).gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_CONN_REQS);
00115         BLE::Instance(BLE::DEFAULT_INSTANCE).gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_ALL_REQS);
00116         controllerStatus = CONNECTED;
00117     } else {
00118         DEBUG_PRINTF_BLE_INTERRUPT("Security failed %d\r\n", status);
00119     }
00120 }
00121 
00122 static void securitySetupInitiatedCallback(Gap::Handle_t, bool allowBonding, bool requireMITM, SecurityManager::SecurityIOCapabilities_t iocaps) {
00123     DEBUG_PRINTF_BLE_INTERRUPT("Security setup initiated\r\n");
00124 }
00125 
00126 static void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) {
00127     // https://developer.mbed.org/compiler/#nav:/keyboard/BLE_API/ble/blecommon.h;
00128     ble_error_t error;
00129     BLE &ble          = params->ble;
00130     
00131     controllerStatus = DISCONNECTED;
00132 
00133     /**< Minimum Connection Interval in 1.25 ms units, see BLE_GAP_CP_LIMITS.*/
00134     uint16_t minConnectionInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(15);
00135     /**< Maximum Connection Interval in 1.25 ms units, see BLE_GAP_CP_LIMITS.*/
00136     uint16_t maxConnectionInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(20);
00137     /**< Slave Latency in number of connection events, see BLE_GAP_CP_LIMITS.*/
00138     uint16_t slaveLatency = 50;
00139     /**< Connection Supervision Timeout in 10 ms units, see BLE_GAP_CP_LIMITS.*/ 
00140     uint16_t connectionSupervisionTimeout = 32 * 100;
00141     Gap::ConnectionParams_t connectionParams = {
00142         minConnectionInterval,
00143         maxConnectionInterval,
00144         slaveLatency,
00145         connectionSupervisionTimeout
00146     };
00147 
00148     error = params->error;
00149     if (error != BLE_ERROR_NONE) {
00150         DEBUG_PRINTF_BLE("error on ble.init() \r\n");
00151         goto return_error;
00152     }
00153 
00154     ble.gap().onDisconnection(onDisconnect);
00155     ble.gap().onConnection(onConnect);
00156     ble.gap().onTimeout(onTimeout);
00157 
00158     // DEBUG_PRINTF_BLE("setup ble security manager\r\n");
00159     ble.securityManager().onSecuritySetupInitiated(securitySetupInitiatedCallback);
00160     ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback);
00161     ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback);
00162     // bonding with hard-coded passkey.
00163     error = ble.securityManager().init(ENABLE_BONDING, REQUIRE_MITM, SecurityManager::IO_CAPS_DISPLAY_ONLY, PASSKEY);
00164     if (error != BLE_ERROR_NONE) {
00165         DEBUG_PRINTF_BLE("error on ble.securityManager().init()");
00166         goto return_error;
00167     }
00168     
00169     // DEBUG_PRINTF_BLE("new KeyboardService\r\n");
00170     keyboardService = new KeyboardService(ble);
00171     keyboardService->init();
00172     DEBUG_PRINTF_BLE("new DeviceInformationService\r\n");
00173     deviceInformationService = new DeviceInformationService(ble, MANUFACTURERERS_NAME, MODEL_NAME, SERIAL_NUMBER, HARDWARE_REVISION, FIRMWARE_REVISION, SOFTWARE_REVISION, &PNP_ID);
00174     DEBUG_PRINTF_BLE("new BatteryService\r\n");
00175     batteryService = new BatteryService(ble, 100);
00176     DEBUG_PRINTF_BLE("new ScanParametersService\r\n");
00177     scanParametersService = new ScanParametersService(ble);
00178     /** TODO
00179     DEBUG_PRINTF_BLE("new DFUService\r\n");
00180     dfuService = new DFUService(ble);
00181     */
00182     
00183     //DEBUG_PRINTF_BLE("setup connection params\r\n");
00184 
00185     ble.gap().setPreferredConnectionParams(&connectionParams);
00186 
00187     // DEBUG_PRINTF_BLE("general setup\r\n");
00188 //  error = ble.gap().accumulateAdvertisingPayload(
00189 //      GapAdvertisingData::BREDR_NOT_SUPPORTED |
00190 //      GapAdvertisingData::LE_GENERAL_DISCOVERABLE
00191 //  );
00192     // shoud be LE_LIMITED_DISCOVERABLE
00193     error = ble.gap().accumulateAdvertisingPayload(
00194         GapAdvertisingData::BREDR_NOT_SUPPORTED |
00195         GapAdvertisingData::LE_LIMITED_DISCOVERABLE
00196     );
00197     if (error != BLE_ERROR_NONE) goto return_error;
00198 
00199     // DEBUG_PRINTF_BLE("set COMPLETE_LIST_16BIT_SERVICE_IDS\r\n");
00200     error = ble.gap().accumulateAdvertisingPayload(
00201         GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS,
00202         (uint8_t*)uuid16_list, sizeof(uuid16_list)
00203     );
00204     if (error != BLE_ERROR_NONE) goto return_error;
00205 
00206     // DEBUG_PRINTF_BLE("set advertising\r\n");
00207     // see 5.1.2: HID over GATT Specification (pg. 25)
00208     ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
00209 
00210     // DEBUG_PRINTF_BLE("set advertising interval\r\n");
00211     ble.gap().setAdvertisingInterval(20);
00212     // DEBUG_PRINTF_BLE("set advertising timeout\r\n");
00213     ble.gap().setAdvertisingTimeout(30);
00214     
00215     /*
00216     ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_DIRECTED);
00217     ble.gap().setAdvertisingTimeout(1.28);
00218     */
00219 
00220     // DEBUG_PRINTF_BLE("set keyboard\r\n");
00221     error = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::KEYBOARD);
00222     if (error != BLE_ERROR_NONE) goto return_error;
00223 
00224     // DEBUG_PRINTF_BLE("set complete local name\r\n");
00225     error = ble.gap().accumulateAdvertisingPayload(
00226         GapAdvertisingData::COMPLETE_LOCAL_NAME,
00227         DEVICE_NAME, sizeof(DEVICE_NAME)
00228     );
00229     if (error != BLE_ERROR_NONE) goto return_error;
00230 
00231     // DEBUG_PRINTF_BLE("set device name\r\n");
00232     error = ble.gap().setDeviceName(DEVICE_NAME);
00233     if (error != BLE_ERROR_NONE) goto return_error;
00234     /* (Valid values are -40, -20, -16, -12, -8, -4, 0, 4) */
00235     ble.gap().setTxPower(0);
00236 
00237     {
00238         BLEProtocol::Address_t peerAddresses[2];
00239         Gap::Whitelist_t whitelist;
00240         whitelist.size = 0;
00241         whitelist.capacity = 2;
00242         whitelist.addresses = peerAddresses;
00243         error = ble.securityManager().getAddressesFromBondTable(whitelist);
00244         DEBUG_PRINTF_BLE("getAddressesFromBondTable %d\r\n", whitelist.size);
00245         BLE::Instance(BLE::DEFAULT_INSTANCE).gap().setWhitelist(whitelist);
00246     //  for (int i = 0; i < whitelist.size; i++) {
00247     //      whitelist.addresses[i]
00248     //  }
00249     }
00250 
00251     ble.gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST);
00252     ble.gap().setScanningPolicyMode(Gap::SCAN_POLICY_FILTER_ALL_ADV);
00253     ble.gap().setInitiatorPolicyMode(Gap::INIT_POLICY_FILTER_ALL_ADV);
00254 
00255     // DEBUG_PRINTF_BLE("advertising\r\n");
00256     error = ble.gap().startAdvertising();
00257     if (error != BLE_ERROR_NONE) goto return_error;
00258     controllerStatus = ADVERTISING;
00259     return;
00260 
00261 return_error:
00262     DEBUG_PRINTF_BLE("error with %d\r\n", error);
00263     return;
00264 }
00265 
00266 bool HIDController::connected() {
00267     return controllerStatus == CONNECTED;
00268 }
00269 
00270 Status_t HIDController::status() {
00271     return controllerStatus;
00272 }
00273 
00274 const char* HIDController::statusString() {
00275     static const char* const disconnected = "disconnected";
00276     static const char* const connecting = "connecting";
00277     static const char* const connected = "connected";
00278     static const char* const timeout = "timeout";
00279     static const char* const advertising = "advertising";
00280     static const char* const unknown = "unknown";
00281 
00282     return controllerStatus == DISCONNECTED ? disconnected:
00283            controllerStatus == CONNECTING ? connecting:
00284            controllerStatus == CONNECTED ? connected:
00285            controllerStatus == TIMEOUT ? timeout:
00286            controllerStatus == ADVERTISING ? advertising:
00287            unknown;
00288 }
00289 
00290 void HIDController::init() {
00291     // https://github.com/jpbrucker/BLE_HID/blob/master/examples/examples_common.cpp
00292     DEBUG_PRINTF_BLE("ble.init\r\n");
00293 
00294     BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
00295     ble.init(bleInitComplete);
00296     
00297     // copied from https://github.com/lancaster-university/microbit-dal/commit/3c314794f07e5ac91331c9e9849475375708ec89
00298     // configure the stack to hold on to CPU during critical timing events.
00299     // mbed-classic performs __disabe_irq calls in its timers, which can cause MIC failures 
00300     // on secure BLE channels.
00301     ble_common_opt_radio_cpu_mutex_t opt;
00302     opt.enable = 1;
00303     sd_ble_opt_set(BLE_COMMON_OPT_RADIO_CPU_MUTEX, (const ble_opt_t *)&opt);
00304      
00305     while (!ble.hasInitialized()) { }
00306     DEBUG_PRINTF_BLE("ble.hasIntialized\r\n");
00307 }
00308 
00309 
00310 void HIDController::waitForEvent() {
00311     BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
00312 
00313     WatchDog::reload();
00314 
00315     keyboardService->processSend();
00316     if (DEBUG_BLE_INTERRUPT) {
00317         ble.waitForEvent();
00318     } else {
00319         // disable internal HFCLK RC Clock surely. It consume 1mA constantly
00320         // TWI / SPI / UART must be disabled and boot without debug mode
00321         while (NRF_UART0->EVENTS_TXDRDY != 1);
00322 
00323         const uint32_t tx = NRF_UART0->PSELTXD;
00324 
00325         NRF_UART0->TASKS_STOPTX = 1;
00326         NRF_UART0->ENABLE = (UART_ENABLE_ENABLE_Disabled << UART_ENABLE_ENABLE_Pos);
00327 
00328         ble.waitForEvent();
00329 
00330         NRF_UART0->ENABLE = (UART_ENABLE_ENABLE_Enabled << UART_ENABLE_ENABLE_Pos);
00331         NRF_UART0->TASKS_STARTTX = 1;
00332         // dummy send to wakeup...
00333         NRF_UART0->PSELTXD = 0xFFFFFFFF;
00334         NRF_UART0->EVENTS_TXDRDY = 0;
00335         NRF_UART0->TXD = 0;
00336         while (NRF_UART0->EVENTS_TXDRDY != 1);
00337         NRF_UART0->PSELTXD = tx;
00338     }
00339 }
00340 
00341 void HIDController::appendReportData(const uint8_t key) {
00342     if (keyboardService) {
00343         keyboardService->appendReportData(key);
00344     }
00345 }
00346 
00347 void HIDController::deleteReportData(const uint8_t key) {
00348     if (keyboardService) {
00349         keyboardService->deleteReportData(key);
00350     }
00351 }
00352 
00353 void HIDController::pressConsumerKey(const uint16_t key) {
00354     if (keyboardService) {
00355         keyboardService->pressConsumerKey(key);
00356     }
00357 }
00358 
00359 void HIDController::releaseConsumerKey() {
00360     if (keyboardService) {
00361         keyboardService->releaseConsumerKey();
00362     }
00363 }
00364 
00365 void HIDController::queueCurrentReportData() {
00366     if (!connected()) return;
00367     if (keyboardService) {
00368         keyboardService->queueCurrentReportData();
00369     }
00370 }
00371 
00372 void HIDController::updateBatteryLevel(const uint8_t percentage, const uint16_t voltage) {
00373     if (!batteryService) return;
00374     DEBUG_PRINTF_BLE("updateBatteryLevel\r\n");
00375     batteryService->updateBatteryLevel(percentage, voltage);
00376 }
00377 
00378 void HIDController::initializeConnection(const bool ignoreWhiteList = false) {
00379     ble_error_t error;
00380 
00381     DEBUG_PRINTF_BLE("initializeConnection(%d)\r\n", ignoreWhiteList);
00382 
00383     BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
00384     ble.gap().setAdvertisingInterval(20);
00385     ble.gap().setAdvertisingTimeout(30);
00386     if (ignoreWhiteList) {
00387         ble.gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST);
00388     } else {
00389         // XXX : startAdvertising returns BLE_ERROR_PARAM_OUT_OF_RANGE
00390         // ble.gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_FILTER_ALL_REQS);
00391         ble.gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST);
00392     }
00393 
00394     // DEBUG_PRINTF_BLE("advertising\r\n");
00395     error = ble.gap().startAdvertising();
00396     if (error != BLE_ERROR_NONE) goto return_error;
00397     controllerStatus = ADVERTISING;
00398     return;
00399 
00400 return_error:
00401     DEBUG_PRINTF_BLE("error with %d\r\n", error);
00402     return;
00403 }