HID-over-GATT implementation with the BLE API. This library allows to create devices such as mouse, keyboard or joystick, over Bluetooth Low Energy.

Dependents:   BLENano_HID BLE_HID_MouseScrollDemo BLE_HID_KeyboardStreamDemo Shervs_TestKeyboard_TinyBLE ... more

The development repository is currently hosted on github. It contains examples and documentation. This is a snapshot of the library. The documentation can be read on github, or on docs.mbed.com.

Committer:
Jean-Philippe Brucker
Date:
Wed Oct 07 11:29:52 2015 +0100
Revision:
1:7a6c2e2c9371
Parent:
0:cfd70fa91663
Publish version 0.1 of the BLE HID lib

This version number is completely arbitrary, and probably won't stick: once the
service is stable, we'll merge it with BLE API.
It is simply used to keep examples in sync with the lib and the github
repository during development.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Jean-Philippe Brucker 1:7a6c2e2c9371 1 /* mbed Microcontroller Library
Jean-Philippe Brucker 1:7a6c2e2c9371 2 * Copyright (c) 2015 ARM Limited
Jean-Philippe Brucker 1:7a6c2e2c9371 3 *
Jean-Philippe Brucker 1:7a6c2e2c9371 4 * Licensed under the Apache License, Version 2.0 (the "License");
Jean-Philippe Brucker 1:7a6c2e2c9371 5 * you may not use this file except in compliance with the License.
Jean-Philippe Brucker 1:7a6c2e2c9371 6 * You may obtain a copy of the License at
Jean-Philippe Brucker 1:7a6c2e2c9371 7 *
Jean-Philippe Brucker 1:7a6c2e2c9371 8 * http://www.apache.org/licenses/LICENSE-2.0
Jean-Philippe Brucker 1:7a6c2e2c9371 9 *
Jean-Philippe Brucker 1:7a6c2e2c9371 10 * Unless required by applicable law or agreed to in writing, software
Jean-Philippe Brucker 1:7a6c2e2c9371 11 * distributed under the License is distributed on an "AS IS" BASIS,
Jean-Philippe Brucker 1:7a6c2e2c9371 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Jean-Philippe Brucker 1:7a6c2e2c9371 13 * See the License for the specific language governing permissions and
Jean-Philippe Brucker 1:7a6c2e2c9371 14 * limitations under the License.
Jean-Philippe Brucker 1:7a6c2e2c9371 15 */
Jean-Philippe Brucker 1:7a6c2e2c9371 16
Jean-Philippe Brucker 0:cfd70fa91663 17 #include "mbed.h"
Jean-Philippe Brucker 0:cfd70fa91663 18 #include "HIDServiceBase.h"
Jean-Philippe Brucker 0:cfd70fa91663 19
Jean-Philippe Brucker 0:cfd70fa91663 20 HIDServiceBase::HIDServiceBase(BLE &_ble,
Jean-Philippe Brucker 0:cfd70fa91663 21 report_map_t reportMap,
Jean-Philippe Brucker 0:cfd70fa91663 22 uint8_t reportMapSize,
Jean-Philippe Brucker 0:cfd70fa91663 23 report_t inputReport,
Jean-Philippe Brucker 0:cfd70fa91663 24 report_t outputReport,
Jean-Philippe Brucker 0:cfd70fa91663 25 report_t featureReport,
Jean-Philippe Brucker 0:cfd70fa91663 26 uint8_t inputReportLength,
Jean-Philippe Brucker 0:cfd70fa91663 27 uint8_t outputReportLength,
Jean-Philippe Brucker 0:cfd70fa91663 28 uint8_t featureReportLength,
Jean-Philippe Brucker 0:cfd70fa91663 29 uint8_t inputReportTickerDelay) :
Jean-Philippe Brucker 0:cfd70fa91663 30 ble(_ble),
Jean-Philippe Brucker 0:cfd70fa91663 31 connected (false),
Jean-Philippe Brucker 0:cfd70fa91663 32 reportMapLength(reportMapSize),
Jean-Philippe Brucker 0:cfd70fa91663 33
Jean-Philippe Brucker 0:cfd70fa91663 34 inputReport(inputReport),
Jean-Philippe Brucker 0:cfd70fa91663 35 outputReport(outputReport),
Jean-Philippe Brucker 0:cfd70fa91663 36 featureReport(featureReport),
Jean-Philippe Brucker 0:cfd70fa91663 37
Jean-Philippe Brucker 0:cfd70fa91663 38 inputReportLength(inputReportLength),
Jean-Philippe Brucker 0:cfd70fa91663 39 outputReportLength(outputReportLength),
Jean-Philippe Brucker 0:cfd70fa91663 40 featureReportLength(featureReportLength),
Jean-Philippe Brucker 0:cfd70fa91663 41
Jean-Philippe Brucker 0:cfd70fa91663 42 protocolMode(REPORT_PROTOCOL),
Jean-Philippe Brucker 0:cfd70fa91663 43
Jean-Philippe Brucker 0:cfd70fa91663 44 inputReportReferenceDescriptor(BLE_UUID_DESCRIPTOR_REPORT_REFERENCE,
Jean-Philippe Brucker 0:cfd70fa91663 45 (uint8_t *)&inputReportReferenceData, 2, 2),
Jean-Philippe Brucker 0:cfd70fa91663 46 outputReportReferenceDescriptor(BLE_UUID_DESCRIPTOR_REPORT_REFERENCE,
Jean-Philippe Brucker 0:cfd70fa91663 47 (uint8_t *)&outputReportReferenceData, 2, 2),
Jean-Philippe Brucker 0:cfd70fa91663 48 featureReportReferenceDescriptor(BLE_UUID_DESCRIPTOR_REPORT_REFERENCE,
Jean-Philippe Brucker 0:cfd70fa91663 49 (uint8_t *)&featureReportReferenceData, 2, 2),
Jean-Philippe Brucker 0:cfd70fa91663 50
Jean-Philippe Brucker 0:cfd70fa91663 51 protocolModeCharacteristic(GattCharacteristic::UUID_PROTOCOL_MODE_CHAR, &protocolMode, 1, 1,
Jean-Philippe Brucker 0:cfd70fa91663 52 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ
Jean-Philippe Brucker 0:cfd70fa91663 53 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE),
Jean-Philippe Brucker 0:cfd70fa91663 54
Jean-Philippe Brucker 0:cfd70fa91663 55 inputReportCharacteristic(GattCharacteristic::UUID_REPORT_CHAR,
Jean-Philippe Brucker 0:cfd70fa91663 56 (uint8_t *)inputReport, inputReportLength, inputReportLength,
Jean-Philippe Brucker 0:cfd70fa91663 57 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ
Jean-Philippe Brucker 0:cfd70fa91663 58 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY
Jean-Philippe Brucker 0:cfd70fa91663 59 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE,
Jean-Philippe Brucker 0:cfd70fa91663 60 inputReportDescriptors(), 1),
Jean-Philippe Brucker 0:cfd70fa91663 61
Jean-Philippe Brucker 0:cfd70fa91663 62 outputReportCharacteristic(GattCharacteristic::UUID_REPORT_CHAR,
Jean-Philippe Brucker 0:cfd70fa91663 63 (uint8_t *)outputReport, outputReportLength, outputReportLength,
Jean-Philippe Brucker 0:cfd70fa91663 64 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ
Jean-Philippe Brucker 0:cfd70fa91663 65 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE
Jean-Philippe Brucker 0:cfd70fa91663 66 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE,
Jean-Philippe Brucker 0:cfd70fa91663 67 outputReportDescriptors(), 1),
Jean-Philippe Brucker 0:cfd70fa91663 68
Jean-Philippe Brucker 0:cfd70fa91663 69 featureReportCharacteristic(GattCharacteristic::UUID_REPORT_CHAR,
Jean-Philippe Brucker 0:cfd70fa91663 70 (uint8_t *)featureReport, featureReportLength, featureReportLength,
Jean-Philippe Brucker 0:cfd70fa91663 71 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ
Jean-Philippe Brucker 0:cfd70fa91663 72 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE,
Jean-Philippe Brucker 0:cfd70fa91663 73 featureReportDescriptors(), 1),
Jean-Philippe Brucker 0:cfd70fa91663 74
Jean-Philippe Brucker 0:cfd70fa91663 75 /*
Jean-Philippe Brucker 0:cfd70fa91663 76 * We need to set reportMap content as const, in order to let the compiler put it into flash
Jean-Philippe Brucker 0:cfd70fa91663 77 * instead of RAM. The characteristic is read-only so it won't be written, but
Jean-Philippe Brucker 0:cfd70fa91663 78 * GattCharacteristic constructor takes non-const arguments only. Hence the cast.
Jean-Philippe Brucker 0:cfd70fa91663 79 */
Jean-Philippe Brucker 0:cfd70fa91663 80 reportMapCharacteristic(GattCharacteristic::UUID_REPORT_MAP_CHAR,
Jean-Philippe Brucker 0:cfd70fa91663 81 const_cast<uint8_t*>(reportMap), reportMapLength, reportMapLength,
Jean-Philippe Brucker 0:cfd70fa91663 82 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ),
Jean-Philippe Brucker 0:cfd70fa91663 83
Jean-Philippe Brucker 0:cfd70fa91663 84 HIDInformationCharacteristic(GattCharacteristic::UUID_HID_INFORMATION_CHAR, HIDInformation()),
Jean-Philippe Brucker 0:cfd70fa91663 85 HIDControlPointCharacteristic(GattCharacteristic::UUID_HID_CONTROL_POINT_CHAR,
Jean-Philippe Brucker 0:cfd70fa91663 86 &controlPointCommand, 1, 1,
Jean-Philippe Brucker 0:cfd70fa91663 87 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE),
Jean-Philippe Brucker 0:cfd70fa91663 88
Jean-Philippe Brucker 0:cfd70fa91663 89 reportTickerDelay(inputReportTickerDelay),
Jean-Philippe Brucker 0:cfd70fa91663 90 reportTickerIsActive(false)
Jean-Philippe Brucker 0:cfd70fa91663 91 {
Jean-Philippe Brucker 0:cfd70fa91663 92 static GattCharacteristic *characteristics[] = {
Jean-Philippe Brucker 0:cfd70fa91663 93 &HIDInformationCharacteristic,
Jean-Philippe Brucker 0:cfd70fa91663 94 &reportMapCharacteristic,
Jean-Philippe Brucker 0:cfd70fa91663 95 &protocolModeCharacteristic,
Jean-Philippe Brucker 0:cfd70fa91663 96 &HIDControlPointCharacteristic,
Jean-Philippe Brucker 0:cfd70fa91663 97 NULL,
Jean-Philippe Brucker 0:cfd70fa91663 98 NULL,
Jean-Philippe Brucker 0:cfd70fa91663 99 NULL,
Jean-Philippe Brucker 0:cfd70fa91663 100 NULL,
Jean-Philippe Brucker 0:cfd70fa91663 101 NULL
Jean-Philippe Brucker 0:cfd70fa91663 102 };
Jean-Philippe Brucker 0:cfd70fa91663 103
Jean-Philippe Brucker 0:cfd70fa91663 104 unsigned int charIndex = 4;
Jean-Philippe Brucker 0:cfd70fa91663 105 /*
Jean-Philippe Brucker 0:cfd70fa91663 106 * Report characteristics are optional, and depend on the reportMap descriptor
Jean-Philippe Brucker 0:cfd70fa91663 107 * Note: at least one should be present, but we don't check that at the moment.
Jean-Philippe Brucker 0:cfd70fa91663 108 */
Jean-Philippe Brucker 0:cfd70fa91663 109 if (inputReportLength)
Jean-Philippe Brucker 0:cfd70fa91663 110 characteristics[charIndex++] = &inputReportCharacteristic;
Jean-Philippe Brucker 0:cfd70fa91663 111 if (outputReportLength)
Jean-Philippe Brucker 0:cfd70fa91663 112 characteristics[charIndex++] = &outputReportCharacteristic;
Jean-Philippe Brucker 0:cfd70fa91663 113 if (featureReportLength)
Jean-Philippe Brucker 0:cfd70fa91663 114 characteristics[charIndex++] = &featureReportCharacteristic;
Jean-Philippe Brucker 0:cfd70fa91663 115
Jean-Philippe Brucker 0:cfd70fa91663 116 /* TODO: let children add some more characteristics, namely boot keyboard and mouse (They are
Jean-Philippe Brucker 0:cfd70fa91663 117 * mandatory as per HIDS spec.) Ex:
Jean-Philippe Brucker 0:cfd70fa91663 118 *
Jean-Philippe Brucker 0:cfd70fa91663 119 * addExtraCharacteristics(characteristics, int& charIndex);
Jean-Philippe Brucker 0:cfd70fa91663 120 */
Jean-Philippe Brucker 0:cfd70fa91663 121
Jean-Philippe Brucker 0:cfd70fa91663 122 GattService service(GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE,
Jean-Philippe Brucker 0:cfd70fa91663 123 characteristics, charIndex);
Jean-Philippe Brucker 0:cfd70fa91663 124
Jean-Philippe Brucker 1:7a6c2e2c9371 125 ble.gattServer().addService(service);
Jean-Philippe Brucker 0:cfd70fa91663 126
Jean-Philippe Brucker 1:7a6c2e2c9371 127 ble.gap().onConnection(this, &HIDServiceBase::onConnection);
Jean-Philippe Brucker 1:7a6c2e2c9371 128 ble.gap().onDisconnection(this, &HIDServiceBase::onDisconnection);
Jean-Philippe Brucker 1:7a6c2e2c9371 129
Jean-Philippe Brucker 1:7a6c2e2c9371 130 ble.gattServer().onDataSent(this, &HIDServiceBase::onDataSent);
Jean-Philippe Brucker 0:cfd70fa91663 131
Jean-Philippe Brucker 0:cfd70fa91663 132 /*
Jean-Philippe Brucker 0:cfd70fa91663 133 * Change preferred connection params, in order to optimize the notification frequency. Most
Jean-Philippe Brucker 0:cfd70fa91663 134 * OSes seem to respect this, even though they are not required to.
Jean-Philippe Brucker 0:cfd70fa91663 135 *
Jean-Philippe Brucker 0:cfd70fa91663 136 * Some OSes don't handle reconnection well, at the moment, so we set the maximum possible
Jean-Philippe Brucker 0:cfd70fa91663 137 * timeout, 32 seconds
Jean-Philippe Brucker 0:cfd70fa91663 138 */
Jean-Philippe Brucker 0:cfd70fa91663 139 uint16_t minInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(reportTickerDelay / 2);
Jean-Philippe Brucker 0:cfd70fa91663 140 if (minInterval < 6)
Jean-Philippe Brucker 0:cfd70fa91663 141 minInterval = 6;
Jean-Philippe Brucker 0:cfd70fa91663 142 uint16_t maxInterval = minInterval * 2;
Jean-Philippe Brucker 0:cfd70fa91663 143 Gap::ConnectionParams_t params = {minInterval, maxInterval, 0, 3200};
Jean-Philippe Brucker 0:cfd70fa91663 144
Jean-Philippe Brucker 0:cfd70fa91663 145 ble.gap().setPreferredConnectionParams(&params);
Jean-Philippe Brucker 0:cfd70fa91663 146
Jean-Philippe Brucker 0:cfd70fa91663 147 SecurityManager::SecurityMode_t securityMode = SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM;
Jean-Philippe Brucker 0:cfd70fa91663 148 protocolModeCharacteristic.requireSecurity(securityMode);
Jean-Philippe Brucker 0:cfd70fa91663 149 reportMapCharacteristic.requireSecurity(securityMode);
Jean-Philippe Brucker 0:cfd70fa91663 150 inputReportCharacteristic.requireSecurity(securityMode);
Jean-Philippe Brucker 0:cfd70fa91663 151 outputReportCharacteristic.requireSecurity(securityMode);
Jean-Philippe Brucker 0:cfd70fa91663 152 featureReportCharacteristic.requireSecurity(securityMode);
Jean-Philippe Brucker 0:cfd70fa91663 153 }
Jean-Philippe Brucker 0:cfd70fa91663 154
Jean-Philippe Brucker 0:cfd70fa91663 155 void HIDServiceBase::startReportTicker(void) {
Jean-Philippe Brucker 0:cfd70fa91663 156 if (reportTickerIsActive)
Jean-Philippe Brucker 0:cfd70fa91663 157 return;
Jean-Philippe Brucker 0:cfd70fa91663 158 reportTicker.attach_us(this, &HIDServiceBase::sendCallback, reportTickerDelay * 1000);
Jean-Philippe Brucker 0:cfd70fa91663 159 reportTickerIsActive = true;
Jean-Philippe Brucker 0:cfd70fa91663 160 }
Jean-Philippe Brucker 0:cfd70fa91663 161
Jean-Philippe Brucker 0:cfd70fa91663 162 void HIDServiceBase::stopReportTicker(void) {
Jean-Philippe Brucker 0:cfd70fa91663 163 reportTicker.detach();
Jean-Philippe Brucker 0:cfd70fa91663 164 reportTickerIsActive = false;
Jean-Philippe Brucker 0:cfd70fa91663 165 }
Jean-Philippe Brucker 0:cfd70fa91663 166
Jean-Philippe Brucker 0:cfd70fa91663 167 void HIDServiceBase::onDataSent(unsigned count) {
Jean-Philippe Brucker 0:cfd70fa91663 168 startReportTicker();
Jean-Philippe Brucker 0:cfd70fa91663 169 }
Jean-Philippe Brucker 0:cfd70fa91663 170
Jean-Philippe Brucker 0:cfd70fa91663 171 GattAttribute** HIDServiceBase::inputReportDescriptors() {
Jean-Philippe Brucker 0:cfd70fa91663 172 inputReportReferenceData.ID = 0;
Jean-Philippe Brucker 0:cfd70fa91663 173 inputReportReferenceData.type = INPUT_REPORT;
Jean-Philippe Brucker 0:cfd70fa91663 174
Jean-Philippe Brucker 0:cfd70fa91663 175 static GattAttribute * descs[] = {
Jean-Philippe Brucker 0:cfd70fa91663 176 &inputReportReferenceDescriptor,
Jean-Philippe Brucker 0:cfd70fa91663 177 };
Jean-Philippe Brucker 0:cfd70fa91663 178 return descs;
Jean-Philippe Brucker 0:cfd70fa91663 179 }
Jean-Philippe Brucker 0:cfd70fa91663 180
Jean-Philippe Brucker 0:cfd70fa91663 181 GattAttribute** HIDServiceBase::outputReportDescriptors() {
Jean-Philippe Brucker 0:cfd70fa91663 182 outputReportReferenceData.ID = 0;
Jean-Philippe Brucker 0:cfd70fa91663 183 outputReportReferenceData.type = OUTPUT_REPORT;
Jean-Philippe Brucker 0:cfd70fa91663 184
Jean-Philippe Brucker 0:cfd70fa91663 185 static GattAttribute * descs[] = {
Jean-Philippe Brucker 0:cfd70fa91663 186 &outputReportReferenceDescriptor,
Jean-Philippe Brucker 0:cfd70fa91663 187 };
Jean-Philippe Brucker 0:cfd70fa91663 188 return descs;
Jean-Philippe Brucker 0:cfd70fa91663 189 }
Jean-Philippe Brucker 0:cfd70fa91663 190
Jean-Philippe Brucker 0:cfd70fa91663 191 GattAttribute** HIDServiceBase::featureReportDescriptors() {
Jean-Philippe Brucker 0:cfd70fa91663 192 featureReportReferenceData.ID = 0;
Jean-Philippe Brucker 0:cfd70fa91663 193 featureReportReferenceData.type = FEATURE_REPORT;
Jean-Philippe Brucker 0:cfd70fa91663 194
Jean-Philippe Brucker 0:cfd70fa91663 195 static GattAttribute * descs[] = {
Jean-Philippe Brucker 0:cfd70fa91663 196 &featureReportReferenceDescriptor,
Jean-Philippe Brucker 0:cfd70fa91663 197 };
Jean-Philippe Brucker 0:cfd70fa91663 198 return descs;
Jean-Philippe Brucker 0:cfd70fa91663 199 }
Jean-Philippe Brucker 0:cfd70fa91663 200
Jean-Philippe Brucker 0:cfd70fa91663 201
Jean-Philippe Brucker 0:cfd70fa91663 202 HID_information_t* HIDServiceBase::HIDInformation() {
Jean-Philippe Brucker 0:cfd70fa91663 203 static HID_information_t info = {HID_VERSION_1_11, 0x00, 0x03};
Jean-Philippe Brucker 0:cfd70fa91663 204
Jean-Philippe Brucker 0:cfd70fa91663 205 return &info;
Jean-Philippe Brucker 0:cfd70fa91663 206 }
Jean-Philippe Brucker 0:cfd70fa91663 207
Jean-Philippe Brucker 0:cfd70fa91663 208 ble_error_t HIDServiceBase::send(const report_t report) {
Jean-Philippe Brucker 0:cfd70fa91663 209 return ble.gattServer().write(inputReportCharacteristic.getValueHandle(),
Jean-Philippe Brucker 0:cfd70fa91663 210 report,
Jean-Philippe Brucker 0:cfd70fa91663 211 inputReportLength);
Jean-Philippe Brucker 0:cfd70fa91663 212 }
Jean-Philippe Brucker 0:cfd70fa91663 213
Jean-Philippe Brucker 0:cfd70fa91663 214 ble_error_t HIDServiceBase::read(report_t report) {
Jean-Philippe Brucker 0:cfd70fa91663 215 // TODO. For the time being, we'll just have HID input reports...
Jean-Philippe Brucker 0:cfd70fa91663 216 return BLE_ERROR_NOT_IMPLEMENTED;
Jean-Philippe Brucker 0:cfd70fa91663 217 }
Jean-Philippe Brucker 0:cfd70fa91663 218
Jean-Philippe Brucker 1:7a6c2e2c9371 219 void HIDServiceBase::onConnection(const Gap::ConnectionCallbackParams_t *params)
Jean-Philippe Brucker 1:7a6c2e2c9371 220 {
Jean-Philippe Brucker 1:7a6c2e2c9371 221 this->connected = true;
Jean-Philippe Brucker 1:7a6c2e2c9371 222 }
Jean-Philippe Brucker 1:7a6c2e2c9371 223
Jean-Philippe Brucker 1:7a6c2e2c9371 224 void HIDServiceBase::onDisconnection(const Gap::DisconnectionCallbackParams_t *params)
Jean-Philippe Brucker 1:7a6c2e2c9371 225 {
Jean-Philippe Brucker 1:7a6c2e2c9371 226 this->connected = false;
Jean-Philippe Brucker 1:7a6c2e2c9371 227 }