gamepad turbo button arduino code

Arduino code for the gamepad turbo button project. also including a gamepad device report example with uf2
This commit is contained in:
Liz 2024-10-08 15:13:37 -04:00
parent 1eb9dad994
commit b2ac355beb
8 changed files with 857 additions and 0 deletions

View file

@ -0,0 +1,291 @@
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example demonstrates use of both device and host, where
* - Device runs on native USB controller (roothub port0)
* - Host depends on MCU:
* - rp2040: bit-banging 2 GPIOs with Pico-PIO-USB library (roothub port1)
*
* Requirements:
* - For rp2040:
* - Pico-PIO-USB library
* - 2 consecutive GPIOs: D+ is defined by PIN_USB_HOST_DP, D- = D+ +1
* - Provide VBus (5v) and GND for peripheral
* - CPU Speed must be either 120 or 240 MHz. Selected via "Menu -> CPU Speed"
*/
// USBHost is defined in usbh_helper.h
#include "usbh_helper.h"
#include "tusb.h"
#include "Adafruit_TinyUSB.h"
#include "gamepad_reports.h"
// HID report descriptor using TinyUSB's template
// Single Report (no ID) descriptor
uint8_t const desc_hid_report[] = {
TUD_HID_REPORT_DESC_GAMEPAD()
};
// USB HID object
Adafruit_USBD_HID usb_hid;
// Report payload defined in src/class/hid/hid.h
// - For Gamepad Button Bit Mask see hid_gamepad_button_bm_t
// - For Gamepad Hat Bit Mask see hid_gamepad_hat_t
hid_gamepad_report_t gp;
bool combo_active = false;
void setup() {
if (!TinyUSBDevice.isInitialized()) {
TinyUSBDevice.begin(0);
}
Serial.begin(115200);
// Setup HID
usb_hid.setPollInterval(2);
usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
usb_hid.begin();
// If already enumerated, additional class driverr begin() e.g msc, hid, midi won't take effect until re-enumeration
if (TinyUSBDevice.mounted()) {
TinyUSBDevice.detach();
delay(10);
TinyUSBDevice.attach();
}
}
#if defined(ARDUINO_ARCH_RP2040)
//--------------------------------------------------------------------+
// For RP2040 use both core0 for device stack, core1 for host stack
//--------------------------------------------------------------------//
//------------- Core0 -------------//
void loop() {
}
//------------- Core1 -------------//
void setup1() {
// configure pio-usb: defined in usbh_helper.h
rp2040_configure_pio_usb();
// run host stack on controller (rhport) 1
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
// host bit-banging processing works done in core1 to free up core0 for other works
USBHost.begin(1);
}
void loop1() {
USBHost.task();
Serial.flush();
if (combo_active) {
turbo_button();
}
}
#endif
//--------------------------------------------------------------------+
// HID Host Callback Functions
//--------------------------------------------------------------------+
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
{
Serial.printf("HID device mounted (address %d, instance %d)\n", dev_addr, instance);
// Start receiving HID reports
if (!tuh_hid_receive_report(dev_addr, instance))
{
Serial.printf("Error: cannot request to receive report\n");
}
}
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
{
Serial.printf("HID device unmounted (address %d, instance %d)\n", dev_addr, instance);
}
void turbo_button() {
if (combo_active) {
while (!usb_hid.ready()) {
yield();
}
Serial.println("A");
gp.buttons = GAMEPAD_BUTTON_A;
usb_hid.sendReport(0, &gp, sizeof(gp));
Serial.println("off");
delay(2);
gp.buttons = 0;
usb_hid.sendReport(0, &gp, sizeof(gp));
delay(2);
}
}
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) {
// Known report when the combo is pressed
//uint8_t combo_report[] = { 0x80, 0x7F, 0x80, 0x7F, 0x28, 0x03, 0x00, 0xFF };
// Check if the incoming report matches the combo report
bool combo_detected = ((report[4] == combo_report[4]) && (report[5] == combo_report[5]));// len == sizeof(combo_report)) && (memcmp(report, combo_report, sizeof(combo_report)) == 0);
// Manage the combo state and print messages
if (combo_detected && !combo_active) {
combo_active = true;
Serial.println("combo!");
} else if (combo_detected && combo_active) {
combo_active = false;
Serial.println("combo released!");
}
if (!(combo_active)) {
if (!(report[BYTE_LEFT_STICK_X] == LEFT_STICK_X_NEUTRAL)) {
int16_t leftStickX = report[BYTE_LEFT_STICK_X];
Serial.print("left stick X: ");
Serial.println(leftStickX);
int16_t new_leftStickX = map(leftStickX, 0, 255, -127, 127);
gp.x = new_leftStickX;
} else {
gp.x = 0;
}
if (!(report[BYTE_LEFT_STICK_Y] == LEFT_STICK_Y_NEUTRAL)) {
int16_t leftStickY = report[BYTE_LEFT_STICK_Y];
Serial.print("left stick Y: ");
Serial.println(leftStickY);
int16_t new_leftStickY = map(leftStickY, 0, 255, -127, 127);
gp.y = new_leftStickY;
} else {
gp.y = 0;
}
if (!(report[BYTE_RIGHT_STICK_X] == RIGHT_STICK_X_NEUTRAL)) {
int8_t rightStickX = report[BYTE_RIGHT_STICK_X];
Serial.print("right stick X: ");
Serial.println(rightStickX);
int16_t new_rightStickX = map(rightStickX, 0, 255, 127, -127);
gp.z = new_rightStickX;
} else {
gp.z = 0;
}
if (!(report[BYTE_RIGHT_STICK_Y] == RIGHT_STICK_Y_NEUTRAL)) {
int8_t rightStickY = report[BYTE_RIGHT_STICK_Y];
Serial.print("right stick Y: ");
Serial.println(rightStickY);
int16_t new_rightStickY = map(rightStickY, 0, 255, -127, 127);
gp.rz = new_rightStickY;
} else {
gp.rz = 0;
}
if (!(report[BYTE_DPAD_BUTTONS] == DPAD_NEUTRAL)) {
// D-Pad is active
uint8_t buttonsSelect = report[BYTE_DPAD_BUTTONS];
switch (buttonsSelect) {
case BUTTON_X:
Serial.println("x");
gp.buttons = GAMEPAD_BUTTON_X;
break;
case BUTTON_A:
Serial.println("a");
gp.buttons = GAMEPAD_BUTTON_A;
break;
case BUTTON_B:
Serial.println("b");
gp.buttons = GAMEPAD_BUTTON_B;
break;
case BUTTON_Y:
Serial.println("y");
gp.buttons = GAMEPAD_BUTTON_Y;
break;
}
} else {
gp.hat = 0;
gp.buttons = 0;
}
if (!(report[BYTE_DPAD_BUTTONS] == DPAD_NEUTRAL)) {
// D-Pad is active
uint8_t dpadDirection = report[BYTE_DPAD_BUTTONS];
switch (dpadDirection) {
case DPAD_UP:
Serial.println("up");
gp.hat = 1; // GAMEPAD_HAT_UP;
break;
case DPAD_UP_RIGHT:
Serial.println("up/right");
gp.hat = 2;
break;
case DPAD_RIGHT:
Serial.println("right");
gp.hat = 3;
break;
case DPAD_DOWN_RIGHT:
Serial.println("down/right");
gp.hat = 4;
break;
case DPAD_DOWN:
Serial.println("down");
gp.hat = 5;
break;
case DPAD_DOWN_LEFT:
Serial.println("down/left");
gp.hat = 6;
break;
case DPAD_LEFT:
Serial.println("left");
gp.hat = 7;
break;
case DPAD_UP_LEFT:
Serial.println("up/left");
gp.hat = 8;
break;
}
} else {
gp.hat = 0;
}
if (!(report[BYTE_MISC_BUTTONS] == MISC_NEUTRAL)) {
// misc are active
uint8_t miscDirection = report[BYTE_MISC_BUTTONS];
switch (miscDirection) {
case BUTTON_LEFT_PADDLE:
Serial.println("left paddle");
gp.buttons = GAMEPAD_BUTTON_TL;
break;
case BUTTON_RIGHT_PADDLE:
Serial.println("right paddle");
gp.buttons = GAMEPAD_BUTTON_TR;
break;
case BUTTON_LEFT_TRIGGER:
Serial.println("left trigger");
gp.buttons = GAMEPAD_BUTTON_TL2;
break;
case BUTTON_RIGHT_TRIGGER:
Serial.println("right trigger");
gp.buttons = GAMEPAD_BUTTON_TR2;
break;
case BUTTON_BACK:
Serial.println("back");
gp.buttons = GAMEPAD_BUTTON_SELECT;
break;
case BUTTON_START:
Serial.println("start");
gp.buttons = GAMEPAD_BUTTON_START;
break;
}
}
} else {
gp.buttons = GAMEPAD_BUTTON_A;
}
while (!usb_hid.ready()) {
yield();
}
usb_hid.sendReport(0, &gp, sizeof(gp));
// Continue to receive the next report
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.println("Error: cannot request to receive report");
}
}

View file

@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
// HID reports for Logitech Gamepad F310
// Update defines and combo_report for your gamepad and/or combo!
uint8_t combo_report[] = { 0x80, 0x7F, 0x80, 0x7F, 0x28, 0x03, 0x00, 0xFF };
// Byte indices for the gamepad report
#define BYTE_LEFT_STICK_X 0 // Left analog stick X-axis
#define BYTE_LEFT_STICK_Y 1 // Left analog stick Y-axis
#define BYTE_RIGHT_STICK_X 2 // Right analog stick X-axis
#define BYTE_RIGHT_STICK_Y 3 // Right analog stick Y-axis
#define BYTE_DPAD_BUTTONS 4 // D-Pad and face buttons
#define BYTE_MISC_BUTTONS 5 // Miscellaneous buttons (triggers, paddles, start, back)
#define BYTE_UNUSED 6 // Unused
#define BYTE_STATUS 7 // Status byte (usually constant)
// Button masks for Byte[4] (DPAD and face buttons)
#define DPAD_MASK 0x07 // Bits 0-2 for D-Pad direction
#define DPAD_NEUTRAL 0x08 // Bit 3 set when D-Pad is neutral
// D-Pad directions (use when DPAD_NEUTRAL is not set)
#define DPAD_UP 0x00 // 0000
#define DPAD_UP_RIGHT 0x01 // 0001
#define DPAD_RIGHT 0x02 // 0010
#define DPAD_DOWN_RIGHT 0x03 // 0011
#define DPAD_DOWN 0x04 // 0100
#define DPAD_DOWN_LEFT 0x05 // 0101
#define DPAD_LEFT 0x06 // 0110
#define DPAD_UP_LEFT 0x07 // 0111
// Face buttons (Byte[4] bits 4-7)
#define BUTTON_X 0x18
#define BUTTON_A 0x28
#define BUTTON_B 0x48
#define BUTTON_Y 0x88
// Button masks for Byte[5] (MISC buttons)
#define MISC_NEUTRAL 0x00
// Miscellaneous buttons (Byte[5])
#define BUTTON_LEFT_PADDLE 0x01
#define BUTTON_RIGHT_PADDLE 0x02
#define BUTTON_LEFT_TRIGGER 0x04
#define BUTTON_RIGHT_TRIGGER 0x08
#define BUTTON_BACK 0x10
#define BUTTON_START 0x20
#define LEFT_STICK_X_NEUTRAL 0x80
#define LEFT_STICK_Y_NEUTRAL 0x7F
#define RIGHT_STICK_X_NEUTRAL 0x80
#define RIGHT_STICK_Y_NEUTRAL 0x7F

View file

@ -0,0 +1,103 @@
// SPDX-FileCopyrightText: 2024 Ha Thach for Adafruit Industries
//
// SPDX-License-Identifier: MIT
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#ifndef USBH_HELPER_H
#define USBH_HELPER_H
#ifdef ARDUINO_ARCH_RP2040
// pio-usb is required for rp2040 host
#include "pio_usb.h"
// Pin D+ for host, D- = D+ + 1
#ifndef PIN_USB_HOST_DP
#define PIN_USB_HOST_DP 16
#endif
// Pin for enabling Host VBUS. comment out if not used
#ifndef PIN_5V_EN
#define PIN_5V_EN 18
#endif
#ifndef PIN_5V_EN_STATE
#define PIN_5V_EN_STATE 1
#endif
#endif // ARDUINO_ARCH_RP2040
#include "Adafruit_TinyUSB.h"
#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
// USB Host using MAX3421E: SPI, CS, INT
#include "SPI.h"
#if defined(ARDUINO_METRO_ESP32S2)
Adafruit_USBH_Host USBHost(&SPI, 15, 14);
#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32_V2)
Adafruit_USBH_Host USBHost(&SPI, 33, 15);
#else
// Default CS and INT are pin 10, 9
Adafruit_USBH_Host USBHost(&SPI, 10, 9);
#endif
#else
// Native USB Host such as rp2040
Adafruit_USBH_Host USBHost;
#endif
//--------------------------------------------------------------------+
// Helper Functions
//--------------------------------------------------------------------+
#ifdef ARDUINO_ARCH_RP2040
static void rp2040_configure_pio_usb(void) {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
while (!Serial) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while (1) {
delay(1);
}
}
#ifdef PIN_5V_EN
pinMode(PIN_5V_EN, OUTPUT);
digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE);
#endif
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = PIN_USB_HOST_DP;
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
// For pico-w, PIO is also used to communicate with cyw43
// Therefore we need to alternate the pio-usb configuration
// details https://github.com/sekigon-gonnoc/Pico-PIO-USB/issues/46
pio_cfg.sm_tx = 3;
pio_cfg.sm_rx = 2;
pio_cfg.sm_eop = 3;
pio_cfg.pio_rx_num = 0;
pio_cfg.pio_tx_num = 1;
pio_cfg.tx_ch = 9;
#endif
USBHost.configure_pio_usb(1, &pio_cfg);
}
#endif
#endif

View file

@ -0,0 +1,306 @@
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
//
// SPDX-License-Identifier: MIT
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This example demonstrates use of both device and host, where
* - Device runs on native USB controller (roothub port0)
* - Host depends on MCU:
* - rp2040: bit-banging 2 GPIOs with Pico-PIO-USB library (roothub port1)
* - samd21/51, nrf52840, esp32: using MAX3421e controller (host shield)
*
* Requirements:
* - For rp2040:
* - Pico-PIO-USB library
* - 2 consecutive GPIOs: D+ is defined by PIN_USB_HOST_DP, D- = D+ +1
* - Provide VBus (5v) and GND for peripheral
* - CPU Speed must be either 120 or 240 MHz. Selected via "Menu -> CPU Speed"
* - For samd21/51, nrf52840, esp32:
* - Additional MAX2341e USB Host shield or featherwing is required
* - SPI instance, CS pin, INT pin are correctly configured in usbh_helper.h
*/
/* Host example will get device descriptors of attached devices and print it out via
* device CDC (Serial) as follows:
* Device 1: ID 046d:c52f
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 0200
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x046d
idProduct 0xc52f
bcdDevice 2200
iManufacturer 1 Logitech
iProduct 2 USB Receiver
iSerialNumber 0
bNumConfigurations 1
*
*/
// USBHost is defined in usbh_helper.h
#include "usbh_helper.h"
#include "tusb.h"
// Language ID: English
#define LANGUAGE_ID 0x0409
typedef struct {
tusb_desc_device_t desc_device;
uint16_t manufacturer[32];
uint16_t product[48];
uint16_t serial[16];
bool mounted;
} dev_info_t;
// CFG_TUH_DEVICE_MAX is defined by tusb_config header
dev_info_t dev_info[CFG_TUH_DEVICE_MAX] = { 0 };
void setup() {
Serial.begin(115200);
#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
// init host stack on controller (rhport) 1
// For rp2040: this is called in core1's setup1()
USBHost.begin(1);
#endif
Serial.println("TinyUSB Dual Device Info Example with HID Report");
}
#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
//--------------------------------------------------------------------+
// Using Host shield MAX3421E controller
//--------------------------------------------------------------------+
void loop() {
USBHost.task();
Serial.flush();
}
#elif defined(ARDUINO_ARCH_RP2040)
//--------------------------------------------------------------------+
// For RP2040 use both core0 for device stack, core1 for host stack
//--------------------------------------------------------------------//
//------------- Core0 -------------//
void loop() {
}
//------------- Core1 -------------//
void setup1() {
// configure pio-usb: defined in usbh_helper.h
rp2040_configure_pio_usb();
// run host stack on controller (rhport) 1
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
// host bit-banging processing works done in core1 to free up core0 for other works
USBHost.begin(1);
}
void loop1() {
USBHost.task();
Serial.flush();
}
#endif
//--------------------------------------------------------------------+
// TinyUSB Host callbacks
//--------------------------------------------------------------------+
void print_device_descriptor(tuh_xfer_t *xfer);
void utf16_to_utf8(uint16_t *temp_buf, size_t buf_len);
void print_lsusb(void) {
bool no_device = true;
for (uint8_t daddr = 1; daddr < CFG_TUH_DEVICE_MAX + 1; daddr++) {
// TODO can use tuh_mounted(daddr), but tinyusb has a bug
// use local connected flag instead
dev_info_t *dev = &dev_info[daddr - 1];
if (dev->mounted) {
Serial.printf("Device %u: ID %04x:%04x %s %s\r\n", daddr,
dev->desc_device.idVendor, dev->desc_device.idProduct,
(char *) dev->manufacturer, (char *) dev->product);
no_device = false;
}
}
if (no_device) {
Serial.println("No device connected (except hub)");
}
}
// Invoked when device is mounted (configured)
void tuh_mount_cb(uint8_t daddr) {
Serial.printf("Device attached, address = %d\r\n", daddr);
dev_info_t *dev = &dev_info[daddr - 1];
dev->mounted = true;
// Get Device Descriptor
tuh_descriptor_get_device(daddr, &dev->desc_device, 18, print_device_descriptor, 0);
}
/// Invoked when device is unmounted (bus reset/unplugged)
void tuh_umount_cb(uint8_t daddr) {
Serial.printf("Device removed, address = %d\r\n", daddr);
dev_info_t *dev = &dev_info[daddr - 1];
dev->mounted = false;
// print device summary
print_lsusb();
}
void print_device_descriptor(tuh_xfer_t *xfer) {
if (XFER_RESULT_SUCCESS != xfer->result) {
Serial.printf("Failed to get device descriptor\r\n");
return;
}
uint8_t const daddr = xfer->daddr;
dev_info_t *dev = &dev_info[daddr - 1];
tusb_desc_device_t *desc = &dev->desc_device;
Serial.printf("Device %u: ID %04x:%04x\r\n", daddr, desc->idVendor, desc->idProduct);
Serial.printf("Device Descriptor:\r\n");
Serial.printf(" bLength %u\r\n" , desc->bLength);
Serial.printf(" bDescriptorType %u\r\n" , desc->bDescriptorType);
Serial.printf(" bcdUSB %04x\r\n" , desc->bcdUSB);
Serial.printf(" bDeviceClass %u\r\n" , desc->bDeviceClass);
Serial.printf(" bDeviceSubClass %u\r\n" , desc->bDeviceSubClass);
Serial.printf(" bDeviceProtocol %u\r\n" , desc->bDeviceProtocol);
Serial.printf(" bMaxPacketSize0 %u\r\n" , desc->bMaxPacketSize0);
Serial.printf(" idVendor 0x%04x\r\n" , desc->idVendor);
Serial.printf(" idProduct 0x%04x\r\n" , desc->idProduct);
Serial.printf(" bcdDevice %04x\r\n" , desc->bcdDevice);
// Get String descriptor using Sync API
Serial.printf(" iManufacturer %u ", desc->iManufacturer);
if (XFER_RESULT_SUCCESS ==
tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, dev->manufacturer, sizeof(dev->manufacturer))) {
utf16_to_utf8(dev->manufacturer, sizeof(dev->manufacturer));
Serial.printf((char *) dev->manufacturer);
}
Serial.printf("\r\n");
Serial.printf(" iProduct %u ", desc->iProduct);
if (XFER_RESULT_SUCCESS ==
tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, dev->product, sizeof(dev->product))) {
utf16_to_utf8(dev->product, sizeof(dev->product));
Serial.printf((char *) dev->product);
}
Serial.printf("\r\n");
Serial.printf(" iSerialNumber %u ", desc->iSerialNumber);
if (XFER_RESULT_SUCCESS ==
tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, dev->serial, sizeof(dev->serial))) {
utf16_to_utf8(dev->serial, sizeof(dev->serial));
Serial.printf((char *) dev->serial);
}
Serial.printf("\r\n");
Serial.printf(" bNumConfigurations %u\r\n", desc->bNumConfigurations);
// print device summary
print_lsusb();
}
//--------------------------------------------------------------------+
// String Descriptor Helper
//--------------------------------------------------------------------+
static void _convert_utf16le_to_utf8(const uint16_t *utf16, size_t utf16_len, uint8_t *utf8, size_t utf8_len) {
// TODO: Check for runover.
(void) utf8_len;
// Get the UTF-16 length out of the data itself.
for (size_t i = 0; i < utf16_len; i++) {
uint16_t chr = utf16[i];
if (chr < 0x80) {
*utf8++ = chr & 0xff;
} else if (chr < 0x800) {
*utf8++ = (uint8_t) (0xC0 | (chr >> 6 & 0x1F));
*utf8++ = (uint8_t) (0x80 | (chr >> 0 & 0x3F));
} else {
// TODO: Verify surrogate.
*utf8++ = (uint8_t) (0xE0 | (chr >> 12 & 0x0F));
*utf8++ = (uint8_t) (0x80 | (chr >> 6 & 0x3F));
*utf8++ = (uint8_t) (0x80 | (chr >> 0 & 0x3F));
}
// TODO: Handle UTF-16 code points that take two entries.
}
}
// Count how many bytes a utf-16-le encoded string will take in utf-8.
static int _count_utf8_bytes(const uint16_t *buf, size_t len) {
size_t total_bytes = 0;
for (size_t i = 0; i < len; i++) {
uint16_t chr = buf[i];
if (chr < 0x80) {
total_bytes += 1;
} else if (chr < 0x800) {
total_bytes += 2;
} else {
total_bytes += 3;
}
// TODO: Handle UTF-16 code points that take two entries.
}
return total_bytes;
}
void utf16_to_utf8(uint16_t *temp_buf, size_t buf_len) {
size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t);
size_t utf8_len = _count_utf8_bytes(temp_buf + 1, utf16_len);
_convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, buf_len);
((uint8_t *) temp_buf)[utf8_len] = '\0';
}
//--------------------------------------------------------------------+
// HID Host Callback Functions
//--------------------------------------------------------------------+
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
{
Serial.printf("HID device mounted (address %d, instance %d)\n", dev_addr, instance);
// Start receiving HID reports
if (!tuh_hid_receive_report(dev_addr, instance))
{
Serial.printf("Error: cannot request to receive report\n");
}
}
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
{
Serial.printf("HID device unmounted (address %d, instance %d)\n", dev_addr, instance);
}
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
{
Serial.printf("Received HID report from device %d instance %d: ", dev_addr, instance);
for (uint16_t i = 0; i < len; i++)
{
Serial.printf("%02X ", report[i]);
}
Serial.printf("\n");
// Continue to receive the next report
if (!tuh_hid_receive_report(dev_addr, instance))
{
Serial.printf("Error: cannot request to receive report\n");
}
}

View file

@ -0,0 +1,103 @@
// SPDX-FileCopyrightText: 2024 Ha Thach for Adafruit Industries
//
// SPDX-License-Identifier: MIT
/*********************************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
Copyright (c) 2019 Ha Thach for Adafruit Industries
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#ifndef USBH_HELPER_H
#define USBH_HELPER_H
#ifdef ARDUINO_ARCH_RP2040
// pio-usb is required for rp2040 host
#include "pio_usb.h"
// Pin D+ for host, D- = D+ + 1
#ifndef PIN_USB_HOST_DP
#define PIN_USB_HOST_DP 16
#endif
// Pin for enabling Host VBUS. comment out if not used
#ifndef PIN_5V_EN
#define PIN_5V_EN 18
#endif
#ifndef PIN_5V_EN_STATE
#define PIN_5V_EN_STATE 1
#endif
#endif // ARDUINO_ARCH_RP2040
#include "Adafruit_TinyUSB.h"
#if defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
// USB Host using MAX3421E: SPI, CS, INT
#include "SPI.h"
#if defined(ARDUINO_METRO_ESP32S2)
Adafruit_USBH_Host USBHost(&SPI, 15, 14);
#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32_V2)
Adafruit_USBH_Host USBHost(&SPI, 33, 15);
#else
// Default CS and INT are pin 10, 9
Adafruit_USBH_Host USBHost(&SPI, 10, 9);
#endif
#else
// Native USB Host such as rp2040
Adafruit_USBH_Host USBHost;
#endif
//--------------------------------------------------------------------+
// Helper Functions
//--------------------------------------------------------------------+
#ifdef ARDUINO_ARCH_RP2040
static void rp2040_configure_pio_usb(void) {
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
uint32_t cpu_hz = clock_get_hz(clk_sys);
if (cpu_hz != 120000000UL && cpu_hz != 240000000UL) {
while (!Serial) {
delay(10); // wait for native usb
}
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
while (1) {
delay(1);
}
}
#ifdef PIN_5V_EN
pinMode(PIN_5V_EN, OUTPUT);
digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE);
#endif
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = PIN_USB_HOST_DP;
#if defined(ARDUINO_RASPBERRY_PI_PICO_W)
// For pico-w, PIO is also used to communicate with cyw43
// Therefore we need to alternate the pio-usb configuration
// details https://github.com/sekigon-gonnoc/Pico-PIO-USB/issues/46
pio_cfg.sm_tx = 3;
pio_cfg.sm_rx = 2;
pio_cfg.sm_eop = 3;
pio_cfg.pio_rx_num = 0;
pio_cfg.pio_tx_num = 1;
pio_cfg.tx_ch = 9;
#endif
USBHost.configure_pio_usb(1, &pio_cfg);
}
#endif
#endif