To read data from a keyboard in CircuitPython, you must use a device which supports USB Host, such as the Adafruit Fruit Jam, Metro RP2350, or Feather RP2040 USB Host.
Feather RP2040 USB Host Wiring
Connecting to the Feather RP2040 USB Host requires plugging in a USB keyboard to the Feather's Host port.
Metro RP2350 USB Host Wiring
Connecting to the Metro RP2350 USB Host port requires soldering pins to the broken out USB Host connections as shown in this guide page. Make the following connections between the Metro USB Host pins and USB Host breakout cable.
- GND to Black wire
- D+ to Green wire
- D- to White wire
- 5V to Red red
Fruit Jam Mini Computer
Connecting to the Fruit Jam requires plugging in a USB keyboard to one of the Fruit Jam's USB Host ports.
As of CircuitPython version 10.0.0-beta.3, the wired USB keyboard works on CircuitPython when connected directly to the USB Host pins or port. Prior releases of CircuitPython required connecting through a USB hub such as the CH334F.
Demo Code: sys.stdin
There are two possible ways to read data from a keyboard in CircuitPython. The less complex way is utilizing sys.stdin. Python has the concept of standard input and output streams, similar to those in Linux/Unix and other operating systems. CircuitPython has this capability and through a lot of behind the scenes code, presents a USB keyboard as a sys.stdin input device. Due to the way this works it is only possible to know when a key is pressed, not how long it remains pressed, nor when it is released.
The sys.stdin method defaults to using the standard US layout for the keyboard. You can set a custom layout using the set_user_keymap()function documented here.
The code to get USB Host Keyboard characters and echo them to serial out is as follows:
# 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="")
Demo Code: USB Host
The other way to read from the keyboard is more involved, the CircuitPython API for it it mimics PyUSB in CPython. The code must:
- Establish the USB connection
- Read USB Reports, sections of bytes sent when an action occurs on the peripheral like a key is pressed.
- Parse the reports and provide meaningful input to the program.
When you use this method it will disable the sys.stdin method shown above. You can only use a single method at a time.
Using the USB Host method makes it possible to determine which keys are currently being pressed, which makes it possible for the code to know when keys are pressed, how long they're held down, and when they get released.
# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import array
import usb
import adafruit_usb_host_descriptors
#interface index, and endpoint addresses for USB Device instance
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 keyboard 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 keyboard
continue
print_keyboard_report(buf)
The character mapping in the code above is for a US 104 key QWERTY keyboard. Other keyboard layouts have different mappings. You can often get the USB value map for other keyboards online or just hook up your keyboard and enter keys to determine which key maps to which character.
A guide to other keymaps may be found on the internet. One resource is https://github.com/hid-io/layouts
Page last edited September 12, 2025
Text editor powered by tinymce.