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:
Thu Oct 29 16:48:26 2015 +0000
Revision:
2:3d9adb26bdc5
Parent:
1:7a6c2e2c9371
Version 0.2

Who changed what in which revision?

UserRevisionLine numberNew 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 2:3d9adb26bdc5 123 virtual 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 2:3d9adb26bdc5 129 virtual 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 };