From 1e512cc11566f32b1e3ae43a0b61d94b9ac7cf23 Mon Sep 17 00:00:00 2001 From: T-vK Date: Wed, 7 Aug 2019 20:00:00 +0000 Subject: [PATCH] Initial commit --- BleConnectionStatus.cpp | 18 +++ BleConnectionStatus.h | 22 +++ BleKeyboard.cpp | 170 +++++++++++++++++++++ BleKeyboard.h | 46 ++++++ KeyboardOutputCallbacks.cpp | 18 +++ KeyboardOutputCallbacks.h | 18 +++ README.md | 77 ++++++++++ examples/SendKeyStrokes/SendKeyStrokes.ino | 46 ++++++ keywords.txt | 24 +++ library.properties | 9 ++ 10 files changed, 448 insertions(+) create mode 100644 BleConnectionStatus.cpp create mode 100644 BleConnectionStatus.h create mode 100644 BleKeyboard.cpp create mode 100644 BleKeyboard.h create mode 100644 KeyboardOutputCallbacks.cpp create mode 100644 KeyboardOutputCallbacks.h create mode 100644 README.md create mode 100644 examples/SendKeyStrokes/SendKeyStrokes.ino create mode 100644 keywords.txt create mode 100644 library.properties diff --git a/BleConnectionStatus.cpp b/BleConnectionStatus.cpp new file mode 100644 index 0000000..f7468f2 --- /dev/null +++ b/BleConnectionStatus.cpp @@ -0,0 +1,18 @@ +#include "BleConnectionStatus.h" + +BleConnectionStatus::BleConnectionStatus(void) { +} + +void BleConnectionStatus::onConnect(BLEServer* pServer) +{ + this->connected = true; + BLE2902* desc = (BLE2902*)this->inputKeyboard->getDescriptorByUUID(BLEUUID((uint16_t)0x2902)); + desc->setNotifications(true); +} + +void BleConnectionStatus::onDisconnect(BLEServer* pServer) +{ + this->connected = false; + BLE2902* desc = (BLE2902*)this->inputKeyboard->getDescriptorByUUID(BLEUUID((uint16_t)0x2902)); + desc->setNotifications(false); +} diff --git a/BleConnectionStatus.h b/BleConnectionStatus.h new file mode 100644 index 0000000..fbd59b6 --- /dev/null +++ b/BleConnectionStatus.h @@ -0,0 +1,22 @@ +#ifndef ESP32_BLE_CONNECTION_STATUS_H +#define ESP32_BLE_CONNECTION_STATUS_H +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include +#include "BLE2902.h" +#include "BLECharacteristic.h" + +class BleConnectionStatus : public BLEServerCallbacks +{ +public: + BleConnectionStatus(void); + bool connected = false; + void onConnect(BLEServer* pServer); + void onDisconnect(BLEServer* pServer); + BLECharacteristic* inputKeyboard; + BLECharacteristic* outputKeyboard; +}; + +#endif // CONFIG_BT_ENABLED +#endif // ESP32_BLE_CONNECTION_STATUS_H diff --git a/BleKeyboard.cpp b/BleKeyboard.cpp new file mode 100644 index 0000000..d35bc40 --- /dev/null +++ b/BleKeyboard.cpp @@ -0,0 +1,170 @@ +#include +#include +#include +#include "BLE2902.h" +#include "BLEHIDDevice.h" +#include "HIDTypes.h" +#include "HIDKeyboardTypes.h" +#include +#include "sdkconfig.h" + +#include "BleConnectionStatus.h" +#include "KeyboardOutputCallbacks.h" +#include "BleKeyboard.h" + +#if defined(CONFIG_ARDUHAL_ESP_LOG) + #include "esp32-hal-log.h" + #define LOG_TAG "" +#else + #include "esp_log.h" + static const char* LOG_TAG = "BLEDevice"; +#endif + +static const uint8_t _hidReportDescriptor[] = { + USAGE_PAGE(1), 0x01, // USAGE_PAGE (Generic Desktop Ctrls) + USAGE(1), 0x06, // USAGE (Keyboard) + COLLECTION(1), 0x01, // COLLECTION (Application) + REPORT_ID(1), 0x01, // REPORT_ID (2) + USAGE_PAGE(1), 0x07, // USAGE_PAGE (Kbrd/Keypad) + USAGE_MINIMUM(1), 0xE0, // USAGE_MINIMUM (0xE0) + USAGE_MAXIMUM(1), 0xE7, // USAGE_MAXIMUM (0xE7) + LOGICAL_MINIMUM(1), 0x00, // LOGICAL_MINIMUM (0) + LOGICAL_MAXIMUM(1), 0x01, // Logical Maximum (1) + REPORT_SIZE(1), 0x01, // REPORT_SIZE (1) + REPORT_COUNT(1), 0x08, // REPORT_COUNT (8) + HIDINPUT(1), 0x02, // INPUT (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + REPORT_COUNT(1), 0x01, // REPORT_COUNT (1) ; 1 byte (Reserved) + REPORT_SIZE(1), 0x08, // REPORT_SIZE (8) + HIDINPUT(1), 0x01, // INPUT (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + REPORT_COUNT(1), 0x05, // REPORT_COUNT (5) ; 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana) + REPORT_SIZE(1), 0x01, // REPORT_SIZE (1) + USAGE_PAGE(1), 0x08, // USAGE_PAGE (LEDs) + USAGE_MINIMUM(1), 0x01, // USAGE_MINIMUM (0x01) ; Num Lock + USAGE_MAXIMUM(1), 0x05, // USAGE_MAXIMUM (0x05) ; Kana + HIDOUTPUT(1), 0x02, // OUTPUT (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + REPORT_COUNT(1), 0x01, // REPORT_COUNT (1) ; 3 bits (Padding) + REPORT_SIZE(1), 0x03, // REPORT_SIZE (3) + HIDOUTPUT(1), 0x01, // OUTPUT (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + REPORT_COUNT(1), 0x06, // REPORT_COUNT (6) ; 6 bytes (Keys) + REPORT_SIZE(1), 0x08, // REPORT_SIZE(8) + LOGICAL_MINIMUM(1), 0x00, // LOGICAL_MINIMUM(0) + LOGICAL_MAXIMUM(1), 0x65, // LOGICAL_MAXIMUM(0x65) ; 101 keys + USAGE_PAGE(1), 0x07, // USAGE_PAGE (Kbrd/Keypad) + USAGE_MINIMUM(1), 0x00, // USAGE_MINIMUM (0) + USAGE_MAXIMUM(1), 0x65, // USAGE_MAXIMUM (0x65) + HIDINPUT(1), 0x00, // INPUT (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + END_COLLECTION(0), // END_COLLECTION + USAGE_PAGE(1), 0x0C, // USAGE_PAGE (Consumer) + USAGE(1), 0x01, // USAGE (Consumer Control) + COLLECTION(1), 0x01, // COLLECTION (Application) + REPORT_ID(2), 0x02, // REPORT_ID (2) + USAGE_PAGE(1), 0x0C, // USAGE_PAGE (Consumer) + LOGICAL_MINIMUM(1), 0x00, // LOGICAL_MINIMUM (0) + LOGICAL_MAXIMUM(1), 0x01, // LOGICAL_MAXIMUM (1) + REPORT_SIZE(1), 0x01, // REPORT_SIZE (1) + REPORT_COUNT(1), 0x07, // REPORT_COUNT (7) + USAGE(1), 0xB5, // USAGE (Scan Next Track) + USAGE(1), 0xB6, // USAGE (Scan Previous Track) + USAGE(1), 0xB7, // USAGE (Stop) + USAGE(1), 0xB8, // USAGE (Eject) + USAGE(1), 0xCD, // USAGE (Play/Pause) + USAGE(1), 0xE2, // USAGE (Mute) + USAGE(1), 0xE9, // USAGE (Volume Increment) + USAGE(1), 0xEA, // USAGE (Volume Decrement) + HIDINPUT(1), 0x02, // INPUT (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + END_COLLECTION(0) // END_COLLECTION +}; + +BleKeyboard::BleKeyboard(std::string deviceName, std::string deviceManufacturer, uint8_t batteryLevel) : _buttons(0) +{ + this->deviceName = deviceName; + this->deviceManufacturer = deviceManufacturer; + this->batteryLevel = batteryLevel; + this->connectionStatus = new BleConnectionStatus(); +} + +void BleKeyboard::begin(void) +{ + xTaskCreate(this->taskServer, "server", 20000, (void *)this, 5, NULL); +} + +void BleKeyboard::end(void) +{ +} + +/* +size_t BleKeyboard::write(uint8_t k) +{ + +} +size_t BleKeyboard::write(const uint8_t *buffer, size_t size) +{ + +} +size_t BleKeyboard::press(uint8_t k) +{ + +} +size_t BleKeyboard::release(uint8_t k) +{ + +} +void BleKeyboard::releaseAll(void) +{ + +} +*/ + +void BleKeyboard::sendReport(KeyReport* keys) +{ + if (this->isConnected()) + { + this->inputKeyboard->setValue((uint8_t*)keys, sizeof(KeyReport)); + this->inputKeyboard->notify(); + } +} + +bool BleKeyboard::isConnected(void) { + return this->connectionStatus->connected; +} + +void BleKeyboard::setBatteryLevel(uint8_t level) { + this->batteryLevel = level; + this->hid->setBatteryLevel(level); +} + +void BleKeyboard::taskServer(void* pvParameter) { + BleKeyboard* bleKeyboardInstance = (BleKeyboard *) pvParameter; //static_cast(pvParameter); + BLEDevice::init(bleKeyboardInstance->deviceName); + BLEServer *pServer = BLEDevice::createServer(); + pServer->setCallbacks(bleKeyboardInstance->connectionStatus); + + bleKeyboardInstance->hid = new BLEHIDDevice(pServer); + bleKeyboardInstance->inputKeyboard = bleKeyboardInstance->hid->inputReport(1); // <-- input REPORTID from report map + bleKeyboardInstance->outputKeyboard = bleKeyboardInstance->hid->outputReport(1); + bleKeyboardInstance->connectionStatus->inputKeyboard = bleKeyboardInstance->inputKeyboard; + bleKeyboardInstance->connectionStatus->outputKeyboard = bleKeyboardInstance->outputKeyboard; + + bleKeyboardInstance->outputKeyboard->setCallbacks(new KeyboardOutputCallbacks()); + + bleKeyboardInstance->hid->manufacturer()->setValue(bleKeyboardInstance->deviceManufacturer); + + bleKeyboardInstance->hid->pnp(0x02, 0xe502, 0xa111, 0x0210); + bleKeyboardInstance->hid->hidInfo(0x00,0x01); + + BLESecurity *pSecurity = new BLESecurity(); + + pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND); + + bleKeyboardInstance->hid->reportMap((uint8_t*)_hidReportDescriptor, sizeof(_hidReportDescriptor)); + bleKeyboardInstance->hid->startServices(); + + BLEAdvertising *pAdvertising = pServer->getAdvertising(); + pAdvertising->setAppearance(HID_KEYBOARD); + pAdvertising->addServiceUUID(bleKeyboardInstance->hid->hidService()->getUUID()); + pAdvertising->start(); + bleKeyboardInstance->hid->setBatteryLevel(bleKeyboardInstance->batteryLevel); + + ESP_LOGD(LOG_TAG, "Advertising started!"); + vTaskDelay(portMAX_DELAY); //delay(portMAX_DELAY); +} diff --git a/BleKeyboard.h b/BleKeyboard.h new file mode 100644 index 0000000..1d93f04 --- /dev/null +++ b/BleKeyboard.h @@ -0,0 +1,46 @@ +#ifndef ESP32_BLE_KEYBOARD_H +#define ESP32_BLE_KEYBOARD_H +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include "BleConnectionStatus.h" +#include "BLEHIDDevice.h" +#include "BLECharacteristic.h" + +// Low level key report: up to 6 keys and shift, ctrl etc at once +typedef struct +{ + uint8_t modifiers; + uint8_t reserved; + uint8_t keys[6]; +} KeyReport; + +class BleKeyboard { +private: + uint8_t _buttons; + BleConnectionStatus* connectionStatus; + BLEHIDDevice* hid; + BLECharacteristic* inputKeyboard; + BLECharacteristic* outputKeyboard; + KeyReport _keyReport; + void buttons(uint8_t b); + static void taskServer(void* pvParameter); +public: + BleKeyboard(std::string deviceName = "Espressif", std::string deviceManufacturer = "ESP32 Bluetooth Keyboard", uint8_t batteryLevel = 100); + void begin(void); + void end(void); + void sendReport(KeyReport* keys); + //size_t write(uint8_t k); + //size_t write(const uint8_t *buffer, size_t size); + //size_t press(uint8_t k); + //size_t release(uint8_t k); + //void releaseAll(void); + bool isConnected(void); + void setBatteryLevel(uint8_t level); + uint8_t batteryLevel; + std::string deviceManufacturer; + std::string deviceName; +}; + +#endif // CONFIG_BT_ENABLED +#endif // ESP32_BLE_KEYBOARD_H diff --git a/KeyboardOutputCallbacks.cpp b/KeyboardOutputCallbacks.cpp new file mode 100644 index 0000000..286ce41 --- /dev/null +++ b/KeyboardOutputCallbacks.cpp @@ -0,0 +1,18 @@ +#include "KeyboardOutputCallbacks.h" + +#if defined(CONFIG_ARDUHAL_ESP_LOG) + #include "esp32-hal-log.h" + #define LOG_TAG "" +#else + #include "esp_log.h" + static const char* LOG_TAG = "BLEDevice"; +#endif + +KeyboardOutputCallbacks::KeyboardOutputCallbacks(void) { +} + +void KeyboardOutputCallbacks::onWrite(BLECharacteristic* me) { + uint8_t* value = (uint8_t*)(me->getValue().c_str()); + ESP_LOGI(LOG_TAG, "special keys: %d", *value); +} + diff --git a/KeyboardOutputCallbacks.h b/KeyboardOutputCallbacks.h new file mode 100644 index 0000000..bce158e --- /dev/null +++ b/KeyboardOutputCallbacks.h @@ -0,0 +1,18 @@ +#ifndef ESP32_BLE_KEYBOARD_OUTPUT_CALLBACKS_H +#define ESP32_BLE_KEYBOARD_OUTPUT_CALLBACKS_H +#include "sdkconfig.h" +#if defined(CONFIG_BT_ENABLED) + +#include +#include "BLE2902.h" +#include "BLECharacteristic.h" + +class KeyboardOutputCallbacks : public BLECharacteristicCallbacks +{ +public: + KeyboardOutputCallbacks(void); + void onWrite(BLECharacteristic* me); +}; + +#endif // CONFIG_BT_ENABLED +#endif // ESP32_BLE_KEYBOARD_OUTPUT_CALLBACKS_H diff --git a/README.md b/README.md new file mode 100644 index 0000000..2afbc4b --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# ESP32 BLE Keyboard library + +This library allows you to make the ESP32 act as a Bluetooth Keyboard and control what it does. + +Warning: This library is not ready yet. Atm keys can only be sent using the `sendReport` method. +The code hasn't been tested at all. It should compile wihtout errors though. + +## Installation +- (Make sure you can use the ESP32 with the Arduino IDE. [Instructions can be found here.](https://github.com/espressif/arduino-esp32#installation-instructions)) +- [Download the latest release of this library from the release page.](https://github.com/T-vK/ESP32-BLE-Keyboard/releases) +- In the Arduino IDE go to "Sketch" -> "Include Library" -> "Add .ZIP Library..." and select the file you just downloaded. +- You can now go to "File" -> "Examples" -> "ESP32 BLE Keyboard" and select any of the examples to get started. + +## Example + +``` C++ +/** + * This example turns the ESP32 into a Bluetooth LE keyboard that types the letter `a` once every 5 seconds. + */ +#include + +BleKeyboard bleKeyboard; + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + bleKeyboard.begin(); +} + +void loop() { + if(bleKeyboard.isConnected()) { + Serial.println("Pressing the a-key via the Bluetooth keyboard"); + + KeyReport keyReport; + keyReport.modifiers = 0x00; + keyReport.reserved = 0x00; + keyReport.keys[0] = 0x61; // a-key + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + + bleKeyboard.sendReport(&keyReport); // a-key down + + delay(50); + + KeyReport keyReport2; + keyReport.modifiers = 0x00; + keyReport.reserved = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + + bleKeyboard.sendReport(&keyReport2); // a-key up + + } + delay(5000); +} +``` + +## API docs +The BleKeyboard interface is almost identical to the Keyboard Interface, so you can use documentation right here: +https://www.arduino.cc/reference/en/language/functions/usb/keyboard/ + +Just remember that you have to use `bleKeyboard` instead of just `Keyboard` and you need these two lines at the top of your script: +``` +#include +BleKeyboard bleKeyboard; +``` + +## Credits + +Credits to [chegewara](https://github.com/chegewara) as this library is based on [this piece of code](https://github.com/nkolban/esp32-snippets/issues/230#issuecomment-473135679) that he provided. \ No newline at end of file diff --git a/examples/SendKeyStrokes/SendKeyStrokes.ino b/examples/SendKeyStrokes/SendKeyStrokes.ino new file mode 100644 index 0000000..aba2499 --- /dev/null +++ b/examples/SendKeyStrokes/SendKeyStrokes.ino @@ -0,0 +1,46 @@ +/** + * This example turns the ESP32 into a Bluetooth LE keyboard that types the letter `a` once every 5 seconds. + */ +#include + +BleKeyboard bleKeyboard; + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + bleKeyboard.begin(); +} + +void loop() { + if(bleKeyboard.isConnected()) { + Serial.println("Pressing the a-key via the Bluetooth keyboard"); + + KeyReport keyReport; + keyReport.modifiers = 0x00; + keyReport.reserved = 0x00; + keyReport.keys[0] = 0x61; // a-key + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + + bleKeyboard.sendReport(&keyReport); // a-key down + + delay(50); + + KeyReport keyReport2; + keyReport.modifiers = 0x00; + keyReport.reserved = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + keyReport.keys[0] = 0x00; + + bleKeyboard.sendReport(&keyReport2); // a-key up + + } + delay(5000); +} \ No newline at end of file diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..0aa35b7 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,24 @@ +####################################### +# Syntax Coloring Map For ESP32 BLE Keyboard +####################################### +# Class +####################################### + +BleKeyboard KEYWORD1 + +####################################### +# Methods and Functions +####################################### + +begin KEYWORD2 +end KEYWORD2 +write KEYWORD2 +press KEYWORD2 +release KEYWORD2 +releaseAll KEYWORD2 +setBatteryLevel KEYWORD2 +isConnected KEYWORD2 + +####################################### +# Constants +####################################### diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..0904cee --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=ESP32 BLE Keyboard +version=0.1.0-alpha +author=T-vK +maintainer=T-vK +sentence=Bluetooth LE Keyboard library for the ESP32. +paragraph=Bluetooth LE Keyboard library for the ESP32. +category=Communication +url=https://github.com/T-vK/ESP32-BLE-Keyboard +architectures=esp32