Description
Operating System
Linux
Arduino IDE version
2.3.6
Board
Adafruit Feather RP2040 with USB Type A Host
ArduinoCore version
4.5.3 (not sure)
TinyUSB Library version
3.4.4
Sketch as ATTACHED TXT
I used some examples from here: https://github.com/adafruit/Adafruit_TinyUSB_Arduino/blob/master/examples/DualRole/HID/hid_mouse_tremor_filter/hid_mouse_tremor_filter.ino
I did slight modifications: I added HEARTBEAT
logging, but code behaves the same way without this changes, I also tried other examples it always behaves the same.
/*********************************************************************
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 run on native usb controller (roothub port0)
* - Host depending on MCUs run on either:
* - rp2040: bit-banging 2 GPIOs with the help of Pico-PIO-USB library (roothub port1)
* - samd21/51, nrf52840, esp32: using MAX3421e controller (host shield)
*
* Requirements:
* - For rp2040:
* - [Pico-PIO-USB](https://github.com/sekigon-gonnoc/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
*/
/* Example sketch receive mouse report from host interface (from e.g consumer mouse)
* and apply a butterworth low pass filter with a specific CUTOFF_FREQUENCY on hid mouse movement report.
* Filtered report are send via device interface (to PC) acting as a "Mouse Tremor Filter".
*/
// USBHost is defined in usbh_helper.h
#define CFG_TUSB_DEBUG 3
#define CFG_TUSB_DEBUG_PRINTF(...) Serial.printf(__VA_ARGS__)
#include <Adafruit_TinyUSB.h>
#include <pio_usb.h>
#include "usbh_helper.h"
#define TU_LOG(...) Serial.printf(__VA_ARGS__)
#define TU_LOG1(...) Serial.printf(__VA_ARGS__)
#define TU_LOG2(...) Serial.printf(__VA_ARGS__)
#define TU_LOG3(...) Serial.printf(__VA_ARGS__)
#define tu_printf(...) Serial.printf(__VA_ARGS__)
// HID report descriptor using TinyUSB's template
// Single Report (no ID) descriptor
uint8_t const desc_hid_report[] = {
TUD_HID_REPORT_DESC_MOUSE()
};
// USB HID object: desc report, desc len, protocol, interval, use out endpoint
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_MOUSE, 2, false);
//------------- Low pass filter with Butterworth -------------//
// Butterworth low-pass filter coefficients
typedef struct {
float b0, b1, b2, a1, a2;
} butterworth_coeffs_t;
#define SAMPLING_FREQUENCY 100.0 // Hz
#define CUTOFF_FREQUENCY 10.0 // Hz
// x, y
butterworth_coeffs_t coeffs[2];
butterworth_coeffs_t butterworth_lowpass(float cutoff_frequency, float sampling_frequency);
void filter_report(hid_mouse_report_t const *report);
// extern "C" int _write(int fd, const void *buf, size_t count) {
// // Redirect printf output to Serial
// Serial.write((const uint8_t*)buf, count);
// return count;
// }
void setup() {
Serial.begin(115200);
// while (!Serial) delay(10);
usb_hid.begin();
coeffs[0] = butterworth_lowpass(CUTOFF_FREQUENCY, SAMPLING_FREQUENCY);
coeffs[1] = butterworth_lowpass(CUTOFF_FREQUENCY, SAMPLING_FREQUENCY);
#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
//while ( !Serial ) delay(10); // wait for native usb
Serial.println("TinyUSB Mouse Tremor Filter Example");
}
#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
//--------------------------------------------------------------------+
void loop() {
Serial.flush();
static uint32_t lastHeartbeat0 = 0;
if (millis() - lastHeartbeat0 > 5000) {
Serial.printf("[%.3lums] HEARTBEAT0: loop running\n", millis());
lastHeartbeat0 = millis();
tu_printf("boo");
TU_LOG("This should appear in Serial\n");
TU_LOG2("TinyUSB initialized\r\n");
}
}
//------------- 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();
static uint32_t lastHeartbeat1 = 0;
if (millis() - lastHeartbeat1 > 5000) {
Serial.printf("[%.3lums] HEARTBEAT1: loop running\n", millis());
lastHeartbeat1 = millis();
Serial.printf("CFG_TUSB_DEBUG: %u\n", CFG_TUSB_DEBUG);
}
}
#endif
//--------------------------------------------------------------------+
// TinyUSB Host callbacks
//--------------------------------------------------------------------+
extern "C"
{
// Invoked when device with hid interface is mounted
// Report descriptor is also available for use.
// tuh_hid_parse_report_descriptor() can be used to parse common/simple enough
// descriptor. Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE,
// it will be skipped therefore report_desc = NULL, desc_len = 0
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
(void) desc_report;
(void) desc_len;
uint16_t vid, pid;
tuh_vid_pid_get(dev_addr, &vid, &pid);
Serial.printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
Serial.printf("VID = %04x, PID = %04x\r\n", vid, pid);
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
if (itf_protocol == HID_ITF_PROTOCOL_MOUSE) {
Serial.printf("HID Mouse\r\n");
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
}
}
// Invoked when device with hid interface is un-mounted
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
Serial.printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
}
// Invoked when received report from device via interrupt endpoint
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
filter_report((hid_mouse_report_t const *) report);
// continue to request to receive report
if (!tuh_hid_receive_report(dev_addr, instance)) {
Serial.printf("Error: cannot request to receive report\r\n");
}
}
} // extern C
//--------------------------------------------------------------------+
// Low pass filter Functions
//--------------------------------------------------------------------+
butterworth_coeffs_t butterworth_lowpass(float cutoff_frequency, float sampling_frequency) {
butterworth_coeffs_t coe;
float omega = 2.0 * PI * cutoff_frequency / sampling_frequency;
float s = sin(omega);
float t = tan(omega / 2.0);
float alpha = s / (2.0 * t);
coe.b0 = 1.0 / (1.0 + 2.0 * alpha + 2.0 * alpha * alpha);
coe.b1 = 2.0 * coe.b0;
coe.b2 = coe.b0;
coe.a1 = 2.0 * (alpha * alpha - 1.0) * coe.b0;
coe.a2 = (1.0 - 2.0 * alpha + 2.0 * alpha * alpha) * coe.b0;
return coe;
}
float butterworth_filter(float data, butterworth_coeffs_t *coeffs, float *filtered, float *prev1, float *prev2) {
float output = coeffs->b0 * data + coeffs->b1 * (*prev1) + coeffs->b2 * (*prev2) - coeffs->a1 * (*filtered) -
coeffs->a2 * (*prev1);
*prev2 = *prev1;
*prev1 = data;
*filtered = output;
return output;
}
void filter_report(hid_mouse_report_t const *report) {
static float filtered[2] = { 0.0, 0.0 };
static float prev1[2] = { 0.0, 0.0 };
static float prev2[2] = { 0.0, 0.0 };
butterworth_filter(report->x, &coeffs[0], &filtered[0], &prev1[0], &prev2[0]);
butterworth_filter(report->y, &coeffs[1], &filtered[1], &prev1[1], &prev2[1]);
hid_mouse_report_t filtered_report = *report;
filtered_report.x = (int8_t) filtered[0];
filtered_report.y = (int8_t) filtered[1];
usb_hid.sendReport(0, &filtered_report, sizeof(filtered_report));
}
Compiled Log as ATTACHED TXT
[152076ms] Core1 HEARTBEAT: USBHost.task() called
[152687ms] HEARTBEAT: loop running
[153688ms] HEARTBEAT: loop running
[154077ms] Core1 HEARTBEAT: USBHost.task() called
[154689ms] HEARTBEAT: loop running
[155690ms] HEARTBEAT: loop running
[156078ms] Core1 HEARTBEAT: USBHost.task() called
[156691ms] HEARTBEAT: loop running
[157692ms] HEARTBEAT: loop running
[158693ms] HEARTBEAT: loop running
[159694ms] HEARTBEAT: loop running
[160695ms] HEARTBEAT: loop running
[161696ms] HEARTBEAT: loop running
What happened ?
Disclaimer: I started a topic on the forum: https://forums.adafruit.com/viewtopic.php?p=1055930#p1055930 but it was a bit fruitless (pun intended).
All context info I have (I'll share more if needed):
NixOS, Arduino IDE 2.3.6
Windows 11, Arduino IDE 2.3.6
SdFat: 2.2.54
Pico PIO USB: 0.7.1
MIDI Library: 5.0.2
Adafruit SPIFlash: 5.1.1
Adafruit NeoPixel: 1.13.0
rp2040: 4.5.3
Adafruit TinyUSB Library: 3.4.4
---
Mice:
- (working one): Logitech m500, wired, 8bit XY resolution (), sends reports of 6 bytes;
- (broken one): Logitech Wireless Mouse M185, wireless with dongle, 16 bit XY resolution, sends reports of 7 bytes;
I connect a wireless mouse with dongle to a board (USB A) while the board connected to a laptop (or PC it did not matter) over USB C.
Everything works as expected, I can move mouse over screen for some time (1 - 5 minutes), then it stops, the board apparently stops receiving HID reports (if I can trust Serial.printf logging) and it does not react on any mouse movement and even plugging unplugging the dongle (I don't get mount / unmount messages).
...then I did more investigating)...
I added 2 heartbeat
serial logs like so:
void loop() {
Serial.flush();
static uint32_t lastHeartbeat0 = 0;
if (millis() - lastHeartbeat0 > 5000) {
Serial.printf("[%.3lums] HEARTBEAT0: loop running\n", millis());
lastHeartbeat0 = millis();
}
}
Which resulted in this log:
[152076ms] Core1 HEARTBEAT: USBHost.task() called
[152687ms] HEARTBEAT: loop running
[153688ms] HEARTBEAT: loop running
[154077ms] Core1 HEARTBEAT: USBHost.task() called
[154689ms] HEARTBEAT: loop running
[155690ms] HEARTBEAT: loop running
[156078ms] Core1 HEARTBEAT: USBHost.task() called
[156691ms] HEARTBEAT: loop running
[157692ms] HEARTBEAT: loop running
[158693ms] HEARTBEAT: loop running
[159694ms] HEARTBEAT: loop running
[160695ms] HEARTBEAT: loop running
[161696ms] HEARTBEAT: loop running
Which I understand as either the core 1 (the one that handles USB host) "crashes" (I'm not sure if it applicable or possible) or goes into infinite loop. So it makes sense that the board does not react to mouse anymore.
...(then I did more digging)...
I used 120 MHz CPU clock at first and it lasts a couple of minutes and then randomly stops working.
I tried 240 MHz clock and it much better, it would last 30 minutes without a problem but then it stops working again.
I also tried 10ms timeout in task()
, like
void loop1() {
USBHost.task(10);
It might have had a good effect (mouse was responsive for longer period of time, around 5 minutes instead of 2), but it might have been an measurement error. (these hangups feel really random and not consistent)
All mice work without any issues with regular PC's of course.
How to reproduce ?
- take any (at least all I tried) host usb sample code
- connect wireless (or maybe this specific model) mouse
- wait
- it stops working
Debug Log
I actually could not enable TinyUSB internal logging, it possibly would really helpful to see if there are any messages leading to the issue. I would appreciate any advice on how to enable the logs.
I use Arduino IDE 2.3.6 on Linux + Adafruit board and Adafruit_TinyUSB, I tried editing ~/.arduino15/packages/rp2040/hardware/rp2040/4.5.3/platform.txt
, and adding CFG_TUSB_DEBUG=3
:
compiler.c.extra_flags=-DCFG_TUSB_DEBUG=3
compiler.cpp.extra_flags=-DCFG_TUSB_DEBUG=3
I tried #define CFG_TUSB_DEBUG=3
at the top of the sketch source code and some other things but I cannot see any logs. I also noticed that TinyUSB uses Serial1 for logging (I might be completely wrong though) and I tried SERIAL_TUSB_DEBUG=Serial
but it did not work neither.
So if someone can help me turn with that it would be great.
Screenshots
No response