Merge pull request #3026 from FoamyGuy/usb_host_keyboard_examples
usb host keyboard examples
This commit is contained in:
commit
7d22755433
9 changed files with 850 additions and 1 deletions
|
|
@ -78,7 +78,7 @@ class Game:
|
||||||
self._loading_group = displayio.Group()
|
self._loading_group = displayio.Group()
|
||||||
self._tile_size = 24 # Default tile size (length and width)
|
self._tile_size = 24 # Default tile size (length and width)
|
||||||
self._digit_dims = (0, 0)
|
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._databuffer = DataBuffer()
|
||||||
self._color_index = {}
|
self._color_index = {}
|
||||||
self._init_display()
|
self._init_display()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
18
USB_Host_Examples/CircuitPython_Keyboard_Stdin/code.py
Normal file
18
USB_Host_Examples/CircuitPython_Keyboard_Stdin/code.py
Normal file
|
|
@ -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="")
|
||||||
167
USB_Host_Examples/CircuitPython_Keyboard_USB_Host/code.py
Normal file
167
USB_Host_Examples/CircuitPython_Keyboard_USB_Host/code.py
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
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)
|
||||||
Loading…
Reference in a new issue