I would like to read input from a USB keyboard and USB mouse on a Pi Pico. Was going to use the TinyUSB library to do this. I have tried the TinyUSB Host example that retrieves info about the USB device and displays it via a serial terminal and it works perfectly. What I can't seem to find is any information or examples on how to read the input from the USB devices. Can anyone point me towards that information?
My ultimate goal is to make an interface to read USB keyboard/mouse for use with a 6502 microprocessor based system.
I did this once upon a time with the Pico C SDK. You have to handle the tuh_hid_report_received_cb callback. It'll use tuh_hid_interface_protocol to determine whether the report is HID_ITF_PROTOCOL_KEYBOARD or something else. "Something else" may eventually call the same keyboard report handler that HID_ITF_PROTOCOL_KEYBOARD does, it'll just take a convoluted path to get there (if it does).
Where it really starts to get interesting is handling/generating repeated key presses and LED handling.
I honestly don't remember where I got the basic framework for the keyboard stuff from (it's literally been years). I just remember that it was missing a lot of stuff (repeated key pressed and LED handling, oddly enough!) that I had to add to make a working keyboard handler. I eventually got everything together into a pseudo library format that I could use. I've never had the need to transfer it all over to the Arduino ecosystem though.
I never looked at the mouse end of things though I imagine there's basic similarities.
Good luck, have fun, and don't tear too much of your hair out as you learn waaaay more about the innards of low level USB stuff than you ever wanted to!
My original plan is to implement an 8-bit parallel interface and connect it to a 6522 VIA chip. An alternative is using a 74LS373 latch to link the Pico to the 6502 data bus. Also, recently I have learned about the OneROM project. That project uses the Pico's PIO to create a bus interface that responds quick enough to tie the Pico directly to the 6502 bus. Might take a peek down that road as well. Figured the Pico could do keyboard scancode-to-ASCII conversion too.
Thank you for getting me started with "tuh_hid_report_received_cb", "tuh_hid_interface_protocol" and "HID_ITF_PROTOCOL_KEYBOARD" calls. That is already more information than I was able to find using Google. My search results kept turning up how to turn the Pico into a HID device and not how to read an HID device. Knowing the names of these calls has already helped.
I haven't really thought of the mouse side of things yet, just wanted to keep that as a possibility for later in the project.
Do you really need the PIA or a latch though? The Pico is orders of magnitude faster than the 6502. It could implement its own tristateable 8-bit parallel interface without any external chips (besides addressing logic) as long as you have 9 free pins. The only problem I see is the voltage levels. I don't remember if the Pico is 5V-tolerant.
Thank you for the example code. I will look it over.
Modern 6502s and my project will run on 3.3V.
In the past I have interfaced Pico's with slow 5V logic through a 220-ohm resistor.
Also just for fun a while ago I put some 74LS93 counters directly on a Pico without any voltage translation. Ran it continuously for over a month and the Pico didn't seem to care. Maybe really long term might be a problem but I think TTL logic output is within tolerances of the Pico. (Don't take that as fact though. Use at your own risk.) 5V CMOS parts might be a bit too much as their output gets a lot close to Vdd then TTL does.
Never thought of using en ESP32 for this. I considered STM32 or PSoC 5LP but the Pico was a cheaper alternative. I'll also look into the ESP32.
if you wish to experiment with OTG Host and device consider the ESP32-S3-DevKitC-1 which has two USB type C connectors one for programming and Serial IO and the other for USB OTG
example from EspUsbHost
File>Examples>EspUsbHost>EspUsbHostKeyboard
Thanks to everyone's help, here is a sample program that allows me to read keyboard or mouse HID devices and outputs the raw data to a serial debug terminal.
Also found a document online called "HID Reporter" by John Park that helped explain some details.
// TinyUSB Host Test
// =================
// This reads keyboard/mouse HID devices and prints the data to a serial terminal.
// Using Adafruit's TinyUSB library for USB Host functions.
#include "Adafruit_TinyUSB.h"
// Assign TinyUSB to a function name.
Adafruit_USBH_Host USBHost;
// Setup (Runs only once.)
void setup()
{
// Initialize Serial Port 1 for debug output.
// Serial Port 0 is unavailable due to USB being used as host.
Serial1.setTX(0);
Serial1.setRX(1);
Serial1.begin(115200);
Serial1.println("\r\nUSB Host Test");
// Initialize USB Host fucntions using built-in USB interface (0).
USBHost.begin(0);
}
// Main Loop (Runs continuously.)
void loop()
{
// TinyUSB's Host Functions
// Seems that this needs to be polled continuously for USB events to be recognized.
USBHost.task();
}
// USBHost calls this when HID device is mounted.
void tuh_hid_mount_cb(uint8_t device_address, uint8_t instance, uint8_t const *descriptor_report, uint16_t descriptor_length)
{
// Print USB device address and instance to debug terminal.
Serial1.printf("MOUNTED: Device Address = %d, Instance = %d \r\n", device_address, instance);
// I think this is telling the USBHost that data has been processed and is ready to receive more, but I'm not sure.
// If this is removed, only one packet of data gets processed.
if (!tuh_hid_receive_report(device_address, instance))
{
Serial1.printf("***ERROR*** : Cannot receive HID report. \r\n");
}
}
// USBHost calles this when HID device is un-mounted.
void tuh_hid_umount_cb(uint8_t device_address, uint8_t instance)
{
// Print USB device address and instance to debug terminal.
Serial1.printf("UN-MOUNTED: Device Address = %d, Instance = %d \r\n", device_address, instance);
}
// USBHost calls this when data packet is received from HID device.
void tuh_hid_report_received_cb(uint8_t device_address, uint8_t instance, uint8_t const *report_data, uint16_t report_length)
{
// Send received data to debug terminal.
// Keyboard HID devices send a packet of 8 bytes per transfer.
// Mouse HID devices seem to send a packet of 3 bytes per transfer.
Serial1.printf("DATA: ");
for (uint16_t a = 0; a < report_length; a++)
{
Serial1.printf("$%02x ", report_data[a]);
}
Serial1.println();
// I think this is telling the USBHost that data has been processed and is ready to receive more, but I'm not sure.
// If this is removed, only one packet of data gets processed.
if (!tuh_hid_receive_report(device_address, instance))
{
Serial1.printf("***ERROR*** : Cannot receive HID report.\r\n");
}
}
I'm still not one-hundred percent sure of why things are the way they are, but I seem to have enough to move forward with my project.