Based on the example USB HID keyboard https://developer.mbed.org/users/jbru/code/BLE_HID_KeyboardStreamDemo/

Dependencies:   BLE_API mbed nRF51822

Fork of HID-kb by Microbug

Committer:
JonnyA
Date:
Wed Nov 14 09:46:45 2018 +0000
Revision:
3:7d7822143a2d
Parent:
1:9e174f8fd9e9
Add simple debounce wait on interrupts for buttons

Who changed what in which revision?

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