From c300a38e9554a78e25d6555671d45aa47311065e Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 23 Apr 2025 14:44:55 -0500 Subject: [PATCH 1/4] usb host keyboard examples --- .../.feather_rp2040.test.only | 0 .../usbh_helper.h | 110 +++++++++ .../usbhost_keyboard_simpletest.ino | 222 ++++++++++++++++++ .../.metro_rp2350_tinyusb.test.only | 0 .../usbh_helper.h | 110 +++++++++ .../usbhost_keyboard_simpletest.ino | 222 ++++++++++++++++++ .../CircuitPython_Keyboard_Stdin/code.py | 18 ++ .../CircuitPython_Keyboard_USB_Host/code.py | 164 +++++++++++++ 8 files changed, 846 insertions(+) create mode 100644 USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/.feather_rp2040.test.only create mode 100644 USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/usbh_helper.h create mode 100644 USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/usbhost_keyboard_simpletest.ino create mode 100644 USB_Host_Examples/Arduino_Metro_RP2350_USB_Host_Keyboard/.metro_rp2350_tinyusb.test.only create mode 100644 USB_Host_Examples/Arduino_Metro_RP2350_USB_Host_Keyboard/usbh_helper.h create mode 100644 USB_Host_Examples/Arduino_Metro_RP2350_USB_Host_Keyboard/usbhost_keyboard_simpletest.ino create mode 100644 USB_Host_Examples/CircuitPython_Keyboard_Stdin/code.py create mode 100644 USB_Host_Examples/CircuitPython_Keyboard_USB_Host/code.py diff --git a/USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/.feather_rp2040.test.only b/USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/.feather_rp2040.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/usbh_helper.h b/USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/usbh_helper.h new file mode 100644 index 000000000..4f3cc5503 --- /dev/null +++ b/USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/usbh_helper.h @@ -0,0 +1,110 @@ +// 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 + +#ifdef ARDUINO_ARCH_RP2350 + + // 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 32 + #endif + + // Pin for enabling Host VBUS. comment out if not used + #ifndef PIN_5V_EN + #define PIN_5V_EN 29 + #endif + + #ifndef PIN_5V_EN_STATE + #define PIN_5V_EN_STATE 1 + #endif +#endif // ARDUINO_ARCH_RP2350 + +#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"); + +#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 diff --git a/USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/usbhost_keyboard_simpletest.ino b/USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/usbhost_keyboard_simpletest.ino new file mode 100644 index 000000000..f03d72986 --- /dev/null +++ b/USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/usbhost_keyboard_simpletest.ino @@ -0,0 +1,222 @@ +// SPDX-FileCopyrightText: 2025 Tim Cocks 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 usb host with a standard HID boot keyboard. + * - 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 + */ + +// USBHost is defined in usbh_helper.h +#include "usbh_helper.h" +#include "tusb.h" +#include "Adafruit_TinyUSB.h" + + +bool printed_blank = false; + +void setup() { + Serial.begin(115200); + + // 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); + delay(3000); + Serial.print("USB D+ Pin:"); + Serial.println(PIN_USB_HOST_DP); + Serial.print("USB 5V Pin:"); + Serial.println(PIN_5V_EN); +} + +void loop() { + USBHost.task(); + Serial.flush(); + +} + +//--------------------------------------------------------------------+ +// 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) { + + if (len > 0){ + + //debug print report data + // Serial.print("Report data: "); + // for (int i = 0; i < len; i++) { + // Serial.print(report[i], HEX); + // Serial.print(" "); + + // } + // Serial.println(); + + printKeyboardReport(report); + } + + // Continue to receive the next report + if (!tuh_hid_receive_report(dev_addr, instance)) { + Serial.println("Error: cannot request to receive report"); + } +} + +// Helper function to print key names +void printKeyName(uint8_t key) { + // This is a simplified list. Full HID keyboard has many more key codes + switch (key) { + case 0x04: Serial.print("A"); break; + case 0x05: Serial.print("B"); break; + case 0x06: Serial.print("C"); break; + case 0x07: Serial.print("D"); break; + case 0x08: Serial.print("E"); break; + case 0x09: Serial.print("F"); break; + case 0x0A: Serial.print("G"); break; + case 0x0B: Serial.print("H"); break; + case 0x0C: Serial.print("I"); break; + case 0x0D: Serial.print("J"); break; + case 0x0E: Serial.print("K"); break; + case 0x0F: Serial.print("L"); break; + case 0x10: Serial.print("M"); break; + case 0x11: Serial.print("N"); break; + case 0x12: Serial.print("O"); break; + case 0x13: Serial.print("P"); break; + case 0x14: Serial.print("Q"); break; + case 0x15: Serial.print("R"); break; + case 0x16: Serial.print("S"); break; + case 0x17: Serial.print("T"); break; + case 0x18: Serial.print("U"); break; + case 0x19: Serial.print("V"); break; + case 0x1A: Serial.print("W"); break; + case 0x1B: Serial.print("X"); break; + case 0x1C: Serial.print("Y"); break; + case 0x1D: Serial.print("Z"); break; + case 0x1E: Serial.print("1"); break; + case 0x1F: Serial.print("2"); break; + case 0x20: Serial.print("3"); break; + case 0x21: Serial.print("4"); break; + case 0x22: Serial.print("5"); break; + case 0x23: Serial.print("6"); break; + case 0x24: Serial.print("7"); break; + case 0x25: Serial.print("8"); break; + case 0x26: Serial.print("9"); break; + case 0x27: Serial.print("0"); break; + case 0x28: Serial.print("ENTER"); break; + case 0x29: Serial.print("ESC"); break; + case 0x2A: Serial.print("BACKSPACE"); break; + case 0x2B: Serial.print("TAB"); break; + case 0x2C: Serial.print("SPACE"); break; + case 0x2D: Serial.print("MINUS"); break; + case 0x2E: Serial.print("EQUAL"); break; + case 0x2F: Serial.print("LBRACKET"); break; + case 0x30: Serial.print("RBRACKET"); break; + case 0x31: Serial.print("BACKSLASH"); break; + case 0x33: Serial.print("SEMICOLON"); break; + case 0x34: Serial.print("QUOTE"); break; + case 0x35: Serial.print("GRAVE"); break; + case 0x36: Serial.print("COMMA"); break; + case 0x37: Serial.print("PERIOD"); break; + case 0x38: Serial.print("SLASH"); break; + case 0x39: Serial.print("CAPS_LOCK"); break; + case 0x4F: Serial.print("RIGHT_ARROW"); break; + case 0x50: Serial.print("LEFT_ARROW"); break; + case 0x51: Serial.print("DOWN_ARROW"); break; + case 0x52: Serial.print("UP_ARROW"); break; + default: + if (key >= 0x3A && key <= 0x45) { // F1-F12 + Serial.print("F"); + Serial.print(key - 0x3A + 1); + } else { + // For keys not handled above, just print the HID code + Serial.print("0x"); + Serial.print(key, HEX); + } + break; + } +} + +void printKeyboardReport(uint8_t const* report) { + // First byte contains modifier keys + uint8_t modifiers = report[0]; + + // Print modifier keys if pressed + if (modifiers > 0) { + Serial.print("Modifiers: "); + + if (modifiers & 0x01) Serial.print("LEFT_CTRL "); + if (modifiers & 0x02) Serial.print("LEFT_SHIFT "); + if (modifiers & 0x04) Serial.print("LEFT_ALT "); + if (modifiers & 0x08) Serial.print("LEFT_GUI "); + if (modifiers & 0x10) Serial.print("RIGHT_CTRL "); + if (modifiers & 0x20) Serial.print("RIGHT_SHIFT "); + if (modifiers & 0x40) Serial.print("RIGHT_ALT "); + if (modifiers & 0x80) Serial.print("RIGHT_GUI "); + + Serial.println(); + } + + // Second byte is reserved (usually 0) + + // Bytes 2-7 contain up to 6 key codes + bool keysPressed = false; + + for (int i = 2; i < 8; i++) { + uint8_t key = report[i]; + + // Skip if no key or error rollover + if (key == 0 || key == 1) { + continue; + } + + if (!keysPressed) { + Serial.print("Keys: "); + keysPressed = true; + } + + // Print key name based on HID Usage Tables for standard keyboard + printKeyName(key); + Serial.print(" "); + } + + if (keysPressed) { + Serial.println(); + } else if (modifiers == 0) { + Serial.println("No keys pressed"); + } +} diff --git a/USB_Host_Examples/Arduino_Metro_RP2350_USB_Host_Keyboard/.metro_rp2350_tinyusb.test.only b/USB_Host_Examples/Arduino_Metro_RP2350_USB_Host_Keyboard/.metro_rp2350_tinyusb.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/USB_Host_Examples/Arduino_Metro_RP2350_USB_Host_Keyboard/usbh_helper.h b/USB_Host_Examples/Arduino_Metro_RP2350_USB_Host_Keyboard/usbh_helper.h new file mode 100644 index 000000000..4f3cc5503 --- /dev/null +++ b/USB_Host_Examples/Arduino_Metro_RP2350_USB_Host_Keyboard/usbh_helper.h @@ -0,0 +1,110 @@ +// 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 + +#ifdef ARDUINO_ARCH_RP2350 + + // 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 32 + #endif + + // Pin for enabling Host VBUS. comment out if not used + #ifndef PIN_5V_EN + #define PIN_5V_EN 29 + #endif + + #ifndef PIN_5V_EN_STATE + #define PIN_5V_EN_STATE 1 + #endif +#endif // ARDUINO_ARCH_RP2350 + +#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"); + +#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 diff --git a/USB_Host_Examples/Arduino_Metro_RP2350_USB_Host_Keyboard/usbhost_keyboard_simpletest.ino b/USB_Host_Examples/Arduino_Metro_RP2350_USB_Host_Keyboard/usbhost_keyboard_simpletest.ino new file mode 100644 index 000000000..f03d72986 --- /dev/null +++ b/USB_Host_Examples/Arduino_Metro_RP2350_USB_Host_Keyboard/usbhost_keyboard_simpletest.ino @@ -0,0 +1,222 @@ +// SPDX-FileCopyrightText: 2025 Tim Cocks 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 usb host with a standard HID boot keyboard. + * - 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 + */ + +// USBHost is defined in usbh_helper.h +#include "usbh_helper.h" +#include "tusb.h" +#include "Adafruit_TinyUSB.h" + + +bool printed_blank = false; + +void setup() { + Serial.begin(115200); + + // 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); + delay(3000); + Serial.print("USB D+ Pin:"); + Serial.println(PIN_USB_HOST_DP); + Serial.print("USB 5V Pin:"); + Serial.println(PIN_5V_EN); +} + +void loop() { + USBHost.task(); + Serial.flush(); + +} + +//--------------------------------------------------------------------+ +// 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) { + + if (len > 0){ + + //debug print report data + // Serial.print("Report data: "); + // for (int i = 0; i < len; i++) { + // Serial.print(report[i], HEX); + // Serial.print(" "); + + // } + // Serial.println(); + + printKeyboardReport(report); + } + + // Continue to receive the next report + if (!tuh_hid_receive_report(dev_addr, instance)) { + Serial.println("Error: cannot request to receive report"); + } +} + +// Helper function to print key names +void printKeyName(uint8_t key) { + // This is a simplified list. Full HID keyboard has many more key codes + switch (key) { + case 0x04: Serial.print("A"); break; + case 0x05: Serial.print("B"); break; + case 0x06: Serial.print("C"); break; + case 0x07: Serial.print("D"); break; + case 0x08: Serial.print("E"); break; + case 0x09: Serial.print("F"); break; + case 0x0A: Serial.print("G"); break; + case 0x0B: Serial.print("H"); break; + case 0x0C: Serial.print("I"); break; + case 0x0D: Serial.print("J"); break; + case 0x0E: Serial.print("K"); break; + case 0x0F: Serial.print("L"); break; + case 0x10: Serial.print("M"); break; + case 0x11: Serial.print("N"); break; + case 0x12: Serial.print("O"); break; + case 0x13: Serial.print("P"); break; + case 0x14: Serial.print("Q"); break; + case 0x15: Serial.print("R"); break; + case 0x16: Serial.print("S"); break; + case 0x17: Serial.print("T"); break; + case 0x18: Serial.print("U"); break; + case 0x19: Serial.print("V"); break; + case 0x1A: Serial.print("W"); break; + case 0x1B: Serial.print("X"); break; + case 0x1C: Serial.print("Y"); break; + case 0x1D: Serial.print("Z"); break; + case 0x1E: Serial.print("1"); break; + case 0x1F: Serial.print("2"); break; + case 0x20: Serial.print("3"); break; + case 0x21: Serial.print("4"); break; + case 0x22: Serial.print("5"); break; + case 0x23: Serial.print("6"); break; + case 0x24: Serial.print("7"); break; + case 0x25: Serial.print("8"); break; + case 0x26: Serial.print("9"); break; + case 0x27: Serial.print("0"); break; + case 0x28: Serial.print("ENTER"); break; + case 0x29: Serial.print("ESC"); break; + case 0x2A: Serial.print("BACKSPACE"); break; + case 0x2B: Serial.print("TAB"); break; + case 0x2C: Serial.print("SPACE"); break; + case 0x2D: Serial.print("MINUS"); break; + case 0x2E: Serial.print("EQUAL"); break; + case 0x2F: Serial.print("LBRACKET"); break; + case 0x30: Serial.print("RBRACKET"); break; + case 0x31: Serial.print("BACKSLASH"); break; + case 0x33: Serial.print("SEMICOLON"); break; + case 0x34: Serial.print("QUOTE"); break; + case 0x35: Serial.print("GRAVE"); break; + case 0x36: Serial.print("COMMA"); break; + case 0x37: Serial.print("PERIOD"); break; + case 0x38: Serial.print("SLASH"); break; + case 0x39: Serial.print("CAPS_LOCK"); break; + case 0x4F: Serial.print("RIGHT_ARROW"); break; + case 0x50: Serial.print("LEFT_ARROW"); break; + case 0x51: Serial.print("DOWN_ARROW"); break; + case 0x52: Serial.print("UP_ARROW"); break; + default: + if (key >= 0x3A && key <= 0x45) { // F1-F12 + Serial.print("F"); + Serial.print(key - 0x3A + 1); + } else { + // For keys not handled above, just print the HID code + Serial.print("0x"); + Serial.print(key, HEX); + } + break; + } +} + +void printKeyboardReport(uint8_t const* report) { + // First byte contains modifier keys + uint8_t modifiers = report[0]; + + // Print modifier keys if pressed + if (modifiers > 0) { + Serial.print("Modifiers: "); + + if (modifiers & 0x01) Serial.print("LEFT_CTRL "); + if (modifiers & 0x02) Serial.print("LEFT_SHIFT "); + if (modifiers & 0x04) Serial.print("LEFT_ALT "); + if (modifiers & 0x08) Serial.print("LEFT_GUI "); + if (modifiers & 0x10) Serial.print("RIGHT_CTRL "); + if (modifiers & 0x20) Serial.print("RIGHT_SHIFT "); + if (modifiers & 0x40) Serial.print("RIGHT_ALT "); + if (modifiers & 0x80) Serial.print("RIGHT_GUI "); + + Serial.println(); + } + + // Second byte is reserved (usually 0) + + // Bytes 2-7 contain up to 6 key codes + bool keysPressed = false; + + for (int i = 2; i < 8; i++) { + uint8_t key = report[i]; + + // Skip if no key or error rollover + if (key == 0 || key == 1) { + continue; + } + + if (!keysPressed) { + Serial.print("Keys: "); + keysPressed = true; + } + + // Print key name based on HID Usage Tables for standard keyboard + printKeyName(key); + Serial.print(" "); + } + + if (keysPressed) { + Serial.println(); + } else if (modifiers == 0) { + Serial.println("No keys pressed"); + } +} diff --git a/USB_Host_Examples/CircuitPython_Keyboard_Stdin/code.py b/USB_Host_Examples/CircuitPython_Keyboard_Stdin/code.py new file mode 100644 index 000000000..250dda0d9 --- /dev/null +++ b/USB_Host_Examples/CircuitPython_Keyboard_Stdin/code.py @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT +import sys +import supervisor + + +# main loop +while True: + # check how many bytes are available + available = supervisor.runtime.serial_bytes_available + + # if there are some bytes available + if available: + # read data from the keyboard input + c = sys.stdin.read(available) + # print the data that was read + print(c, end="") diff --git a/USB_Host_Examples/CircuitPython_Keyboard_USB_Host/code.py b/USB_Host_Examples/CircuitPython_Keyboard_USB_Host/code.py new file mode 100644 index 000000000..e820902b4 --- /dev/null +++ b/USB_Host_Examples/CircuitPython_Keyboard_USB_Host/code.py @@ -0,0 +1,164 @@ +import array + +import usb +import adafruit_usb_host_descriptors + +# lists for mouse interface indexes, endpoint addresses, and USB Device instances +# each of these will end up with length 2 once we find both mice +kbd_interface_index = None +kbd_endpoint_address = None +keyboard = None + +# scan for connected USB devices +for device in usb.core.find(find_all=True): + # check for boot mouse endpoints on this device + kbd_interface_index, kbd_endpoint_address = ( + adafruit_usb_host_descriptors.find_boot_keyboard_endpoint(device) + ) + # if a boot keyboard interface index and endpoint address were found + if kbd_interface_index is not None and kbd_interface_index is not None: + keyboard = device + + # detach device from kernel if needed + if keyboard.is_kernel_driver_active(0): + keyboard.detach_kernel_driver(0) + + # set the configuration so it can be used + keyboard.set_configuration() + +if keyboard is None: + raise RuntimeError("No boot keyboard endpoint found") + +buf = array.array("b", [0] * 8) + + +def print_keyboard_report(report_data): + # Dictionary for modifier keys (first byte) + modifier_dict = { + 0x01: "LEFT_CTRL", + 0x02: "LEFT_SHIFT", + 0x04: "LEFT_ALT", + 0x08: "LEFT_GUI", + 0x10: "RIGHT_CTRL", + 0x20: "RIGHT_SHIFT", + 0x40: "RIGHT_ALT", + 0x80: "RIGHT_GUI", + } + + # Dictionary for key codes (main keys) + key_dict = { + 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", + 0x1E: "1", + 0x1F: "2", + 0x20: "3", + 0x21: "4", + 0x22: "5", + 0x23: "6", + 0x24: "7", + 0x25: "8", + 0x26: "9", + 0x27: "0", + 0x28: "ENTER", + 0x29: "ESC", + 0x2A: "BACKSPACE", + 0x2B: "TAB", + 0x2C: "SPACE", + 0x2D: "MINUS", + 0x2E: "EQUAL", + 0x2F: "LBRACKET", + 0x30: "RBRACKET", + 0x31: "BACKSLASH", + 0x33: "SEMICOLON", + 0x34: "QUOTE", + 0x35: "GRAVE", + 0x36: "COMMA", + 0x37: "PERIOD", + 0x38: "SLASH", + 0x39: "CAPS_LOCK", + 0x4F: "RIGHT_ARROW", + 0x50: "LEFT_ARROW", + 0x51: "DOWN_ARROW", + 0x52: "UP_ARROW", + } + + # Add F1-F12 keys to the dictionary + for i in range(12): + key_dict[0x3A + i] = f"F{i + 1}" + + # First byte contains modifier keys + modifiers = report_data[0] + + # Print modifier keys if pressed + if modifiers > 0: + print("Modifiers:", end=" ") + + # Check each bit for modifiers and print if pressed + for bit, name in modifier_dict.items(): + if modifiers & bit: + print(name, end=" ") + + print() + + # Bytes 2-7 contain up to 6 key codes (byte 1 is reserved) + keys_pressed = False + + for i in range(2, 8): + key = report_data[i] + + # Skip if no key or error rollover + if key in {0, 1}: + continue + + if not keys_pressed: + print("Keys:", end=" ") + keys_pressed = True + + # Print key name based on dictionary lookup + if key in key_dict: + print(key_dict[key], end=" ") + else: + # For keys not in the dictionary, print the HID code + print(f"0x{key:02X}", end=" ") + + if keys_pressed: + print() + elif modifiers == 0: + print("No keys pressed") + + +while True: + # try to read data from the keyboard + try: + count = keyboard.read(kbd_endpoint_address, buf, timeout=10) + + # if there is no data it will raise USBTimeoutError + except usb.core.USBTimeoutError: + # Nothing to do if there is no data for this mouse + continue + + print_keyboard_report(buf) From 9010f17295dad7a7e02d97c0b0392d33db609f8f Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 23 Apr 2025 14:50:06 -0500 Subject: [PATCH 2/4] fix SPDX --- USB_Host_Examples/CircuitPython_Keyboard_USB_Host/code.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/USB_Host_Examples/CircuitPython_Keyboard_USB_Host/code.py b/USB_Host_Examples/CircuitPython_Keyboard_USB_Host/code.py index e820902b4..419bf63c5 100644 --- a/USB_Host_Examples/CircuitPython_Keyboard_USB_Host/code.py +++ b/USB_Host_Examples/CircuitPython_Keyboard_USB_Host/code.py @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT import array import usb From b8d94ec2dbe63e9008ba6b271014b65f687f30d2 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 23 Apr 2025 15:00:22 -0500 Subject: [PATCH 3/4] use tinyusb feather testonly --- ...rp2040.test.only => .feather_rp2040_usbhost_tinyusb.test.only} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/{.feather_rp2040.test.only => .feather_rp2040_usbhost_tinyusb.test.only} (100%) diff --git a/USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/.feather_rp2040.test.only b/USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/.feather_rp2040_usbhost_tinyusb.test.only similarity index 100% rename from USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/.feather_rp2040.test.only rename to USB_Host_Examples/Arduino_Feather_RP2040_USB_Host_Keyboard/.feather_rp2040_usbhost_tinyusb.test.only From 7622520b69da3abc031bc295fd3182c9fd427a59 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 23 Apr 2025 17:53:47 -0500 Subject: [PATCH 4/4] pylint disable too-many-function-args --- Metro/Metro_RP2350_Chips_Challenge/game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Metro/Metro_RP2350_Chips_Challenge/game.py b/Metro/Metro_RP2350_Chips_Challenge/game.py index cfb23a349..b66392626 100755 --- a/Metro/Metro_RP2350_Chips_Challenge/game.py +++ b/Metro/Metro_RP2350_Chips_Challenge/game.py @@ -78,7 +78,7 @@ def __init__(self, display, data_file, audio): self._loading_group = displayio.Group() self._tile_size = 24 # Default tile size (length and width) self._digit_dims = (0, 0) - self._gamelogic = GameLogic(data_file, audio) + self._gamelogic = GameLogic(data_file, audio) # pylint: disable=too-many-function-args self._databuffer = DataBuffer() self._color_index = {} self._init_display()