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:   Seeed_Tiny_BLE_FTHR_Peripheral

Fork of BLE_HID by Jean-Philippe Brucker

Committer:
Jean-Philippe Brucker
Date:
Tue Sep 15 20:16:58 2015 +0100
Revision:
0:cfd70fa91663
Child:
1:7a6c2e2c9371
Initial version

Provides all the components for an HID-over-GATT Profile implementation using
BLE API.

Who changed what in which revision?

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