ble nano hid over gatt

Dependencies:   BLE_API mbed-dev nRF51822

Committer:
cho45
Date:
Wed Aug 31 13:44:47 2016 +0000
Revision:
60:b899414e1d34
Parent:
59:2d6c0bff2151
Child:
75:351d7ffe81d1
??????????

Who changed what in which revision?

UserRevisionLine numberNew contents of line
cho45 42:2c3be8694896 1
cho45 5:65d4e94735b6 2 /* mbed Microcontroller Library
cho45 5:65d4e94735b6 3 * Copyright (c) 2015 ARM Limited
cho45 5:65d4e94735b6 4 *
cho45 5:65d4e94735b6 5 * Licensed under the Apache License, Version 2.0 (the "License");
cho45 5:65d4e94735b6 6 * you may not use this file except in compliance with the License.
cho45 5:65d4e94735b6 7 * You may obtain a copy of the License at
cho45 5:65d4e94735b6 8 *
cho45 5:65d4e94735b6 9 * http://www.apache.org/licenses/LICENSE-2.0
cho45 5:65d4e94735b6 10 *
cho45 5:65d4e94735b6 11 * Unless required by applicable law or agreed to in writing, software
cho45 5:65d4e94735b6 12 * distributed under the License is distributed on an "AS IS" BASIS,
cho45 5:65d4e94735b6 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
cho45 5:65d4e94735b6 14 * See the License for the specific language governing permissions and
cho45 5:65d4e94735b6 15 * limitations under the License.
cho45 5:65d4e94735b6 16 */
cho45 5:65d4e94735b6 17
cho45 5:65d4e94735b6 18 #include "HIDServiceBase.h"
cho45 5:65d4e94735b6 19 #include "keyboard.h"
cho45 7:b9270a37345b 20 #include "CircularBuffer.h"
cho45 5:65d4e94735b6 21
cho45 5:65d4e94735b6 22 /**
cho45 5:65d4e94735b6 23 * Report descriptor for a standard 101 keys keyboard, following the HID specification example:
cho45 5:65d4e94735b6 24 * - 8 bytes input report (1 byte for modifiers and 6 for keys)
cho45 5:65d4e94735b6 25 * - 1 byte output report (LEDs)
cho45 5:65d4e94735b6 26 */
cho45 59:2d6c0bff2151 27 const report_map_t KEYBOARD_REPORT_MAP = {
cho45 5:65d4e94735b6 28 USAGE_PAGE(1), 0x01, // Generic Desktop Ctrls
cho45 5:65d4e94735b6 29 USAGE(1), 0x06, // Keyboard
cho45 5:65d4e94735b6 30 COLLECTION(1), 0x01, // Application
cho45 5:65d4e94735b6 31 USAGE_PAGE(1), 0x07, // Kbrd/Keypad
cho45 5:65d4e94735b6 32 USAGE_MINIMUM(1), 0xE0,
cho45 5:65d4e94735b6 33 USAGE_MAXIMUM(1), 0xE7,
cho45 5:65d4e94735b6 34 LOGICAL_MINIMUM(1), 0x00,
cho45 5:65d4e94735b6 35 LOGICAL_MAXIMUM(1), 0x01,
cho45 5:65d4e94735b6 36 REPORT_SIZE(1), 0x01, // 1 byte (Modifier)
cho45 5:65d4e94735b6 37 REPORT_COUNT(1), 0x08,
cho45 5:65d4e94735b6 38 INPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position
cho45 5:65d4e94735b6 39 REPORT_COUNT(1), 0x01, // 1 byte (Reserved)
cho45 5:65d4e94735b6 40 REPORT_SIZE(1), 0x08,
cho45 5:65d4e94735b6 41 INPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position
cho45 5:65d4e94735b6 42 REPORT_COUNT(1), 0x05, // 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana)
cho45 5:65d4e94735b6 43 REPORT_SIZE(1), 0x01,
cho45 5:65d4e94735b6 44 USAGE_PAGE(1), 0x08, // LEDs
cho45 5:65d4e94735b6 45 USAGE_MINIMUM(1), 0x01, // Num Lock
cho45 5:65d4e94735b6 46 USAGE_MAXIMUM(1), 0x05, // Kana
cho45 5:65d4e94735b6 47 OUTPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile
cho45 5:65d4e94735b6 48 REPORT_COUNT(1), 0x01, // 3 bits (Padding)
cho45 5:65d4e94735b6 49 REPORT_SIZE(1), 0x03,
cho45 5:65d4e94735b6 50 OUTPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile
cho45 5:65d4e94735b6 51 REPORT_COUNT(1), 0x06, // 6 bytes (Keys)
cho45 5:65d4e94735b6 52 REPORT_SIZE(1), 0x08,
cho45 5:65d4e94735b6 53 LOGICAL_MINIMUM(1), 0x00,
cho45 5:65d4e94735b6 54 LOGICAL_MAXIMUM(1), 0x65, // 101 keys
cho45 5:65d4e94735b6 55 USAGE_PAGE(1), 0x07, // Kbrd/Keypad
cho45 5:65d4e94735b6 56 USAGE_MINIMUM(1), 0x00,
cho45 5:65d4e94735b6 57 USAGE_MAXIMUM(1), 0x65,
cho45 5:65d4e94735b6 58 INPUT(1), 0x00, // Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position
cho45 5:65d4e94735b6 59 END_COLLECTION(0),
cho45 5:65d4e94735b6 60 };
cho45 5:65d4e94735b6 61
cho45 60:b899414e1d34 62 union InputReportData {
cho45 60:b899414e1d34 63 uint8_t raw[8];
cho45 60:b899414e1d34 64 struct {
cho45 60:b899414e1d34 65 uint8_t modifier;
cho45 60:b899414e1d34 66 uint8_t padding;
cho45 60:b899414e1d34 67 uint8_t keycode[6];
cho45 60:b899414e1d34 68 } data;
cho45 60:b899414e1d34 69 };
cho45 60:b899414e1d34 70
cho45 60:b899414e1d34 71 union {
cho45 60:b899414e1d34 72 uint8_t raw[1];
cho45 60:b899414e1d34 73 } outputReportData;
cho45 60:b899414e1d34 74
cho45 5:65d4e94735b6 75 class KeyboardService : public HIDServiceBase {
cho45 7:b9270a37345b 76
cho45 9:d1daefbf1fbd 77 InputReportData inputReportDataPublished;
cho45 7:b9270a37345b 78 InputReportData inputReportData;
cho45 5:65d4e94735b6 79
cho45 60:b899414e1d34 80 // 5ms ごとのスイッチ判定・20ms ごとの送信
cho45 60:b899414e1d34 81 // 人間の連打限界を16press/secとし keydown/keyup それぞれでレポートを送ることを考えると
cho45 60:b899414e1d34 82 // 32report/sec = 31msec で、限界で連打しても20msごとの送信にまにあう
cho45 60:b899414e1d34 83 // ただし上記は1キーに対してであり、複数キーを全力で押すとキューに溜まることがある
cho45 60:b899414e1d34 84 // 実際にカウントして測ってみたところ、どんなに乱暴に連打しても8ぐらいまでしかいかない
cho45 60:b899414e1d34 85 CircularBuffer<InputReportData, 8, uint8_t> inputReportBuffer;
cho45 7:b9270a37345b 86 InputReportData inputReportDataSending;
cho45 7:b9270a37345b 87 bool isSending;
cho45 7:b9270a37345b 88
cho45 5:65d4e94735b6 89 static const uint8_t MODIFIER_LEFT_CONTROL = 1<<0;
cho45 5:65d4e94735b6 90 static const uint8_t MODIFIER_LEFT_SHIFT = 1<<1;
cho45 5:65d4e94735b6 91 static const uint8_t MODIFIER_LEFT_ALT = 1<<2;
cho45 5:65d4e94735b6 92 static const uint8_t MODIFIER_LEFT_GUI = 1<<3;
cho45 5:65d4e94735b6 93 static const uint8_t MODIFIER_RIGHT_CONTROL = 1<<4;
cho45 5:65d4e94735b6 94 static const uint8_t MODIFIER_RIGHT_SHIFT = 1<<5;
cho45 5:65d4e94735b6 95 static const uint8_t MODIFIER_RIGHT_ALT = 1<<6;
cho45 5:65d4e94735b6 96 static const uint8_t MODIFIER_RIGHT_GUI = 1<<7;
cho45 5:65d4e94735b6 97
cho45 5:65d4e94735b6 98 public:
cho45 45:f4be69c936f6 99 bool sendAvailable;
cho45 60:b899414e1d34 100 uint8_t bufferCount;
cho45 45:f4be69c936f6 101
cho45 5:65d4e94735b6 102 KeyboardService(BLE& _ble) :
cho45 5:65d4e94735b6 103 HIDServiceBase(
cho45 5:65d4e94735b6 104 _ble,
cho45 5:65d4e94735b6 105 KEYBOARD_REPORT_MAP,
cho45 5:65d4e94735b6 106 sizeof(KEYBOARD_REPORT_MAP),
cho45 9:d1daefbf1fbd 107 inputReport = inputReportDataPublished.raw,
cho45 5:65d4e94735b6 108 outputReport = outputReportData.raw,
cho45 5:65d4e94735b6 109 featureReport = NULL,
cho45 5:65d4e94735b6 110 inputReportLength = sizeof(inputReportData),
cho45 5:65d4e94735b6 111 outputReportLength = sizeof(outputReportData),
cho45 5:65d4e94735b6 112 featureReportLength = 0,
cho45 45:f4be69c936f6 113 reportTickerDelay = 20
cho45 7:b9270a37345b 114 ),
cho45 45:f4be69c936f6 115 isSending(false),
cho45 60:b899414e1d34 116 sendAvailable(false),
cho45 60:b899414e1d34 117 bufferCount(0)
cho45 5:65d4e94735b6 118 {
cho45 6:f1c3ea8bc850 119 for (int i = 0; i < 8; i++) {
cho45 6:f1c3ea8bc850 120 inputReportData.raw[i] = 0;
cho45 6:f1c3ea8bc850 121 }
cho45 6:f1c3ea8bc850 122 outputReportData.raw[0] = 0;
cho45 7:b9270a37345b 123
cho45 7:b9270a37345b 124 inputReportBuffer.reset();
cho45 5:65d4e94735b6 125 }
cho45 5:65d4e94735b6 126
cho45 48:d6938de02f62 127 void appendReportData(const uint8_t keycode) {
cho45 5:65d4e94735b6 128 uint8_t modifier = toModifierBit(keycode);
cho45 5:65d4e94735b6 129 if (modifier) {
cho45 5:65d4e94735b6 130 inputReportData.data.modifier |= modifier;
cho45 5:65d4e94735b6 131 return;
cho45 5:65d4e94735b6 132 }
cho45 5:65d4e94735b6 133
cho45 5:65d4e94735b6 134
cho45 5:65d4e94735b6 135 for (int i = 0; i < 6; i++) {
cho45 5:65d4e94735b6 136 if (inputReportData.data.keycode[i] == 0) {
cho45 5:65d4e94735b6 137 inputReportData.data.keycode[i] = keycode;
cho45 5:65d4e94735b6 138 return;
cho45 5:65d4e94735b6 139 }
cho45 5:65d4e94735b6 140 }
cho45 5:65d4e94735b6 141
cho45 5:65d4e94735b6 142 // TODO: report data is full
cho45 5:65d4e94735b6 143 }
cho45 5:65d4e94735b6 144
cho45 48:d6938de02f62 145 void deleteReportData(const uint8_t keycode) {
cho45 5:65d4e94735b6 146 uint8_t modifier = toModifierBit(keycode);
cho45 5:65d4e94735b6 147 if (modifier) {
cho45 5:65d4e94735b6 148 inputReportData.data.modifier &= ~modifier;
cho45 5:65d4e94735b6 149 return;
cho45 5:65d4e94735b6 150 }
cho45 5:65d4e94735b6 151
cho45 5:65d4e94735b6 152 for (int i = 0; i < 6; i++) {
cho45 5:65d4e94735b6 153 if (inputReportData.data.keycode[i] == keycode) {
cho45 5:65d4e94735b6 154 inputReportData.data.keycode[i] = 0;
cho45 5:65d4e94735b6 155 return;
cho45 5:65d4e94735b6 156 }
cho45 5:65d4e94735b6 157 }
cho45 5:65d4e94735b6 158 }
cho45 5:65d4e94735b6 159
cho45 9:d1daefbf1fbd 160 void queueCurrentReportData() {
cho45 60:b899414e1d34 161 bufferCount++;
cho45 9:d1daefbf1fbd 162 inputReportBuffer.push(inputReportData);
cho45 9:d1daefbf1fbd 163 startReportTicker();
cho45 9:d1daefbf1fbd 164 }
cho45 9:d1daefbf1fbd 165
cho45 48:d6938de02f62 166 uint8_t toModifierBit(const uint8_t keycode) const {
cho45 5:65d4e94735b6 167 switch (keycode) {
cho45 5:65d4e94735b6 168 case KEY_LeftControl: return MODIFIER_LEFT_CONTROL;
cho45 5:65d4e94735b6 169 case KEY_LeftShift: return MODIFIER_LEFT_SHIFT;
cho45 5:65d4e94735b6 170 case KEY_LeftAlt: return MODIFIER_LEFT_ALT;
cho45 5:65d4e94735b6 171 case KEY_LeftGUI: return MODIFIER_LEFT_GUI;
cho45 5:65d4e94735b6 172 case KEY_RightControl: return MODIFIER_RIGHT_CONTROL;
cho45 5:65d4e94735b6 173 case KEY_RightShift: return MODIFIER_RIGHT_SHIFT;
cho45 5:65d4e94735b6 174 case KEY_RightAlt: return MODIFIER_RIGHT_ALT;
cho45 5:65d4e94735b6 175 case KEY_RightGUI: return MODIFIER_RIGHT_GUI;
cho45 5:65d4e94735b6 176 }
cho45 5:65d4e94735b6 177 return 0;
cho45 5:65d4e94735b6 178 }
cho45 5:65d4e94735b6 179
cho45 5:65d4e94735b6 180 bool isKeyPressed() {
cho45 5:65d4e94735b6 181 for (int i = 0; i < 8; i++) {
cho45 5:65d4e94735b6 182 if (inputReportData.raw[i]) {
cho45 5:65d4e94735b6 183 return 1;
cho45 5:65d4e94735b6 184 }
cho45 5:65d4e94735b6 185 }
cho45 5:65d4e94735b6 186 return 0;
cho45 5:65d4e94735b6 187 }
cho45 5:65d4e94735b6 188
cho45 5:65d4e94735b6 189 virtual void sendCallback(void) {
cho45 7:b9270a37345b 190 // do not call printf in this function... it cause BLE_STACK_BUSY
cho45 45:f4be69c936f6 191 sendAvailable = true;
cho45 45:f4be69c936f6 192 }
cho45 45:f4be69c936f6 193
cho45 45:f4be69c936f6 194 void processSend() {
cho45 45:f4be69c936f6 195 if (!sendAvailable) {
cho45 45:f4be69c936f6 196 return;
cho45 45:f4be69c936f6 197 }
cho45 45:f4be69c936f6 198 sendAvailable = false;
cho45 45:f4be69c936f6 199
cho45 45:f4be69c936f6 200 // isSending の場合現在送信中の report があり、再送中である可能性がある
cho45 45:f4be69c936f6 201 // そうではない場合のみ queue から pop して新しく send する
cho45 9:d1daefbf1fbd 202 if (!isSending) {
cho45 9:d1daefbf1fbd 203 if (!inputReportBuffer.pop(inputReportDataSending)) {
cho45 51:d9297f8a60b7 204 // 送るデータがないなら送信をやめる
cho45 51:d9297f8a60b7 205 stopReportTicker();
cho45 51:d9297f8a60b7 206 return;
cho45 7:b9270a37345b 207 }
cho45 60:b899414e1d34 208 bufferCount--;
cho45 7:b9270a37345b 209 }
cho45 7:b9270a37345b 210
cho45 7:b9270a37345b 211 static uint8_t busyCount = 0;
cho45 7:b9270a37345b 212 isSending = true;
cho45 9:d1daefbf1fbd 213 ble_error_t error = HIDServiceBase::send(inputReportDataSending.raw);
cho45 5:65d4e94735b6 214 if (error == BLE_STACK_BUSY) {
cho45 7:b9270a37345b 215 if (busyCount++ > 10) {
cho45 7:b9270a37345b 216 busyCount = 0;
cho45 7:b9270a37345b 217 stopReportTicker();
cho45 7:b9270a37345b 218 }
cho45 5:65d4e94735b6 219 // retry after
cho45 5:65d4e94735b6 220 return;
cho45 5:65d4e94735b6 221 }
cho45 7:b9270a37345b 222 isSending = false;
cho45 7:b9270a37345b 223 }
cho45 5:65d4e94735b6 224
cho45 7:b9270a37345b 225 virtual void onDataSent(unsigned int count) {
cho45 7:b9270a37345b 226 startReportTicker();
cho45 5:65d4e94735b6 227 }
cho45 21:d801c32231b0 228
cho45 21:d801c32231b0 229 virtual void stopReportTicker(void) {
cho45 34:7da766a8aa96 230 if (reportTickerIsActive) {
cho45 34:7da766a8aa96 231 HIDServiceBase::stopReportTicker();
cho45 34:7da766a8aa96 232 }
cho45 21:d801c32231b0 233 }
cho45 5:65d4e94735b6 234 };
cho45 5:65d4e94735b6 235
cho45 5:65d4e94735b6 236