diff --git a/BleKeyboard.cpp b/BleKeyboard.cpp index a6b6bcc..70dbefc 100644 --- a/BleKeyboard.cpp +++ b/BleKeyboard.cpp @@ -4,7 +4,6 @@ #include "BLE2902.h" #include "BLEHIDDevice.h" #include "HIDTypes.h" -#include "HIDKeyboardTypes.h" #include #include "sdkconfig.h" @@ -64,7 +63,7 @@ static const uint8_t _hidReportDescriptor[] = { USAGE_PAGE(1), 0x0C, // USAGE_PAGE (Consumer) USAGE(1), 0x01, // USAGE (Consumer Control) COLLECTION(1), 0x01, // COLLECTION (Application) - REPORT_ID(1), MEDIA_KEYS_ID, // REPORT_ID (2) + REPORT_ID(1), MEDIA_KEYS_ID, // REPORT_ID (3) USAGE_PAGE(1), 0x0C, // USAGE_PAGE (Consumer) LOGICAL_MINIMUM(1), 0x00, // LOGICAL_MINIMUM (0) LOGICAL_MAXIMUM(1), 0x01, // LOGICAL_MAXIMUM (1) @@ -90,7 +89,7 @@ static const uint8_t _hidReportDescriptor[] = { END_COLLECTION(0) // END_COLLECTION }; -BleKeyboard::BleKeyboard(std::string deviceName, std::string deviceManufacturer, uint8_t batteryLevel) : _buttons(0) +BleKeyboard::BleKeyboard(std::string deviceName, std::string deviceManufacturer, uint8_t batteryLevel) { this->deviceName = deviceName; this->deviceManufacturer = deviceManufacturer; @@ -107,47 +106,6 @@ 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(); - } -} - -void BleKeyboard::sendReport(MediaKeyReport* keys) -{ - if (this->isConnected()) - { - this->inputMediaKeys->setValue((uint8_t*)keys, sizeof(MediaKeyReport)); - this->inputMediaKeys->notify(); - } -} - bool BleKeyboard::isConnected(void) { return this->connectionStatus->connected; } @@ -192,3 +150,308 @@ void BleKeyboard::taskServer(void* pvParameter) { ESP_LOGD(LOG_TAG, "Advertising started!"); vTaskDelay(portMAX_DELAY); //delay(portMAX_DELAY); } + +void BleKeyboard::sendReport(KeyReport* keys) +{ + if (this->isConnected()) + { + this->inputKeyboard->setValue((uint8_t*)keys, sizeof(KeyReport)); + this->inputKeyboard->notify(); + } +} + +void BleKeyboard::sendReport(MediaKeyReport* keys) +{ + if (this->isConnected()) + { + this->inputMediaKeys->setValue((uint8_t*)keys, sizeof(MediaKeyReport)); + this->inputMediaKeys->notify(); + } +} + +extern +const uint8_t _asciimap[128] PROGMEM; + +#define SHIFT 0x80 +const uint8_t _asciimap[128] = +{ + 0x00, // NUL + 0x00, // SOH + 0x00, // STX + 0x00, // ETX + 0x00, // EOT + 0x00, // ENQ + 0x00, // ACK + 0x00, // BEL + 0x2a, // BS Backspace + 0x2b, // TAB Tab + 0x28, // LF Enter + 0x00, // VT + 0x00, // FF + 0x00, // CR + 0x00, // SO + 0x00, // SI + 0x00, // DEL + 0x00, // DC1 + 0x00, // DC2 + 0x00, // DC3 + 0x00, // DC4 + 0x00, // NAK + 0x00, // SYN + 0x00, // ETB + 0x00, // CAN + 0x00, // EM + 0x00, // SUB + 0x00, // ESC + 0x00, // FS + 0x00, // GS + 0x00, // RS + 0x00, // US + + 0x2c, // ' ' + 0x1e|SHIFT, // ! + 0x34|SHIFT, // " + 0x20|SHIFT, // # + 0x21|SHIFT, // $ + 0x22|SHIFT, // % + 0x24|SHIFT, // & + 0x34, // ' + 0x26|SHIFT, // ( + 0x27|SHIFT, // ) + 0x25|SHIFT, // * + 0x2e|SHIFT, // + + 0x36, // , + 0x2d, // - + 0x37, // . + 0x38, // / + 0x27, // 0 + 0x1e, // 1 + 0x1f, // 2 + 0x20, // 3 + 0x21, // 4 + 0x22, // 5 + 0x23, // 6 + 0x24, // 7 + 0x25, // 8 + 0x26, // 9 + 0x33|SHIFT, // : + 0x33, // ; + 0x36|SHIFT, // < + 0x2e, // = + 0x37|SHIFT, // > + 0x38|SHIFT, // ? + 0x1f|SHIFT, // @ + 0x04|SHIFT, // A + 0x05|SHIFT, // B + 0x06|SHIFT, // C + 0x07|SHIFT, // D + 0x08|SHIFT, // E + 0x09|SHIFT, // F + 0x0a|SHIFT, // G + 0x0b|SHIFT, // H + 0x0c|SHIFT, // I + 0x0d|SHIFT, // J + 0x0e|SHIFT, // K + 0x0f|SHIFT, // L + 0x10|SHIFT, // M + 0x11|SHIFT, // N + 0x12|SHIFT, // O + 0x13|SHIFT, // P + 0x14|SHIFT, // Q + 0x15|SHIFT, // R + 0x16|SHIFT, // S + 0x17|SHIFT, // T + 0x18|SHIFT, // U + 0x19|SHIFT, // V + 0x1a|SHIFT, // W + 0x1b|SHIFT, // X + 0x1c|SHIFT, // Y + 0x1d|SHIFT, // Z + 0x2f, // [ + 0x31, // bslash + 0x30, // ] + 0x23|SHIFT, // ^ + 0x2d|SHIFT, // _ + 0x35, // ` + 0x04, // a + 0x05, // b + 0x06, // c + 0x07, // d + 0x08, // e + 0x09, // f + 0x0a, // g + 0x0b, // h + 0x0c, // i + 0x0d, // j + 0x0e, // k + 0x0f, // l + 0x10, // m + 0x11, // n + 0x12, // o + 0x13, // p + 0x14, // q + 0x15, // r + 0x16, // s + 0x17, // t + 0x18, // u + 0x19, // v + 0x1a, // w + 0x1b, // x + 0x1c, // y + 0x1d, // z + 0x2f|SHIFT, // { + 0x31|SHIFT, // | + 0x30|SHIFT, // } + 0x35|SHIFT, // ~ + 0 // DEL +}; + + +uint8_t USBPutChar(uint8_t c); + +// press() adds the specified key (printing, non-printing, or modifier) +// to the persistent key report and sends the report. Because of the way +// USB HID works, the host acts like the key remains pressed until we +// call release(), releaseAll(), or otherwise clear the report and resend. +size_t BleKeyboard::press(uint8_t k) +{ + uint8_t i; + if (k >= 136) { // it's a non-printing key (not a modifier) + k = k - 136; + } else if (k >= 128) { // it's a modifier key + _keyReport.modifiers |= (1<<(k-128)); + k = 0; + } else { // it's a printing key + k = pgm_read_byte(_asciimap + k); + if (!k) { + setWriteError(); + return 0; + } + if (k & 0x80) { // it's a capital letter or other character reached with shift + _keyReport.modifiers |= 0x02; // the left shift modifier + k &= 0x7F; + } + } + + // Add k to the key report only if it's not already present + // and if there is an empty slot. + if (_keyReport.keys[0] != k && _keyReport.keys[1] != k && + _keyReport.keys[2] != k && _keyReport.keys[3] != k && + _keyReport.keys[4] != k && _keyReport.keys[5] != k) { + + for (i=0; i<6; i++) { + if (_keyReport.keys[i] == 0x00) { + _keyReport.keys[i] = k; + break; + } + } + if (i == 6) { + setWriteError(); + return 0; + } + } + sendReport(&_keyReport); + return 1; +} + +size_t BleKeyboard::press(const MediaKeyReport k) +{ + uint16_t k_16 = k[1] | (k[0] << 8); + uint16_t mediaKeyReport_16 = _mediaKeyReport[1] | (_mediaKeyReport[0] << 8); + + mediaKeyReport_16 |= k_16; + _mediaKeyReport[0] = (uint8_t)((mediaKeyReport_16 & 0xFF00) >> 8); + _mediaKeyReport[1] = (uint8_t)(mediaKeyReport_16 & 0x00FF); + + sendReport(&_mediaKeyReport); + return 1; +} + +// release() takes the specified key out of the persistent key report and +// sends the report. This tells the OS the key is no longer pressed and that +// it shouldn't be repeated any more. +size_t BleKeyboard::release(uint8_t k) +{ + uint8_t i; + if (k >= 136) { // it's a non-printing key (not a modifier) + k = k - 136; + } else if (k >= 128) { // it's a modifier key + _keyReport.modifiers &= ~(1<<(k-128)); + k = 0; + } else { // it's a printing key + k = pgm_read_byte(_asciimap + k); + if (!k) { + return 0; + } + if (k & 0x80) { // it's a capital letter or other character reached with shift + _keyReport.modifiers &= ~(0x02); // the left shift modifier + k &= 0x7F; + } + } + + // Test the key report to see if k is present. Clear it if it exists. + // Check all positions in case the key is present more than once (which it shouldn't be) + for (i=0; i<6; i++) { + if (0 != k && _keyReport.keys[i] == k) { + _keyReport.keys[i] = 0x00; + } + } + + sendReport(&_keyReport); + return 1; +} + +size_t BleKeyboard::release(const MediaKeyReport k) +{ + uint16_t k_16 = k[1] | (k[0] << 8); + uint16_t mediaKeyReport_16 = _mediaKeyReport[1] | (_mediaKeyReport[0] << 8); + mediaKeyReport_16 &= ~k_16; + _mediaKeyReport[0] = (uint8_t)((mediaKeyReport_16 & 0xFF00) >> 8); + _mediaKeyReport[1] = (uint8_t)(mediaKeyReport_16 & 0x00FF); + + sendReport(&_mediaKeyReport); + return 1; +} + +void BleKeyboard::releaseAll(void) +{ + _keyReport.keys[0] = 0; + _keyReport.keys[1] = 0; + _keyReport.keys[2] = 0; + _keyReport.keys[3] = 0; + _keyReport.keys[4] = 0; + _keyReport.keys[5] = 0; + _keyReport.modifiers = 0; + _mediaKeyReport[0] = 0; + _mediaKeyReport[1] = 0; + sendReport(&_keyReport); +} + +size_t BleKeyboard::write(uint8_t c) +{ + uint8_t p = press(c); // Keydown + release(c); // Keyup + return p; // just return the result of press() since release() almost always returns 1 +} + +size_t BleKeyboard::write(const MediaKeyReport c) +{ + uint16_t p = press(c); // Keydown + release(c); // Keyup + return p; // just return the result of press() since release() almost always returns 1 +} + +size_t BleKeyboard::write(const uint8_t *buffer, size_t size) { + size_t n = 0; + while (size--) { + if (*buffer != '\r') { + if (write(*buffer)) { + n++; + } else { + break; + } + } + buffer++; + } + return n; +} \ No newline at end of file diff --git a/BleKeyboard.h b/BleKeyboard.h index ce7e9d0..4e4c494 100644 --- a/BleKeyboard.h +++ b/BleKeyboard.h @@ -6,6 +6,77 @@ #include "BleConnectionStatus.h" #include "BLEHIDDevice.h" #include "BLECharacteristic.h" +#include "Print.h" + + +const uint8_t KEY_LEFT_CTRL = 0x80; +const uint8_t KEY_LEFT_SHIFT = 0x81; +const uint8_t KEY_LEFT_ALT = 0x82; +const uint8_t KEY_LEFT_GUI = 0x83; +const uint8_t KEY_RIGHT_CTRL = 0x84; +const uint8_t KEY_RIGHT_SHIFT = 0x85; +const uint8_t KEY_RIGHT_ALT = 0x86; +const uint8_t KEY_RIGHT_GUI = 0x87; + +const uint8_t KEY_UP_ARROW = 0xDA; +const uint8_t KEY_DOWN_ARROW = 0xD9; +const uint8_t KEY_LEFT_ARROW = 0xD8; +const uint8_t KEY_RIGHT_ARROW = 0xD7; +const uint8_t KEY_BACKSPACE = 0xB2; +const uint8_t KEY_TAB = 0xB3; +const uint8_t KEY_RETURN = 0xB0; +const uint8_t KEY_ESC = 0xB1; +const uint8_t KEY_INSERT = 0xD1; +const uint8_t KEY_DELETE = 0xD4; +const uint8_t KEY_PAGE_UP = 0xD3; +const uint8_t KEY_PAGE_DOWN = 0xD6; +const uint8_t KEY_HOME = 0xD2; +const uint8_t KEY_END = 0xD5; +const uint8_t KEY_CAPS_LOCK = 0xC1; +const uint8_t KEY_F1 = 0xC2; +const uint8_t KEY_F2 = 0xC3; +const uint8_t KEY_F3 = 0xC4; +const uint8_t KEY_F4 = 0xC5; +const uint8_t KEY_F5 = 0xC6; +const uint8_t KEY_F6 = 0xC7; +const uint8_t KEY_F7 = 0xC8; +const uint8_t KEY_F8 = 0xC9; +const uint8_t KEY_F9 = 0xCA; +const uint8_t KEY_F10 = 0xCB; +const uint8_t KEY_F11 = 0xCC; +const uint8_t KEY_F12 = 0xCD; +const uint8_t KEY_F13 = 0xF0; +const uint8_t KEY_F14 = 0xF1; +const uint8_t KEY_F15 = 0xF2; +const uint8_t KEY_F16 = 0xF3; +const uint8_t KEY_F17 = 0xF4; +const uint8_t KEY_F18 = 0xF5; +const uint8_t KEY_F19 = 0xF6; +const uint8_t KEY_F20 = 0xF7; +const uint8_t KEY_F21 = 0xF8; +const uint8_t KEY_F22 = 0xF9; +const uint8_t KEY_F23 = 0xFA; +const uint8_t KEY_F24 = 0xFB; + +typedef uint8_t MediaKeyReport[2]; + +const MediaKeyReport KEY_MEDIA_NEXT_TRACK = {1, 0}; +const MediaKeyReport KEY_MEDIA_PREVIOUS_TRACK = {2, 0}; +const MediaKeyReport KEY_MEDIA_STOP = {4, 0}; +const MediaKeyReport KEY_MEDIA_PLAY_PAUSE = {8, 0}; +const MediaKeyReport KEY_MEDIA_MUTE = {16, 0}; +const MediaKeyReport KEY_MEDIA_VOLUME_UP = {32, 0}; +const MediaKeyReport KEY_MEDIA_VOLUME_DOWN = {64, 0}; +const MediaKeyReport KEY_MEDIA_WWW_HOME = {128, 0}; // Opens "My Computer" on Windows +const MediaKeyReport KEY_MEDIA_LOCAL_MACHINE_BROWSER = {0, 1}; // Opens "My Computer" on Windows +const MediaKeyReport KEY_MEDIA_CALCULATOR = {0, 2}; +const MediaKeyReport KEY_MEDIA_WWW_BOOKMARKS = {0, 4}; +const MediaKeyReport KEY_MEDIA_WWW_SEARCH = {0, 8}; +const MediaKeyReport KEY_MEDIA_WWW_STOP = {0, 16}; +const MediaKeyReport KEY_MEDIA_WWW_BACK = {0, 32}; +const MediaKeyReport KEY_MEDIA_CONSUMER_CONTROL_CONFIGURATION = {0, 64}; // Media Selection +const MediaKeyReport KEY_MEDIA_EMAIL_READER = {0, 128}; + // Low level key report: up to 6 keys and shift, ctrl etc at once typedef struct @@ -15,9 +86,8 @@ typedef struct uint8_t keys[6]; } KeyReport; -typedef uint8_t MediaKeyReport[2]; - -class BleKeyboard { +class BleKeyboard : public Print +{ private: uint8_t _buttons; BleConnectionStatus* connectionStatus; @@ -35,11 +105,14 @@ class BleKeyboard { void end(void); void sendReport(KeyReport* keys); void sendReport(MediaKeyReport* 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); + size_t press(uint8_t k); + size_t press(const MediaKeyReport k); + size_t release(uint8_t k); + size_t release(const MediaKeyReport k); + size_t write(uint8_t c); + size_t write(const MediaKeyReport c); + size_t write(const uint8_t *buffer, size_t size); + void releaseAll(void); bool isConnected(void); void setBatteryLevel(uint8_t level); uint8_t batteryLevel; diff --git a/examples/SendKeyStrokes/SendKeyStrokes.ino b/examples/SendKeyStrokes/SendKeyStrokes.ino index b1df002..ab36e03 100644 --- a/examples/SendKeyStrokes/SendKeyStrokes.ino +++ b/examples/SendKeyStrokes/SendKeyStrokes.ino @@ -43,7 +43,7 @@ if(bleKeyboard.isConnected()) { delay(1000) */ - +/* Serial.println("Pressing the Play/Pause-key via the Bluetooth keyboard..."); MediaKeyReport mediaKeyReport; @@ -57,7 +57,10 @@ if(bleKeyboard.isConnected()) { mediaKeyReport2[0] = 0; mediaKeyReport2[1] = 0; bleKeyboard.sendReport(&mediaKeyReport2); // play/pause-key up (2=MEDIA_KEYS_ID) +*/ + bleKeyboard.write(KEY_MEDIA_PLAY_PAUSE); + Serial.println("Key sent!"); } delay(5000); } \ No newline at end of file