
51822 gatt gamepad no test https://github.com/ozanaltinkaya/BLE_Gamepad_NRF51822
Dependencies: mbed BLE_API nRF51822
Revision 0:706cf6936c11, committed 2021-07-20
- Comitter:
- weisimin
- Date:
- Tue Jul 20 09:23:54 2021 +0000
- Commit message:
- BLE GATT GAMEPAD NO TEST
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BLE_API.lib Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/#65474dc93927
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BLE_HID/GamepadService.h Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,155 @@ +/* mbed Microcontroller Library + * Copyright (c) 2015 ARM Limited + * Modifications copyright (C) 2017 Ozan ALTINKAYA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbed.h" + +#include "HIDServiceBase.h" + +enum ButtonState +{ + BUTTON_UP, + BUTTON_DOWN +}; + +enum GamepadButton +{ + GAMEPAD_BUTTON_A = 0x1, + GAMEPAD_BUTTON_B = 0x2, + GAMEPAD_BUTTON_C = 0x4, + GAMEPAD_BUTTON_D = 0x8, + GAMEPAD_BUTTON_E = 0x10, + GAMEPAD_BUTTON_F = 0x20, + GAMEPAD_BUTTON_G = 0x40, + GAMEPAD_BUTTON_H = 0x80, + + GAMEPAD_BUTTON_AXIS_Xp = 0x01, + GAMEPAD_BUTTON_AXIS_Xn = 0x03, + GAMEPAD_BUTTON_AXIS_Yp = 0x04, + GAMEPAD_BUTTON_AXIS_Yn = 0x0C, + +}; + +report_map_t GAMEPAD_REPORT_MAP = { + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x05, // Gamepad + COLLECTION(1), 0x01, // Application + USAGE(1), 0x01, // Pointer + COLLECTION(1), 0x00, // Physical + USAGE(1), 0x30, // X Axis + USAGE(1), 0x31, // Y Axis + LOGICAL_MINIMUM(1), 0xFF, // -1 + LOGICAL_MAXIMUM(1), 0x01, // 1 + REPORT_COUNT(1), 0x02, // 2 Fields, X-Y + REPORT_SIZE(1), 0x02, // 2 bits per Axis + INPUT(1), 0x02, // Data, Variable, Absolute + END_COLLECTION(0), + + REPORT_COUNT(1), 0x04, // 4 Bit Padding + REPORT_SIZE(1), 0x01, // 4 Bit Padding + INPUT(1), 0x03, // Constant, Variable, Absolute + + USAGE_PAGE(1), 0x09, // Buttons + USAGE_MINIMUM(1), 0x01, + USAGE_MAXIMUM(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_COUNT(1), 0x08, // 2 bits (Buttons) + REPORT_SIZE(1), 0x01, + INPUT(1), 0x02, // Data, Variable, Absolute + +// REPORT_COUNT(1), 0x02, // 2 Bit Padding +// REPORT_SIZE(1), 0x01, // 2 Bit Padding +// INPUT(1), 0x03, // Constant, Variable, Absolute + END_COLLECTION(0), +}; + +uint8_t report[] = { 0, 0 }; + +class GamepadService: public HIDServiceBase +{ +public: + GamepadService(BLE &_ble) : + HIDServiceBase(_ble, + GAMEPAD_REPORT_MAP, sizeof(GAMEPAD_REPORT_MAP), + inputReport = report, + outputReport = NULL, + featureReport = NULL, + inputReportLength = sizeof(inputReport), + outputReportLength = 0, + featureReportLength = 0, + reportTickerDelay = 6), + buttonsState (0), + axisState (0), + failedReports (0) + { + + startReportTicker(); + } + + int setXAxis(GamepadButton button, ButtonState state) + { + if (state == BUTTON_DOWN) + XaxisState = button; + else + XaxisState = 0x00; + + return 0; + } + + int setYAxis(GamepadButton button, ButtonState state) + { + if (state == BUTTON_DOWN) + YaxisState = button; + else + YaxisState = 0x00; + + return 0; + } + + int setButton(GamepadButton button, ButtonState state) + { + if (state == BUTTON_UP) + buttonsState &= ~(button); + else + buttonsState |= button; + + return 0; + } + + + virtual void sendCallback(void) { + if (!connected) + return; + + report[1] = buttonsState; + report[0] = YaxisState | XaxisState; + + + if (send(report)) + failedReports++; + } + +protected: + uint8_t buttonsState; + uint8_t axisState; + uint8_t XaxisState; + uint8_t YaxisState; + + +public: + uint32_t failedReports; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BLE_HID/HIDServiceBase.cpp Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,227 @@ +/* mbed Microcontroller Library + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbed.h" +#include "HIDServiceBase.h" + +HIDServiceBase::HIDServiceBase(BLE &_ble, + report_map_t reportMap, + uint8_t reportMapSize, + report_t inputReport, + report_t outputReport, + report_t featureReport, + uint8_t inputReportLength, + uint8_t outputReportLength, + uint8_t featureReportLength, + uint8_t inputReportTickerDelay) : + ble(_ble), + connected (false), + reportMapLength(reportMapSize), + + inputReport(inputReport), + outputReport(outputReport), + featureReport(featureReport), + + inputReportLength(inputReportLength), + outputReportLength(outputReportLength), + featureReportLength(featureReportLength), + + protocolMode(REPORT_PROTOCOL), + + inputReportReferenceDescriptor(BLE_UUID_DESCRIPTOR_REPORT_REFERENCE, + (uint8_t *)&inputReportReferenceData, 2, 2), + outputReportReferenceDescriptor(BLE_UUID_DESCRIPTOR_REPORT_REFERENCE, + (uint8_t *)&outputReportReferenceData, 2, 2), + featureReportReferenceDescriptor(BLE_UUID_DESCRIPTOR_REPORT_REFERENCE, + (uint8_t *)&featureReportReferenceData, 2, 2), + + protocolModeCharacteristic(GattCharacteristic::UUID_PROTOCOL_MODE_CHAR, &protocolMode, 1, 1, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ + | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE), + + inputReportCharacteristic(GattCharacteristic::UUID_REPORT_CHAR, + (uint8_t *)inputReport, inputReportLength, inputReportLength, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ + | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY + | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE, + inputReportDescriptors(), 1), + + outputReportCharacteristic(GattCharacteristic::UUID_REPORT_CHAR, + (uint8_t *)outputReport, outputReportLength, outputReportLength, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ + | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE + | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE, + outputReportDescriptors(), 1), + + featureReportCharacteristic(GattCharacteristic::UUID_REPORT_CHAR, + (uint8_t *)featureReport, featureReportLength, featureReportLength, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ + | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE, + featureReportDescriptors(), 1), + + /* + * We need to set reportMap content as const, in order to let the compiler put it into flash + * instead of RAM. The characteristic is read-only so it won't be written, but + * GattCharacteristic constructor takes non-const arguments only. Hence the cast. + */ + reportMapCharacteristic(GattCharacteristic::UUID_REPORT_MAP_CHAR, + const_cast<uint8_t*>(reportMap), reportMapLength, reportMapLength, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), + + HIDInformationCharacteristic(GattCharacteristic::UUID_HID_INFORMATION_CHAR, HIDInformation()), + HIDControlPointCharacteristic(GattCharacteristic::UUID_HID_CONTROL_POINT_CHAR, + &controlPointCommand, 1, 1, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE), + + reportTickerDelay(inputReportTickerDelay), + reportTickerIsActive(false) +{ + static GattCharacteristic *characteristics[] = { + &HIDInformationCharacteristic, + &reportMapCharacteristic, + &protocolModeCharacteristic, + &HIDControlPointCharacteristic, + NULL, + NULL, + NULL, + NULL, + NULL + }; + + unsigned int charIndex = 4; + /* + * Report characteristics are optional, and depend on the reportMap descriptor + * Note: at least one should be present, but we don't check that at the moment. + */ + if (inputReportLength) + characteristics[charIndex++] = &inputReportCharacteristic; + if (outputReportLength) + characteristics[charIndex++] = &outputReportCharacteristic; + if (featureReportLength) + characteristics[charIndex++] = &featureReportCharacteristic; + + /* TODO: let children add some more characteristics, namely boot keyboard and mouse (They are + * mandatory as per HIDS spec.) Ex: + * + * addExtraCharacteristics(characteristics, int& charIndex); + */ + + GattService service(GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE, + characteristics, charIndex); + + ble.gattServer().addService(service); + + ble.gap().onConnection(this, &HIDServiceBase::onConnection); + ble.gap().onDisconnection(this, &HIDServiceBase::onDisconnection); + + ble.gattServer().onDataSent(this, &HIDServiceBase::onDataSent); + + /* + * Change preferred connection params, in order to optimize the notification frequency. Most + * OSes seem to respect this, even though they are not required to. + * + * Some OSes don't handle reconnection well, at the moment, so we set the maximum possible + * timeout, 32 seconds + */ + uint16_t minInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(reportTickerDelay / 2); + if (minInterval < 6) + minInterval = 6; + uint16_t maxInterval = minInterval * 2; + Gap::ConnectionParams_t params = {minInterval, maxInterval, 0, 3200}; + + ble.gap().setPreferredConnectionParams(¶ms); + + SecurityManager::SecurityMode_t securityMode = SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM; + protocolModeCharacteristic.requireSecurity(securityMode); + reportMapCharacteristic.requireSecurity(securityMode); + inputReportCharacteristic.requireSecurity(securityMode); + outputReportCharacteristic.requireSecurity(securityMode); + featureReportCharacteristic.requireSecurity(securityMode); +} + +void HIDServiceBase::startReportTicker(void) { + if (reportTickerIsActive) + return; + reportTicker.attach_us(this, &HIDServiceBase::sendCallback, reportTickerDelay * 1000); + reportTickerIsActive = true; +} + +void HIDServiceBase::stopReportTicker(void) { + reportTicker.detach(); + reportTickerIsActive = false; +} + +void HIDServiceBase::onDataSent(unsigned count) { + startReportTicker(); +} + +GattAttribute** HIDServiceBase::inputReportDescriptors() { + inputReportReferenceData.ID = 0; + inputReportReferenceData.type = INPUT_REPORT; + + static GattAttribute * descs[] = { + &inputReportReferenceDescriptor, + }; + return descs; +} + +GattAttribute** HIDServiceBase::outputReportDescriptors() { + outputReportReferenceData.ID = 0; + outputReportReferenceData.type = OUTPUT_REPORT; + + static GattAttribute * descs[] = { + &outputReportReferenceDescriptor, + }; + return descs; +} + +GattAttribute** HIDServiceBase::featureReportDescriptors() { + featureReportReferenceData.ID = 0; + featureReportReferenceData.type = FEATURE_REPORT; + + static GattAttribute * descs[] = { + &featureReportReferenceDescriptor, + }; + return descs; +} + + +HID_information_t* HIDServiceBase::HIDInformation() { + static HID_information_t info = {HID_VERSION_1_11, 0x00, 0x03}; + + return &info; +} + +ble_error_t HIDServiceBase::send(const report_t report) { + return ble.gattServer().write(inputReportCharacteristic.getValueHandle(), + report, + inputReportLength); +} + +ble_error_t HIDServiceBase::read(report_t report) { + // TODO. For the time being, we'll just have HID input reports... + return BLE_ERROR_NOT_IMPLEMENTED; +} + +void HIDServiceBase::onConnection(const Gap::ConnectionCallbackParams_t *params) +{ + this->connected = true; +} + +void HIDServiceBase::onDisconnection(const Gap::DisconnectionCallbackParams_t *params) +{ + this->connected = false; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BLE_HID/HIDServiceBase.h Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,199 @@ +/* mbed Microcontroller Library + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HID_SERVICE_BASE_H_ +#define HID_SERVICE_BASE_H_ + +#include "mbed.h" + +#include "ble/BLE.h" +#include "USBHID_Types.h" + +#define BLE_UUID_DESCRIPTOR_REPORT_REFERENCE 0x2908 + +typedef const uint8_t report_map_t[]; +typedef const uint8_t * report_t; + +typedef struct { + uint16_t bcdHID; + uint8_t bCountryCode; + uint8_t flags; +} HID_information_t; + +enum ReportType { + INPUT_REPORT = 0x1, + OUTPUT_REPORT = 0x2, + FEATURE_REPORT = 0x3, +}; + +enum ProtocolMode { + BOOT_PROTOCOL = 0x0, + REPORT_PROTOCOL = 0x1, +}; + +typedef struct { + uint8_t ID; + uint8_t type; +} report_reference_t; + + +class HIDServiceBase { +public: + /** + * Constructor + * + * @param _ble + * BLE object to add this service to + * @param reportMap + * Byte array representing the input/output report formats. In USB HID jargon, it + * is called "HID report descriptor". + * @param reportMapLength + * Size of the reportMap array + * @param outputReportLength + * Maximum length of a sent report (up to 64 bytes) (default: 64 bytes) + * @param inputReportLength + * Maximum length of a received report (up to 64 bytes) (default: 64 bytes) + * @param inputReportTickerDelay + * Delay between input report notifications, in ms. Acceptable values depend directly on + * GAP's connInterval parameter, so it shouldn't be less than 12ms + * Preferred GAP connection interval is set after this value, in order to send + * notifications as quick as possible: minimum connection interval will be set to + * (inputReportTickerDelay / 2) + */ + HIDServiceBase(BLE &_ble, + report_map_t reportMap, + uint8_t reportMapLength, + report_t inputReport, + report_t outputReport, + report_t featureReport, + uint8_t inputReportLength = 0, + uint8_t outputReportLength = 0, + uint8_t featureReportLength = 0, + uint8_t inputReportTickerDelay = 50); + + /** + * Send Report + * + * @param report Report to send. Must be of size @ref inputReportLength + * @return The write status + * + * @note Don't call send() directly for multiple reports! Use reportTicker for that, in order + * to avoid overloading the BLE stack, and let it handle events between each report. + */ + virtual ble_error_t send(const report_t report); + + /** + * Read Report + * + * @param report Report to fill. Must be of size @ref outputReportLength + * @return The read status + */ + virtual ble_error_t read(report_t report); + + virtual void onConnection(const Gap::ConnectionCallbackParams_t *params); + virtual void onDisconnection(const Gap::DisconnectionCallbackParams_t *params); + + virtual bool isConnected(void) + { + return connected; + } + +protected: + /** + * Called by BLE API when data has been successfully sent. + * + * @param count Number of reports sent + * + * @note Subclasses can override this to avoid starting the report ticker when there is nothing + * to send + */ + virtual void onDataSent(unsigned count); + + /** + * Start the ticker that sends input reports at regular interval + * + * @note reportTickerIsActive describes the state of the ticker and can be used by HIDS + * implementations. + */ + virtual void startReportTicker(void); + + /** + * Stop the input report ticker + */ + virtual void stopReportTicker(void); + + /** + * Called by input report ticker at regular interval (reportTickerDelay). This must be + * overriden by HIDS implementations to call the @ref send() with a report, if necessary. + */ + virtual void sendCallback(void) = 0; + + /** + * Create the Gatt descriptor for a report characteristic + */ + GattAttribute** inputReportDescriptors(); + GattAttribute** outputReportDescriptors(); + GattAttribute** featureReportDescriptors(); + + /** + * Create the HID information structure + */ + HID_information_t* HIDInformation(); + +protected: + BLE &ble; + bool connected; + + int reportMapLength; + + report_t inputReport; + report_t outputReport; + report_t featureReport; + + uint8_t inputReportLength; + uint8_t outputReportLength; + uint8_t featureReportLength; + + uint8_t controlPointCommand; + uint8_t protocolMode; + + report_reference_t inputReportReferenceData; + report_reference_t outputReportReferenceData; + report_reference_t featureReportReferenceData; + + GattAttribute inputReportReferenceDescriptor; + GattAttribute outputReportReferenceDescriptor; + GattAttribute featureReportReferenceDescriptor; + + // Optional gatt characteristics: + GattCharacteristic protocolModeCharacteristic; + + // Report characteristics (each sort of optional) + GattCharacteristic inputReportCharacteristic; + GattCharacteristic outputReportCharacteristic; + GattCharacteristic featureReportCharacteristic; + + // Required gatt characteristics: Report Map, Information, Control Point + GattCharacteristic reportMapCharacteristic; + ReadOnlyGattCharacteristic<HID_information_t> HIDInformationCharacteristic; + GattCharacteristic HIDControlPointCharacteristic; + + Ticker reportTicker; + uint32_t reportTickerDelay; + bool reportTickerIsActive; +}; + +#endif /* !HID_SERVICE_BASE_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BLE_HID/JoystickService.h Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,128 @@ +/* mbed Microcontroller Library + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbed.h" + +#include "HIDServiceBase.h" + +enum ButtonState +{ + BUTTON_UP, + BUTTON_DOWN +}; + +enum JoystickButton +{ + JOYSTICK_BUTTON_1 = 0x1, + JOYSTICK_BUTTON_2 = 0x2, +}; + +report_map_t JOYSTICK_REPORT_MAP = { + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x04, // Joystick + COLLECTION(1), 0x01, // Application + COLLECTION(1), 0x00, // Physical + USAGE_PAGE(1), 0x09, // Buttons + USAGE_MINIMUM(1), 0x01, + USAGE_MAXIMUM(1), 0x03, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_COUNT(1), 0x03, // 2 bits (Buttons) + REPORT_SIZE(1), 0x01, + INPUT(1), 0x02, // Data, Variable, Absolute + REPORT_COUNT(1), 0x01, // 6 bits (Padding) + REPORT_SIZE(1), 0x05, + INPUT(1), 0x01, // Constant + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x30, // X + USAGE(1), 0x31, // Y + USAGE(1), 0x32, // Z + USAGE(1), 0x33, // Rx + LOGICAL_MINIMUM(1), 0x81, // -127 + LOGICAL_MAXIMUM(1), 0x7f, // 127 + REPORT_SIZE(1), 0x08, // Three bytes + REPORT_COUNT(1), 0x04, + INPUT(1), 0x02, // Data, Variable, Absolute (unlike mouse) + END_COLLECTION(0), + END_COLLECTION(0), +}; + +uint8_t report[] = { 0, 0, 0, 0, 0 }; + +class JoystickService: public HIDServiceBase +{ +public: + JoystickService(BLE &_ble) : + HIDServiceBase(_ble, + JOYSTICK_REPORT_MAP, sizeof(JOYSTICK_REPORT_MAP), + inputReport = report, + outputReport = NULL, + featureReport = NULL, + inputReportLength = sizeof(inputReport), + outputReportLength = 0, + featureReportLength = 0, + reportTickerDelay = 20), + buttonsState (0), + failedReports (0) + { + speed[0] = 0; + speed[1] = 0; + speed[2] = 0; + speed[3] = 0; + + startReportTicker(); + } + + int setSpeed(int8_t x, int8_t y, int8_t z) + { + speed[0] = x; + speed[1] = y; + speed[2] = z; + + return 0; + } + + int setButton(JoystickButton button, ButtonState state) + { + if (state == BUTTON_UP) + buttonsState &= ~(button); + else + buttonsState |= button; + + return 0; + } + + virtual void sendCallback(void) { + if (!connected) + return; + + report[0] = buttonsState & 0x7; + report[1] = speed[0]; + report[2] = speed[1]; + report[3] = speed[2]; + report[4] = speed[3]; + + if (send(report)) + failedReports++; + } + +protected: + uint8_t buttonsState; + uint8_t speed[4]; + +public: + uint32_t failedReports; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BLE_HID/KeyboardService.h Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,385 @@ +/* mbed Microcontroller Library + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include "mbed.h" +#include "CircularBuffer.h" + +#include "HIDServiceBase.h" +#include "Keyboard_types.h" + +/* TODO: make this easier to configure by application (e.g. as a template parameter for + * KeyboardService) */ +#ifndef KEYBUFFER_SIZE +#define KEYBUFFER_SIZE 256 +#endif + +/** + * Report descriptor for a standard 101 keys keyboard, following the HID specification example: + * - 8 bytes input report (1 byte for modifiers and 6 for keys) + * - 1 byte output report (LEDs) + */ +report_map_t KEYBOARD_REPORT_MAP = { + USAGE_PAGE(1), 0x01, // Generic Desktop Ctrls + USAGE(1), 0x06, // Keyboard + COLLECTION(1), 0x01, // Application + USAGE_PAGE(1), 0x07, // Kbrd/Keypad + USAGE_MINIMUM(1), 0xE0, + USAGE_MAXIMUM(1), 0xE7, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_SIZE(1), 0x01, // 1 byte (Modifier) + REPORT_COUNT(1), 0x08, + INPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position + REPORT_COUNT(1), 0x01, // 1 byte (Reserved) + REPORT_SIZE(1), 0x08, + INPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position + REPORT_COUNT(1), 0x05, // 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana) + REPORT_SIZE(1), 0x01, + USAGE_PAGE(1), 0x08, // LEDs + USAGE_MINIMUM(1), 0x01, // Num Lock + USAGE_MAXIMUM(1), 0x05, // Kana + OUTPUT(1), 0x02, // Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile + REPORT_COUNT(1), 0x01, // 3 bits (Padding) + REPORT_SIZE(1), 0x03, + OUTPUT(1), 0x01, // Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile + REPORT_COUNT(1), 0x06, // 6 bytes (Keys) + REPORT_SIZE(1), 0x08, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x65, // 101 keys + USAGE_PAGE(1), 0x07, // Kbrd/Keypad + USAGE_MINIMUM(1), 0x00, + USAGE_MAXIMUM(1), 0x65, + INPUT(1), 0x00, // Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position + END_COLLECTION(0), +}; + +/// "keys pressed" report +static uint8_t inputReportData[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +/// "keys released" report +static const uint8_t emptyInputReportData[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +/// LEDs report +static uint8_t outputReportData[] = { 0 }; + + +/** + * @class KeyBuffer + * + * Buffer used to store keys to send. + * Internally, it is a CircularBuffer, with the added capability of putting the last char back in, + * when we're unable to send it (ie. when BLE stack is busy) + */ +class KeyBuffer: public CircularBuffer<uint8_t, KEYBUFFER_SIZE> +{ +public: + KeyBuffer() : + CircularBuffer(), + dataIsPending (false), + keyUpIsPending (false) + { + } + + /** + * Mark a character as pending. When a freshly popped character cannot be sent, because the + * underlying stack is busy, we set it as pending, and it will get popped in priority by @ref + * getPending once reports can be sent again. + * + * @param data The character to send in priority. The second keyUp report is implied. + */ + void setPending(uint8_t data) + { + MBED_ASSERT(dataIsPending == false); + + dataIsPending = true; + pendingData = data; + keyUpIsPending = true; + } + + /** + * Get pending char. Either from the high priority buffer (set with setPending), or from the + * circular buffer. + * + * @param data Filled with the pending data, when present + * @return true if data was filled + */ + bool getPending(uint8_t &data) + { + if (dataIsPending) { + dataIsPending = false; + data = pendingData; + return true; + } + + return pop(data); + } + + bool isSomethingPending(void) + { + return dataIsPending || keyUpIsPending || !empty(); + } + + /** + * Signal that a keyUp report is pending. This means that a character has successfully been + * sent, but the subsequent keyUp report failed. This report is of highest priority than the + * next character. + */ + void setKeyUpPending(void) + { + keyUpIsPending = true; + } + + /** + * Signal that no high-priority report is pending anymore, we can go back to the normal queue. + */ + void clearKeyUpPending(void) + { + keyUpIsPending = false; + } + + bool isKeyUpPending(void) + { + return keyUpIsPending; + } + +protected: + bool dataIsPending; + uint8_t pendingData; + bool keyUpIsPending; +}; + + +/** + * @class KeyboardService + * @brief HID-over-Gatt keyboard service + * + * Send keyboard reports over BLE. Users should rely on the high-level functions provided by the + * Stream API. Because we can't send batches of HID reports, we store pending keys in a circular + * buffer and rely on the report ticker to spread them over time. + * + * @code + * BLE ble; + * KeyboardService kbd(ble); + * + * void once_connected_and_paired_callback(void) + * { + * // Sequentially send keys 'Shift'+'h', 'e', 'l', 'l', 'o', '!' and <enter> + * kbd.printf("Hello!\n"); + * } + * @endcode + */ +class KeyboardService : public HIDServiceBase, public Stream +{ +public: + KeyboardService(BLE &_ble) : + HIDServiceBase(_ble, + KEYBOARD_REPORT_MAP, sizeof(KEYBOARD_REPORT_MAP), + inputReport = emptyInputReportData, + outputReport = outputReportData, + featureReport = NULL, + inputReportLength = sizeof(inputReportData), + outputReportLength = sizeof(outputReportData), + featureReportLength = 0, + reportTickerDelay = 24), + failedReports(0) + { + } + + virtual void onConnection(const Gap::ConnectionCallbackParams_t *params) + { + HIDServiceBase::onConnection(params); + + /* Drain buffer, in case we've been disconnected while transmitting */ + if (!reportTickerIsActive && keyBuffer.isSomethingPending()) + startReportTicker(); + } + + virtual void onDisconnection(const Gap::DisconnectionCallbackParams_t *params) + { + stopReportTicker(); + HIDServiceBase::onDisconnection(params); + } + + /** + * Send raw report. Should only be called by sendCallback. + */ + virtual ble_error_t send(const report_t report) + { + static unsigned int consecutiveFailures = 0; + ble_error_t ret = HIDServiceBase::send(report); + + /* + * Wait until a buffer is available (onDataSent) + * TODO. This won't work, because BUSY error is not only returned when we're short of + * notification buffers, but in other cases as well (e.g. when disconnected). We need to + * find a reliable way of knowing when we actually need to wait for onDataSent to be called. + if (ret == BLE_STACK_BUSY) + stopReportTicker(); + */ + if (ret == BLE_STACK_BUSY) + consecutiveFailures++; + else + consecutiveFailures = 0; + + if (consecutiveFailures > 20) { + /* + * We're not transmitting anything anymore. Might as well avoid overloading the + * system in case it can magically fix itself. Ticker will start again on next _putc + * call, or on next connection. + */ + stopReportTicker(); + consecutiveFailures = 0; + } + + return ret; + } + + /** + * Send an empty report, representing keyUp event + */ + ble_error_t keyUpCode(void) + { + return send(emptyInputReportData); + } + + /** + * Send a character, defined by a modifier (CTRL, SHIFT, ALT) and the key + * + * @param key Character to send (as defined in USB HID Usage Tables) + * @param modifier Optional modifiers (logical OR of enum MODIFIER_KEY) + * + * @returns BLE_ERROR_NONE on success, or an error code otherwise. + */ + ble_error_t keyDownCode(uint8_t key, uint8_t modifier) + { + inputReportData[0] = modifier; + inputReportData[2] = keymap[key].usage; + + return send(inputReportData); + } + + /** + * Push a key on the internal FIFO + * + * @param c ASCII character to send + * + * @returns 0 on success, or ENOMEM when the FIFO is full. + */ + virtual int _putc(int c) { + if (keyBuffer.full()) { + return ENOMEM; + } + + keyBuffer.push((unsigned char)c); + + if (!reportTickerIsActive) + startReportTicker(); + + return 0; + } + + uint8_t lockStatus() { + // TODO: implement numlock/capslock/scrolllock + return 0; + } + + /** + * Pop a key from the internal FIFO, and attempt to send it over BLE + * + * keyUp reports should theoretically be sent after every keyDown, but we optimize the + * throughput by only sending one when strictly necessary: + * - when we need to repeat the same key + * - when there is no more key to report + * + * In case of error, put the key event back in the buffer, and retry on next tick. + */ + virtual void sendCallback(void) { + ble_error_t ret; + uint8_t c; + static uint8_t previousKey = 0; + + if (keyBuffer.isSomethingPending() && !keyBuffer.isKeyUpPending()) { + bool hasData = keyBuffer.getPending(c); + + /* + * If something is pending and is not a keyUp, getPending *must* return something. The + * following is only a sanity check. + */ + MBED_ASSERT(hasData); + + if (!hasData) + return; + + if (previousKey == c) { + /* + * When the same key needs to be sent twice, we need to interleave a keyUp report, + * or else the OS won't be able to differentiate them. + * Push the key back into the buffer, and continue to keyUpCode. + */ + keyBuffer.setPending(c); + } else { + ret = keyDownCode(c, keymap[c].modifier); + if (ret) { + keyBuffer.setPending(c); + failedReports++; + } else { + previousKey = c; + } + + return; + } + } + + ret = keyUpCode(); + if (ret) { + keyBuffer.setKeyUpPending(); + failedReports++; + } else { + keyBuffer.clearKeyUpPending(); + previousKey = 0; + + /* Idle when there is nothing more to send */ + if (!keyBuffer.isSomethingPending()) + stopReportTicker(); + } + } + + /** + * Restart report ticker if it was disabled, after too many consecutive failures. + * + * This is called by the BLE stack. + * + * @param count Number of reports (notifications) sent + */ + virtual void onDataSent(unsigned count) + { + if (!reportTickerIsActive && keyBuffer.isSomethingPending()) + startReportTicker(); + } + + unsigned long failedReports; + +protected: + virtual int _getc() { + return 0; + } + +protected: + KeyBuffer keyBuffer; + + //GattCharacteristic boot_keyboard_input_report; + //GattCharacteristic boot_keyboard_output_report; +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BLE_HID/Keyboard_types.h Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,403 @@ +/* Copyright (c) 2015 mbed.org, MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Note: this file was pulled from different parts of the USBHID library, in mbed SDK + */ + +#ifndef KEYBOARD_DEFS_H +#define KEYBOARD_DEFS_H + +#define REPORT_ID_KEYBOARD 1 +#define REPORT_ID_VOLUME 3 + +/* Modifiers */ +enum MODIFIER_KEY { + KEY_CTRL = 1, + KEY_SHIFT = 2, + KEY_ALT = 4, +}; + + +enum MEDIA_KEY { + KEY_NEXT_TRACK, /*!< next Track Button */ + KEY_PREVIOUS_TRACK, /*!< Previous track Button */ + KEY_STOP, /*!< Stop Button */ + KEY_PLAY_PAUSE, /*!< Play/Pause Button */ + KEY_MUTE, /*!< Mute Button */ + KEY_VOLUME_UP, /*!< Volume Up Button */ + KEY_VOLUME_DOWN, /*!< Volume Down Button */ +}; + +enum FUNCTION_KEY { + KEY_F1 = 128, /* F1 key */ + KEY_F2, /* F2 key */ + KEY_F3, /* F3 key */ + KEY_F4, /* F4 key */ + KEY_F5, /* F5 key */ + KEY_F6, /* F6 key */ + KEY_F7, /* F7 key */ + KEY_F8, /* F8 key */ + KEY_F9, /* F9 key */ + KEY_F10, /* F10 key */ + KEY_F11, /* F11 key */ + KEY_F12, /* F12 key */ + + KEY_PRINT_SCREEN, /* Print Screen key */ + KEY_SCROLL_LOCK, /* Scroll lock */ + KEY_CAPS_LOCK, /* caps lock */ + KEY_NUM_LOCK, /* num lock */ + KEY_INSERT, /* Insert key */ + KEY_HOME, /* Home key */ + KEY_PAGE_UP, /* Page Up key */ + KEY_PAGE_DOWN, /* Page Down key */ + + RIGHT_ARROW, /* Right arrow */ + LEFT_ARROW, /* Left arrow */ + DOWN_ARROW, /* Down arrow */ + UP_ARROW, /* Up arrow */ +}; + +typedef struct { + unsigned char usage; + unsigned char modifier; +} KEYMAP; + +#ifdef US_KEYBOARD +/* US keyboard (as HID standard) */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x34, KEY_SHIFT}, /* " */ + {0x20, KEY_SHIFT}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x1f, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x31, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x31, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x35, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; + +#else +/* UK keyboard */ +#define KEYMAP_SIZE (152) +const KEYMAP keymap[KEYMAP_SIZE] = { + {0, 0}, /* NUL */ + {0, 0}, /* SOH */ + {0, 0}, /* STX */ + {0, 0}, /* ETX */ + {0, 0}, /* EOT */ + {0, 0}, /* ENQ */ + {0, 0}, /* ACK */ + {0, 0}, /* BEL */ + {0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */ + {0x2b, 0}, /* TAB */ /* Keyboard Tab */ + {0x28, 0}, /* LF */ /* Keyboard Return (Enter) */ + {0, 0}, /* VT */ + {0, 0}, /* FF */ + {0, 0}, /* CR */ + {0, 0}, /* SO */ + {0, 0}, /* SI */ + {0, 0}, /* DEL */ + {0, 0}, /* DC1 */ + {0, 0}, /* DC2 */ + {0, 0}, /* DC3 */ + {0, 0}, /* DC4 */ + {0, 0}, /* NAK */ + {0, 0}, /* SYN */ + {0, 0}, /* ETB */ + {0, 0}, /* CAN */ + {0, 0}, /* EM */ + {0, 0}, /* SUB */ + {0, 0}, /* ESC */ + {0, 0}, /* FS */ + {0, 0}, /* GS */ + {0, 0}, /* RS */ + {0, 0}, /* US */ + {0x2c, 0}, /* */ + {0x1e, KEY_SHIFT}, /* ! */ + {0x1f, KEY_SHIFT}, /* " */ + {0x32, 0}, /* # */ + {0x21, KEY_SHIFT}, /* $ */ + {0x22, KEY_SHIFT}, /* % */ + {0x24, KEY_SHIFT}, /* & */ + {0x34, 0}, /* ' */ + {0x26, KEY_SHIFT}, /* ( */ + {0x27, KEY_SHIFT}, /* ) */ + {0x25, KEY_SHIFT}, /* * */ + {0x2e, KEY_SHIFT}, /* + */ + {0x36, 0}, /* , */ + {0x2d, 0}, /* - */ + {0x37, 0}, /* . */ + {0x38, 0}, /* / */ + {0x27, 0}, /* 0 */ + {0x1e, 0}, /* 1 */ + {0x1f, 0}, /* 2 */ + {0x20, 0}, /* 3 */ + {0x21, 0}, /* 4 */ + {0x22, 0}, /* 5 */ + {0x23, 0}, /* 6 */ + {0x24, 0}, /* 7 */ + {0x25, 0}, /* 8 */ + {0x26, 0}, /* 9 */ + {0x33, KEY_SHIFT}, /* : */ + {0x33, 0}, /* ; */ + {0x36, KEY_SHIFT}, /* < */ + {0x2e, 0}, /* = */ + {0x37, KEY_SHIFT}, /* > */ + {0x38, KEY_SHIFT}, /* ? */ + {0x34, KEY_SHIFT}, /* @ */ + {0x04, KEY_SHIFT}, /* A */ + {0x05, KEY_SHIFT}, /* B */ + {0x06, KEY_SHIFT}, /* C */ + {0x07, KEY_SHIFT}, /* D */ + {0x08, KEY_SHIFT}, /* E */ + {0x09, KEY_SHIFT}, /* F */ + {0x0a, KEY_SHIFT}, /* G */ + {0x0b, KEY_SHIFT}, /* H */ + {0x0c, KEY_SHIFT}, /* I */ + {0x0d, KEY_SHIFT}, /* J */ + {0x0e, KEY_SHIFT}, /* K */ + {0x0f, KEY_SHIFT}, /* L */ + {0x10, KEY_SHIFT}, /* M */ + {0x11, KEY_SHIFT}, /* N */ + {0x12, KEY_SHIFT}, /* O */ + {0x13, KEY_SHIFT}, /* P */ + {0x14, KEY_SHIFT}, /* Q */ + {0x15, KEY_SHIFT}, /* R */ + {0x16, KEY_SHIFT}, /* S */ + {0x17, KEY_SHIFT}, /* T */ + {0x18, KEY_SHIFT}, /* U */ + {0x19, KEY_SHIFT}, /* V */ + {0x1a, KEY_SHIFT}, /* W */ + {0x1b, KEY_SHIFT}, /* X */ + {0x1c, KEY_SHIFT}, /* Y */ + {0x1d, KEY_SHIFT}, /* Z */ + {0x2f, 0}, /* [ */ + {0x64, 0}, /* \ */ + {0x30, 0}, /* ] */ + {0x23, KEY_SHIFT}, /* ^ */ + {0x2d, KEY_SHIFT}, /* _ */ + {0x35, 0}, /* ` */ + {0x04, 0}, /* a */ + {0x05, 0}, /* b */ + {0x06, 0}, /* c */ + {0x07, 0}, /* d */ + {0x08, 0}, /* e */ + {0x09, 0}, /* f */ + {0x0a, 0}, /* g */ + {0x0b, 0}, /* h */ + {0x0c, 0}, /* i */ + {0x0d, 0}, /* j */ + {0x0e, 0}, /* k */ + {0x0f, 0}, /* l */ + {0x10, 0}, /* m */ + {0x11, 0}, /* n */ + {0x12, 0}, /* o */ + {0x13, 0}, /* p */ + {0x14, 0}, /* q */ + {0x15, 0}, /* r */ + {0x16, 0}, /* s */ + {0x17, 0}, /* t */ + {0x18, 0}, /* u */ + {0x19, 0}, /* v */ + {0x1a, 0}, /* w */ + {0x1b, 0}, /* x */ + {0x1c, 0}, /* y */ + {0x1d, 0}, /* z */ + {0x2f, KEY_SHIFT}, /* { */ + {0x64, KEY_SHIFT}, /* | */ + {0x30, KEY_SHIFT}, /* } */ + {0x32, KEY_SHIFT}, /* ~ */ + {0,0}, /* DEL */ + + {0x3a, 0}, /* F1 */ + {0x3b, 0}, /* F2 */ + {0x3c, 0}, /* F3 */ + {0x3d, 0}, /* F4 */ + {0x3e, 0}, /* F5 */ + {0x3f, 0}, /* F6 */ + {0x40, 0}, /* F7 */ + {0x41, 0}, /* F8 */ + {0x42, 0}, /* F9 */ + {0x43, 0}, /* F10 */ + {0x44, 0}, /* F11 */ + {0x45, 0}, /* F12 */ + + {0x46, 0}, /* PRINT_SCREEN */ + {0x47, 0}, /* SCROLL_LOCK */ + {0x39, 0}, /* CAPS_LOCK */ + {0x53, 0}, /* NUM_LOCK */ + {0x49, 0}, /* INSERT */ + {0x4a, 0}, /* HOME */ + {0x4b, 0}, /* PAGE_UP */ + {0x4e, 0}, /* PAGE_DOWN */ + + {0x4f, 0}, /* RIGHT_ARROW */ + {0x50, 0}, /* LEFT_ARROW */ + {0x51, 0}, /* DOWN_ARROW */ + {0x52, 0}, /* UP_ARROW */ +}; +#endif + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BLE_HID/MouseService.h Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,219 @@ +/* mbed Microcontroller Library + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbed.h" + +#include "HIDServiceBase.h" + +enum ButtonState +{ + BUTTON_UP, + BUTTON_DOWN +}; + +enum MouseButton +{ + MOUSE_BUTTON_LEFT = 0x1, + MOUSE_BUTTON_RIGHT = 0x2, + MOUSE_BUTTON_MIDDLE = 0x4, +}; + +/** + * Report descriptor for a standard 3 buttons + wheel mouse with relative X/Y + * moves + */ +report_map_t MOUSE_REPORT_MAP = { + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x02, // Mouse + COLLECTION(1), 0x01, // Application + USAGE(1), 0x01, // Pointer + COLLECTION(1), 0x00, // Physical + USAGE_PAGE(1), 0x09, // Buttons + USAGE_MINIMUM(1), 0x01, + USAGE_MAXIMUM(1), 0x03, + LOGICAL_MINIMUM(1), 0x00, + LOGICAL_MAXIMUM(1), 0x01, + REPORT_COUNT(1), 0x03, // 3 bits (Buttons) + REPORT_SIZE(1), 0x01, + INPUT(1), 0x02, // Data, Variable, Absolute + REPORT_COUNT(1), 0x01, // 5 bits (Padding) + REPORT_SIZE(1), 0x05, + INPUT(1), 0x01, // Constant + USAGE_PAGE(1), 0x01, // Generic Desktop + USAGE(1), 0x30, // X + USAGE(1), 0x31, // Y + USAGE(1), 0x38, // Wheel + LOGICAL_MINIMUM(1), 0x81, // -127 + LOGICAL_MAXIMUM(1), 0x7f, // 127 + REPORT_SIZE(1), 0x08, // Three bytes + REPORT_COUNT(1), 0x03, + INPUT(1), 0x06, // Data, Variable, Relative + END_COLLECTION(0), + END_COLLECTION(0), +}; + +uint8_t report[] = { 0, 0, 0, 0 }; + +/** + * @class MouseService + * @brief HID-over-Gatt mouse service. + * + * Send mouse moves and button informations over BLE. + * + * @code + * BLE ble; + * MouseService mouse(ble); + * + * Timeout timeout; + * + * void stop_mouse_move(void) + * { + * // Set mouse state to immobile + * mouse.setButton(MOUSE_BUTTON_LEFT, MOUSE_UP); + * mouse.setSpeed(0, 0, 0); + * } + * + * void start_mouse_move(void) + * { + * // Move left with a left button down. If the focus is on a drawing + * // software, for instance, this should draw a line. + * mouse.setButton(MOUSE_BUTTON_LEFT, MOUSE_DOWN); + * mouse.setSpeed(1, 0, 0); + * + * timeout.attach(stop_mouse_move, 0.2); + * } + * @endcode + */ +class MouseService: public HIDServiceBase +{ +public: + MouseService(BLE &_ble) : + HIDServiceBase(_ble, + MOUSE_REPORT_MAP, sizeof(MOUSE_REPORT_MAP), + inputReport = report, + outputReport = NULL, + featureReport = NULL, + inputReportLength = sizeof(inputReport), + outputReportLength = 0, + featureReportLength = 0, + reportTickerDelay = 20), + buttonsState (0), + failedReports (0) + { + speed[0] = 0; + speed[1] = 0; + speed[2] = 0; + + startReportTicker(); + } + + virtual void onConnection(const Gap::ConnectionCallbackParams_t *params) + { + HIDServiceBase::onConnection(params); + startReportTicker(); + } + + virtual void onDisconnection(const Gap::DisconnectionCallbackParams_t *params) + { + stopReportTicker(); + HIDServiceBase::onDisconnection(params); + } + + /** + * Set X, Y, Z speed of the mouse. Parameters are sticky and will be + * transmitted on every tick. Users should therefore reset them to 0 when + * the device is immobile. + * + * @param x Speed on hoizontal axis + * @param y Speed on vertical axis + * @param wheel Scroll speed + * + * @returns A status code + * + * @note Directions depend on the operating system's configuration. It is + * customary to increase values on the X axis from left to right, and on the + * Y axis from top to bottom. + * Wheel is less standard, although positive values will usually scroll up. + */ + int setSpeed(int8_t x, int8_t y, int8_t wheel) + { + speed[0] = x; + speed[1] = y; + speed[2] = wheel; + + startReportTicker(); + + return 0; + } + + /** + * Toggle the state of one button + * + * @returns A status code + */ + int setButton(MouseButton button, ButtonState state) + { + if (state == BUTTON_UP) + buttonsState &= ~(button); + else + buttonsState |= button; + + startReportTicker(); + + return 0; + } + + /** + * Called by the report ticker + */ + virtual void sendCallback(void) { + uint8_t buttons = buttonsState & 0x7; + + if (!connected) + return; + + bool can_sleep = (report[0] == 0 + && report[1] == 0 + && report[2] == 0 + && report[3] == 0 + && report[0] == buttons + && report[1] == speed[0] + && report[2] == speed[1] + && report[3] == speed[2]); + + if (can_sleep) { + /* TODO: find out why there always is two more calls to sendCallback after this + * stopReportTicker(). */ + stopReportTicker(); + return; + } + + report[0] = buttons; + report[1] = speed[0]; + report[2] = speed[1]; + report[3] = speed[2]; + + if (send(report)) + failedReports++; + } + +protected: + uint8_t buttonsState; + uint8_t speed[3]; + +public: + uint32_t failedReports; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BLE_HID/USBHID_Types.h Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,91 @@ +/* Copyright (c) 2010-2011 mbed.org, MIT License +* +* Permission is hereby granted, free of charge, to any person obtaining a copy of this software +* and associated documentation files (the "Software"), to deal in the Software without +* restriction, including without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all copies or +* substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING +* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef USBCLASS_HID_TYPES +#define USBCLASS_HID_TYPES + +#include <stdint.h> + +/* */ +#define HID_VERSION_1_11 (0x0111) + +/* HID Class */ +#define HID_CLASS (3) +#define HID_SUBCLASS_NONE (0) +#define HID_PROTOCOL_NONE (0) + +/* Descriptors */ +#define HID_DESCRIPTOR (33) +#define HID_DESCRIPTOR_LENGTH (0x09) +#define REPORT_DESCRIPTOR (34) + +/* Class requests */ +#define GET_REPORT (0x1) +#define GET_IDLE (0x2) +#define SET_REPORT (0x9) +#define SET_IDLE (0xa) + +/* HID Class Report Descriptor */ +/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */ +/* of data as per HID Class standard */ + +/* Main items */ +#define INPUT(size) (0x80 | size) +#define OUTPUT(size) (0x90 | size) +#define FEATURE(size) (0xb0 | size) +#define COLLECTION(size) (0xa0 | size) +#define END_COLLECTION(size) (0xc0 | size) + +/* Global items */ +#define USAGE_PAGE(size) (0x04 | size) +#define LOGICAL_MINIMUM(size) (0x14 | size) +#define LOGICAL_MAXIMUM(size) (0x24 | size) +#define PHYSICAL_MINIMUM(size) (0x34 | size) +#define PHYSICAL_MAXIMUM(size) (0x44 | size) +#define UNIT_EXPONENT(size) (0x54 | size) +#define UNIT(size) (0x64 | size) +#define REPORT_SIZE(size) (0x74 | size) +#define REPORT_ID(size) (0x84 | size) +#define REPORT_COUNT(size) (0x94 | size) +#define PUSH(size) (0xa4 | size) +#define POP(size) (0xb4 | size) + +/* Local items */ +#define USAGE(size) (0x08 | size) +#define USAGE_MINIMUM(size) (0x18 | size) +#define USAGE_MAXIMUM(size) (0x28 | size) +#define DESIGNATOR_INDEX(size) (0x38 | size) +#define DESIGNATOR_MINIMUM(size) (0x48 | size) +#define DESIGNATOR_MAXIMUM(size) (0x58 | size) +#define STRING_INDEX(size) (0x78 | size) +#define STRING_MINIMUM(size) (0x88 | size) +#define STRING_MAXIMUM(size) (0x98 | size) +#define DELIMITER(size) (0xa8 | size) + +/* HID Report */ +/* Where report IDs are used the first byte of 'data' will be the */ +/* report ID and 'length' will include this report ID byte. */ + +#define MAX_HID_REPORT_SIZE (64) + +typedef struct { + uint32_t length; + uint8_t data[MAX_HID_REPORT_SIZE]; +} HID_REPORT; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples_common.cpp Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,76 @@ +/* mbed Microcontroller Library + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ble/services/BatteryService.h" +#include "ble/services/DeviceInformationService.h" + +#include "examples_common.h" + +static void passkeyDisplayCallback(Gap::Handle_t handle, const SecurityManager::Passkey_t passkey) +{ + printf("Input passKey: "); + for (unsigned i = 0; i < Gap::ADDR_LEN; i++) { + printf("%c", passkey[i]); + } + printf("\r\n"); +} + +static void securitySetupCompletedCallback(Gap::Handle_t handle, SecurityManager::SecurityCompletionStatus_t status) +{ + if (status == SecurityManager::SEC_STATUS_SUCCESS) { + printf("Security success %d\r\n", status); + } else { + printf("Security failed %d\r\n", status); + } +} + +static void securitySetupInitiatedCallback(Gap::Handle_t, bool allowBonding, bool requireMITM, SecurityManager::SecurityIOCapabilities_t iocaps) +{ + printf("Security setup initiated\r\n"); +} + +void initializeSecurity(BLE &ble) +{ + bool enableBonding = true; + bool requireMITM = HID_SECURITY_REQUIRE_MITM; + + ble.securityManager().onSecuritySetupInitiated(securitySetupInitiatedCallback); + ble.securityManager().onPasskeyDisplay(passkeyDisplayCallback); + ble.securityManager().onSecuritySetupCompleted(securitySetupCompletedCallback); + + ble.securityManager().init(enableBonding, requireMITM, HID_SECURITY_IOCAPS); +} + +void initializeHOGP(BLE &ble) +{ + static const uint16_t uuid16_list[] = {GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE, + GattService::UUID_DEVICE_INFORMATION_SERVICE, + GattService::UUID_BATTERY_SERVICE}; + + DeviceInformationService deviceInfo(ble, "ARM", "m1", "abc", "def", "ghi", "jkl"); + + BatteryService batteryInfo(ble, 80); + + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | + GapAdvertisingData::LE_GENERAL_DISCOVERABLE); + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, + (uint8_t *)uuid16_list, sizeof(uuid16_list)); + + // see 5.1.2: HID over GATT Specification (pg. 25) + ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); + // 30ms to 50ms is recommended (5.1.2) + ble.gap().setAdvertisingInterval(50); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples_common.h Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,69 @@ +/* mbed Microcontroller Library + * Copyright (c) 2015 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HID_EXAMPLES_COMMON_H_ +#define HID_EXAMPLES_COMMON_H_ + +/** + * Functions and configuration common to all HID demos + */ + +#include "ble/BLE.h" + +#include "HIDServiceBase.h" + +/** + * IO capabilities of the device. During development, you most likely want "JustWorks", which means + * no IO capabilities. + * It is also possible to use IO_CAPS_DISPLAY_ONLY to generate and show a pincode on the serial + * output. + */ +#ifndef HID_SECURITY_IOCAPS +#define HID_SECURITY_IOCAPS (SecurityManager::IO_CAPS_NONE) +#endif + +/** + * Security level. MITM disabled forces "Just Works". If you require MITM, HID_SECURITY_IOCAPS must + * be at least IO_CAPS_DISPLAY_ONLY. + */ +#ifndef HID_SECURITY_REQUIRE_MITM +#define HID_SECURITY_REQUIRE_MITM false +#endif + +/** + * Disable debug messages by setting NDEBUG + */ +#ifndef NDEBUG +#define HID_DEBUG(...) printf(__VA_ARGS__) +#else +#define HID_DEBUG(...) +#endif + +/** + * Initialize security manager: set callback functions and required security level + */ +void initializeSecurity(BLE &ble); + +/** + * - Initialize auxiliary services required by the HID-over-GATT Profile. + * - Initialize common Gap advertisement. + * + * Demos only have to set a custom device name and appearance, and their HID + * service. + */ +void initializeHOGP(BLE &ble); + +#endif /* !BLE_HID_COMMON_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,222 @@ +/* mbed Microcontroller Library + * Copyright (c) 2015 ARM Limited + * Modifications copyright (C) 2017 Ozan ALTINKAYA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbed.h" + +#include "ble/BLE.h" +#include "KeyboardService.h" +#include "GamepadService.h" +#include "examples_common.h" + +/** + * This program implements a complete HID-over-Gatt Profile: + * - HID is provided by KeyboardService + * - Battery Service + * - Device Information Service + * + * Complete strings can be sent over BLE using printf. Please note, however, than a 12char string + * will take about 500ms to transmit, principally because of the limited notification rate in BLE. + * KeyboardService uses a circular buffer to store the strings to send, and calls to putc will fail + * once this buffer is full. This will result in partial strings being sent to the client. + */ + +DigitalOut waiting_led(P0_2); +DigitalOut connected_led(P0_19); + +DigitalIn axisXp(P0_19); +DigitalIn axisXn(P0_20); +DigitalIn axisYp(P0_18); +DigitalIn axisYn(P0_17); + +DigitalIn buttonA(P0_22); +DigitalIn buttonB(P0_24); +DigitalIn buttonC(P0_5); +DigitalIn buttonD(P0_28); +DigitalIn buttonE(P0_29); +DigitalIn buttonF(P0_13); +DigitalIn buttonG(P0_25); +DigitalIn buttonH(P0_23); + +//Serial pc(USBTX, USBRX); // tx, rx + +BLE ble; +GamepadService *kbdServicePtr; + + +static const char DEVICE_NAME[] = "ArcadeGamepad"; +static const char SHORT_DEVICE_NAME[] = "gpd1"; + +static void onDisconnect(const Gap::DisconnectionCallbackParams_t *params) +{ +// pc.printf("disconnected\r\n"); + connected_led = 0; + + ble.gap().startAdvertising(); // restart advertising +} + +static void onConnect(const Gap::ConnectionCallbackParams_t *params) +{ +// pc.printf("connected\r\n"); + waiting_led = false; +} + +static void waiting() { + if (!kbdServicePtr->isConnected()) + waiting_led = !waiting_led; + else + waiting_led = 1; +} + + +void update(){ + if(buttonA){ + kbdServicePtr->setButton(GAMEPAD_BUTTON_A, BUTTON_DOWN); + } + + else{ + kbdServicePtr->setButton(GAMEPAD_BUTTON_A, BUTTON_UP); + } + + if(buttonB){ + kbdServicePtr->setButton(GAMEPAD_BUTTON_B, BUTTON_DOWN); + } + + else{ + kbdServicePtr->setButton(GAMEPAD_BUTTON_B, BUTTON_UP); + } + + if(buttonC){ + kbdServicePtr->setButton(GAMEPAD_BUTTON_C, BUTTON_DOWN); + } + + else{ + kbdServicePtr->setButton(GAMEPAD_BUTTON_C, BUTTON_UP); + } + + if(buttonD){ + kbdServicePtr->setButton(GAMEPAD_BUTTON_D, BUTTON_DOWN); + } + + else{ + kbdServicePtr->setButton(GAMEPAD_BUTTON_D, BUTTON_UP); + } + + if(buttonE){ + kbdServicePtr->setButton(GAMEPAD_BUTTON_E, BUTTON_DOWN); + } + + else{ + kbdServicePtr->setButton(GAMEPAD_BUTTON_E, BUTTON_UP); + } + + if(buttonF){ + kbdServicePtr->setButton(GAMEPAD_BUTTON_F, BUTTON_DOWN); + } + + else{ + kbdServicePtr->setButton(GAMEPAD_BUTTON_F, BUTTON_UP); + } + + if(buttonG){ + kbdServicePtr->setButton(GAMEPAD_BUTTON_G, BUTTON_DOWN); + } + + else{ + kbdServicePtr->setButton(GAMEPAD_BUTTON_G, BUTTON_UP); + } + + if(buttonH){ + kbdServicePtr->setButton(GAMEPAD_BUTTON_H, BUTTON_DOWN); + } + + else{ + kbdServicePtr->setButton(GAMEPAD_BUTTON_H, BUTTON_UP); + } + + + + + // AXIS CHECK + + + + + if((axisXp) || (axisXn)){ + if(axisXp) + kbdServicePtr->setXAxis(GAMEPAD_BUTTON_AXIS_Xp, BUTTON_DOWN); + if(axisXn) + kbdServicePtr->setXAxis(GAMEPAD_BUTTON_AXIS_Xn, BUTTON_DOWN); + } + + else{ + kbdServicePtr->setXAxis(GAMEPAD_BUTTON_AXIS_Xp, BUTTON_UP); + } + + if((axisYp)||(axisYn)){ + if(axisYp) + kbdServicePtr->setYAxis(GAMEPAD_BUTTON_AXIS_Yp, BUTTON_DOWN); + if(axisYn) + kbdServicePtr->setYAxis(GAMEPAD_BUTTON_AXIS_Yn, BUTTON_DOWN); + } + + else{ + kbdServicePtr->setYAxis(GAMEPAD_BUTTON_AXIS_Yp, BUTTON_UP); + } +} + + +int main() +{ + Ticker heartbeat; + Ticker updater; + +// pc.printf("initialising ticker\r\n"); + + heartbeat.attach(waiting, 1); + updater.attach(update, 0.006); + +// pc.printf("initialising ble\r\n"); + ble.init(); + + ble.gap().onDisconnection(onDisconnect); + ble.gap().onConnection(onConnect); + + initializeSecurity(ble); + +// pc.printf("adding hid service\r\n"); + GamepadService kbdService(ble); + kbdServicePtr = &kbdService; + +// pc.printf("adding device info and battery service\r\n"); + initializeHOGP(ble); + +// pc.printf("setting up gap\r\n"); + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GAMEPAD); + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, + (const uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); + ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME, + (const uint8_t *)SHORT_DEVICE_NAME, sizeof(SHORT_DEVICE_NAME)); + + ble.gap().setDeviceName((const uint8_t *)DEVICE_NAME); + +// pc.printf("advertising\r\n"); + ble.gap().startAdvertising(); + + while (true) { + ble.waitForEvent(); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,1 @@ +https://os.mbed.com/users/mbed_official/code/mbed/builds/65be27845400 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nRF51822.lib Tue Jul 20 09:23:54 2021 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/teams/Nordic-Semiconductor/code/nRF51822/#c90ae1400bf2