adafruit / adafruit/Adafruit_TinyUSB_Arduino

Wireless mouse leads to core hang up / How to enable logging

Open
#527 10 comments 0 reactions 0 assignees View on GitHub
Bug
Dominant language
C
Stars
654
Forks
183
PR merge metrics
No merged PRs in 30d

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.

```cpp
/*********************************************************************
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
#include
#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:

```cpp
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
```cpp
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 ?

1. take any (at least all I tried) host usb sample code
2. connect wireless (or maybe this specific model) mouse
3. wait
4. 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_

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with examples/DualRole/HID/hid_mouse_tremor_filter/hid_mouse_tremor_filter.ino and the referenced usbh_helper.h, then reproduce the wireless and wired mouse behavior at 120 and 240 MHz. Compare the core heartbeat and HID mount/report logs while investigating where USBHost.task stops responding; done means the hang is explained and a reliable fix or diagnostic path is documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, linux
Domain
embedded-iot
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.