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
MouseService.h@1:7a6c2e2c9371, 2015-10-07 (annotated)
- Committer:
- Jean-Philippe Brucker
- Date:
- Wed Oct 07 11:29:52 2015 +0100
- Revision:
- 1:7a6c2e2c9371
- Parent:
- 0:cfd70fa91663
- Child:
- 2:3d9adb26bdc5
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?
User | Revision | Line number | New 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 | |
Jean-Philippe Brucker |
0:cfd70fa91663 | 19 | #include "HIDServiceBase.h" |
Jean-Philippe Brucker |
0:cfd70fa91663 | 20 | |
Jean-Philippe Brucker |
0:cfd70fa91663 | 21 | enum ButtonState |
Jean-Philippe Brucker |
0:cfd70fa91663 | 22 | { |
Jean-Philippe Brucker |
0:cfd70fa91663 | 23 | BUTTON_UP, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 24 | BUTTON_DOWN |
Jean-Philippe Brucker |
0:cfd70fa91663 | 25 | }; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 26 | |
Jean-Philippe Brucker |
0:cfd70fa91663 | 27 | enum MouseButton |
Jean-Philippe Brucker |
0:cfd70fa91663 | 28 | { |
Jean-Philippe Brucker |
0:cfd70fa91663 | 29 | MOUSE_BUTTON_LEFT = 0x1, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 30 | MOUSE_BUTTON_RIGHT = 0x2, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 31 | MOUSE_BUTTON_MIDDLE = 0x4, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 32 | }; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 33 | |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 34 | /** |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 35 | * Report descriptor for a standard 3 buttons + wheel mouse with relative X/Y |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 36 | * moves |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 37 | */ |
Jean-Philippe Brucker |
0:cfd70fa91663 | 38 | report_map_t MOUSE_REPORT_MAP = { |
Jean-Philippe Brucker |
0:cfd70fa91663 | 39 | USAGE_PAGE(1), 0x01, // Generic Desktop |
Jean-Philippe Brucker |
0:cfd70fa91663 | 40 | USAGE(1), 0x02, // Mouse |
Jean-Philippe Brucker |
0:cfd70fa91663 | 41 | COLLECTION(1), 0x01, // Application |
Jean-Philippe Brucker |
0:cfd70fa91663 | 42 | USAGE(1), 0x01, // Pointer |
Jean-Philippe Brucker |
0:cfd70fa91663 | 43 | COLLECTION(1), 0x00, // Physical |
Jean-Philippe Brucker |
0:cfd70fa91663 | 44 | USAGE_PAGE(1), 0x09, // Buttons |
Jean-Philippe Brucker |
0:cfd70fa91663 | 45 | USAGE_MINIMUM(1), 0x01, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 46 | USAGE_MAXIMUM(1), 0x03, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 47 | LOGICAL_MINIMUM(1), 0x00, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 48 | LOGICAL_MAXIMUM(1), 0x01, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 49 | REPORT_COUNT(1), 0x03, // 3 bits (Buttons) |
Jean-Philippe Brucker |
0:cfd70fa91663 | 50 | REPORT_SIZE(1), 0x01, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 51 | INPUT(1), 0x02, // Data, Variable, Absolute |
Jean-Philippe Brucker |
0:cfd70fa91663 | 52 | REPORT_COUNT(1), 0x01, // 5 bits (Padding) |
Jean-Philippe Brucker |
0:cfd70fa91663 | 53 | REPORT_SIZE(1), 0x05, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 54 | INPUT(1), 0x01, // Constant |
Jean-Philippe Brucker |
0:cfd70fa91663 | 55 | USAGE_PAGE(1), 0x01, // Generic Desktop |
Jean-Philippe Brucker |
0:cfd70fa91663 | 56 | USAGE(1), 0x30, // X |
Jean-Philippe Brucker |
0:cfd70fa91663 | 57 | USAGE(1), 0x31, // Y |
Jean-Philippe Brucker |
0:cfd70fa91663 | 58 | USAGE(1), 0x38, // Wheel |
Jean-Philippe Brucker |
0:cfd70fa91663 | 59 | LOGICAL_MINIMUM(1), 0x81, // -127 |
Jean-Philippe Brucker |
0:cfd70fa91663 | 60 | LOGICAL_MAXIMUM(1), 0x7f, // 127 |
Jean-Philippe Brucker |
0:cfd70fa91663 | 61 | REPORT_SIZE(1), 0x08, // Three bytes |
Jean-Philippe Brucker |
0:cfd70fa91663 | 62 | REPORT_COUNT(1), 0x03, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 63 | INPUT(1), 0x06, // Data, Variable, Relative |
Jean-Philippe Brucker |
0:cfd70fa91663 | 64 | END_COLLECTION(0), |
Jean-Philippe Brucker |
0:cfd70fa91663 | 65 | END_COLLECTION(0), |
Jean-Philippe Brucker |
0:cfd70fa91663 | 66 | }; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 67 | |
Jean-Philippe Brucker |
0:cfd70fa91663 | 68 | uint8_t report[] = { 0, 0, 0, 0 }; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 69 | |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 70 | /** |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 71 | * @class MouseService |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 72 | * @brief HID-over-Gatt mouse service. |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 73 | * |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 74 | * Send mouse moves and button informations over BLE. |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 75 | * |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 76 | * @code |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 77 | * BLE ble; |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 78 | * MouseService mouse(ble); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 79 | * |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 80 | * Timeout timeout; |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 81 | * |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 82 | * void stop_mouse_move(void) |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 83 | * { |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 84 | * // Set mouse state to immobile |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 85 | * mouse.setButton(MOUSE_BUTTON_LEFT, MOUSE_UP); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 86 | * mouse.setSpeed(0, 0, 0); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 87 | * } |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 88 | * |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 89 | * void start_mouse_move(void) |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 90 | * { |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 91 | * // Move left with a left button down. If the focus is on a drawing |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 92 | * // software, for instance, this should draw a line. |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 93 | * mouse.setButton(MOUSE_BUTTON_LEFT, MOUSE_DOWN); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 94 | * mouse.setSpeed(1, 0, 0); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 95 | * |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 96 | * timeout.attach(stop_mouse_move, 0.2); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 97 | * } |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 98 | * @endcode |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 99 | */ |
Jean-Philippe Brucker |
0:cfd70fa91663 | 100 | class MouseService: public HIDServiceBase |
Jean-Philippe Brucker |
0:cfd70fa91663 | 101 | { |
Jean-Philippe Brucker |
0:cfd70fa91663 | 102 | public: |
Jean-Philippe Brucker |
0:cfd70fa91663 | 103 | MouseService(BLE &_ble) : |
Jean-Philippe Brucker |
0:cfd70fa91663 | 104 | HIDServiceBase(_ble, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 105 | MOUSE_REPORT_MAP, sizeof(MOUSE_REPORT_MAP), |
Jean-Philippe Brucker |
0:cfd70fa91663 | 106 | inputReport = report, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 107 | outputReport = NULL, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 108 | featureReport = NULL, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 109 | inputReportLength = sizeof(inputReport), |
Jean-Philippe Brucker |
0:cfd70fa91663 | 110 | outputReportLength = 0, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 111 | featureReportLength = 0, |
Jean-Philippe Brucker |
0:cfd70fa91663 | 112 | reportTickerDelay = 20), |
Jean-Philippe Brucker |
0:cfd70fa91663 | 113 | buttonsState (0), |
Jean-Philippe Brucker |
0:cfd70fa91663 | 114 | failedReports (0) |
Jean-Philippe Brucker |
0:cfd70fa91663 | 115 | { |
Jean-Philippe Brucker |
0:cfd70fa91663 | 116 | speed[0] = 0; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 117 | speed[1] = 0; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 118 | speed[2] = 0; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 119 | |
Jean-Philippe Brucker |
0:cfd70fa91663 | 120 | startReportTicker(); |
Jean-Philippe Brucker |
0:cfd70fa91663 | 121 | } |
Jean-Philippe Brucker |
0:cfd70fa91663 | 122 | |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 123 | void onConnection(const Gap::ConnectionCallbackParams_t *params) |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 124 | { |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 125 | HIDServiceBase::onConnection(params); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 126 | startReportTicker(); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 127 | } |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 128 | |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 129 | void onDisconnection(const Gap::DisconnectionCallbackParams_t *params) |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 130 | { |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 131 | stopReportTicker(); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 132 | HIDServiceBase::onDisconnection(params); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 133 | } |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 134 | |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 135 | /** |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 136 | * Set X, Y, Z speed of the mouse. Parameters are sticky and will be |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 137 | * transmitted on every tick. Users should therefore reset them to 0 when |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 138 | * the device is immobile. |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 139 | * |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 140 | * @param x Speed on hoizontal axis |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 141 | * @param y Speed on vertical axis |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 142 | * @param wheel Scroll speed |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 143 | * |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 144 | * @returns A status code |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 145 | * |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 146 | * @note Directions depend on the operating system's configuration. It is |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 147 | * customary to increase values on the X axis from left to right, and on the |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 148 | * Y axis from top to bottom. |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 149 | * Wheel is less standard, although positive values will usually scroll up. |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 150 | */ |
Jean-Philippe Brucker |
0:cfd70fa91663 | 151 | int setSpeed(int8_t x, int8_t y, int8_t wheel) |
Jean-Philippe Brucker |
0:cfd70fa91663 | 152 | { |
Jean-Philippe Brucker |
0:cfd70fa91663 | 153 | speed[0] = x; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 154 | speed[1] = y; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 155 | speed[2] = wheel; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 156 | |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 157 | startReportTicker(); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 158 | |
Jean-Philippe Brucker |
0:cfd70fa91663 | 159 | return 0; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 160 | } |
Jean-Philippe Brucker |
0:cfd70fa91663 | 161 | |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 162 | /** |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 163 | * Toggle the state of one button |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 164 | * |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 165 | * @returns A status code |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 166 | */ |
Jean-Philippe Brucker |
0:cfd70fa91663 | 167 | int setButton(MouseButton button, ButtonState state) |
Jean-Philippe Brucker |
0:cfd70fa91663 | 168 | { |
Jean-Philippe Brucker |
0:cfd70fa91663 | 169 | if (state == BUTTON_UP) |
Jean-Philippe Brucker |
0:cfd70fa91663 | 170 | buttonsState &= ~(button); |
Jean-Philippe Brucker |
0:cfd70fa91663 | 171 | else |
Jean-Philippe Brucker |
0:cfd70fa91663 | 172 | buttonsState |= button; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 173 | |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 174 | startReportTicker(); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 175 | |
Jean-Philippe Brucker |
0:cfd70fa91663 | 176 | return 0; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 177 | } |
Jean-Philippe Brucker |
0:cfd70fa91663 | 178 | |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 179 | /** |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 180 | * Called by the report ticker |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 181 | */ |
Jean-Philippe Brucker |
0:cfd70fa91663 | 182 | virtual void sendCallback(void) { |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 183 | uint8_t buttons = buttonsState & 0x7; |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 184 | |
Jean-Philippe Brucker |
0:cfd70fa91663 | 185 | if (!connected) |
Jean-Philippe Brucker |
0:cfd70fa91663 | 186 | return; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 187 | |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 188 | bool can_sleep = (report[0] == 0 |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 189 | && report[1] == 0 |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 190 | && report[2] == 0 |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 191 | && report[3] == 0 |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 192 | && report[0] == buttons |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 193 | && report[1] == speed[0] |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 194 | && report[2] == speed[1] |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 195 | && report[3] == speed[2]); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 196 | |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 197 | if (can_sleep) { |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 198 | /* TODO: find out why there always is two more calls to sendCallback after this |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 199 | * stopReportTicker(). */ |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 200 | stopReportTicker(); |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 201 | return; |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 202 | } |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 203 | |
Jean-Philippe Brucker |
1:7a6c2e2c9371 | 204 | report[0] = buttons; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 205 | report[1] = speed[0]; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 206 | report[2] = speed[1]; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 207 | report[3] = speed[2]; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 208 | |
Jean-Philippe Brucker |
0:cfd70fa91663 | 209 | if (send(report)) |
Jean-Philippe Brucker |
0:cfd70fa91663 | 210 | failedReports++; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 211 | } |
Jean-Philippe Brucker |
0:cfd70fa91663 | 212 | |
Jean-Philippe Brucker |
0:cfd70fa91663 | 213 | protected: |
Jean-Philippe Brucker |
0:cfd70fa91663 | 214 | uint8_t buttonsState; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 215 | uint8_t speed[3]; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 216 | |
Jean-Philippe Brucker |
0:cfd70fa91663 | 217 | public: |
Jean-Philippe Brucker |
0:cfd70fa91663 | 218 | uint32_t failedReports; |
Jean-Philippe Brucker |
0:cfd70fa91663 | 219 | }; |