Created
September 15, 2022 05:04
-
-
Save RefactorFactory/62dd9f98d40e55584966bed28fa98868 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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 | |
*/ | |
#if ARDUINO_USB_MODE | |
#warning This sketch should be used when USB is in OTG mode | |
void setup(){} | |
void loop(){} | |
#else | |
#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 busyTask(void *pvParameter) | |
{ | |
while (true) { | |
const int ms = random(1, 4 + 1); | |
unsigned long start = millis(); | |
while (millis() < start + ms) | |
; | |
vTaskDelay(random(1, 4 + 1) / portTICK_RATE_MS); | |
} | |
} | |
void blinkTask(void *pvParameter) | |
{ | |
const int led = LED_BUILTIN; | |
pinMode(led, OUTPUT); | |
while (true) { | |
digitalWrite(led, LOW); | |
vTaskDelay(1000 / portTICK_RATE_MS); | |
digitalWrite(led, HIGH); | |
vTaskDelay(1000 / portTICK_RATE_MS); | |
} | |
} | |
void setup() { | |
// make the pushButton pin an input: | |
pinMode(buttonPin, INPUT_PULLUP); | |
// initialize control over the keyboard: | |
Keyboard.begin(); | |
USB.begin(); | |
const int STACK_SIZE = 8 * 1024; | |
xTaskCreate(&blinkTask, "blinkTask", STACK_SIZE, NULL, 5, NULL); | |
xTaskCreate(&busyTask, "busyTask", STACK_SIZE, NULL, 5, NULL); | |
} | |
char logbuf[1024]; | |
bool printoff; | |
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)) { | |
*/ | |
if (buttonState == LOW) { | |
// increment the button counter | |
counter++; | |
// type out a message | |
Keyboard.print("a"); | |
/* | |
Keyboard.print("You pressed the button "); | |
Keyboard.print(counter); | |
Keyboard.println(" times."); | |
*/ | |
} | |
// save the current button state for comparison next time: | |
previousButtonState = buttonState; | |
if (logbuf[0]) { | |
printoff = true; | |
Keyboard.print(logbuf); | |
printoff = false; | |
logbuf[0] = 0; | |
} | |
} | |
int my_log_printf(const char *format, ...) { | |
if (printoff) { | |
return 0; | |
} | |
char loc_buf[64]; | |
char * temp = loc_buf; | |
va_list arg; | |
va_list copy; | |
va_start(arg, format); | |
va_copy(copy, arg); | |
int len = vsnprintf(temp, sizeof(loc_buf), format, copy); | |
va_end(copy); | |
if(len < 0) { | |
va_end(arg); | |
return 0; | |
}; | |
if(len >= sizeof(loc_buf)){ | |
temp = (char*) malloc(len+1); | |
if(temp == NULL) { | |
va_end(arg); | |
return 0; | |
} | |
len = vsnprintf(temp, len+1, format, arg); | |
} | |
va_end(arg); | |
strcat(logbuf, temp); | |
strcat(logbuf, "\n"); | |
if(temp != loc_buf){ | |
free(temp); | |
} | |
return len; | |
} | |
#endif /* ARDUINO_USB_MODE */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG | |
static const char * tinyusb_hid_device_report_types[4] = {"INVALID", "INPUT", "OUTPUT", "FEATURE"}; | |
#endif | |
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; | |
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; | |
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); | |
} | |
// TinyUSB is in the process of changing the type of the last argument to | |
// tud_hid_report_complete_cb(), so extract the type from the version of TinyUSB that we're | |
// compiled with. | |
template <class F> struct ArgType; | |
template <class R, class T1, class T2, class T3> | |
struct ArgType<R(*)(T1, T2, T3)> { | |
typedef T1 type1; | |
typedef T2 type2; | |
typedef T3 type3; | |
}; | |
typedef ArgType<decltype(&tud_hid_report_complete_cb)>::type3 tud_hid_report_complete_cb_len_t; | |
void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, tud_hid_report_complete_cb_len_t len){ | |
if (tinyusb_hid_device_input_sem) { | |
xSemaphoreGive(tinyusb_hid_device_input_sem); | |
} | |
} | |
int my_log_printf(const char *format, ...); | |
#undef log_i | |
#define log_i(format, ...) my_log_printf(ARDUHAL_LOG_FORMAT(I, format), ##__VA_ARGS__) | |
#undef log_e | |
#define log_e(format, ...) my_log_printf(ARDUHAL_LOG_FORMAT(E, format), ##__VA_ARGS__) | |
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 { | |
// Wait for report to be sent successfully. | |
unsigned long start_time0 = millis(); | |
int take_result0 = xSemaphoreTake(tinyusb_hid_device_input_sem, 0); | |
unsigned long end_time0 = millis(); | |
unsigned long start_time1 = millis(); | |
int take_result1 = xSemaphoreTake(tinyusb_hid_device_input_sem, timeout_ms / portTICK_PERIOD_MS); | |
unsigned long end_time1 = millis(); | |
if(take_result1 != pdTRUE){ | |
log_e("report %u wait failed", id); | |
res = false; | |
} | |
log_i("take_result0: %d, took %u ms; take_result1: %d, took %u ms", take_result0, end_time0 - start_time0, | |
take_result1, end_time1 - start_time1); | |
} | |
} | |
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 */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment