Implement USB HID Device Support for ESP32-S2 (#5538)

* Add support and example for USB HID Devices
* Add support and example for USB Vendor
This commit is contained in:
Me No Dev 2021-08-23 17:27:34 +03:00 committed by GitHub
parent b1d072df9f
commit c45cff5f83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 3259 additions and 47 deletions

View File

@ -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

View File

@ -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 <cstring>
#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

View File

@ -15,6 +15,7 @@
#pragma once
#include <stdbool.h>
#include "USBMSC.h"
#if CONFIG_TINYUSB_MSC_ENABLED
#include "esp_event.h"

View File

@ -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

View File

@ -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)

View File

@ -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 */

View File

@ -13,13 +13,12 @@
// limitations under the License.
#pragma once
#include <inttypes.h>
#include "Stream.h"
#include "esp32-hal.h"
#include "sdkconfig.h"
#if CONFIG_TINYUSB_CDC_ENABLED
#include <inttypes.h>
#include "esp_event.h"
#include "Stream.h"
ESP_EVENT_DECLARE_BASE(ARDUINO_USB_CDC_EVENTS);

View File

@ -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 */

View File

@ -15,7 +15,8 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#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 */

View File

@ -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;

View File

@ -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; i<data->len; 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; i<data->len; 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; i<data->len; 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);
}
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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; i<data->len; 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; i<data->len; 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());
}
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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; i<tinyusb_loaded_hid_devices_num; i++){
tinyusb_hid_device_t * device = &tinyusb_hid_devices[i];
if(device->device && device->reports_num){
for(uint8_t r=0; r<device->reports_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; i<device->reports_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; r<device->reports_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; i<tinyusb_loaded_hid_devices_num; i++){
uint16_t len = tinyusb_on_add_descriptor(i, dst);
if (!len) {
break;
} else {
dst += len;
}
}
esp_hid_report_map_t *hid_report_map = esp_hid_parse_report_map(tinyusb_hid_device_descriptor, tinyusb_hid_device_descriptor_len);
if(hid_report_map){
log_d("Loaded HID Desriptor with the following reports:");
for(uint8_t i=0; i<hid_report_map->reports_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<USB_HID_DEVICES_MAX; i++){
memset(&tinyusb_hid_devices[i], 0, sizeof(tinyusb_hid_device_t));
}
tinyusb_hid_devices_num = 0;
tinyusb_enable_interface(USB_INTERFACE_HID, TUD_HID_INOUT_DESC_LEN, tusb_hid_load_descriptor);
}
}
void USBHID::begin(){
if(tinyusb_hid_device_input_sem == NULL){
tinyusb_hid_device_input_sem = xSemaphoreCreateBinary();
}
if(tinyusb_hid_device_input_mutex == NULL){
tinyusb_hid_device_input_mutex = xSemaphoreCreateMutex();
}
}
void USBHID::end(){
if (tinyusb_hid_device_input_sem != NULL) {
vSemaphoreDelete(tinyusb_hid_device_input_sem);
tinyusb_hid_device_input_sem = NULL;
}
if (tinyusb_hid_device_input_mutex != NULL) {
vSemaphoreDelete(tinyusb_hid_device_input_mutex);
tinyusb_hid_device_input_mutex = NULL;
}
}
bool USBHID::ready(void){
return tud_hid_n_ready(0);
}
void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint8_t len){
if (tinyusb_hid_device_input_sem) {
xSemaphoreGive(tinyusb_hid_device_input_sem);
}
}
bool USBHID::SendReport(uint8_t id, const void* data, size_t len, uint32_t timeout_ms){
if(!tinyusb_hid_device_input_sem || !tinyusb_hid_device_input_mutex){
log_e("TX Semaphore is NULL. You must call USBHID::begin() before you can send reports");
return false;
}
if(xSemaphoreTake(tinyusb_hid_device_input_mutex, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
log_e("report %u mutex failed", id);
return false;
}
bool res = ready();
if(!res){
log_e("not ready");
} else {
res = tud_hid_n_report(0, id, data, len);
if(!res){
log_e("report %u failed", id);
} else {
xSemaphoreTake(tinyusb_hid_device_input_sem, 0);
if(xSemaphoreTake(tinyusb_hid_device_input_sem, timeout_ms / portTICK_PERIOD_MS) != pdTRUE){
log_e("report %u wait failed", id);
res = false;
}
}
}
xSemaphoreGive(tinyusb_hid_device_input_mutex);
return res;
}
bool USBHID::addDevice(USBHIDDevice * device, uint16_t descriptor_len){
if(device && tinyusb_loaded_hid_devices_num < USB_HID_DEVICES_MAX){
if(!tinyusb_enable_hid_device(descriptor_len, device)){
return false;
}
return true;
}
return false;
}
void USBHID::onEvent(esp_event_handler_t callback){
onEvent(ARDUINO_USB_HID_ANY_EVENT, callback);
}
void USBHID::onEvent(arduino_usb_hid_event_t event, esp_event_handler_t callback){
arduino_usb_event_handler_register_with(ARDUINO_USB_HID_EVENTS, event, callback, this);
}
#endif /* CONFIG_TINYUSB_HID_ENABLED */

View File

@ -0,0 +1,79 @@
// 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 <stdint.h>
#include <stdbool.h>
#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 */

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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<len; i++){
if(rx_queue == NULL || !xQueueSend(rx_queue, buffer+i, 0)){
len = i+1;
log_e("RX Queue Overflow");
break;
}
}
arduino_usb_hid_vendor_event_data_t p = {0};
p.buffer = buffer;
p.len = len;
arduino_usb_event_post(ARDUINO_USB_HID_VENDOR_EVENTS, ARDUINO_USB_HID_VENDOR_OUTPUT_EVENT, &p, sizeof(arduino_usb_hid_vendor_event_data_t), portMAX_DELAY);
}
size_t USBHIDVendor::write(const uint8_t* buffer, size_t len){
uint8_t hid_in[HID_VENDOR_REPORT_SIZE];
const uint8_t * data = (const uint8_t *)buffer;
uint8_t size_offset = prepend_size?1:0;
size_t to_send = len, max_send=HID_VENDOR_REPORT_SIZE - size_offset, will_send=0;
while(to_send){
will_send = to_send;
if(will_send > 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 */

View File

@ -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

View File

@ -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<len; i++){
if(rx_queue == NULL || !xQueueSend(rx_queue, buffer+i, 0)){
len = i+1;
log_e("RX Queue Overflow");
break;
}
}
arduino_usb_vendor_event_data_t p = {0};
p.data.len = len;
arduino_usb_event_post(ARDUINO_USB_VENDOR_EVENTS, ARDUINO_USB_VENDOR_DATA_EVENT, &p, sizeof(arduino_usb_vendor_event_data_t), portMAX_DELAY);
}
size_t USBVendor::write(const uint8_t* buffer, size_t len){
if(!mounted()){
log_e("not mounted");
return 0;
}
size_t max_len = tud_vendor_n_write_available(itf);
if(len > 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 */

View File

@ -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 */

View File

@ -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