Wireless mouse leads to core hang up / How to enable logging
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
There is a few update to pio-usb which isn't released yet, make sure
- use the git latest on both pio-usb and this repo.
- after enabed -DCFG_TUSB_DEBUG=2, the log will be output via UART tx/rx. Try to hook an ftdi/cp210x to those pin and use different terminal. USB log cannot be printed to cdc since that is also usb communication, which will just loop forever.
Hi, I’m experiencing a very similar issue where the core hangs after a few minutes. Setup: Wired mouse dual_role hid_mouse_log_filter.ino example (unchanged) TinyUSB version 3.5.0 Pico-PIO-USB version 0.7.2 Same board and IDE
Observation: Without moving the mouse, the core hangs after about 15 minutes. When moving the mouse: ~5 minutes with Boot Protocol (3 bytes) 1–3 minutes with Report Protocol (11 bytes) added tuh_hid_set_protocol for testing using high Polling rate (8000hz) or low Polling rate (125hz) did not (noticeably) change anything I'm just getting started with this board and TinyUSB, so I’m afraid that’s all I can contribute at the moment.
Update:
- I bought a USB UART adapter connected it to the board and managed to see some logs
- I used
0.7.3(which was released 3 days ago) and latest (commit hash:805aa2d41ad9e607c1d086fb7bd197e5a1dd88e2) from this repo
Unfortunately it did not help :(
The logs are quite verbose (with CFG_TUSB_DEBUG=3) but I could not see anything wrong there, no errors, no warnings, no weird messages. All seem to work just fine until it stops.
The fact that it happens so inconsistently and I cannot correlate it to anything makes the troubleshooting process quite uncomfortable.
If anyone has any idea of how it can be fixed please let me know, I'm planning to try another board to see if it's gonna be any better there.
use CFG_TUSB_DEBUG=2, then uncomment to have CFG_TUD_LOG_LEVEL=3, https://github.com/adafruit/Adafruit_TinyUSB_Arduino/blob/master/src/arduino/ports/rp2040/tusb_config_rp2040.h#L72 that would disable device stack log. Which make thing easier to follow the host stack.
I'm having the exact same issue as you described. The USB Host Stack just completely stops working and crashes after a couple minutes of use. This issue didn't happen before so I think it has something to do with the newer libraries.
@Mascota404 that sounds really interesting.
This issue didn't happen before so I think it has something to do with the newer libraries.
I wonder if it's possible to just downgrade to a previous version then. Do you know the version you used before that does not have such problem (or at least approximate timeframe of when it was working for you)? Do you see any downsides to just downgrade libs versions?
what kinda fixes is it forcing it to poll every 1ms like this:
static unsigned long lastUsbTaskTime = 0;
unsigned long currentMicros = micros();
if (currentMicros - lastUsbTaskTime >= 1000) {
USBHost.task();
tight_loop_contents();
lastUsbTaskTime = currentMicros;
}
But there is also a really weird sensitivity issue that happens, where if you go to a mouse polling test website and just draw circles with your mouse, every 20 seconds you lose like 100 hz and it gets a little slower before it recovers. I've been looking into this issue for a while, so if you want contact me on discord: mascotadev
what kinda fixes is it forcing it to poll every 1ms like this:
I added this snippet to my own code and log filter example. I think it helped and it worked longer but this is just a felling i didn't measure anything. It still stops working after few minutes. Maybe my IDE settings are wrong idk
Using semaphores from FreeRTOS fixes the crashing entirely. Like this:
#include <memory>
#include "hid_report_parser.h"
SemaphoreHandle_t usbSemaphore;
// pio-usb is required for rp2040 host
#include "pio_usb.h"
#include "pio-usb-host-pins.h"
#include "Adafruit_TinyUSB.h"
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_HOST
#define CFG_TUSB_OS OPT_OS_FREERTOS
#include <FreeRTOS.h>
#include <semphr.h>
#include "pico/util/datetime.h"
void setup() {
mouse_hid.setPollInterval(1);
mouse_hid.begin();
while (!USBDevice.mounted()) delay(1);
usbSemaphore = xSemaphoreCreateBinary();
xSemaphoreGive(usbSemaphore);
}
if (xSemaphoreTake(usbSemaphore, pdMS_TO_TICKS(1))) {
current_buttons = in_buttons;
int8_t in_wheel = m.axes[hid::MouseConfig::V_SCROLL];
int16_t dx = m.axes[hid::MouseConfig::X];
int16_t dy = m.axes[hid::MouseConfig::Y];
uint8_t out[7] = {
in_buttons, // 1 byte buttons
(uint8_t)(dx & 0xFF), // X low byte
(uint8_t)((dx >> 8) & 0xFF), // X high byte
(uint8_t)(dy & 0xFF), // Y low byte
(uint8_t)((dy >> 8) & 0xFF), // Y high byte
(uint8_t)in_wheel, // 1 byte vertical wheel
(uint8_t)0 // 1 byte horizontal wheel (pan)
};
if (mouse_hid.ready()) {
mouse_hid.sendReport(0, out, sizeof(out));
}
xSemaphoreGive(usbSemaphore);
}
Using semaphores from FreeRTOS fixes the crashing entirely. Like this:
#include <memory> #include "hid_report_parser.h" SemaphoreHandle_t usbSemaphore; // pio-usb is required for rp2040 host #include "pio_usb.h" #include "pio-usb-host-pins.h" #include "Adafruit_TinyUSB.h" #define CFG_TUSB_RHPORT0_MODE OPT_MODE_HOST #define CFG_TUSB_OS OPT_OS_FREERTOS #include <FreeRTOS.h> #include <semphr.h> #include "pico/util/datetime.h" void setup() { mouse_hid.setPollInterval(1); mouse_hid.begin(); while (!USBDevice.mounted()) delay(1); usbSemaphore = xSemaphoreCreateBinary(); xSemaphoreGive(usbSemaphore); } if (xSemaphoreTake(usbSemaphore, pdMS_TO_TICKS(1))) { current_buttons = in_buttons; int8_t in_wheel = m.axes[hid::MouseConfig::V_SCROLL]; int16_t dx = m.axes[hid::MouseConfig::X]; int16_t dy = m.axes[hid::MouseConfig::Y]; uint8_t out[7] = { in_buttons, // 1 byte buttons (uint8_t)(dx & 0xFF), // X low byte (uint8_t)((dx >> 8) & 0xFF), // X high byte (uint8_t)(dy & 0xFF), // Y low byte (uint8_t)((dy >> 8) & 0xFF), // Y high byte (uint8_t)in_wheel, // 1 byte vertical wheel (uint8_t)0 // 1 byte horizontal wheel (pan) }; if (mouse_hid.ready()) { mouse_hid.sendReport(0, out, sizeof(out)); } xSemaphoreGive(usbSemaphore); }
Cheers helped me with getting me a solution that worked too
Adding the Semaphore stuff, helped solve my similar issue of my mouse seemingly suspending / disconnecting / stopping and wouldn't come back unless the board was reset every few minutes.
Tools > Operating System > FreeRTOS SMP
#include "usbh_helper.h"
#include "tusb.h"
#include "Adafruit_TinyUSB.h"
#include "hid_mouse_reports.h"
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_HOST
#define CFG_TUSB_OS OPT_OS_FREERTOS
#define __FREERTOS
#include <FreeRTOS.h>
#include <semphr.h>
SemaphoreHandle_t usbSemaphore;
// Core 0 - device
void setup() {
if (!TinyUSBDevice.isInitialized()) {
TinyUSBDevice.begin(0);
}
//disable com port
TinyUSBDevice.clearConfiguration();
Serial1.begin(115200);
// Setup HID
usb_hid.setPollInterval(1);
usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
usb_hid.begin();
// If already enumerated, additional class driverr begin() e.g msc, hid, midi won't take effect until re-enumeration
if (TinyUSBDevice.mounted()) {
TinyUSBDevice.detach();
delay(10);
TinyUSBDevice.attach();
}
}
// Core 0 - device
void loop() {
// Process incoming serial commands
processSerialCommand();
// Handle PC commands
if (pc_command_pending) {
// if (xSemaphoreTake(usbSemaphore, pdMS_TO_TICKS(1))) {
hid_mouse_report_16bit_t rpt = {0};
rpt.x = pc_dx;
rpt.y = pc_dy;
rpt.wheel = pc_wheel;
rpt.buttons = pc_buttons;
while (!usb_hid.ready()) { yield(); }
usb_hid.sendReport(0, &rpt, sizeof(rpt));
// For clicks, send release after a short delay
if (pc_buttons != 0) {
delay(50);
rpt.buttons = 0;
while (!usb_hid.ready()) { yield(); }
usb_hid.sendReport(0, &rpt, sizeof(rpt));
}
pc_command_pending = false;
// xSemaphoreGive(usbSemaphore);
// }
}
// Core1 - host
void setup1() {
usbSemaphore = xSemaphoreCreateBinary();
xSemaphoreGive(usbSemaphore); // initially available
// 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);
}
// Core1 - host
void loop1() {
if (xSemaphoreTake(usbSemaphore, pdMS_TO_TICKS(1))) {
USBHost.task(); // safe access
xSemaphoreGive(usbSemaphore);
}
// Serial.println("loop");
}