From c45cff5f839317699c63b428cfa06aa4e7f60d1e Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Mon, 23 Aug 2021 17:27:34 +0300 Subject: [PATCH] Implement USB HID Device Support for ESP32-S2 (#5538) * Add support and example for USB HID Devices * Add support and example for USB Vendor --- CMakeLists.txt | 8 + cores/esp32/FirmwareMSC.cpp | 6 +- cores/esp32/FirmwareMSC.h | 1 + cores/esp32/USB.cpp | 7 +- cores/esp32/USB.h | 5 +- cores/esp32/USBCDC.cpp | 9 +- cores/esp32/USBCDC.h | 7 +- cores/esp32/USBMSC.cpp | 10 +- cores/esp32/USBMSC.h | 6 +- cores/esp32/esp32-hal-tinyusb.c | 15 +- .../USB/examples/CompositeDevice/.skip.esp32 | 0 .../examples/CompositeDevice/.skip.esp32c3 | 0 .../CompositeDevice/CompositeDevice.ino | 213 ++++++++++ .../USB/examples/ConsumerControl/.skip.esp32 | 0 .../examples/ConsumerControl/.skip.esp32c3 | 0 .../ConsumerControl/ConsumerControl.ino | 21 + .../USB/examples/CustomHIDDevice/.skip.esp32 | 0 .../examples/CustomHIDDevice/.skip.esp32c3 | 0 .../CustomHIDDevice/CustomHIDDevice.ino | 79 ++++ libraries/USB/examples/Gamepad/.skip.esp32 | 0 libraries/USB/examples/Gamepad/.skip.esp32c3 | 0 libraries/USB/examples/Gamepad/Gamepad.ino | 21 + libraries/USB/examples/HIDVendor/.skip.esp32 | 0 .../USB/examples/HIDVendor/.skip.esp32c3 | 0 .../USB/examples/HIDVendor/HIDVendor.ino | 52 +++ .../Keyboard/KeyboardLogout/.skip.esp32 | 0 .../Keyboard/KeyboardLogout/.skip.esp32c3 | 0 .../KeyboardLogout/KeyboardLogout.ino | 92 +++++ .../Keyboard/KeyboardMessage/.skip.esp32 | 0 .../Keyboard/KeyboardMessage/.skip.esp32c3 | 0 .../KeyboardMessage/KeyboardMessage.ino | 55 +++ .../Keyboard/KeyboardReprogram/.skip.esp32 | 0 .../Keyboard/KeyboardReprogram/.skip.esp32c3 | 0 .../KeyboardReprogram/KeyboardReprogram.ino | 106 +++++ .../Keyboard/KeyboardSerial/.skip.esp32 | 0 .../Keyboard/KeyboardSerial/.skip.esp32c3 | 0 .../KeyboardSerial/KeyboardSerial.ino | 40 ++ .../KeyboardAndMouseControl/.skip.esp32 | 0 .../KeyboardAndMouseControl/.skip.esp32c3 | 0 .../KeyboardAndMouseControl.ino | 95 +++++ .../Mouse/ButtonMouseControl/.skip.esp32 | 0 .../Mouse/ButtonMouseControl/.skip.esp32c3 | 0 .../ButtonMouseControl/ButtonMouseControl.ino | 86 +++++ .../USB/examples/SystemControl/.skip.esp32 | 0 .../USB/examples/SystemControl/.skip.esp32c3 | 0 .../examples/SystemControl/SystemControl.ino | 21 + libraries/USB/examples/USBVendor/.skip.esp32 | 0 .../USB/examples/USBVendor/.skip.esp32c3 | 0 .../USB/examples/USBVendor/USBVendor.ino | 191 +++++++++ libraries/USB/src/USBHID.cpp | 365 ++++++++++++++++++ libraries/USB/src/USBHID.h | 79 ++++ libraries/USB/src/USBHIDConsumerControl.cpp | 57 +++ libraries/USB/src/USBHIDConsumerControl.h | 85 ++++ libraries/USB/src/USBHIDGamepad.cpp | 121 ++++++ libraries/USB/src/USBHIDGamepad.h | 87 +++++ libraries/USB/src/USBHIDKeyboard.cpp | 354 +++++++++++++++++ libraries/USB/src/USBHIDKeyboard.h | 140 +++++++ libraries/USB/src/USBHIDMouse.cpp | 92 +++++ libraries/USB/src/USBHIDMouse.h | 54 +++ libraries/USB/src/USBHIDSystemControl.cpp | 59 +++ libraries/USB/src/USBHIDSystemControl.h | 39 ++ libraries/USB/src/USBHIDVendor.cpp | 236 +++++++++++ libraries/USB/src/USBHIDVendor.h | 67 ++++ libraries/USB/src/USBVendor.cpp | 216 +++++++++++ libraries/USB/src/USBVendor.h | 94 +++++ libraries/USB/src/USB_NOT.h | 15 - 66 files changed, 3259 insertions(+), 47 deletions(-) create mode 100644 libraries/USB/examples/CompositeDevice/.skip.esp32 create mode 100644 libraries/USB/examples/CompositeDevice/.skip.esp32c3 create mode 100644 libraries/USB/examples/CompositeDevice/CompositeDevice.ino create mode 100644 libraries/USB/examples/ConsumerControl/.skip.esp32 create mode 100644 libraries/USB/examples/ConsumerControl/.skip.esp32c3 create mode 100644 libraries/USB/examples/ConsumerControl/ConsumerControl.ino create mode 100644 libraries/USB/examples/CustomHIDDevice/.skip.esp32 create mode 100644 libraries/USB/examples/CustomHIDDevice/.skip.esp32c3 create mode 100644 libraries/USB/examples/CustomHIDDevice/CustomHIDDevice.ino create mode 100644 libraries/USB/examples/Gamepad/.skip.esp32 create mode 100644 libraries/USB/examples/Gamepad/.skip.esp32c3 create mode 100644 libraries/USB/examples/Gamepad/Gamepad.ino create mode 100644 libraries/USB/examples/HIDVendor/.skip.esp32 create mode 100644 libraries/USB/examples/HIDVendor/.skip.esp32c3 create mode 100644 libraries/USB/examples/HIDVendor/HIDVendor.ino create mode 100644 libraries/USB/examples/Keyboard/KeyboardLogout/.skip.esp32 create mode 100644 libraries/USB/examples/Keyboard/KeyboardLogout/.skip.esp32c3 create mode 100644 libraries/USB/examples/Keyboard/KeyboardLogout/KeyboardLogout.ino create mode 100644 libraries/USB/examples/Keyboard/KeyboardMessage/.skip.esp32 create mode 100644 libraries/USB/examples/Keyboard/KeyboardMessage/.skip.esp32c3 create mode 100644 libraries/USB/examples/Keyboard/KeyboardMessage/KeyboardMessage.ino create mode 100644 libraries/USB/examples/Keyboard/KeyboardReprogram/.skip.esp32 create mode 100644 libraries/USB/examples/Keyboard/KeyboardReprogram/.skip.esp32c3 create mode 100644 libraries/USB/examples/Keyboard/KeyboardReprogram/KeyboardReprogram.ino create mode 100644 libraries/USB/examples/Keyboard/KeyboardSerial/.skip.esp32 create mode 100644 libraries/USB/examples/Keyboard/KeyboardSerial/.skip.esp32c3 create mode 100644 libraries/USB/examples/Keyboard/KeyboardSerial/KeyboardSerial.ino create mode 100644 libraries/USB/examples/KeyboardAndMouseControl/.skip.esp32 create mode 100644 libraries/USB/examples/KeyboardAndMouseControl/.skip.esp32c3 create mode 100644 libraries/USB/examples/KeyboardAndMouseControl/KeyboardAndMouseControl.ino create mode 100644 libraries/USB/examples/Mouse/ButtonMouseControl/.skip.esp32 create mode 100644 libraries/USB/examples/Mouse/ButtonMouseControl/.skip.esp32c3 create mode 100644 libraries/USB/examples/Mouse/ButtonMouseControl/ButtonMouseControl.ino create mode 100644 libraries/USB/examples/SystemControl/.skip.esp32 create mode 100644 libraries/USB/examples/SystemControl/.skip.esp32c3 create mode 100644 libraries/USB/examples/SystemControl/SystemControl.ino create mode 100644 libraries/USB/examples/USBVendor/.skip.esp32 create mode 100644 libraries/USB/examples/USBVendor/.skip.esp32c3 create mode 100644 libraries/USB/examples/USBVendor/USBVendor.ino create mode 100644 libraries/USB/src/USBHID.cpp create mode 100644 libraries/USB/src/USBHID.h create mode 100644 libraries/USB/src/USBHIDConsumerControl.cpp create mode 100644 libraries/USB/src/USBHIDConsumerControl.h create mode 100644 libraries/USB/src/USBHIDGamepad.cpp create mode 100644 libraries/USB/src/USBHIDGamepad.h create mode 100644 libraries/USB/src/USBHIDKeyboard.cpp create mode 100644 libraries/USB/src/USBHIDKeyboard.h create mode 100644 libraries/USB/src/USBHIDMouse.cpp create mode 100644 libraries/USB/src/USBHIDMouse.h create mode 100644 libraries/USB/src/USBHIDSystemControl.cpp create mode 100644 libraries/USB/src/USBHIDSystemControl.h create mode 100644 libraries/USB/src/USBHIDVendor.cpp create mode 100644 libraries/USB/src/USBHIDVendor.h create mode 100644 libraries/USB/src/USBVendor.cpp create mode 100644 libraries/USB/src/USBVendor.h delete mode 100644 libraries/USB/src/USB_NOT.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2801ba61..cdb1f079 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,14 @@ set(LIBRARY_SRCS libraries/Ticker/src/Ticker.cpp libraries/Update/src/Updater.cpp libraries/Update/src/HttpsOTAUpdate.cpp + libraries/USB/src/USBHID.cpp + libraries/USB/src/USBHIDMouse.cpp + libraries/USB/src/USBHIDKeyboard.cpp + libraries/USB/src/USBHIDGamepad.cpp + libraries/USB/src/USBHIDConsumerControl.cpp + libraries/USB/src/USBHIDSystemControl.cpp + libraries/USB/src/USBHIDVendor.cpp + libraries/USB/src/USBVendor.cpp libraries/WebServer/src/WebServer.cpp libraries/WebServer/src/Parsing.cpp libraries/WebServer/src/detail/mimetable.cpp diff --git a/cores/esp32/FirmwareMSC.cpp b/cores/esp32/FirmwareMSC.cpp index c399a013..473e220b 100644 --- a/cores/esp32/FirmwareMSC.cpp +++ b/cores/esp32/FirmwareMSC.cpp @@ -11,17 +11,17 @@ // 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 "FirmwareMSC.h" + +#if CONFIG_TINYUSB_MSC_ENABLED #include -#include "FirmwareMSC.h" #include "esp_partition.h" #include "esp_ota_ops.h" #include "esp32-hal.h" #include "pins_arduino.h" #include "firmware_msc_fat.h" -#if CONFIG_TINYUSB_MSC_ENABLED - #ifndef USB_FW_MSC_VENDOR_ID #define USB_FW_MSC_VENDOR_ID "ESP32" //max 8 chars #endif diff --git a/cores/esp32/FirmwareMSC.h b/cores/esp32/FirmwareMSC.h index 3caaf6a0..570feac8 100644 --- a/cores/esp32/FirmwareMSC.h +++ b/cores/esp32/FirmwareMSC.h @@ -15,6 +15,7 @@ #pragma once #include #include "USBMSC.h" + #if CONFIG_TINYUSB_MSC_ENABLED #include "esp_event.h" diff --git a/cores/esp32/USB.cpp b/cores/esp32/USB.cpp index 1221707c..28daa800 100644 --- a/cores/esp32/USB.cpp +++ b/cores/esp32/USB.cpp @@ -11,10 +11,13 @@ // 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 "USB.h" + +#if CONFIG_TINYUSB_ENABLED + #include "esp32-hal.h" #include "esp32-hal-tinyusb.h" -#include "USB.h" -#if CONFIG_TINYUSB_ENABLED +#include "common/tusb_common.h" #ifndef USB_VID #define USB_VID USB_ESPRESSIF_VID diff --git a/cores/esp32/USB.h b/cores/esp32/USB.h index d64962c7..131ff6d4 100644 --- a/cores/esp32/USB.h +++ b/cores/esp32/USB.h @@ -14,12 +14,11 @@ #pragma once #include "sdkconfig.h" + #if CONFIG_TINYUSB_ENABLED -#include "Arduino.h" -#include "USBCDC.h" -#include "common/tusb_common.h" #include "esp_event.h" +#include "USBCDC.h" #define ARDUINO_USB_ON_BOOT (ARDUINO_USB_CDC_ON_BOOT|ARDUINO_USB_MSC_ON_BOOT|ARDUINO_USB_DFU_ON_BOOT) diff --git a/cores/esp32/USBCDC.cpp b/cores/esp32/USBCDC.cpp index 7a9cf5e0..ce2380fe 100644 --- a/cores/esp32/USBCDC.cpp +++ b/cores/esp32/USBCDC.cpp @@ -11,17 +11,16 @@ // 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 "esp32-hal.h" -#include "esp32-hal-tinyusb.h" #include "USB.h" +#if CONFIG_TINYUSB_CDC_ENABLED + #include "USBCDC.h" -#if CONFIG_TINYUSB_ENABLED +#include "esp32-hal-tinyusb.h" ESP_EVENT_DEFINE_BASE(ARDUINO_USB_CDC_EVENTS); esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait); esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg); -#if CFG_TUD_CDC #define MAX_USB_CDC_DEVICES 2 USBCDC * devices[MAX_USB_CDC_DEVICES] = {NULL, NULL}; @@ -389,5 +388,3 @@ USBCDC Serial(0); #endif #endif /* CONFIG_TINYUSB_CDC_ENABLED */ - -#endif /* CONFIG_TINYUSB_ENABLED */ diff --git a/cores/esp32/USBCDC.h b/cores/esp32/USBCDC.h index 26df8a3c..ced588f4 100644 --- a/cores/esp32/USBCDC.h +++ b/cores/esp32/USBCDC.h @@ -13,13 +13,12 @@ // limitations under the License. #pragma once -#include - -#include "Stream.h" -#include "esp32-hal.h" +#include "sdkconfig.h" #if CONFIG_TINYUSB_CDC_ENABLED +#include #include "esp_event.h" +#include "Stream.h" ESP_EVENT_DECLARE_BASE(ARDUINO_USB_CDC_EVENTS); diff --git a/cores/esp32/USBMSC.cpp b/cores/esp32/USBMSC.cpp index c6327ecd..479d68e5 100644 --- a/cores/esp32/USBMSC.cpp +++ b/cores/esp32/USBMSC.cpp @@ -11,12 +11,12 @@ // 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 "esp32-hal.h" -#include "esp32-hal-tinyusb.h" #include "USBMSC.h" -#if CFG_TUD_MSC +#if CONFIG_TINYUSB_MSC_ENABLED + +#include "esp32-hal-tinyusb.h" + extern "C" uint16_t tusb_msc_load_descriptor(uint8_t * dst, uint8_t * itf) { uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB MSC"); @@ -257,4 +257,4 @@ void USBMSC::mediaPresent(bool media_present){ msc_luns[_lun].media_present = media_present; } -#endif /* CONFIG_USB_MSC_ENABLED */ +#endif /* CONFIG_TINYUSB_MSC_ENABLED */ diff --git a/cores/esp32/USBMSC.h b/cores/esp32/USBMSC.h index 799322a3..287a57ce 100644 --- a/cores/esp32/USBMSC.h +++ b/cores/esp32/USBMSC.h @@ -15,7 +15,8 @@ #pragma once #include #include -#include "esp32-hal.h" +#include "sdkconfig.h" + #if CONFIG_TINYUSB_MSC_ENABLED // Invoked when received Start Stop Unit command @@ -46,4 +47,5 @@ public: private: uint8_t _lun; }; -#endif + +#endif /* CONFIG_TINYUSB_MSC_ENABLED */ diff --git a/cores/esp32/esp32-hal-tinyusb.c b/cores/esp32/esp32-hal-tinyusb.c index 0da912b0..a413c752 100644 --- a/cores/esp32/esp32-hal-tinyusb.c +++ b/cores/esp32/esp32-hal-tinyusb.c @@ -424,12 +424,6 @@ static bool tinyusb_load_enabled_interfaces(){ log_e("Descriptor Load Failed"); return false; } else { - if(i == USB_INTERFACE_CDC){ - if(!tinyusb_reserve_out_endpoint(3) ||!tinyusb_reserve_in_endpoint(4) || !tinyusb_reserve_in_endpoint(5)){ - log_e("CDC Reserve Endpoints Failed"); - return false; - } - } dst += len; } } @@ -505,6 +499,7 @@ static void tinyusb_apply_device_config(tinyusb_device_config_t *config){ && (config->usb_class != TUSB_CLASS_CDC) ){ config->usb_class = TUSB_CLASS_CDC; + config->usb_protocol = 0x00; } WEBUSB_ENABLED = config->webusb_enabled; @@ -573,6 +568,12 @@ esp_err_t tinyusb_enable_interface(tinyusb_interface_t interface, uint16_t descr log_e("Interface %s invalid or already enabled", (interface >= USB_INTERFACE_MAX)?"":tinyusb_interface_names[interface]); return ESP_FAIL; } + if(interface == USB_INTERFACE_CDC){ + if(!tinyusb_reserve_out_endpoint(3) ||!tinyusb_reserve_in_endpoint(4) || !tinyusb_reserve_in_endpoint(5)){ + log_e("CDC Reserve Endpoints Failed"); + return ESP_FAIL; + } + } tinyusb_loaded_interfaces_mask |= (1U << interface); tinyusb_config_descriptor_len += descriptor_len; tinyusb_loaded_interfaces_callbacks[interface] = cb; @@ -586,7 +587,7 @@ esp_err_t tinyusb_init(tinyusb_device_config_t *config) { } tinyusb_is_initialized = true; - tinyusb_endpoints.val = 0; + //tinyusb_endpoints.val = 0; tinyusb_apply_device_config(config); if (!tinyusb_load_enabled_interfaces()) { tinyusb_is_initialized = false; diff --git a/libraries/USB/examples/CompositeDevice/.skip.esp32 b/libraries/USB/examples/CompositeDevice/.skip.esp32 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/CompositeDevice/.skip.esp32c3 b/libraries/USB/examples/CompositeDevice/.skip.esp32c3 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/CompositeDevice/CompositeDevice.ino b/libraries/USB/examples/CompositeDevice/CompositeDevice.ino new file mode 100644 index 00000000..af811420 --- /dev/null +++ b/libraries/USB/examples/CompositeDevice/CompositeDevice.ino @@ -0,0 +1,213 @@ +#include "USB.h" +#include "USBHIDMouse.h" +#include "USBHIDKeyboard.h" +#include "USBHIDGamepad.h" +#include "USBHIDConsumerControl.h" +#include "USBHIDSystemControl.h" +#include "USBHIDVendor.h" +#include "FirmwareMSC.h" + +#if !ARDUINO_USB_MSC_ON_BOOT +FirmwareMSC MSC_Update; +#endif +#if ARDUINO_USB_CDC_ON_BOOT +#define HWSerial Serial0 +#define USBSerial Serial +#else +#define HWSerial Serial +USBCDC USBSerial; +#endif + +USBHID HID; +USBHIDKeyboard Keyboard; +USBHIDMouse Mouse; +USBHIDGamepad Gamepad; +USBHIDConsumerControl ConsumerControl; +USBHIDSystemControl SystemControl; +USBHIDVendor Vendor; + +const int buttonPin = 0; +int previousButtonState = HIGH; + +static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){ + if(event_base == ARDUINO_USB_EVENTS){ + arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data; + switch (event_id){ + case ARDUINO_USB_STARTED_EVENT: + HWSerial.println("USB PLUGGED"); + break; + case ARDUINO_USB_STOPPED_EVENT: + HWSerial.println("USB UNPLUGGED"); + break; + case ARDUINO_USB_SUSPEND_EVENT: + HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en); + break; + case ARDUINO_USB_RESUME_EVENT: + HWSerial.println("USB RESUMED"); + break; + + default: + break; + } + } else if(event_base == ARDUINO_USB_CDC_EVENTS){ + arduino_usb_cdc_event_data_t * data = (arduino_usb_cdc_event_data_t*)event_data; + switch (event_id){ + case ARDUINO_USB_CDC_CONNECTED_EVENT: + HWSerial.println("CDC CONNECTED"); + break; + case ARDUINO_USB_CDC_DISCONNECTED_EVENT: + HWSerial.println("CDC DISCONNECTED"); + break; + case ARDUINO_USB_CDC_LINE_STATE_EVENT: + HWSerial.printf("CDC LINE STATE: dtr: %u, rts: %u\n", data->line_state.dtr, data->line_state.rts); + break; + case ARDUINO_USB_CDC_LINE_CODING_EVENT: + HWSerial.printf("CDC LINE CODING: bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u\n", data->line_coding.bit_rate, data->line_coding.data_bits, data->line_coding.stop_bits, data->line_coding.parity); + break; + case ARDUINO_USB_CDC_RX_EVENT: + HWSerial.printf("CDC RX [%u]:", data->rx.len); + { + uint8_t buf[data->rx.len]; + size_t len = USBSerial.read(buf, data->rx.len); + HWSerial.write(buf, len); + } + HWSerial.println(); + break; + + default: + break; + } + } else if(event_base == ARDUINO_FIRMWARE_MSC_EVENTS){ + arduino_firmware_msc_event_data_t * data = (arduino_firmware_msc_event_data_t*)event_data; + switch (event_id){ + case ARDUINO_FIRMWARE_MSC_START_EVENT: + HWSerial.println("MSC Update Start"); + break; + case ARDUINO_FIRMWARE_MSC_WRITE_EVENT: + //HWSerial.printf("MSC Update Write %u bytes at offset %u\n", data->write.size, data->write.offset); + HWSerial.print("."); + break; + case ARDUINO_FIRMWARE_MSC_END_EVENT: + HWSerial.printf("\nMSC Update End: %u bytes\n", data->end.size); + break; + case ARDUINO_FIRMWARE_MSC_ERROR_EVENT: + HWSerial.printf("MSC Update ERROR! Progress: %u bytes\n", data->error.size); + break; + case ARDUINO_FIRMWARE_MSC_POWER_EVENT: + HWSerial.printf("MSC Update Power: power: %u, start: %u, eject: %u\n", data->power.power_condition, data->power.start, data->power.load_eject); + break; + + default: + break; + } + } else if(event_base == ARDUINO_USB_HID_EVENTS){ + arduino_usb_hid_event_data_t * data = (arduino_usb_hid_event_data_t*)event_data; + switch (event_id){ + case ARDUINO_USB_HID_SET_PROTOCOL_EVENT: + HWSerial.printf("HID SET PROTOCOL: %s\n", data->set_protocol.protocol?"REPORT":"BOOT"); + break; + case ARDUINO_USB_HID_SET_IDLE_EVENT: + HWSerial.printf("HID SET IDLE: %u\n", data->set_idle.idle_rate); + break; + + default: + break; + } + } else if(event_base == ARDUINO_USB_HID_KEYBOARD_EVENTS){ + arduino_usb_hid_keyboard_event_data_t * data = (arduino_usb_hid_keyboard_event_data_t*)event_data; + switch (event_id){ + case ARDUINO_USB_HID_KEYBOARD_LED_EVENT: + HWSerial.printf("HID KEYBOARD LED: NumLock:%u, CapsLock:%u, ScrollLock:%u\n", data->numlock, data->capslock, data->scrolllock); + break; + + default: + break; + } + } else if(event_base == ARDUINO_USB_HID_VENDOR_EVENTS){ + arduino_usb_hid_vendor_event_data_t * data = (arduino_usb_hid_vendor_event_data_t*)event_data; + switch (event_id){ + case ARDUINO_USB_HID_VENDOR_GET_FEATURE_EVENT: + HWSerial.printf("HID VENDOR GET FEATURE: len:%u\n", data->len); + for(uint16_t i=0; ilen; i++){ + HWSerial.write(data->buffer[i]?data->buffer[i]:'.'); + } + HWSerial.println(); + break; + case ARDUINO_USB_HID_VENDOR_SET_FEATURE_EVENT: + HWSerial.printf("HID VENDOR SET FEATURE: len:%u\n", data->len); + for(uint16_t i=0; ilen; i++){ + HWSerial.write(data->buffer[i]?data->buffer[i]:'.'); + } + HWSerial.println(); + break; + case ARDUINO_USB_HID_VENDOR_OUTPUT_EVENT: + HWSerial.printf("HID VENDOR OUTPUT: len:%u\n", data->len); + for(uint16_t i=0; ilen; i++){ + HWSerial.write(Vendor.read()); + } + HWSerial.println(); + break; + + default: + break; + } + } +} + +void setup() { + HWSerial.begin(115200); + HWSerial.setDebugOutput(true); + + USB.onEvent(usbEventCallback); + USBSerial.onEvent(usbEventCallback); + MSC_Update.onEvent(usbEventCallback); + HID.onEvent(usbEventCallback); + Keyboard.onEvent(usbEventCallback); + Vendor.onEvent(usbEventCallback); + + USBSerial.begin(); + MSC_Update.begin(); + Vendor.begin(); + Mouse.begin(); + Keyboard.begin(); + Gamepad.begin(); + ConsumerControl.begin(); + SystemControl.begin(); + USB.begin(); +} + +void loop() { + int buttonState = digitalRead(buttonPin); + if (HID.ready() && buttonState != previousButtonState) { + previousButtonState = buttonState; + if (buttonState == LOW) { + HWSerial.println("Button Pressed"); + USBSerial.println("Button Pressed"); + Vendor.println("Button Pressed"); + Mouse.move(10,10); + Keyboard.pressRaw(HID_KEY_CAPS_LOCK); + Gamepad.leftStick(100,100); + ConsumerControl.press(CONSUMER_CONTROL_VOLUME_INCREMENT); + //SystemControl.press(SYSTEM_CONTROL_POWER_OFF); + } else { + Keyboard.releaseRaw(HID_KEY_CAPS_LOCK); + Gamepad.leftStick(0,0); + ConsumerControl.release(); + //SystemControl.release(); + Vendor.println("Button Released"); + USBSerial.println("Button Released"); + HWSerial.println("Button Released"); + } + delay(100); + } + + while(HWSerial.available()){ + size_t l = HWSerial.available(); + uint8_t b[l]; + l = HWSerial.read(b, l); + USBSerial.write(b, l); + if(HID.ready()){ + Vendor.write(b,l); + } + } +} diff --git a/libraries/USB/examples/ConsumerControl/.skip.esp32 b/libraries/USB/examples/ConsumerControl/.skip.esp32 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/ConsumerControl/.skip.esp32c3 b/libraries/USB/examples/ConsumerControl/.skip.esp32c3 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/ConsumerControl/ConsumerControl.ino b/libraries/USB/examples/ConsumerControl/ConsumerControl.ino new file mode 100644 index 00000000..ab6fb19d --- /dev/null +++ b/libraries/USB/examples/ConsumerControl/ConsumerControl.ino @@ -0,0 +1,21 @@ +#include "USB.h" +#include "USBHIDConsumerControl.h" +USBHIDConsumerControl ConsumerControl; + +const int buttonPin = 0; +int previousButtonState = HIGH; + +void setup() { + pinMode(buttonPin, INPUT_PULLUP); + ConsumerControl.begin(); + USB.begin(); +} + +void loop() { + int buttonState = digitalRead(buttonPin); + if ((buttonState != previousButtonState) && (buttonState == LOW)) { + ConsumerControl.press(CONSUMER_CONTROL_VOLUME_INCREMENT); + ConsumerControl.release(); + } + previousButtonState = buttonState; +} diff --git a/libraries/USB/examples/CustomHIDDevice/.skip.esp32 b/libraries/USB/examples/CustomHIDDevice/.skip.esp32 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/CustomHIDDevice/.skip.esp32c3 b/libraries/USB/examples/CustomHIDDevice/.skip.esp32c3 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/CustomHIDDevice/CustomHIDDevice.ino b/libraries/USB/examples/CustomHIDDevice/CustomHIDDevice.ino new file mode 100644 index 00000000..15d69b6a --- /dev/null +++ b/libraries/USB/examples/CustomHIDDevice/CustomHIDDevice.ino @@ -0,0 +1,79 @@ +#include "USB.h" +#include "USBHID.h" +USBHID HID; + +static const uint8_t report_descriptor[] = { // 8 axis + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x04, // Usage (Joystick) + 0xa1, 0x01, // Collection (Application) + 0xa1, 0x00, // Collection (Physical) + 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x32, // Usage (Z) + 0x09, 0x33, // Usage (Rx) + 0x09, 0x34, // Usage (Ry) + 0x09, 0x35, // Usage (Rz) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7f, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8) + 0x95, 0x08, // Report Count (8) + 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0xC0, // End Collection + 0xC0, // End Collection +}; + +class CustomHIDDevice: public USBHIDDevice { +public: + CustomHIDDevice(void){ + static bool initialized = false; + if(!initialized){ + initialized = true; + HID.addDevice(this, sizeof(report_descriptor)); + } + } + + void begin(void){ + HID.begin(); + } + + uint16_t _onGetDescriptor(uint8_t* buffer){ + memcpy(buffer, report_descriptor, sizeof(report_descriptor)); + return sizeof(report_descriptor); + } + + bool send(uint8_t * value){ + return HID.SendReport(0, value, 8); + } +}; + +CustomHIDDevice Device; + +const int buttonPin = 0; +int previousButtonState = HIGH; +uint8_t axis[8]; + +void setup() { + Serial.begin(115200); + Serial.setDebugOutput(true); + pinMode(buttonPin, INPUT_PULLUP); + Device.begin(); + USB.begin(); +} + +void loop() { + int buttonState = digitalRead(buttonPin); + if (HID.ready() && buttonState != previousButtonState) { + previousButtonState = buttonState; + if (buttonState == LOW) { + Serial.println("Button Pressed"); + axis[0] = random() & 0xFF; + Device.send(axis); + } else { + Serial.println("Button Released"); + } + delay(100); + } +} diff --git a/libraries/USB/examples/Gamepad/.skip.esp32 b/libraries/USB/examples/Gamepad/.skip.esp32 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/Gamepad/.skip.esp32c3 b/libraries/USB/examples/Gamepad/.skip.esp32c3 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/Gamepad/Gamepad.ino b/libraries/USB/examples/Gamepad/Gamepad.ino new file mode 100644 index 00000000..da9ff69e --- /dev/null +++ b/libraries/USB/examples/Gamepad/Gamepad.ino @@ -0,0 +1,21 @@ +#include "USB.h" +#include "USBHIDGamepad.h" +USBHIDGamepad Gamepad; + +const int buttonPin = 0; +int previousButtonState = HIGH; + +void setup() { + pinMode(buttonPin, INPUT_PULLUP); + Gamepad.begin(); + USB.begin(); +} + +void loop() { + int buttonState = digitalRead(buttonPin); + if ((buttonState != previousButtonState) && (buttonState == LOW)) { + Gamepad.pressButton(BUTTON_START); + Gamepad.releaseButton(BUTTON_START); + } + previousButtonState = buttonState; +} diff --git a/libraries/USB/examples/HIDVendor/.skip.esp32 b/libraries/USB/examples/HIDVendor/.skip.esp32 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/HIDVendor/.skip.esp32c3 b/libraries/USB/examples/HIDVendor/.skip.esp32c3 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/HIDVendor/HIDVendor.ino b/libraries/USB/examples/HIDVendor/HIDVendor.ino new file mode 100644 index 00000000..223edba1 --- /dev/null +++ b/libraries/USB/examples/HIDVendor/HIDVendor.ino @@ -0,0 +1,52 @@ +#include "USB.h" +#include "USBHIDVendor.h" +USBHIDVendor Vendor; + +const int buttonPin = 0; +int previousButtonState = HIGH; + +static void vendorEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data){ + if(event_base == ARDUINO_USB_HID_VENDOR_EVENTS){ + arduino_usb_hid_vendor_event_data_t * data = (arduino_usb_hid_vendor_event_data_t*)event_data; + switch (event_id){ + case ARDUINO_USB_HID_VENDOR_GET_FEATURE_EVENT: + Serial.printf("HID VENDOR GET FEATURE: len:%u\n", data->len); + break; + case ARDUINO_USB_HID_VENDOR_SET_FEATURE_EVENT: + Serial.printf("HID VENDOR SET FEATURE: len:%u\n", data->len); + for(uint16_t i=0; ilen; i++){ + Serial.printf("0x%02X ",data->buffer); + } + Serial.println(); + break; + case ARDUINO_USB_HID_VENDOR_OUTPUT_EVENT: + Serial.printf("HID VENDOR OUTPUT: len:%u\n", data->len); + // for(uint16_t i=0; ilen; i++){ + // Serial.write(Vendor.read()); + // } + break; + + default: + break; + } + } +} + +void setup() { + pinMode(buttonPin, INPUT_PULLUP); + Serial.begin(115200); + Vendor.onEvent(vendorEventCallback); + Vendor.begin(); + USB.begin(); +} + +void loop() { + int buttonState = digitalRead(buttonPin); + if ((buttonState != previousButtonState) && (buttonState == LOW)) { + Vendor.println("Hello World!"); + } + previousButtonState = buttonState; + while(Vendor.available()){ + Serial.write(Vendor.read()); + } +} diff --git a/libraries/USB/examples/Keyboard/KeyboardLogout/.skip.esp32 b/libraries/USB/examples/Keyboard/KeyboardLogout/.skip.esp32 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/Keyboard/KeyboardLogout/.skip.esp32c3 b/libraries/USB/examples/Keyboard/KeyboardLogout/.skip.esp32c3 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/Keyboard/KeyboardLogout/KeyboardLogout.ino b/libraries/USB/examples/Keyboard/KeyboardLogout/KeyboardLogout.ino new file mode 100644 index 00000000..a7f8214b --- /dev/null +++ b/libraries/USB/examples/Keyboard/KeyboardLogout/KeyboardLogout.ino @@ -0,0 +1,92 @@ +/* + Keyboard logout + + This sketch demonstrates the Keyboard library. + + When you connect pin 2 to ground, it performs a logout. + It uses keyboard combinations to do this, as follows: + + On Windows, CTRL-ALT-DEL followed by ALT-l + On Ubuntu, CTRL-ALT-DEL, and ENTER + On OSX, CMD-SHIFT-q + + To wake: Spacebar. + + Circuit: + - Arduino Leonardo or Micro + - wire to connect D2 to ground + + created 6 Mar 2012 + modified 27 Mar 2012 + by Tom Igoe + + This example is in the public domain. + + http://www.arduino.cc/en/Tutorial/KeyboardLogout +*/ + +#define OSX 0 +#define WINDOWS 1 +#define UBUNTU 2 + +#include "USB.h" +#include "USBHIDKeyboard.h" +USBHIDKeyboard Keyboard; + +// change this to match your platform: +int platform = OSX; + +void setup() { + // make pin 0 an input and turn on the pull-up resistor so it goes high unless + // connected to ground: + pinMode(0, INPUT_PULLUP); + Keyboard.begin(); + USB.begin(); +} + +void loop() { + while (digitalRead(0) == HIGH) { + // do nothing until pin 2 goes low + delay(500); + } + delay(1000); + + switch (platform) { + case OSX: + Keyboard.press(KEY_LEFT_GUI); + // Shift-Q logs out: + Keyboard.press(KEY_LEFT_SHIFT); + Keyboard.press('Q'); + delay(100); + Keyboard.releaseAll(); + // enter: + Keyboard.write(KEY_RETURN); + break; + case WINDOWS: + // CTRL-ALT-DEL: + Keyboard.press(KEY_LEFT_CTRL); + Keyboard.press(KEY_LEFT_ALT); + Keyboard.press(KEY_DELETE); + delay(100); + Keyboard.releaseAll(); + // ALT-l: + delay(2000); + Keyboard.press(KEY_LEFT_ALT); + Keyboard.press('l'); + Keyboard.releaseAll(); + break; + case UBUNTU: + // CTRL-ALT-DEL: + Keyboard.press(KEY_LEFT_CTRL); + Keyboard.press(KEY_LEFT_ALT); + Keyboard.press(KEY_DELETE); + delay(1000); + Keyboard.releaseAll(); + // Enter to confirm logout: + Keyboard.write(KEY_RETURN); + break; + } + + // do nothing: + while (true) delay(1000); +} diff --git a/libraries/USB/examples/Keyboard/KeyboardMessage/.skip.esp32 b/libraries/USB/examples/Keyboard/KeyboardMessage/.skip.esp32 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/Keyboard/KeyboardMessage/.skip.esp32c3 b/libraries/USB/examples/Keyboard/KeyboardMessage/.skip.esp32c3 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/Keyboard/KeyboardMessage/KeyboardMessage.ino b/libraries/USB/examples/Keyboard/KeyboardMessage/KeyboardMessage.ino new file mode 100644 index 00000000..30f92a47 --- /dev/null +++ b/libraries/USB/examples/Keyboard/KeyboardMessage/KeyboardMessage.ino @@ -0,0 +1,55 @@ +/* + Keyboard Message test + + For the Arduino Leonardo and Micro. + + Sends a text string when a button is pressed. + + The circuit: + - pushbutton attached from pin 0 to ground + - 10 kilohm resistor attached from pin 0 to +5V + + created 24 Oct 2011 + modified 27 Mar 2012 + by Tom Igoe + modified 11 Nov 2013 + by Scott Fitzgerald + + This example code is in the public domain. + + http://www.arduino.cc/en/Tutorial/KeyboardMessage +*/ + +#include "USB.h" +#include "USBHIDKeyboard.h" +USBHIDKeyboard Keyboard; + +const int buttonPin = 0; // input pin for pushbutton +int previousButtonState = HIGH; // for checking the state of a pushButton +int counter = 0; // button push counter + +void setup() { + // make the pushButton pin an input: + pinMode(buttonPin, INPUT_PULLUP); + // initialize control over the keyboard: + Keyboard.begin(); + USB.begin(); +} + +void loop() { + // read the pushbutton: + int buttonState = digitalRead(buttonPin); + // if the button state has changed, + if ((buttonState != previousButtonState) + // and it's currently pressed: + && (buttonState == LOW)) { + // increment the button counter + counter++; + // type out a message + Keyboard.print("You pressed the button "); + Keyboard.print(counter); + Keyboard.println(" times."); + } + // save the current button state for comparison next time: + previousButtonState = buttonState; +} diff --git a/libraries/USB/examples/Keyboard/KeyboardReprogram/.skip.esp32 b/libraries/USB/examples/Keyboard/KeyboardReprogram/.skip.esp32 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/Keyboard/KeyboardReprogram/.skip.esp32c3 b/libraries/USB/examples/Keyboard/KeyboardReprogram/.skip.esp32c3 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/Keyboard/KeyboardReprogram/KeyboardReprogram.ino b/libraries/USB/examples/Keyboard/KeyboardReprogram/KeyboardReprogram.ino new file mode 100644 index 00000000..3c652055 --- /dev/null +++ b/libraries/USB/examples/Keyboard/KeyboardReprogram/KeyboardReprogram.ino @@ -0,0 +1,106 @@ +/* + Arduino Programs Blink + + This sketch demonstrates the Keyboard library. + + For Leonardo and Due boards only. + + When you connect pin 2 to ground, it creates a new window with a key + combination (CTRL-N), then types in the Blink sketch, then auto-formats the + text using another key combination (CTRL-T), then uploads the sketch to the + currently selected Arduino using a final key combination (CTRL-U). + + Circuit: + - Arduino Leonardo, Micro, Due, LilyPad USB, or Yún + - wire to connect D2 to ground + + created 5 Mar 2012 + modified 29 Mar 2012 + by Tom Igoe + modified 3 May 2014 + by Scott Fitzgerald + + This example is in the public domain. + + http://www.arduino.cc/en/Tutorial/KeyboardReprogram +*/ + +#include "USB.h" +#include "USBHIDKeyboard.h" +USBHIDKeyboard Keyboard; + +// use this option for OSX. +// Comment it out if using Windows or Linux: +char ctrlKey = KEY_LEFT_GUI; +// use this option for Windows and Linux. +// leave commented out if using OSX: +// char ctrlKey = KEY_LEFT_CTRL; + + +void setup() { + // make pin 0 an input and turn on the pull-up resistor so it goes high unless + // connected to ground: + pinMode(0, INPUT_PULLUP); + // initialize control over the keyboard: + Keyboard.begin(); + USB.begin(); +} + +void loop() { + while (digitalRead(0) == HIGH) { + // do nothing until pin 0 goes low + delay(500); + } + delay(1000); + // new document: + Keyboard.press(ctrlKey); + Keyboard.press('n'); + delay(100); + Keyboard.releaseAll(); + // wait for new window to open: + delay(1000); + + // versions of the Arduino IDE after 1.5 pre-populate new sketches with + // setup() and loop() functions let's clear the window before typing anything new + // select all + Keyboard.press(ctrlKey); + Keyboard.press('a'); + delay(500); + Keyboard.releaseAll(); + // delete the selected text + Keyboard.write(KEY_BACKSPACE); + delay(500); + + // Type out "blink": + Keyboard.println("void setup() {"); + Keyboard.println("pinMode(13, OUTPUT);"); + Keyboard.println("}"); + Keyboard.println(); + Keyboard.println("void loop() {"); + Keyboard.println("digitalWrite(13, HIGH);"); + Keyboard.print("delay(3000);"); + // 3000 ms is too long. Delete it: + for (int keystrokes = 0; keystrokes < 6; keystrokes++) { + delay(500); + Keyboard.write(KEY_BACKSPACE); + } + // make it 1000 instead: + Keyboard.println("1000);"); + Keyboard.println("digitalWrite(13, LOW);"); + Keyboard.println("delay(1000);"); + Keyboard.println("}"); + // tidy up: + Keyboard.press(ctrlKey); + Keyboard.press('t'); + delay(100); + Keyboard.releaseAll(); + delay(3000); + // upload code: + Keyboard.press(ctrlKey); + Keyboard.press('u'); + delay(100); + Keyboard.releaseAll(); + + // wait for the sweet oblivion of reprogramming: + while (true)delay(1000); +} diff --git a/libraries/USB/examples/Keyboard/KeyboardSerial/.skip.esp32 b/libraries/USB/examples/Keyboard/KeyboardSerial/.skip.esp32 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/Keyboard/KeyboardSerial/.skip.esp32c3 b/libraries/USB/examples/Keyboard/KeyboardSerial/.skip.esp32c3 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/Keyboard/KeyboardSerial/KeyboardSerial.ino b/libraries/USB/examples/Keyboard/KeyboardSerial/KeyboardSerial.ino new file mode 100644 index 00000000..e3bb8769 --- /dev/null +++ b/libraries/USB/examples/Keyboard/KeyboardSerial/KeyboardSerial.ino @@ -0,0 +1,40 @@ +/* + Keyboard test + + Reads a byte from the serial port, sends a keystroke back. + The sent keystroke is one higher than what's received, e.g. if you send a, + you get b, send A you get B, and so forth. + + The circuit: + - none + + created 21 Oct 2011 + modified 27 Mar 2012 + by Tom Igoe + + This example code is in the public domain. + + http://www.arduino.cc/en/Tutorial/KeyboardSerial +*/ + +#include "USB.h" +#include "USBHIDKeyboard.h" +USBHIDKeyboard Keyboard; + +void setup() { + // open the serial port: + Serial.begin(115200); + // initialize control over the keyboard: + Keyboard.begin(); + USB.begin(); +} + +void loop() { + // check for incoming serial data: + if (Serial.available() > 0) { + // read incoming serial data: + char inChar = Serial.read(); + // Type the next ASCII value from what you received: + Keyboard.write(inChar + 1); + } +} diff --git a/libraries/USB/examples/KeyboardAndMouseControl/.skip.esp32 b/libraries/USB/examples/KeyboardAndMouseControl/.skip.esp32 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/KeyboardAndMouseControl/.skip.esp32c3 b/libraries/USB/examples/KeyboardAndMouseControl/.skip.esp32c3 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/KeyboardAndMouseControl/KeyboardAndMouseControl.ino b/libraries/USB/examples/KeyboardAndMouseControl/KeyboardAndMouseControl.ino new file mode 100644 index 00000000..6cf564bb --- /dev/null +++ b/libraries/USB/examples/KeyboardAndMouseControl/KeyboardAndMouseControl.ino @@ -0,0 +1,95 @@ +/* + KeyboardAndMouseControl + + Hardware: + - five pushbuttons attached to D12, D13, D14, D15, D0 + + The mouse movement is always relative. This sketch reads four pushbuttons, and + uses them to set the movement of the mouse. + + WARNING: When you use the Mouse.move() command, the Arduino takes over your + mouse! Make sure you have control before you use the mouse commands. + + created 15 Mar 2012 + modified 27 Mar 2012 + by Tom Igoe + + This example code is in the public domain. + + http://www.arduino.cc/en/Tutorial/KeyboardAndMouseControl +*/ + +#include "USB.h" +#include "USBHIDMouse.h" +#include "USBHIDKeyboard.h" +USBHIDMouse Mouse; +USBHIDKeyboard Keyboard; + +// set pin numbers for the five buttons: +const int upButton = 12; +const int downButton = 13; +const int leftButton = 14; +const int rightButton = 15; +const int mouseButton = 0; + +void setup() { // initialize the buttons' inputs: + pinMode(upButton, INPUT_PULLUP); + pinMode(downButton, INPUT_PULLUP); + pinMode(leftButton, INPUT_PULLUP); + pinMode(rightButton, INPUT_PULLUP); + pinMode(mouseButton, INPUT_PULLUP); + + Serial.begin(115200); + // initialize mouse control: + Mouse.begin(); + Keyboard.begin(); + USB.begin(); +} + +void loop() { + // use serial input to control the mouse: + if (Serial.available() > 0) { + char inChar = Serial.read(); + + switch (inChar) { + case 'u': + // move mouse up + Mouse.move(0, -40); + break; + case 'd': + // move mouse down + Mouse.move(0, 40); + break; + case 'l': + // move mouse left + Mouse.move(-40, 0); + break; + case 'r': + // move mouse right + Mouse.move(40, 0); + break; + case 'm': + // perform mouse left click + Mouse.click(MOUSE_LEFT); + break; + } + } + + // use the pushbuttons to control the keyboard: + if (digitalRead(upButton) == LOW) { + Keyboard.write('u'); + } + if (digitalRead(downButton) == LOW) { + Keyboard.write('d'); + } + if (digitalRead(leftButton) == LOW) { + Keyboard.write('l'); + } + if (digitalRead(rightButton) == LOW) { + Keyboard.write('r'); + } + if (digitalRead(mouseButton) == LOW) { + Keyboard.write('m'); + } + delay(5); +} diff --git a/libraries/USB/examples/Mouse/ButtonMouseControl/.skip.esp32 b/libraries/USB/examples/Mouse/ButtonMouseControl/.skip.esp32 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/Mouse/ButtonMouseControl/.skip.esp32c3 b/libraries/USB/examples/Mouse/ButtonMouseControl/.skip.esp32c3 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/Mouse/ButtonMouseControl/ButtonMouseControl.ino b/libraries/USB/examples/Mouse/ButtonMouseControl/ButtonMouseControl.ino new file mode 100644 index 00000000..0a7ee0ca --- /dev/null +++ b/libraries/USB/examples/Mouse/ButtonMouseControl/ButtonMouseControl.ino @@ -0,0 +1,86 @@ +/* + ButtonMouseControl + + Controls the mouse from five pushbuttons on an Arduino Leonardo, Micro or Due. + + Hardware: + - five pushbuttons attached to D12, D13, D14, D15, D0 + + The mouse movement is always relative. This sketch reads four pushbuttons, + and uses them to set the movement of the mouse. + + WARNING: When you use the Mouse.move() command, the Arduino takes over your + mouse! Make sure you have control before you use the mouse commands. + + created 15 Mar 2012 + modified 27 Mar 2012 + by Tom Igoe + + This example code is in the public domain. + + http://www.arduino.cc/en/Tutorial/ButtonMouseControl +*/ + +#include "USB.h" +#include "USBHIDMouse.h" +USBHIDMouse Mouse; + + +// set pin numbers for the five buttons: +const int upButton = 12; +const int downButton = 13; +const int leftButton = 14; +const int rightButton = 15; +const int mouseButton = 0; + +int range = 5; // output range of X or Y movement; affects movement speed +int responseDelay = 10; // response delay of the mouse, in ms + + +void setup() { + // initialize the buttons' inputs: + pinMode(upButton, INPUT_PULLUP); + pinMode(downButton, INPUT_PULLUP); + pinMode(leftButton, INPUT_PULLUP); + pinMode(rightButton, INPUT_PULLUP); + pinMode(mouseButton, INPUT_PULLUP); + // initialize mouse control: + Mouse.begin(); + USB.begin(); +} + +void loop() { + // read the buttons: + int upState = digitalRead(upButton); + int downState = digitalRead(downButton); + int rightState = digitalRead(rightButton); + int leftState = digitalRead(leftButton); + int clickState = digitalRead(mouseButton); + + // calculate the movement distance based on the button states: + int xDistance = (leftState - rightState) * range; + int yDistance = (upState - downState) * range; + + // if X or Y is non-zero, move: + if ((xDistance != 0) || (yDistance != 0)) { + Mouse.move(xDistance, yDistance, 0); + } + + // if the mouse button is pressed: + if (clickState == LOW) { + // if the mouse is not pressed, press it: + if (!Mouse.isPressed(MOUSE_LEFT)) { + Mouse.press(MOUSE_LEFT); + } + } + // else the mouse button is not pressed: + else { + // if the mouse is pressed, release it: + if (Mouse.isPressed(MOUSE_LEFT)) { + Mouse.release(MOUSE_LEFT); + } + } + + // a delay so the mouse doesn't move too fast: + delay(responseDelay); +} diff --git a/libraries/USB/examples/SystemControl/.skip.esp32 b/libraries/USB/examples/SystemControl/.skip.esp32 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/SystemControl/.skip.esp32c3 b/libraries/USB/examples/SystemControl/.skip.esp32c3 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/SystemControl/SystemControl.ino b/libraries/USB/examples/SystemControl/SystemControl.ino new file mode 100644 index 00000000..7d644ea9 --- /dev/null +++ b/libraries/USB/examples/SystemControl/SystemControl.ino @@ -0,0 +1,21 @@ +#include "USB.h" +#include "USBHIDSystemControl.h" +USBHIDSystemControl SystemControl; + +const int buttonPin = 0; +int previousButtonState = HIGH; + +void setup() { + pinMode(buttonPin, INPUT_PULLUP); + SystemControl.begin(); + USB.begin(); +} + +void loop() { + int buttonState = digitalRead(buttonPin); + if ((buttonState != previousButtonState) && (buttonState == LOW)) { + SystemControl.press(SYSTEM_CONTROL_POWER_OFF); + SystemControl.release(); + } + previousButtonState = buttonState; +} diff --git a/libraries/USB/examples/USBVendor/.skip.esp32 b/libraries/USB/examples/USBVendor/.skip.esp32 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/USBVendor/.skip.esp32c3 b/libraries/USB/examples/USBVendor/.skip.esp32c3 new file mode 100644 index 00000000..e69de29b diff --git a/libraries/USB/examples/USBVendor/USBVendor.ino b/libraries/USB/examples/USBVendor/USBVendor.ino new file mode 100644 index 00000000..f8976a07 --- /dev/null +++ b/libraries/USB/examples/USBVendor/USBVendor.ino @@ -0,0 +1,191 @@ +#include "USB.h" +#include "USBVendor.h" + +#if ARDUINO_USB_CDC_ON_BOOT +#define HWSerial Serial0 +#else +#define HWSerial Serial +#endif + +USBVendor Vendor; +const int buttonPin = 0; + +//CDC Control Requests +#define REQUEST_SET_LINE_CODING 0x20 +#define REQUEST_GET_LINE_CODING 0x21 +#define REQUEST_SET_CONTROL_LINE_STATE 0x22 + +//CDC Line Coding Control Request Structure +typedef struct __attribute__ ((packed)) { + uint32_t bit_rate; + uint8_t stop_bits; //0: 1 stop bit, 1: 1.5 stop bits, 2: 2 stop bits + uint8_t parity; //0: None, 1: Odd, 2: Even, 3: Mark, 4: Space + uint8_t data_bits; //5, 6, 7, 8 or 16 +} request_line_coding_t; + +static request_line_coding_t vendor_line_coding = {9600, 0, 0, 8}; + +// Bit 0: DTR (Data Terminal Ready), Bit 1: RTS (Request to Send) +static uint8_t vendor_line_state = 0; + +//USB and Vendor events +static void usbEventCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + if (event_base == ARDUINO_USB_EVENTS) { + arduino_usb_event_data_t * data = (arduino_usb_event_data_t*)event_data; + switch (event_id) { + case ARDUINO_USB_STARTED_EVENT: + HWSerial.println("USB PLUGGED"); + break; + case ARDUINO_USB_STOPPED_EVENT: + HWSerial.println("USB UNPLUGGED"); + break; + case ARDUINO_USB_SUSPEND_EVENT: + HWSerial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en); + break; + case ARDUINO_USB_RESUME_EVENT: + HWSerial.println("USB RESUMED"); + break; + + default: + break; + } + } else if (event_base == ARDUINO_USB_VENDOR_EVENTS) { + arduino_usb_vendor_event_data_t * data = (arduino_usb_vendor_event_data_t*)event_data; + switch (event_id) { + case ARDUINO_USB_VENDOR_DATA_EVENT: + HWSerial.printf("Vendor RX: len:%u\n", data->data.len); + for (uint16_t i = 0; i < data->data.len; i++) { + HWSerial.write(Vendor.read()); + } + HWSerial.println(); + break; + + default: + break; + } + } +} + +static const char * strRequestDirections[] = {"OUT", "IN"}; +static const char * strRequestTypes[] = {"STANDARD", "CLASS", "VENDOR", "INVALID"}; +static const char * strRequestRecipients[] = {"DEVICE", "INTERFACE", "ENDPOINT", "OTHER"}; +static const char * strRequestStages[] = {"SETUP", "DATA", "ACK"}; + +//Handle USB requests to the vendor interface +bool vendorRequestCallback(uint8_t rhport, uint8_t requestStage, arduino_usb_control_request_t const * request) { + HWSerial.printf("Vendor Request: Stage: %5s, Direction: %3s, Type: %8s, Recipient: %9s, bRequest: 0x%02x, wValue: 0x%04x, wIndex: %u, wLength: %u\n", + strRequestStages[requestStage], + strRequestDirections[request->bmRequestDirection], + strRequestTypes[request->bmRequestType], + strRequestRecipients[request->bmRequestRecipient], + request->bRequest, request->wValue, request->wIndex, request->wLength); + + bool result = false; + + if (request->bmRequestDirection == REQUEST_DIRECTION_OUT && + request->bmRequestType == REQUEST_TYPE_STANDARD && + request->bmRequestRecipient == REQUEST_RECIPIENT_INTERFACE && + request->bRequest == 0x0b + ) { + if (requestStage == REQUEST_STAGE_SETUP) { + // response with status OK + result = Vendor.sendResponse(rhport, request); + } else { + result = true; + } + } else + //Implement CDC Control Requests + if (request->bmRequestType == REQUEST_TYPE_CLASS && request->bmRequestRecipient == REQUEST_RECIPIENT_DEVICE) { + switch (request->bRequest) { + + case REQUEST_SET_LINE_CODING: //0x20 + // Accept only direction OUT with data size 7 + if (request->wLength != sizeof(request_line_coding_t) || request->bmRequestDirection != REQUEST_DIRECTION_OUT) { + break; + } + if (requestStage == REQUEST_STAGE_SETUP) { + //Send the response in setup stage (it will write the data to vendor_line_coding in the DATA stage) + result = Vendor.sendResponse(rhport, request, (void*) &vendor_line_coding, sizeof(request_line_coding_t)); + } else if (requestStage == REQUEST_STAGE_ACK) { + //In the ACK stage the response is complete + HWSerial.printf("Vendor Line Coding: bit_rate: %u, data_bits: %u, stop_bits: %u, parity: %u\n", vendor_line_coding.bit_rate, vendor_line_coding.data_bits, vendor_line_coding.stop_bits, vendor_line_coding.parity); + } + result = true; + break; + + case REQUEST_GET_LINE_CODING: //0x21 + // Accept only direction IN with data size 7 + if (request->wLength != sizeof(request_line_coding_t) || request->bmRequestDirection != REQUEST_DIRECTION_IN) { + break; + } + if (requestStage == REQUEST_STAGE_SETUP) { + //Send the response in setup stage (it will write the data to vendor_line_coding in the DATA stage) + result = Vendor.sendResponse(rhport, request, (void*) &vendor_line_coding, sizeof(request_line_coding_t)); + } + result = true; + break; + + case REQUEST_SET_CONTROL_LINE_STATE: //0x22 + // Accept only direction OUT with data size 0 + if (request->wLength != 0 || request->bmRequestDirection != REQUEST_DIRECTION_OUT) { + break; + } + if (requestStage == REQUEST_STAGE_SETUP) { + //Send the response in setup stage + vendor_line_state = request->wValue; + result = Vendor.sendResponse(rhport, request); + } else if (requestStage == REQUEST_STAGE_ACK) { + //In the ACK stage the response is complete + bool dtr = (vendor_line_state & 1) != 0; + bool rts = (vendor_line_state & 2) != 0; + HWSerial.printf("Vendor Line State: dtr: %u, rts: %u\n", dtr, rts); + } + result = true; + break; + + default: + // stall unknown request + break; + } + } + + return result; +} + +void setup() { + pinMode(buttonPin, INPUT_PULLUP); + HWSerial.begin(115200); + HWSerial.setDebugOutput(true); + + Vendor.onEvent(usbEventCallback); + Vendor.onRequest(vendorRequestCallback); + Vendor.begin(); + + USB.onEvent(usbEventCallback); + USB.webUSB(true); + USB.webUSBURL("http://localhost/webusb"); + USB.begin(); +} + +void loop() { + static int previousButtonState = HIGH; + int buttonState = digitalRead(buttonPin); + if (buttonState != previousButtonState) { + previousButtonState = buttonState; + if (buttonState == LOW) { + HWSerial.println("Button Pressed"); + Vendor.println("Button Pressed"); + } else { + Vendor.println("Button Released"); + HWSerial.println("Button Released"); + } + delay(100); + } + + while (HWSerial.available()) { + size_t l = HWSerial.available(); + uint8_t b[l]; + l = HWSerial.read(b, l); + Vendor.write(b, l); + } +} diff --git a/libraries/USB/src/USBHID.cpp b/libraries/USB/src/USBHID.cpp new file mode 100644 index 00000000..2d497f22 --- /dev/null +++ b/libraries/USB/src/USBHID.cpp @@ -0,0 +1,365 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// 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 "USBHID.h" + +#if CONFIG_TINYUSB_HID_ENABLED + +#include "esp32-hal-tinyusb.h" +#include "USB.h" +#include "esp_hid_common.h" + +#define USB_HID_DEVICES_MAX 10 + +ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_EVENTS); +esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait); +esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg); + +typedef struct { + USBHIDDevice * device; + uint8_t reports_num; + uint8_t * report_ids; +} tinyusb_hid_device_t; + +static tinyusb_hid_device_t tinyusb_hid_devices[USB_HID_DEVICES_MAX]; + +static uint8_t tinyusb_hid_devices_num = 0; +static bool tinyusb_hid_devices_is_initialized = false; +static xSemaphoreHandle tinyusb_hid_device_input_sem = NULL; +static xSemaphoreHandle tinyusb_hid_device_input_mutex = NULL; + +static bool tinyusb_hid_is_initialized = false; +static uint8_t tinyusb_loaded_hid_devices_num = 0; +static uint16_t tinyusb_hid_device_descriptor_len = 0; +static uint8_t * tinyusb_hid_device_descriptor = NULL; +static const char * tinyusb_hid_device_report_types[4] = {"INVALID", "INPUT", "OUTPUT", "FEATURE"}; + +static bool tinyusb_enable_hid_device(uint16_t descriptor_len, USBHIDDevice * device){ + if(tinyusb_hid_is_initialized){ + log_e("TinyUSB HID has already started! Device not enabled"); + return false; + } + if(tinyusb_loaded_hid_devices_num >= USB_HID_DEVICES_MAX){ + log_e("Maximum devices already enabled! Device not enabled"); + return false; + } + tinyusb_hid_device_descriptor_len += descriptor_len; + tinyusb_hid_devices[tinyusb_loaded_hid_devices_num++].device = device; + + log_d("Device[%u] len: %u", tinyusb_loaded_hid_devices_num-1, descriptor_len); + return true; +} + +USBHIDDevice * tinyusb_get_device_by_report_id(uint8_t report_id){ + for(uint8_t i=0; idevice && device->reports_num){ + for(uint8_t r=0; rreports_num; r++){ + if(report_id == device->report_ids[r]){ + return device->device; + } + } + } + } + return NULL; +} + +static uint16_t tinyusb_on_get_feature(uint8_t report_id, uint8_t* buffer, uint16_t reqlen){ + USBHIDDevice * device = tinyusb_get_device_by_report_id(report_id); + if(device){ + return device->_onGetFeature(report_id, buffer, reqlen); + } + return 0; +} + +static bool tinyusb_on_set_feature(uint8_t report_id, const uint8_t* buffer, uint16_t reqlen){ + USBHIDDevice * device = tinyusb_get_device_by_report_id(report_id); + if(device){ + device->_onSetFeature(report_id, buffer, reqlen); + return true; + } + return false; +} + +static bool tinyusb_on_set_output(uint8_t report_id, const uint8_t* buffer, uint16_t reqlen){ + USBHIDDevice * device = tinyusb_get_device_by_report_id(report_id); + if(device){ + device->_onOutput(report_id, buffer, reqlen); + return true; + } + return false; +} + +static uint16_t tinyusb_on_add_descriptor(uint8_t device_index, uint8_t * dst){ + uint16_t res = 0; + uint8_t report_id = 0, reports_num = 0; + tinyusb_hid_device_t * device = &tinyusb_hid_devices[device_index]; + if(device->device){ + res = device->device->_onGetDescriptor(dst); + if(res){ + + esp_hid_report_map_t *hid_report_map = esp_hid_parse_report_map(dst, res); + if(hid_report_map){ + if(device->report_ids){ + free(device->report_ids); + } + device->reports_num = hid_report_map->reports_len; + device->report_ids = (uint8_t*)malloc(device->reports_num); + memset(device->report_ids, 0, device->reports_num); + reports_num = device->reports_num; + + for(uint8_t i=0; ireports_num; i++){ + if(hid_report_map->reports[i].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT){ + report_id = hid_report_map->reports[i].report_id; + for(uint8_t r=0; rreports_num; r++){ + if(!report_id){ + //todo: handle better when device has no report ID set + break; + } else if(report_id == device->report_ids[r]){ + //already added + reports_num--; + break; + } else if(!device->report_ids[r]){ + //empty slot + device->report_ids[r] = report_id; + break; + } + } + } else { + reports_num--; + } + } + device->reports_num = reports_num; + esp_hid_free_report_map(hid_report_map); + } + + } + } + return res; +} + + +static bool tinyusb_load_enabled_hid_devices(){ + if(tinyusb_hid_device_descriptor != NULL){ + return true; + } + tinyusb_hid_device_descriptor = (uint8_t *)malloc(tinyusb_hid_device_descriptor_len); + if (tinyusb_hid_device_descriptor == NULL) { + log_e("HID Descriptor Malloc Failed"); + return false; + } + uint8_t * dst = tinyusb_hid_device_descriptor; + + for(uint8_t i=0; ireports_len; i++){ + if(hid_report_map->reports[i].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT){ + log_d(" ID: %3u, Type: %7s, Size: %2u, Usage: %8s", + hid_report_map->reports[i].report_id, + esp_hid_report_type_str(hid_report_map->reports[i].report_type), + hid_report_map->reports[i].value_len, + esp_hid_usage_str(hid_report_map->reports[i].usage) + ); + } + } + esp_hid_free_report_map(hid_report_map); + } else { + log_e("Failed to parse the hid report descriptor!"); + return false; + } + + return true; +} + +extern "C" uint16_t tusb_hid_load_descriptor(uint8_t * dst, uint8_t * itf) +{ + if(tinyusb_hid_is_initialized){ + return 0; + } + tinyusb_hid_is_initialized = true; + + uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB HID"); + uint8_t ep_in = tinyusb_get_free_in_endpoint(); + TU_VERIFY (ep_in != 0); + uint8_t ep_out = tinyusb_get_free_out_endpoint(); + TU_VERIFY (ep_out != 0); + uint8_t descriptor[TUD_HID_INOUT_DESC_LEN] = { + // HID Input & Output descriptor + // Interface number, string index, protocol, report descriptor len, EP OUT & IN address, size & polling interval + TUD_HID_INOUT_DESCRIPTOR(*itf, str_index, HID_ITF_PROTOCOL_NONE, tinyusb_hid_device_descriptor_len, ep_out, (uint8_t)(0x80 | ep_in), 64, 1) + }; + *itf+=1; + memcpy(dst, descriptor, TUD_HID_INOUT_DESC_LEN); + return TUD_HID_INOUT_DESC_LEN; +} + + + + +// Invoked when received GET HID REPORT DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance){ + log_v("instance: %u", instance); + if(!tinyusb_load_enabled_hid_devices()){ + return NULL; + } + return tinyusb_hid_device_descriptor; +} + +// Invoked when received SET_PROTOCOL request +// protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1) +void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol){ + log_v("instance: %u, protocol:%u", instance, protocol); + arduino_usb_hid_event_data_t p = {0}; + p.instance = instance; + p.set_protocol.protocol = protocol; + arduino_usb_event_post(ARDUINO_USB_HID_EVENTS, ARDUINO_USB_HID_SET_PROTOCOL_EVENT, &p, sizeof(arduino_usb_hid_event_data_t), portMAX_DELAY); +} + +// Invoked when received SET_IDLE request. return false will stall the request +// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication +// - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms). +bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate){ + log_v("instance: %u, idle_rate:%u", instance, idle_rate); + arduino_usb_hid_event_data_t p = {0}; + p.instance = instance; + p.set_idle.idle_rate = idle_rate; + arduino_usb_event_post(ARDUINO_USB_HID_EVENTS, ARDUINO_USB_HID_SET_IDLE_EVENT, &p, sizeof(arduino_usb_hid_event_data_t), portMAX_DELAY); + return true; +} + +// Invoked when received GET_REPORT control request +// Application must fill buffer report's content and return its length. +// Return zero will cause the stack to STALL request +uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen){ + uint16_t res = tinyusb_on_get_feature(report_id, buffer, reqlen); + if(!res){ + log_d("instance: %u, report_id: %u, report_type: %s, reqlen: %u", instance, report_id, tinyusb_hid_device_report_types[report_type], reqlen); + } + return res; +} + +// Invoked when received SET_REPORT control request or +// received data on OUT endpoint ( Report ID = 0, Type = 0 ) +void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize){ + if(!report_id && !report_type){ + if(!tinyusb_on_set_output(0, buffer, bufsize) && !tinyusb_on_set_output(buffer[0], buffer+1, bufsize-1)){ + log_d("instance: %u, report_id: %u, report_type: %s, bufsize: %u", instance, buffer[0], tinyusb_hid_device_report_types[HID_REPORT_TYPE_OUTPUT], bufsize-1); + } + } else { + if(!tinyusb_on_set_feature(report_id, buffer, bufsize)){ + log_d("instance: %u, report_id: %u, report_type: %s, bufsize: %u", instance, report_id, tinyusb_hid_device_report_types[report_type], bufsize); + } + } +} + +USBHID::USBHID(){ + if(!tinyusb_hid_devices_is_initialized){ + tinyusb_hid_devices_is_initialized = true; + for(uint8_t i=0; i +#include +#include "sdkconfig.h" + +#if CONFIG_TINYUSB_HID_ENABLED +#include "esp_event.h" +#include "class/hid/hid.h" +#include "class/hid/hid_device.h" + +// Used by the included TinyUSB drivers +enum { + HID_REPORT_ID_NONE, + HID_REPORT_ID_KEYBOARD, + HID_REPORT_ID_MOUSE, + HID_REPORT_ID_GAMEPAD, + HID_REPORT_ID_CONSUMER_CONTROL, + HID_REPORT_ID_SYSTEM_CONTROL, + HID_REPORT_ID_VENDOR +}; + +ESP_EVENT_DECLARE_BASE(ARDUINO_USB_HID_EVENTS); + +typedef enum { + ARDUINO_USB_HID_ANY_EVENT = ESP_EVENT_ANY_ID, + ARDUINO_USB_HID_SET_PROTOCOL_EVENT = 0, + ARDUINO_USB_HID_SET_IDLE_EVENT, + ARDUINO_USB_HID_MAX_EVENT, +} arduino_usb_hid_event_t; + +typedef struct { + uint8_t instance; + union { + struct { + uint8_t protocol; + } set_protocol; + struct { + uint8_t idle_rate; + } set_idle; + }; +} arduino_usb_hid_event_data_t; + +class USBHIDDevice +{ +public: + virtual uint16_t _onGetDescriptor(uint8_t* buffer){return 0;} + virtual uint16_t _onGetFeature(uint8_t report_id, uint8_t* buffer, uint16_t len){return 0;} + virtual void _onSetFeature(uint8_t report_id, const uint8_t* buffer, uint16_t len){} + virtual void _onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len){} +}; + +class USBHID +{ +public: + USBHID(void); + void begin(void); + void end(void); + bool ready(void); + bool SendReport(uint8_t report_id, const void* data, size_t len, uint32_t timeout_ms = 100); + void onEvent(esp_event_handler_t callback); + void onEvent(arduino_usb_hid_event_t event, esp_event_handler_t callback); + static bool addDevice(USBHIDDevice * device, uint16_t descriptor_len); +}; + +#endif /* CONFIG_TINYUSB_HID_ENABLED */ diff --git a/libraries/USB/src/USBHIDConsumerControl.cpp b/libraries/USB/src/USBHIDConsumerControl.cpp new file mode 100644 index 00000000..b1863dee --- /dev/null +++ b/libraries/USB/src/USBHIDConsumerControl.cpp @@ -0,0 +1,57 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// 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 "USBHID.h" + +#if CONFIG_TINYUSB_HID_ENABLED + +#include "USBHIDConsumerControl.h" + +static const uint8_t report_descriptor[] = { + TUD_HID_REPORT_DESC_CONSUMER(HID_REPORT_ID(HID_REPORT_ID_CONSUMER_CONTROL)) +}; + +USBHIDConsumerControl::USBHIDConsumerControl(): hid(){ + static bool initialized = false; + if(!initialized){ + initialized = true; + hid.addDevice(this, sizeof(report_descriptor)); + } +} + +uint16_t USBHIDConsumerControl::_onGetDescriptor(uint8_t* dst){ + memcpy(dst, report_descriptor, sizeof(report_descriptor)); + return sizeof(report_descriptor); +} + +void USBHIDConsumerControl::begin(){ + hid.begin(); +} + +void USBHIDConsumerControl::end(){ +} + +bool USBHIDConsumerControl::send(uint16_t value){ + return hid.SendReport(HID_REPORT_ID_CONSUMER_CONTROL, &value, 2); +} + +size_t USBHIDConsumerControl::press(uint16_t k){ + return send(k); +} + +size_t USBHIDConsumerControl::release(){ + return send(0); +} + + +#endif /* CONFIG_TINYUSB_HID_ENABLED */ diff --git a/libraries/USB/src/USBHIDConsumerControl.h b/libraries/USB/src/USBHIDConsumerControl.h new file mode 100644 index 00000000..9c60ff69 --- /dev/null +++ b/libraries/USB/src/USBHIDConsumerControl.h @@ -0,0 +1,85 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#pragma once +#include "USBHID.h" +#if CONFIG_TINYUSB_HID_ENABLED + +// Power Control +#define CONSUMER_CONTROL_POWER 0x0030 +#define CONSUMER_CONTROL_RESET 0x0031 +#define CONSUMER_CONTROL_SLEEP 0x0032 + +// Screen Brightness +#define CONSUMER_CONTROL_BRIGHTNESS_INCREMENT 0x006F +#define CONSUMER_CONTROL_BRIGHTNESS_DECREMENT 0x0070 + +// These HID usages operate only on mobile systems (battery powered) and +// require Windows 8 (build 8302 or greater). +#define CONSUMER_CONTROL_WIRELESS_RADIO_CONTROLS 0x000C +#define CONSUMER_CONTROL_WIRELESS_RADIO_BUTTONS 0x00C6 +#define CONSUMER_CONTROL_WIRELESS_RADIO_LED 0x00C7 +#define CONSUMER_CONTROL_WIRELESS_RADIO_SLIDER_SWITCH 0x00C8 + +// Media Control +#define CONSUMER_CONTROL_PLAY_PAUSE 0x00CD +#define CONSUMER_CONTROL_SCAN_NEXT 0x00B5 +#define CONSUMER_CONTROL_SCAN_PREVIOUS 0x00B6 +#define CONSUMER_CONTROL_STOP 0x00B7 +#define CONSUMER_CONTROL_VOLUME 0x00E0 +#define CONSUMER_CONTROL_MUTE 0x00E2 +#define CONSUMER_CONTROL_BASS 0x00E3 +#define CONSUMER_CONTROL_TREBLE 0x00E4 +#define CONSUMER_CONTROL_BASS_BOOST 0x00E5 +#define CONSUMER_CONTROL_VOLUME_INCREMENT 0x00E9 +#define CONSUMER_CONTROL_VOLUME_DECREMENT 0x00EA +#define CONSUMER_CONTROL_BASS_INCREMENT 0x0152 +#define CONSUMER_CONTROL_BASS_DECREMENT 0x0153 +#define CONSUMER_CONTROL_TREBLE_INCREMENT 0x0154 +#define CONSUMER_CONTROL_TREBLE_DECREMENT 0x0155 + +// Application Launcher +#define CONSUMER_CONTROL_CONFIGURATION 0x0183 +#define CONSUMER_CONTROL_EMAIL_READER 0x018A +#define CONSUMER_CONTROL_CALCULATOR 0x0192 +#define CONSUMER_CONTROL_LOCAL_BROWSER 0x0194 + +// Browser/Explorer Specific +#define CONSUMER_CONTROL_SEARCH 0x0221 +#define CONSUMER_CONTROL_HOME 0x0223 +#define CONSUMER_CONTROL_BACK 0x0224 +#define CONSUMER_CONTROL_FORWARD 0x0225 +#define CONSUMER_CONTROL_BR_STOP 0x0226 +#define CONSUMER_CONTROL_REFRESH 0x0227 +#define CONSUMER_CONTROL_BOOKMARKS 0x022A + +// Mouse Horizontal scroll +#define CONSUMER_CONTROL_PAN 0x0238 + +class USBHIDConsumerControl: public USBHIDDevice { +private: + USBHID hid; + bool send(uint16_t value); +public: + USBHIDConsumerControl(void); + void begin(void); + void end(void); + size_t press(uint16_t k); + size_t release(); + + // internal use + uint16_t _onGetDescriptor(uint8_t* buffer); +}; + +#endif diff --git a/libraries/USB/src/USBHIDGamepad.cpp b/libraries/USB/src/USBHIDGamepad.cpp new file mode 100644 index 00000000..9ddf67f4 --- /dev/null +++ b/libraries/USB/src/USBHIDGamepad.cpp @@ -0,0 +1,121 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// 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 "USBHID.h" + +#if CONFIG_TINYUSB_HID_ENABLED + +#include "USBHIDGamepad.h" + +static const uint8_t report_descriptor[] = { + TUD_HID_REPORT_DESC_GAMEPAD(HID_REPORT_ID(HID_REPORT_ID_GAMEPAD)) +}; + +USBHIDGamepad::USBHIDGamepad(): hid(), _x(0), _y(0), _z(0), _rz(0), _rx(0), _ry(0), _hat(0), _buttons(0){ + static bool initialized = false; + if(!initialized){ + initialized = true; + hid.addDevice(this, sizeof(report_descriptor)); + } +} + +uint16_t USBHIDGamepad::_onGetDescriptor(uint8_t* dst){ + memcpy(dst, report_descriptor, sizeof(report_descriptor)); + return sizeof(report_descriptor); +} + +void USBHIDGamepad::begin(){ + hid.begin(); +} + +void USBHIDGamepad::end(){ + +} + +bool USBHIDGamepad::write(){ + hid_gamepad_report_t report = { + .x = _x, + .y = _y, + .z = _z, + .rz = _rz, + .rx = _rx, + .ry = _ry, + .hat = _hat, + .buttons = _buttons + }; + return hid.SendReport(HID_REPORT_ID_GAMEPAD, &report, sizeof(report)); +} + +bool USBHIDGamepad::leftStick(int8_t x, int8_t y){ + _x = x; + _y = y; + return write(); +} + +bool USBHIDGamepad::rightStick(int8_t z, int8_t rz){ + _z = z; + _rz = rz; + return write(); +} + +bool USBHIDGamepad::leftTrigger(int8_t rx){ + _rx = rx; + return write(); +} + +bool USBHIDGamepad::rightTrigger(int8_t ry){ + _ry = ry; + return write(); +} + +bool USBHIDGamepad::hat(uint8_t hat){ + if(hat > 9){ + return false; + } + _hat = hat; + return write(); +} + +bool USBHIDGamepad::pressButton(uint8_t button){ + if(button > 31){ + return false; + } + _buttons |= (1 << button); + return write(); +} + +bool USBHIDGamepad::releaseButton(uint8_t button){ + if(button > 31){ + return false; + } + _buttons &= ~(1 << button); + return write(); +} + +bool USBHIDGamepad::send(int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons){ + if(hat > 9){ + return false; + } + _x = x; + _y = y; + _z = z; + _rz = rz; + _rx = rx; + _ry = ry; + _hat = hat; + _buttons = buttons; + return write(); +} + + +#endif /* CONFIG_TINYUSB_HID_ENABLED */ diff --git a/libraries/USB/src/USBHIDGamepad.h b/libraries/USB/src/USBHIDGamepad.h new file mode 100644 index 00000000..72b096db --- /dev/null +++ b/libraries/USB/src/USBHIDGamepad.h @@ -0,0 +1,87 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#pragma once +#include "USBHID.h" +#if CONFIG_TINYUSB_HID_ENABLED + +/// Standard Gamepad Buttons Naming from Linux input event codes +/// https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h +#define BUTTON_A 0 +#define BUTTON_B 1 +#define BUTTON_C 2 +#define BUTTON_X 3 +#define BUTTON_Y 4 +#define BUTTON_Z 5 +#define BUTTON_TL 6 +#define BUTTON_TR 7 +#define BUTTON_TL2 8 +#define BUTTON_TR2 9 +#define BUTTON_SELECT 10 +#define BUTTON_START 11 +#define BUTTON_MODE 12 +#define BUTTON_THUMBL 13 +#define BUTTON_THUMBR 14 + +#define BUTTON_SOUTH BUTTON_A +#define BUTTON_EAST BUTTON_B +#define BUTTON_NORTH BUTTON_X +#define BUTTON_WEST BUTTON_Y + +/// Standard Gamepad HAT/DPAD Buttons (from Linux input event codes) +#define HAT_CENTER 0 +#define HAT_UP 1 +#define HAT_UP_RIGHT 2 +#define HAT_RIGHT 3 +#define HAT_DOWN_RIGHT 4 +#define HAT_DOWN 5 +#define HAT_DOWN_LEFT 6 +#define HAT_LEFT 7 +#define HAT_UP_LEFT 8 + +class USBHIDGamepad: public USBHIDDevice { +private: + USBHID hid; + int8_t _x; ///< Delta x movement of left analog-stick + int8_t _y; ///< Delta y movement of left analog-stick + int8_t _z; ///< Delta z movement of right analog-joystick + int8_t _rz; ///< Delta Rz movement of right analog-joystick + int8_t _rx; ///< Delta Rx movement of analog left trigger + int8_t _ry; ///< Delta Ry movement of analog right trigger + uint8_t _hat; ///< Buttons mask for currently pressed buttons in the DPad/hat + uint32_t _buttons; ///< Buttons mask for currently pressed buttons + bool write(); +public: + USBHIDGamepad(void); + void begin(void); + void end(void); + + bool leftStick(int8_t x, int8_t y); + bool rightStick(int8_t z, int8_t rz); + + bool leftTrigger(int8_t rx); + bool rightTrigger(int8_t ry); + + bool hat(uint8_t hat); + + bool pressButton(uint8_t button); + bool releaseButton(uint8_t button); + + bool send(int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons); + + // internal use + uint16_t _onGetDescriptor(uint8_t* buffer); +}; + +#endif diff --git a/libraries/USB/src/USBHIDKeyboard.cpp b/libraries/USB/src/USBHIDKeyboard.cpp new file mode 100644 index 00000000..9a9445de --- /dev/null +++ b/libraries/USB/src/USBHIDKeyboard.cpp @@ -0,0 +1,354 @@ +/* + Keyboard.cpp + + Copyright (c) 2015, Arduino LLC + Original code (pre-library): Copyright (c) 2011, Peter Barrett + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "USBHID.h" + +#if CONFIG_TINYUSB_HID_ENABLED + +#include "USBHIDKeyboard.h" + +ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_KEYBOARD_EVENTS); +esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait); +esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg); + +static const uint8_t report_descriptor[] = { + TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(HID_REPORT_ID_KEYBOARD)) +}; + +USBHIDKeyboard::USBHIDKeyboard(): hid(){ + static bool initialized = false; + if(!initialized){ + initialized = true; + hid.addDevice(this, sizeof(report_descriptor)); + } +} + +uint16_t USBHIDKeyboard::_onGetDescriptor(uint8_t* dst){ + memcpy(dst, report_descriptor, sizeof(report_descriptor)); + return sizeof(report_descriptor); +} + +void USBHIDKeyboard::begin(){ + hid.begin(); +} + +void USBHIDKeyboard::end(){ +} + +void USBHIDKeyboard::onEvent(esp_event_handler_t callback){ + onEvent(ARDUINO_USB_HID_KEYBOARD_ANY_EVENT, callback); +} +void USBHIDKeyboard::onEvent(arduino_usb_hid_keyboard_event_t event, esp_event_handler_t callback){ + arduino_usb_event_handler_register_with(ARDUINO_USB_HID_KEYBOARD_EVENTS, event, callback, this); +} + +void USBHIDKeyboard::_onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len){ + if(report_id == HID_REPORT_ID_KEYBOARD){ + arduino_usb_hid_keyboard_event_data_t p = {0}; + p.leds = buffer[0]; + arduino_usb_event_post(ARDUINO_USB_HID_KEYBOARD_EVENTS, ARDUINO_USB_HID_KEYBOARD_LED_EVENT, &p, sizeof(arduino_usb_hid_keyboard_event_data_t), portMAX_DELAY); + } +} + +void USBHIDKeyboard::sendReport(KeyReport* keys) +{ + hid_keyboard_report_t report; + report.reserved = 0; + report.modifier = keys->modifiers; + if (keys->keys) { + memcpy(report.keycode, keys->keys, 6); + } else { + memset(report.keycode, 0, 6); + } + hid.SendReport(HID_REPORT_ID_KEYBOARD, &report, sizeof(report)); +} + +#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 +}; + +size_t USBHIDKeyboard::pressRaw(uint8_t k) +{ + uint8_t i; + if (k >= 0xE0 && k < 0xE8) { + // it's a modifier key + _keyReport.modifiers |= (1<<(k-0x80)); + } else if (k && k < 0xA5) { + // 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) { + return 0; + } + } + } else { + //not a modifier and not a key + return 0; + } + sendReport(&_keyReport); + return 1; +} + +size_t USBHIDKeyboard::releaseRaw(uint8_t k) +{ + uint8_t i; + if (k >= 0xE0 && k < 0xE8) { + // it's a modifier key + _keyReport.modifiers &= ~(1<<(k-0x80)); + } else if (k && k < 0xA5) { + // 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; + } + } + } else { + //not a modifier and not a key + return 0; + } + + sendReport(&_keyReport); + return 1; +} + +// 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 USBHIDKeyboard::press(uint8_t k) +{ + uint8_t i; + if (k >= 0x88) { // it's a non-printing key (not a modifier) + k = k - 0x88; + } else if (k >= 0x80) { // it's a modifier key + _keyReport.modifiers |= (1<<(k-0x80)); + k = 0; + } else { // it's a printing key + k = _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; + } + } + return pressRaw(k); +} + +// 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 USBHIDKeyboard::release(uint8_t k) +{ + uint8_t i; + if (k >= 0x88) { // it's a non-printing key (not a modifier) + k = k - 0x88; + } else if (k >= 0x80) { // it's a modifier key + _keyReport.modifiers &= ~(1<<(k-0x80)); + k = 0; + } else { // it's a printing key + k = _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; + } + } + return releaseRaw(k); +} + +void USBHIDKeyboard::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; + sendReport(&_keyReport); +} + +size_t USBHIDKeyboard::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 USBHIDKeyboard::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; +} + +#endif /* CONFIG_TINYUSB_HID_ENABLED */ diff --git a/libraries/USB/src/USBHIDKeyboard.h b/libraries/USB/src/USBHIDKeyboard.h new file mode 100644 index 00000000..5f9fdfc4 --- /dev/null +++ b/libraries/USB/src/USBHIDKeyboard.h @@ -0,0 +1,140 @@ +/* + Keyboard.h + + Copyright (c) 2015, Arduino LLC + Original code (pre-library): Copyright (c) 2011, Peter Barrett + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once +#include "Print.h" +#include "USBHID.h" +#if CONFIG_TINYUSB_HID_ENABLED + +#include "esp_event.h" + +ESP_EVENT_DECLARE_BASE(ARDUINO_USB_HID_KEYBOARD_EVENTS); + +typedef enum { + ARDUINO_USB_HID_KEYBOARD_ANY_EVENT = ESP_EVENT_ANY_ID, + ARDUINO_USB_HID_KEYBOARD_LED_EVENT = 0, + ARDUINO_USB_HID_KEYBOARD_MAX_EVENT, +} arduino_usb_hid_keyboard_event_t; + +typedef union { + struct { + uint8_t numlock:1; + uint8_t capslock:1; + uint8_t scrolllock:1; + uint8_t compose:1; + uint8_t kana:1; + uint8_t reserved:3; + }; + uint8_t leds; +} arduino_usb_hid_keyboard_event_data_t; + +#define KEY_LEFT_CTRL 0x80 +#define KEY_LEFT_SHIFT 0x81 +#define KEY_LEFT_ALT 0x82 +#define KEY_LEFT_GUI 0x83 +#define KEY_RIGHT_CTRL 0x84 +#define KEY_RIGHT_SHIFT 0x85 +#define KEY_RIGHT_ALT 0x86 +#define KEY_RIGHT_GUI 0x87 + +#define KEY_UP_ARROW 0xDA +#define KEY_DOWN_ARROW 0xD9 +#define KEY_LEFT_ARROW 0xD8 +#define KEY_RIGHT_ARROW 0xD7 +#define KEY_BACKSPACE 0xB2 +#define KEY_TAB 0xB3 +#define KEY_RETURN 0xB0 +#define KEY_ESC 0xB1 +#define KEY_INSERT 0xD1 +#define KEY_DELETE 0xD4 +#define KEY_PAGE_UP 0xD3 +#define KEY_PAGE_DOWN 0xD6 +#define KEY_HOME 0xD2 +#define KEY_END 0xD5 +#define KEY_CAPS_LOCK 0xC1 +#define KEY_F1 0xC2 +#define KEY_F2 0xC3 +#define KEY_F3 0xC4 +#define KEY_F4 0xC5 +#define KEY_F5 0xC6 +#define KEY_F6 0xC7 +#define KEY_F7 0xC8 +#define KEY_F8 0xC9 +#define KEY_F9 0xCA +#define KEY_F10 0xCB +#define KEY_F11 0xCC +#define KEY_F12 0xCD +#define KEY_F13 0xF0 +#define KEY_F14 0xF1 +#define KEY_F15 0xF2 +#define KEY_F16 0xF3 +#define KEY_F17 0xF4 +#define KEY_F18 0xF5 +#define KEY_F19 0xF6 +#define KEY_F20 0xF7 +#define KEY_F21 0xF8 +#define KEY_F22 0xF9 +#define KEY_F23 0xFA +#define KEY_F24 0xFB + +#define LED_NUMLOCK 0x01 +#define LED_CAPSLOCK 0x02 +#define LED_SCROLLLOCK 0x04 +#define LED_COMPOSE 0x08 +#define LED_KANA 0x10 + +// 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 USBHIDKeyboard: public USBHIDDevice, public Print +{ +private: + USBHID hid; + KeyReport _keyReport; + void sendReport(KeyReport* keys); +public: + USBHIDKeyboard(void); + void begin(void); + void end(void); + 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); + + //raw functions work with TinyUSB's HID_KEY_* macros + size_t pressRaw(uint8_t k); + size_t releaseRaw(uint8_t k); + + void onEvent(esp_event_handler_t callback); + void onEvent(arduino_usb_hid_keyboard_event_t event, esp_event_handler_t callback); + + // internal use + uint16_t _onGetDescriptor(uint8_t* buffer); + void _onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len); +}; + +#endif diff --git a/libraries/USB/src/USBHIDMouse.cpp b/libraries/USB/src/USBHIDMouse.cpp new file mode 100644 index 00000000..30b802a4 --- /dev/null +++ b/libraries/USB/src/USBHIDMouse.cpp @@ -0,0 +1,92 @@ +/* + Mouse.cpp + + Copyright (c) 2015, Arduino LLC + Original code (pre-library): Copyright (c) 2011, Peter Barrett + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "USBHID.h" + +#if CONFIG_TINYUSB_HID_ENABLED + +#include "USBHIDMouse.h" + +static const uint8_t report_descriptor[] = { + TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(HID_REPORT_ID_MOUSE)) +}; + +USBHIDMouse::USBHIDMouse(): hid(), _buttons(0){ + static bool initialized = false; + if(!initialized){ + initialized = true; + hid.addDevice(this, sizeof(report_descriptor)); + } +} + +uint16_t USBHIDMouse::_onGetDescriptor(uint8_t* dst){ + memcpy(dst, report_descriptor, sizeof(report_descriptor)); + return sizeof(report_descriptor); +} + +void USBHIDMouse::begin(){ + hid.begin(); +} + +void USBHIDMouse::end(){ +} + +void USBHIDMouse::move(int8_t x, int8_t y, int8_t wheel, int8_t pan){ + hid_mouse_report_t report = { + .buttons = _buttons, + .x = x, + .y = y, + .wheel = wheel, + .pan = pan + }; + hid.SendReport(HID_REPORT_ID_MOUSE, &report, sizeof(report)); +} + +void USBHIDMouse::click(uint8_t b){ + _buttons = b; + move(0,0); + _buttons = 0; + move(0,0); +} + +void USBHIDMouse::buttons(uint8_t b){ + if (b != _buttons){ + _buttons = b; + move(0,0); + } +} + +void USBHIDMouse::press(uint8_t b){ + buttons(_buttons | b); +} + +void USBHIDMouse::release(uint8_t b){ + buttons(_buttons & ~b); +} + +bool USBHIDMouse::isPressed(uint8_t b){ + if ((b & _buttons) > 0) { + return true; + } + return false; +} + + +#endif /* CONFIG_TINYUSB_HID_ENABLED */ diff --git a/libraries/USB/src/USBHIDMouse.h b/libraries/USB/src/USBHIDMouse.h new file mode 100644 index 00000000..17adf17b --- /dev/null +++ b/libraries/USB/src/USBHIDMouse.h @@ -0,0 +1,54 @@ +/* + Mouse.h + + Copyright (c) 2015, Arduino LLC + Original code (pre-library): Copyright (c) 2011, Peter Barrett + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once +#include "USBHID.h" +#if CONFIG_TINYUSB_HID_ENABLED + +#define MOUSE_LEFT 0x01 +#define MOUSE_RIGHT 0x02 +#define MOUSE_MIDDLE 0x04 +#define MOUSE_BACKWARD 0x08 +#define MOUSE_FORWARD 0x10 +#define MOUSE_ALL 0x1F + +class USBHIDMouse: public USBHIDDevice { +private: + USBHID hid; + uint8_t _buttons; + void buttons(uint8_t b); + bool write(int8_t x, int8_t y, int8_t vertical, int8_t horizontal); +public: + USBHIDMouse(void); + void begin(void); + void end(void); + + void click(uint8_t b = MOUSE_LEFT); + void move(int8_t x, int8_t y, int8_t wheel = 0, int8_t pan = 0); + void press(uint8_t b = MOUSE_LEFT); // press LEFT by default + void release(uint8_t b = MOUSE_LEFT); // release LEFT by default + bool isPressed(uint8_t b = MOUSE_LEFT); // check LEFT by default + + // internal use + uint16_t _onGetDescriptor(uint8_t* buffer); +}; + +#endif diff --git a/libraries/USB/src/USBHIDSystemControl.cpp b/libraries/USB/src/USBHIDSystemControl.cpp new file mode 100644 index 00000000..93ef897f --- /dev/null +++ b/libraries/USB/src/USBHIDSystemControl.cpp @@ -0,0 +1,59 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// 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 "USBHID.h" + +#if CONFIG_TINYUSB_HID_ENABLED + +#include "USBHIDSystemControl.h" + +static const uint8_t report_descriptor[] = { + TUD_HID_REPORT_DESC_SYSTEM_CONTROL(HID_REPORT_ID(HID_REPORT_ID_SYSTEM_CONTROL)) +}; + +USBHIDSystemControl::USBHIDSystemControl(): hid(){ + static bool initialized = false; + if(!initialized){ + initialized = true; + hid.addDevice(this, sizeof(report_descriptor)); + } +} + +uint16_t USBHIDSystemControl::_onGetDescriptor(uint8_t* dst){ + memcpy(dst, report_descriptor, sizeof(report_descriptor)); + return sizeof(report_descriptor); +} + +void USBHIDSystemControl::begin(){ + hid.begin(); +} + +void USBHIDSystemControl::end(){ +} + +bool USBHIDSystemControl::send(uint8_t value){ + return hid.SendReport(HID_REPORT_ID_SYSTEM_CONTROL, &value, 1); +} + +size_t USBHIDSystemControl::press(uint8_t k){ + if(k > 3){ + return 0; + } + return send(k); +} + +size_t USBHIDSystemControl::release(){ + return send(0); +} + +#endif /* CONFIG_TINYUSB_HID_ENABLED */ diff --git a/libraries/USB/src/USBHIDSystemControl.h b/libraries/USB/src/USBHIDSystemControl.h new file mode 100644 index 00000000..05ba573f --- /dev/null +++ b/libraries/USB/src/USBHIDSystemControl.h @@ -0,0 +1,39 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#pragma once +#include "USBHID.h" +#if CONFIG_TINYUSB_HID_ENABLED + +#define SYSTEM_CONTROL_NONE 0 +#define SYSTEM_CONTROL_POWER_OFF 1 +#define SYSTEM_CONTROL_STANDBY 2 +#define SYSTEM_CONTROL_WAKE_HOST 3 + +class USBHIDSystemControl: public USBHIDDevice { +private: + USBHID hid; + bool send(uint8_t value); +public: + USBHIDSystemControl(void); + void begin(void); + void end(void); + size_t press(uint8_t k); + size_t release(); + + // internal use + uint16_t _onGetDescriptor(uint8_t* buffer); +}; + +#endif diff --git a/libraries/USB/src/USBHIDVendor.cpp b/libraries/USB/src/USBHIDVendor.cpp new file mode 100644 index 00000000..53b0e999 --- /dev/null +++ b/libraries/USB/src/USBHIDVendor.cpp @@ -0,0 +1,236 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// 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 "USBHID.h" + +#if CONFIG_TINYUSB_HID_ENABLED + +#include "esp32-hal-log.h" +#include "USBHIDVendor.h" + +ESP_EVENT_DEFINE_BASE(ARDUINO_USB_HID_VENDOR_EVENTS); +esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait); +esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg); + +// HID Generic Input, Output & Feature +// - 1st parameter is report size (mandatory) +// - 2nd parameter is report id HID_REPORT_ID(n) (optional) +#define TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(report_size, ...) \ + HID_USAGE_PAGE_N ( HID_USAGE_PAGE_VENDOR, 2 ),\ + HID_USAGE ( 0x01 ),\ + HID_COLLECTION ( HID_COLLECTION_APPLICATION ),\ + /* Report ID if any */\ + __VA_ARGS__ \ + /* Input */ \ + HID_USAGE ( 0x02 ),\ + HID_LOGICAL_MIN ( 0x00 ),\ + HID_LOGICAL_MAX ( 0xff ),\ + HID_REPORT_SIZE ( 8 ),\ + HID_REPORT_COUNT( report_size ),\ + HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ + /* Output */ \ + HID_USAGE ( 0x03 ),\ + HID_LOGICAL_MIN ( 0x00 ),\ + HID_LOGICAL_MAX ( 0xff ),\ + HID_REPORT_SIZE ( 8 ),\ + HID_REPORT_COUNT( report_size ),\ + HID_OUTPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ + /* Feature */ \ + HID_USAGE ( 0x04 ),\ + HID_LOGICAL_MIN ( 0x00 ),\ + HID_LOGICAL_MAX ( 0xff ),\ + HID_REPORT_SIZE ( 8 ),\ + HID_REPORT_COUNT( report_size ),\ + HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\ + HID_COLLECTION_END \ + +#define TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE_LEN 46 + +// max size is 64 and we need one byte for the report ID +static uint8_t HID_VENDOR_REPORT_SIZE = 63; +static uint8_t feature[64]; +static xQueueHandle rx_queue = NULL; +static bool prepend_size = false; + +USBHIDVendor::USBHIDVendor(uint8_t report_size, bool prepend): hid(){ + static bool initialized = false; + if(!initialized){ + initialized = true; + hid.addDevice(this, TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE_LEN); + memset(feature, 0, 64); + if(report_size < 64){ + HID_VENDOR_REPORT_SIZE = report_size; + } + prepend_size = prepend; + } +} + +uint16_t USBHIDVendor::_onGetDescriptor(uint8_t* dst){ + uint8_t report_descriptor[] = { + TUD_HID_REPORT_DESC_GENERIC_INOUT_FEATURE(HID_VENDOR_REPORT_SIZE, HID_REPORT_ID(HID_REPORT_ID_VENDOR)) + }; + memcpy(dst, report_descriptor, sizeof(report_descriptor)); + return sizeof(report_descriptor); +} + +void USBHIDVendor::prependInputPacketsWithSize(bool enable){ + prepend_size = enable; +} + +size_t USBHIDVendor::setRxBufferSize(size_t rx_queue_len){ + if(rx_queue){ + if(!rx_queue_len){ + vQueueDelete(rx_queue); + rx_queue = NULL; + } + return 0; + } + rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t)); + if(!rx_queue){ + return 0; + } + return rx_queue_len; +} + +void USBHIDVendor::begin(){ + hid.begin(); + setRxBufferSize(256);//default if not preset +} + +void USBHIDVendor::end(){ + setRxBufferSize(0); +} + +void USBHIDVendor::onEvent(esp_event_handler_t callback){ + onEvent(ARDUINO_USB_HID_VENDOR_ANY_EVENT, callback); +} + +void USBHIDVendor::onEvent(arduino_usb_hid_vendor_event_t event, esp_event_handler_t callback){ + arduino_usb_event_handler_register_with(ARDUINO_USB_HID_VENDOR_EVENTS, event, callback, this); +} + +uint16_t USBHIDVendor::_onGetFeature(uint8_t report_id, uint8_t* buffer, uint16_t len){ + if(report_id != HID_REPORT_ID_VENDOR){ + return 0; + } + memcpy(buffer, feature, len); + arduino_usb_hid_vendor_event_data_t p = {0}; + p.buffer = feature; + p.len = len; + arduino_usb_event_post(ARDUINO_USB_HID_VENDOR_EVENTS, ARDUINO_USB_HID_VENDOR_GET_FEATURE_EVENT, &p, sizeof(arduino_usb_hid_vendor_event_data_t), portMAX_DELAY); + return len; +} + +void USBHIDVendor::_onSetFeature(uint8_t report_id, const uint8_t* buffer, uint16_t len){ + if(report_id != HID_REPORT_ID_VENDOR){ + return; + } + memcpy(feature, buffer, len); + arduino_usb_hid_vendor_event_data_t p = {0}; + p.buffer = feature; + p.len = len; + arduino_usb_event_post(ARDUINO_USB_HID_VENDOR_EVENTS, ARDUINO_USB_HID_VENDOR_SET_FEATURE_EVENT, &p, sizeof(arduino_usb_hid_vendor_event_data_t), portMAX_DELAY); +} + +void USBHIDVendor::_onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len){ + if(report_id != HID_REPORT_ID_VENDOR){ + return; + } + for(uint32_t i=0; i max_send){ + will_send = max_send; + } + if(prepend_size){ + hid_in[0] = will_send; + } + // We can get INPUT only when data length equals the input report size + memcpy(hid_in + size_offset, data, will_send); + // pad with zeroes + memset(hid_in + size_offset + will_send, 0, max_send - will_send); + if(!hid.SendReport(HID_REPORT_ID_VENDOR, hid_in, HID_VENDOR_REPORT_SIZE)){ + return len - to_send; + } + to_send -= will_send; + data += will_send; + } + return len; +} + +size_t USBHIDVendor::write(uint8_t c){ + return write(&c, 1); +} + +int USBHIDVendor::available(void){ + if(rx_queue == NULL){ + return -1; + } + return uxQueueMessagesWaiting(rx_queue); +} + +int USBHIDVendor::peek(void){ + if(rx_queue == NULL){ + return -1; + } + uint8_t c; + if(xQueuePeek(rx_queue, &c, 0)) { + return c; + } + return -1; +} + +int USBHIDVendor::read(void){ + if(rx_queue == NULL){ + return -1; + } + uint8_t c = 0; + if(xQueueReceive(rx_queue, &c, 0)) { + return c; + } + return -1; +} + +size_t USBHIDVendor::read(uint8_t *buffer, size_t size){ + if(rx_queue == NULL){ + return -1; + } + uint8_t c = 0; + size_t count = 0; + while(count < size && xQueueReceive(rx_queue, &c, 0)){ + buffer[count++] = c; + } + return count; +} + +void USBHIDVendor::flush(void){} + + +#endif /* CONFIG_TINYUSB_HID_ENABLED */ diff --git a/libraries/USB/src/USBHIDVendor.h b/libraries/USB/src/USBHIDVendor.h new file mode 100644 index 00000000..fe11c0b5 --- /dev/null +++ b/libraries/USB/src/USBHIDVendor.h @@ -0,0 +1,67 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#pragma once +#include "Stream.h" +#include "USBHID.h" +#if CONFIG_TINYUSB_HID_ENABLED + +ESP_EVENT_DECLARE_BASE(ARDUINO_USB_HID_VENDOR_EVENTS); + +typedef enum { + ARDUINO_USB_HID_VENDOR_ANY_EVENT = ESP_EVENT_ANY_ID, + ARDUINO_USB_HID_VENDOR_SET_FEATURE_EVENT = 0, + ARDUINO_USB_HID_VENDOR_GET_FEATURE_EVENT, + ARDUINO_USB_HID_VENDOR_OUTPUT_EVENT, + ARDUINO_USB_HID_VENDOR_MAX_EVENT, +} arduino_usb_hid_vendor_event_t; + +typedef struct { + const uint8_t* buffer; + uint16_t len; +} arduino_usb_hid_vendor_event_data_t; + +class USBHIDVendor: public USBHIDDevice, public Stream { +private: + USBHID hid; +public: + // Max report size is 64, but we need one byte for report ID, so in reality max is 63. + // Because input packets are always with length equal to the report size + // it will not be known how many bytes actually matter. Setting 'prepend_size' to 'true' will + // make the first byte of each packet to be the length of data in that packet. + // This comes with penalty of one byte, but is very useful when using Vendor for streaming + USBHIDVendor(uint8_t report_size=63, bool prepend_size=false); + void begin(void); + void end(void); + void prependInputPacketsWithSize(bool enable); + size_t setRxBufferSize(size_t); + size_t write(const uint8_t* buffer, size_t len); + size_t write(uint8_t); + int available(void); + int peek(void); + int read(void); + size_t read(uint8_t *buffer, size_t size); + void flush(void); + + void onEvent(esp_event_handler_t callback); + void onEvent(arduino_usb_hid_vendor_event_t event, esp_event_handler_t callback); + + // internal use + uint16_t _onGetDescriptor(uint8_t* buffer); + uint16_t _onGetFeature(uint8_t report_id, uint8_t* buffer, uint16_t len); + void _onSetFeature(uint8_t report_id, const uint8_t* buffer, uint16_t len); + void _onOutput(uint8_t report_id, const uint8_t* buffer, uint16_t len); +}; + +#endif diff --git a/libraries/USB/src/USBVendor.cpp b/libraries/USB/src/USBVendor.cpp new file mode 100644 index 00000000..0a91fac9 --- /dev/null +++ b/libraries/USB/src/USBVendor.cpp @@ -0,0 +1,216 @@ +// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD +// +// 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 "USBVendor.h" + +#if CONFIG_TINYUSB_VENDOR_ENABLED + +#include "esp32-hal-tinyusb.h" + +ESP_EVENT_DEFINE_BASE(ARDUINO_USB_VENDOR_EVENTS); +esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait); +esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg); + +static USBVendor * _Vendor = NULL; +static xQueueHandle rx_queue = NULL; +static uint8_t USB_VENDOR_ENDPOINT_SIZE = 64; + +uint16_t tusb_vendor_load_descriptor(uint8_t * dst, uint8_t * itf) +{ + uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB Vendor"); + uint8_t ep_num = tinyusb_get_free_duplex_endpoint(); + TU_VERIFY (ep_num != 0); + uint8_t descriptor[TUD_VENDOR_DESC_LEN] = { + // Interface number, string index, EP Out & IN address, EP size + TUD_VENDOR_DESCRIPTOR(*itf, str_index, ep_num, (uint8_t)(0x80 | ep_num), USB_VENDOR_ENDPOINT_SIZE) + }; + *itf+=1; + memcpy(dst, descriptor, TUD_VENDOR_DESC_LEN); + return TUD_VENDOR_DESC_LEN; +} + +void tud_vendor_rx_cb(uint8_t itf){ + log_v("%u", len); + size_t len = tud_vendor_n_available(itf); + if(len){ + uint8_t buffer[len]; + len = tud_vendor_n_read(itf, buffer, len); + log_buf_v(buffer, len); + if(_Vendor) { + _Vendor->_onRX(buffer, len); + } + } else { + if(_Vendor) { + _Vendor->_onRX(NULL, len); + } + } +} + +extern "C" bool tinyusb_vendor_control_request_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request){ + log_v("Port: %u, Stage: %u, Direction: %u, Type: %u, Recipient: %u, bRequest: 0x%x, wValue: %u, wIndex: %u, wLength: %u", + rhport, stage, request->bmRequestType_bit.direction, + request->bmRequestType_bit.type, request->bmRequestType_bit.recipient, + request->bRequest, request->wValue, request->wIndex, request->wLength); + + if(_Vendor) { + return _Vendor->_onRequest(rhport, stage, (arduino_usb_control_request_t const *)request); + } + return false; +} + +USBVendor::USBVendor(uint8_t endpoint_size):itf(0), cb(NULL){ + if(!_Vendor){ + _Vendor = this; + if(endpoint_size <= 64){ + USB_VENDOR_ENDPOINT_SIZE = endpoint_size; + } + tinyusb_enable_interface(USB_INTERFACE_VENDOR, TUD_VENDOR_DESC_LEN, tusb_vendor_load_descriptor); + } else { + itf = _Vendor->itf; + cb = _Vendor->cb; + } +} + +size_t USBVendor::setRxBufferSize(size_t rx_queue_len){ + if(rx_queue){ + if(!rx_queue_len){ + vQueueDelete(rx_queue); + rx_queue = NULL; + } + return 0; + } + rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t)); + if(!rx_queue){ + return 0; + } + return rx_queue_len; +} + +void USBVendor::begin(){ + setRxBufferSize(256);//default if not preset +} + +void USBVendor::end(){ + setRxBufferSize(0); +} + +void USBVendor::onEvent(esp_event_handler_t callback){ + onEvent(ARDUINO_USB_VENDOR_ANY_EVENT, callback); +} + +void USBVendor::onEvent(arduino_usb_vendor_event_t event, esp_event_handler_t callback){ + arduino_usb_event_handler_register_with(ARDUINO_USB_VENDOR_EVENTS, event, callback, this); +} + +bool USBVendor::mounted(){ + return tud_vendor_n_mounted(itf); +} + +bool USBVendor::sendResponse(uint8_t rhport, arduino_usb_control_request_t const * request, void * data, size_t len){ + if(!request){ + return false; + } + if(!data || !len){ + return tud_control_status(rhport, (tusb_control_request_t const *)request); + } else { + return tud_control_xfer(rhport, (tusb_control_request_t const *)request, data, len); + } +} + +void USBVendor::onRequest(arduino_usb_vendor_control_request_handler_t handler){ + cb = handler; +} + +bool USBVendor::_onRequest(uint8_t rhport, uint8_t stage, arduino_usb_control_request_t const * request){ + if(cb){ + return cb(rhport, stage, request); + } + return false; +} + +void USBVendor::_onRX(const uint8_t* buffer, size_t len){ + for(uint32_t i=0; i max_len){ + len = max_len; + } + if(len){ + return tud_vendor_n_write(itf, buffer, len); + } + return len; +} + +size_t USBVendor::write(uint8_t c){ + return write(&c, 1); +} + +int USBVendor::available(void){ + if(rx_queue == NULL){ + return -1; + } + return uxQueueMessagesWaiting(rx_queue); +} + +int USBVendor::peek(void){ + if(rx_queue == NULL){ + return -1; + } + uint8_t c; + if(xQueuePeek(rx_queue, &c, 0)) { + return c; + } + return -1; +} + +int USBVendor::read(void){ + if(rx_queue == NULL){ + return -1; + } + uint8_t c = 0; + if(xQueueReceive(rx_queue, &c, 0)) { + return c; + } + return -1; +} + +size_t USBVendor::read(uint8_t *buffer, size_t size){ + if(rx_queue == NULL){ + return -1; + } + uint8_t c = 0; + size_t count = 0; + while(count < size && xQueueReceive(rx_queue, &c, 0)){ + buffer[count++] = c; + } + return count; +} + +void USBVendor::flush(void){} + +#endif /* CONFIG_TINYUSB_VENDOR_ENABLED */ diff --git a/libraries/USB/src/USBVendor.h b/libraries/USB/src/USBVendor.h new file mode 100644 index 00000000..2016a76a --- /dev/null +++ b/libraries/USB/src/USBVendor.h @@ -0,0 +1,94 @@ +// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#pragma once +#include "Stream.h" +#include "sdkconfig.h" + +#if CONFIG_TINYUSB_VENDOR_ENABLED +#include "esp_event.h" + +ESP_EVENT_DECLARE_BASE(ARDUINO_USB_VENDOR_EVENTS); + +#define REQUEST_STAGE_SETUP 0 +#define REQUEST_STAGE_DATA 1 +#define REQUEST_STAGE_ACK 2 + +#define REQUEST_TYPE_STANDARD 0 +#define REQUEST_TYPE_CLASS 1 +#define REQUEST_TYPE_VENDOR 2 +#define REQUEST_TYPE_INVALID 3 + +#define REQUEST_RECIPIENT_DEVICE 0 +#define REQUEST_RECIPIENT_INTERFACE 1 +#define REQUEST_RECIPIENT_ENDPOINT 2 +#define REQUEST_RECIPIENT_OTHER 3 + +#define REQUEST_DIRECTION_OUT 0 +#define REQUEST_DIRECTION_IN 1 + +typedef struct __attribute__ ((packed)) { + struct __attribute__ ((packed)) { + uint8_t bmRequestRecipient : 5; + uint8_t bmRequestType : 2; + uint8_t bmRequestDirection : 1; + }; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +} arduino_usb_control_request_t; + +typedef enum { + ARDUINO_USB_VENDOR_ANY_EVENT = ESP_EVENT_ANY_ID, + ARDUINO_USB_VENDOR_DATA_EVENT, + ARDUINO_USB_VENDOR_MAX_EVENT, +} arduino_usb_vendor_event_t; + +typedef union { + struct { + uint16_t len; + } data; +} arduino_usb_vendor_event_data_t; + +typedef bool (*arduino_usb_vendor_control_request_handler_t)(uint8_t rhport, uint8_t stage, arduino_usb_control_request_t const * request); + +class USBVendor: public Stream { +private: + uint8_t itf; + arduino_usb_vendor_control_request_handler_t cb; +public: + USBVendor(uint8_t endpoint_size=64); + void begin(void); + void end(void); + size_t setRxBufferSize(size_t); + bool mounted(void); + size_t write(const uint8_t* buffer, size_t len); + size_t write(uint8_t); + int available(void); + int peek(void); + int read(void); + size_t read(uint8_t *buffer, size_t size); + void flush(void); + + void onEvent(esp_event_handler_t callback); + void onEvent(arduino_usb_vendor_event_t event, esp_event_handler_t callback); + void onRequest(arduino_usb_vendor_control_request_handler_t handler); + bool sendResponse(uint8_t rhport, arduino_usb_control_request_t const * request, void * data=NULL, size_t len=0); + + bool _onRequest(uint8_t rhport, uint8_t stage, arduino_usb_control_request_t const * request); + void _onRX(const uint8_t* buffer, size_t len); +}; + +#endif /* CONFIG_TINYUSB_VENDOR_ENABLED */ diff --git a/libraries/USB/src/USB_NOT.h b/libraries/USB/src/USB_NOT.h deleted file mode 100644 index f349ad19..00000000 --- a/libraries/USB/src/USB_NOT.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD -// -// 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. - -#pragma once