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:
parent
1eb9dad994
commit
b2ac355beb
8 changed files with 857 additions and 0 deletions
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Binary file not shown.
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Reference in a new issue