Skip to content

Commit 761cfd3

Browse files
authored
Merge pull request #290 from adafruit/add-mouse-tremor
add mouse tremor with Butterworth filter
2 parents a2bcb19 + c2ffe2d commit 761cfd3

File tree

4 files changed

+245
-16
lines changed

4 files changed

+245
-16
lines changed

examples/DualRole/HID/hid_device_report/hid_device_report.ino

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@
2626
#include "Adafruit_TinyUSB.h"
2727

2828
// Pin D+ for host, D- = D+ + 1
29-
#ifndef PIN_PIO_USB_HOST_DP
30-
#define PIN_PIO_USB_HOST_DP 20
29+
#ifndef PIN_USB_HOST_DP
30+
#define PIN_USB_HOST_DP 16
3131
#endif
3232

3333
// Pin for enabling Host VBUS. comment out if not used
34-
#ifndef PIN_PIO_USB_HOST_VBUSEN
35-
#define PIN_PIO_USB_HOST_VBUSEN 22
34+
#ifndef PIN_5V_EN
35+
#define PIN_5V_EN 18
3636
#endif
3737

38-
#ifndef PIN_PIO_USB_HOST_VBUSEN_STATE
39-
#define PIN_PIO_USB_HOST_VBUSEN_STATE 1
38+
#ifndef PIN_5V_EN_STATE
39+
#define PIN_5V_EN_STATE 1
4040
#endif
4141

4242
// Language ID: English
@@ -51,16 +51,15 @@ Adafruit_USBH_Host USBHost;
5151

5252
void setup()
5353
{
54-
Serial1.begin(115200);
55-
5654
Serial.begin(115200);
57-
//while ( !Serial ) delay(10); // wait for native usb
55+
while ( !Serial ) delay(10); // wait for native usb
5856

5957
Serial.println("TinyUSB Dual Device Info Example");
6058
}
6159

6260
void loop()
6361
{
62+
Serial.flush();
6463
}
6564

6665
//--------------------------------------------------------------------+
@@ -80,13 +79,13 @@ void setup1() {
8079
while(1) delay(1);
8180
}
8281

83-
#ifdef PIN_PIO_USB_HOST_VBUSEN
84-
pinMode(PIN_PIO_USB_HOST_VBUSEN, OUTPUT);
85-
digitalWrite(PIN_PIO_USB_HOST_VBUSEN, PIN_PIO_USB_HOST_VBUSEN_STATE);
82+
#ifdef PIN_5V_EN
83+
pinMode(PIN_5V_EN, OUTPUT);
84+
digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE);
8685
#endif
8786

8887
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
89-
pio_cfg.pin_dp = PIN_PIO_USB_HOST_DP;
88+
pio_cfg.pin_dp = PIN_USB_HOST_DP;
9089
USBHost.configure_pio_usb(1, &pio_cfg);
9190

9291
// run host stack on controller (rhport) 1
@@ -100,14 +99,16 @@ void loop1()
10099
USBHost.task();
101100
}
102101

102+
extern "C" {
103+
103104
// Invoked when device with hid interface is mounted
104105
// Report descriptor is also available for use.
105106
// tuh_hid_parse_report_descriptor() can be used to parse common/simple enough
106107
// descriptor. Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE,
107108
// it will be skipped therefore report_desc = NULL, desc_len = 0
108109
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
109-
(void)desc_report;
110-
(void)desc_len;
110+
(void) desc_report;
111+
(void) desc_len;
111112
uint16_t vid, pid;
112113
tuh_vid_pid_get(dev_addr, &vid, &pid);
113114

@@ -135,3 +136,5 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons
135136
Serial.printf("Error: cannot request to receive report\r\n");
136137
}
137138
}
139+
140+
} // extern C

examples/DualRole/HID/hid_mouse_tremor_filter/.feather_rp2040_tinyusb.test.only

Whitespace-only changes.
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
/*********************************************************************
2+
Adafruit invests time and resources providing this open source code,
3+
please support Adafruit and open-source hardware by purchasing
4+
products from Adafruit!
5+
6+
MIT license, check LICENSE for more information
7+
Copyright (c) 2019 Ha Thach for Adafruit Industries
8+
All text above, and the splash screen below must be included in
9+
any redistribution
10+
*********************************************************************/
11+
12+
13+
/* This example demonstrates use of both device and host, where
14+
* - Device run on native usb controller (controller0)
15+
* - Host run on bit-banging 2 GPIOs with the help of Pico-PIO-USB library (controller1)
16+
*
17+
* Example sketch receive keyboard report from host interface (from e.g consumer keyboard)
18+
* and remap it to another key and send it via device interface (to PC). For simplicity,
19+
* this example only toggle shift key to the report, effectively remap:
20+
* - all character key <-> upper case
21+
* - number <-> its symbol (with shift)
22+
*
23+
* Requirements:
24+
* - [Pico-PIO-USB](https://github.com/sekigon-gonnoc/Pico-PIO-USB) library
25+
* - 2 consecutive GPIOs: D+ is defined by PIN_PIO_USB_HOST_DP, D- = D+ +1
26+
* - Provide VBus (5v) and GND for peripheral
27+
* - CPU Speed must be either 120 or 240 Mhz. Selected via "Menu -> CPU Speed"
28+
*/
29+
30+
// pio-usb is required for rp2040 host
31+
#include "pio_usb.h"
32+
#include "Adafruit_TinyUSB.h"
33+
34+
// Pin D+ for host, D- = D+ + 1
35+
#ifndef PIN_USB_HOST_DP
36+
#define PIN_USB_HOST_DP 16
37+
#endif
38+
39+
// Pin for enabling Host VBUS. comment out if not used
40+
#ifndef PIN_5V_EN
41+
#define PIN_5V_EN 18
42+
#endif
43+
44+
#ifndef PIN_5V_EN_STATE
45+
#define PIN_5V_EN_STATE 1
46+
#endif
47+
48+
// Language ID: English
49+
#define LANGUAGE_ID 0x0409
50+
51+
// USB Host object
52+
Adafruit_USBH_Host USBHost;
53+
54+
// HID report descriptor using TinyUSB's template
55+
// Single Report (no ID) descriptor
56+
uint8_t const desc_hid_report[] =
57+
{
58+
TUD_HID_REPORT_DESC_MOUSE()
59+
};
60+
61+
// USB HID object. For ESP32 these values cannot be changed after this declaration
62+
// desc report, desc len, protocol, interval, use out endpoint
63+
Adafruit_USBD_HID usb_hid(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_MOUSE, 2, false);
64+
65+
//------------- Low pass filter with Butterworth -------------//
66+
// Butterworth low-pass filter coefficients
67+
typedef struct {
68+
float b0, b1, b2, a1, a2;
69+
} butterworth_coeffs_t;
70+
71+
#define SAMPLING_FREQUENCY 100.0 // Hz
72+
#define CUTOFF_FREQUENCY 10.0 // Hz
73+
74+
// x, y
75+
butterworth_coeffs_t coeffs[2];
76+
77+
butterworth_coeffs_t butterworth_lowpass(float cutoff_frequency, float sampling_frequency);
78+
void filter_report(hid_mouse_report_t const* report);
79+
80+
//--------------------------------------------------------------------+
81+
// Setup and Loop on Core0
82+
//--------------------------------------------------------------------+
83+
84+
void setup()
85+
{
86+
Serial.begin(115200);
87+
usb_hid.begin();
88+
89+
coeffs[0] = butterworth_lowpass(CUTOFF_FREQUENCY, SAMPLING_FREQUENCY);
90+
coeffs[1] = butterworth_lowpass(CUTOFF_FREQUENCY, SAMPLING_FREQUENCY);
91+
92+
//while ( !Serial ) delay(10); // wait for native usb
93+
Serial.println("TinyUSB Mouse Tremor Filter Example");
94+
}
95+
96+
void loop()
97+
{
98+
Serial.flush();
99+
}
100+
101+
//--------------------------------------------------------------------+
102+
// Setup and Loop on Core1
103+
//--------------------------------------------------------------------+
104+
105+
void setup1() {
106+
//while ( !Serial ) delay(10); // wait for native usb
107+
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
108+
109+
// Check for CPU frequency, must be multiple of 120Mhz for bit-banging USB
110+
uint32_t cpu_hz = clock_get_hz(clk_sys);
111+
if ( cpu_hz != 120000000UL && cpu_hz != 240000000UL ) {
112+
while ( !Serial ) delay(10); // wait for native usb
113+
Serial.printf("Error: CPU Clock = %lu, PIO USB require CPU clock must be multiple of 120 Mhz\r\n", cpu_hz);
114+
Serial.printf("Change your CPU Clock to either 120 or 240 Mhz in Menu->CPU Speed \r\n");
115+
while(1) delay(1);
116+
}
117+
118+
#ifdef PIN_5V_EN
119+
pinMode(PIN_5V_EN, OUTPUT);
120+
digitalWrite(PIN_5V_EN, PIN_5V_EN_STATE);
121+
#endif
122+
123+
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
124+
pio_cfg.pin_dp = PIN_USB_HOST_DP;
125+
USBHost.configure_pio_usb(1, &pio_cfg);
126+
127+
// run host stack on controller (rhport) 1
128+
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
129+
// host bit-banging processing works done in core1 to free up core0 for other works
130+
USBHost.begin(1);
131+
}
132+
133+
void loop1()
134+
{
135+
USBHost.task();
136+
}
137+
138+
139+
extern "C"
140+
{
141+
142+
// Invoked when device with hid interface is mounted
143+
// Report descriptor is also available for use.
144+
// tuh_hid_parse_report_descriptor() can be used to parse common/simple enough
145+
// descriptor. Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE,
146+
// it will be skipped therefore report_desc = NULL, desc_len = 0
147+
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
148+
(void) desc_report;
149+
(void) desc_len;
150+
uint16_t vid, pid;
151+
tuh_vid_pid_get(dev_addr, &vid, &pid);
152+
153+
Serial.printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
154+
Serial.printf("VID = %04x, PID = %04x\r\n", vid, pid);
155+
156+
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
157+
if (itf_protocol == HID_ITF_PROTOCOL_MOUSE) {
158+
Serial.printf("HID Mouse\r\n");
159+
if (!tuh_hid_receive_report(dev_addr, instance)) {
160+
Serial.printf("Error: cannot request to receive report\r\n");
161+
}
162+
}
163+
}
164+
165+
// Invoked when device with hid interface is un-mounted
166+
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
167+
Serial.printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
168+
}
169+
170+
171+
// Invoked when received report from device via interrupt endpoint
172+
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
173+
filter_report((hid_mouse_report_t const *) report);
174+
175+
// continue to request to receive report
176+
if (!tuh_hid_receive_report(dev_addr, instance)) {
177+
Serial.printf("Error: cannot request to receive report\r\n");
178+
}
179+
}
180+
181+
} // extern C
182+
183+
//--------------------------------------------------------------------+
184+
// Low pass filter Functions
185+
//--------------------------------------------------------------------+
186+
187+
butterworth_coeffs_t butterworth_lowpass(float cutoff_frequency, float sampling_frequency) {
188+
butterworth_coeffs_t coe;
189+
190+
float omega = 2.0 * PI * cutoff_frequency / sampling_frequency;
191+
float c = cos(omega);
192+
float s = sin(omega);
193+
float t = tan(omega / 2.0);
194+
float alpha = s / (2.0 * t);
195+
196+
coe.b0 = 1.0 / (1.0 + 2.0 * alpha + 2.0 * alpha * alpha);
197+
coe.b1 = 2.0 * coe.b0;
198+
coe.b2 = coe.b0;
199+
coe.a1 = 2.0 * (alpha * alpha - 1.0) * coe.b0;
200+
coe.a2 = (1.0 - 2.0 * alpha + 2.0 * alpha * alpha) * coe.b0;
201+
202+
return coe;
203+
}
204+
205+
float butterworth_filter(float data, butterworth_coeffs_t *coeffs, float *filtered, float *prev1, float *prev2) {
206+
float output = coeffs->b0 * data + coeffs->b1 * (*prev1) + coeffs->b2 * (*prev2) - coeffs->a1 * (*filtered) - coeffs->a2 * (*prev1);
207+
*prev2 = *prev1;
208+
*prev1 = data;
209+
*filtered = output;
210+
return output;
211+
}
212+
213+
void filter_report(hid_mouse_report_t const* report) {
214+
static float filtered[2] = { 0.0, 0.0 };
215+
static float prev1[2] = { 0.0, 0.0 };
216+
static float prev2[2] = { 0.0, 0.0 };
217+
218+
butterworth_filter(report->x, &coeffs[0], &filtered[0], &prev1[0], &prev2[0]);
219+
butterworth_filter(report->y, &coeffs[1], &filtered[1], &prev1[1], &prev2[1]);
220+
221+
hid_mouse_report_t filtered_report = *report;
222+
filtered_report.x = (int8_t) filtered[0];
223+
filtered_report.y = (int8_t) filtered[1];
224+
225+
usb_hid.sendReport(0, &filtered_report, sizeof(filtered_report));
226+
}

examples/DualRole/HID/hid_remapper/hid_remapper.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_re
134134

135135
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
136136
if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD) {
137-
Serial.printf("HID Keyboard\r\n", vid, pid);
137+
Serial.printf("HID Keyboard\r\n");
138138
if (!tuh_hid_receive_report(dev_addr, instance)) {
139139
Serial.printf("Error: cannot request to receive report\r\n");
140140
}

0 commit comments

Comments
 (0)