diff --git a/examples/NKROKeyboard/NKROKeyboard.ino b/examples/NKROKeyboard/NKROKeyboard.ino new file mode 100644 index 0000000..dae9385 --- /dev/null +++ b/examples/NKROKeyboard/NKROKeyboard.ino @@ -0,0 +1,68 @@ +/* + Copyright (c) 2014-2015 NicoHood + See the readme for credit to other people. + + NKROKeyboard example + + Press a button to press a lot of keys at the same time. + NKRO can push 113 keys at the same time, + the other Keyboards only 6 keys + 8 modifiers! + + See HID Project documentation for more information. + https://github.com/NicoHood/HID/wiki/Keyboard-API +*/ + +#include "HID-Project.h" + +const int pinLed = LED_BUILTIN; +const int pinButton = 2; + +void setup() { + pinMode(pinLed, OUTPUT); + pinMode(pinButton, INPUT_PULLUP); + + // Sends a clean report to the host. This is important on any Arduino type. + NKROKeyboard.begin(); +} + +void loop() { + // Press a lot of keys at the same time + if (!digitalRead(pinButton)) { + digitalWrite(pinLed, HIGH); + + NKROKeyboard.press('a'); + NKROKeyboard.press('b'); + NKROKeyboard.press('c'); + NKROKeyboard.press('d'); + NKROKeyboard.press('e'); + NKROKeyboard.press('f'); + NKROKeyboard.press('g'); + NKROKeyboard.press('h'); + NKROKeyboard.press('i'); + NKROKeyboard.press('j'); + NKROKeyboard.press('k'); + NKROKeyboard.press('l'); + NKROKeyboard.press('m'); + NKROKeyboard.press('n'); + NKROKeyboard.press('o'); + NKROKeyboard.press('p'); + NKROKeyboard.press('q'); + NKROKeyboard.press('r'); + NKROKeyboard.press('s'); + NKROKeyboard.press('t'); + NKROKeyboard.press('u'); + NKROKeyboard.press('v'); + NKROKeyboard.press('w'); + NKROKeyboard.press('x'); + NKROKeyboard.press('y'); + NKROKeyboard.press('z'); + + // Release all keys and hit enter + NKROKeyboard.releaseAll(); + NKROKeyboard.println(); + + // Simple debounce + delay(300); + } +} + diff --git a/src/HID-Project.h b/src/HID-Project.h index e5d5b8c..222e6b5 100644 --- a/src/HID-Project.h +++ b/src/HID-Project.h @@ -138,4 +138,5 @@ THE SOFTWARE. #else #include "ImprovedKeylayouts.h" #include "ImprovedKeyboard.h" +#include "NKROKeyboard.h" #endif diff --git a/src/NKROKeyboard.cpp b/src/NKROKeyboard.cpp new file mode 100644 index 0000000..96befe6 --- /dev/null +++ b/src/NKROKeyboard.cpp @@ -0,0 +1,283 @@ +/* +Copyright (c) 2015 NicoHood +See the readme for credit to other people. + +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. +*/ + +#include "NKROKeyboard.h" + +#if defined(_USING_HID) + +//================================================================================ +//================================================================================ +// NKROKeyboard + +static const u8 _hidReportDescriptor[] PROGMEM = { + // NKRO Keyboard + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) 47 */ + 0x09, 0x06, /* USAGE (Keyboard) */ + 0xa1, 0x01, /* COLLECTION (Application) */ + 0x85, HID_REPORTID_NKRO_KEYBOARD, /* REPORT_ID */ + 0x05, 0x07, /* USAGE_PAGE (Keyboard) */ + + /* Keyboard Modifiers (shift, alt, ...) */ + 0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */ + 0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x95, 0x08, /* REPORT_COUNT (8) */ + 0x81, 0x02, /* INPUT (Data,Var,Abs) */ + +#if defined(HID_KEYBOARD_LEDS_ENABLED) +//TODO remove reserved bytes to add 3 more custom data bits for advanced users? + /* 5 LEDs for num lock etc */ + 0x05, 0x08, /* USAGE_PAGE (LEDs) */ + 0x19, 0x01, /* USAGE_MINIMUM (Num Lock) */ + 0x29, 0x05, /* USAGE_MAXIMUM (Kana) */ + 0x95, 0x05, /* REPORT_COUNT (5) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x91, 0x02, /* OUTPUT (Data,Var,Abs) */ + /* Reserved 3 bits */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x03, /* REPORT_SIZE (3) */ + 0x91, 0x03, /* OUTPUT (Cnst,Var,Abs) */ +#endif + + /* 104 Keys as bitmap */ + 0x05, 0x07, /* Usage Page (Key Codes) */ + 0x19, 0x00, /* Usage Minimum (0) */ + 0x29, NKRO_KEY_COUNT - 1, /* Usage Maximum (103) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, NKRO_KEY_COUNT, /* Report Count (104) */ + 0x81, 0x02, /* Input (Data, Variable, Absolute) */ + + /* 1 Custom Keyboard key */ + 0x95, 0x01, /* REPORT_COUNT (1) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x26, 0xE7, 0x00, /* LOGICAL_MAXIMUM (231) */ + /*0x05, 0x07, USAGE_PAGE (Keyboard) */ + 0x19, 0x00, /* USAGE_MINIMUM (Reserved (no event indicated)) */ + 0x29, 0xE7, /* USAGE_MAXIMUM (Keyboard Right GUI) */ + 0x81, 0x00, /* INPUT (Data,Ary,Abs) */ + + /* End */ + 0xC0 /* End Collection */ +}; + +NKROKeyboard_::NKROKeyboard_(void) : +HIDDevice((uint8_t*)_hidReportDescriptor, sizeof(_hidReportDescriptor), HID_REPORTID_NKRO_KEYBOARD) +#if defined(HID_KEYBOARD_LEDS_ENABLED) +,leds(0) +#endif +{ + // HID Descriptor is appended via the inherited HIDDevice class +} + +void NKROKeyboard_::begin(void) +{ + releaseAll(); +} + +void NKROKeyboard_::end(void) +{ + releaseAll(); +} + +void NKROKeyboard_::sendReport(HID_NKROKeyboardReport_Data_t* keys) +{ + // Call the inherited function. + // This wrapper still saves us some bytes +#if defined(USE_BOOT_KEYBOARD_PROTOCOL) + if(getProtocol() != 1){ + SendRawReport(keys, sizeof(HID_NKROKeyboardReport_Data_t)); + } + else +#endif + SendReport(keys, sizeof(HID_NKROKeyboardReport_Data_t)); +} + +#if defined(HID_KEYBOARD_LEDS_ENABLED) +void NKROKeyboard_::setReportData(const void* data, int len){ + // Save led state + if(len == 1) + leds = *(uint8_t*)data; +} + +uint8_t Keyboard_::getLeds(void){ + return leds; +} +#endif + +// 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 NKROKeyboard_::press(uint8_t k) +{ + // it's a non-printing key (not a modifier) + if (k >= 136) + k = k - 136; + + // it's a modifier key + else if (k >= 128) + k = k - 128; + + // it's a printing key + else { + k = pgm_read_byte(_asciimap + k); + if (!k) + return 0; + + // it's a capital letter or other character reached with shift + if (k & SHIFT) { + // the left shift modifier + _keyReport.modifiers |= 0x02; + k = k ^ SHIFT; + } + } + + addKeycodeToReport(k); + sendReport(&_keyReport); + 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 NKROKeyboard_::release(uint8_t k) +{ + // it's a non-printing key (not a modifier) + if (k >= 136) + k = k - 136; + + // it's a modifier key + else if (k >= 128) + k = k - 128; + + // it's a printing key + else { + k = pgm_read_byte(_asciimap + k); + if (!k) + return 0; + + // it's a capital letter or other character reached with shift + if (k & SHIFT) { + // the left shift modifier + _keyReport.modifiers &= ~(0x02); + k = k ^ SHIFT; + } + } + + removeKeycodeFromReport(k); + sendReport(&_keyReport); + + return 1; +} + +void NKROKeyboard_::releaseAll(void) +{ + // release all keys + memset(&_keyReport, 0x00, sizeof(_keyReport)); + sendReport(&_keyReport); +} + +size_t NKROKeyboard_::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 +} + + +// pressKeycode() 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 releaseKeycode(), releaseAll(), or otherwise clear the report and resend. +size_t NKROKeyboard_::pressKeycode(uint8_t k) +{ + if (!addKeycodeToReport(k)) { + return 0; + } + sendReport(&_keyReport); +} + +size_t NKROKeyboard_::addKeycodeToReport(uint8_t k) +{ + // keymap key + if (k < NKRO_KEY_COUNT) + _keyReport.keys[k / 8] |= 1 << (k % 8); + + // it's a modifier key + else if ((k >= HID_KEYBOARD_LEFT_CONTROL) && (k <= HID_KEYBOARD_RIGHT_GUI)) + _keyReport.modifiers |= (0x01 << (k - HID_KEYBOARD_LEFT_CONTROL)); + + // custom key + else + _keyReport.key = k; + + return 1; +} + + +// releaseKeycode() 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. +// When send is set to FALSE (= 0) no sendReport() is executed. This comes in +// handy when combining key releases (e.g. SHIFT+A). +size_t NKROKeyboard_::releaseKeycode(uint8_t k) +{ + if (!removeKeycodeFromReport(k)) { + return 0; + } + sendReport(&_keyReport); +} + +size_t NKROKeyboard_::removeKeycodeFromReport(uint8_t k) +{ + // keymap key + if (k < NKRO_KEY_COUNT) + _keyReport.keys[k / 8] &= ~(1 << (k % 8)); + + // it's a modifier key + else if ((k >= HID_KEYBOARD_LEFT_CONTROL) && (k <= HID_KEYBOARD_RIGHT_GUI)) + _keyReport.modifiers &= ~(0x01 << (k - HID_KEYBOARD_LEFT_CONTROL)); + + // custom key + else + _keyReport.key = 0x00; + + return 1; +} + + +size_t NKROKeyboard_::writeKeycode(uint8_t c) +{ + uint8_t p = pressKeycode(c); // Keydown + releaseKeycode(c); // Keyup + return (p); // just return the result of pressKeycode() since release() almost always returns 1 +} + +NKROKeyboard_ NKROKeyboard; + +#endif diff --git a/src/NKROKeyboard.h b/src/NKROKeyboard.h new file mode 100644 index 0000000..ee133b4 --- /dev/null +++ b/src/NKROKeyboard.h @@ -0,0 +1,83 @@ +/* +Copyright (c) 2014-2015 NicoHood +See the readme for credit to other people. + +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. +*/ + +#pragma once + +#include "HID.h" + +#if !defined(_USING_HID) + +#warning "Using legacy HID core (non pluggable)" + +#else + +#include "HID-Project.h" +#include "ImprovedKeylayouts.h" + +// Max value for USB EP_SIZE 16 +// +1 reportID, +1 modifier, +1 custom key +#define NKRO_KEY_COUNT (8*13) + +typedef union{ + // modifier + keymap + 1 custom key + uint8_t whole8[]; + uint16_t whole16[]; + uint32_t whole32[]; + struct{ + uint8_t modifiers; + uint8_t keys[NKRO_KEY_COUNT / 8]; + uint8_t key; + }; +} HID_NKROKeyboardReport_Data_t; + +class NKROKeyboard_ : public Print, private HIDDevice +{ +protected: + HID_NKROKeyboardReport_Data_t _keyReport; + void sendReport(HID_NKROKeyboardReport_Data_t* keys); +#if defined(HID_KEYBOARD_LEDS_ENABLED) + virtual void setReportData(const void* data, int len); + uint8_t leds; +#endif +public: + NKROKeyboard_(void); + void begin(void); + void end(void); + size_t write(uint8_t k); + size_t press(uint8_t k); + size_t release(uint8_t k); + void releaseAll(void); + + size_t writeKeycode(uint8_t k); + size_t pressKeycode(uint8_t k); + size_t releaseKeycode(uint8_t k); + size_t addKeycodeToReport(uint8_t k); + size_t removeKeycodeFromReport(uint8_t k); + +#if defined(HID_KEYBOARD_LEDS_ENABLED) + uint8_t getLeds(void); +#endif +}; +extern NKROKeyboard_ NKROKeyboard; + +#endif