MicroBit as BLE gamepad

Dependencies:   BLE_API mbed-dev-bin nRF51822

Dependents:   microbit

Fork of microbit-dal by Lancaster University

Committer:
rengro01
Date:
Mon Jan 30 08:29:06 2017 +0000
Revision:
75:df904445f561
Parent:
74:1b9850e39cd1
MicroBit as BLE gamepad

Who changed what in which revision?

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