Skip to content

Commit b2ac355

Browse files
committed
gamepad turbo button arduino code
Arduino code for the gamepad turbo button project. also including a gamepad device report example with uf2
1 parent 1eb9dad commit b2ac355

File tree

8 files changed

+857
-0
lines changed

8 files changed

+857
-0
lines changed

USB_Host_Turbo_Button_Gamepad/Arduino_USB_Host_Turbo_Button_Gamepad/.feather_rp2040_usbhost_tinyusb.test.only

Whitespace-only changes.
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
/*********************************************************************
6+
Adafruit invests time and resources providing this open source code,
7+
please support Adafruit and open-source hardware by purchasing
8+
products from Adafruit!
9+
10+
MIT license, check LICENSE for more information
11+
Copyright (c) 2019 Ha Thach for Adafruit Industries
12+
All text above, and the splash screen below must be included in
13+
any redistribution
14+
*********************************************************************/
15+
16+
/* This example demonstrates use of both device and host, where
17+
* - Device runs on native USB controller (roothub port0)
18+
* - Host depends on MCU:
19+
* - rp2040: bit-banging 2 GPIOs with Pico-PIO-USB library (roothub port1)
20+
*
21+
* Requirements:
22+
* - For rp2040:
23+
* - Pico-PIO-USB library
24+
* - 2 consecutive GPIOs: D+ is defined by PIN_USB_HOST_DP, D- = D+ +1
25+
* - Provide VBus (5v) and GND for peripheral
26+
* - CPU Speed must be either 120 or 240 MHz. Selected via "Menu -> CPU Speed"
27+
*/
28+
29+
// USBHost is defined in usbh_helper.h
30+
#include "usbh_helper.h"
31+
#include "tusb.h"
32+
#include "Adafruit_TinyUSB.h"
33+
#include "gamepad_reports.h"
34+
35+
// HID report descriptor using TinyUSB's template
36+
// Single Report (no ID) descriptor
37+
uint8_t const desc_hid_report[] = {
38+
TUD_HID_REPORT_DESC_GAMEPAD()
39+
};
40+
41+
// USB HID object
42+
Adafruit_USBD_HID usb_hid;
43+
44+
// Report payload defined in src/class/hid/hid.h
45+
// - For Gamepad Button Bit Mask see hid_gamepad_button_bm_t
46+
// - For Gamepad Hat Bit Mask see hid_gamepad_hat_t
47+
hid_gamepad_report_t gp;
48+
49+
bool combo_active = false;
50+
51+
void setup() {
52+
if (!TinyUSBDevice.isInitialized()) {
53+
TinyUSBDevice.begin(0);
54+
}
55+
Serial.begin(115200);
56+
// Setup HID
57+
usb_hid.setPollInterval(2);
58+
usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
59+
usb_hid.begin();
60+
61+
// If already enumerated, additional class driverr begin() e.g msc, hid, midi won't take effect until re-enumeration
62+
if (TinyUSBDevice.mounted()) {
63+
TinyUSBDevice.detach();
64+
delay(10);
65+
TinyUSBDevice.attach();
66+
}
67+
}
68+
69+
#if defined(ARDUINO_ARCH_RP2040)
70+
//--------------------------------------------------------------------+
71+
// For RP2040 use both core0 for device stack, core1 for host stack
72+
//--------------------------------------------------------------------//
73+
74+
//------------- Core0 -------------//
75+
void loop() {
76+
}
77+
78+
//------------- Core1 -------------//
79+
void setup1() {
80+
// configure pio-usb: defined in usbh_helper.h
81+
rp2040_configure_pio_usb();
82+
83+
// run host stack on controller (rhport) 1
84+
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
85+
// host bit-banging processing works done in core1 to free up core0 for other works
86+
USBHost.begin(1);
87+
}
88+
89+
void loop1() {
90+
USBHost.task();
91+
Serial.flush();
92+
if (combo_active) {
93+
turbo_button();
94+
}
95+
}
96+
#endif
97+
98+
//--------------------------------------------------------------------+
99+
// HID Host Callback Functions
100+
//--------------------------------------------------------------------+
101+
102+
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
103+
{
104+
Serial.printf("HID device mounted (address %d, instance %d)\n", dev_addr, instance);
105+
106+
// Start receiving HID reports
107+
if (!tuh_hid_receive_report(dev_addr, instance))
108+
{
109+
Serial.printf("Error: cannot request to receive report\n");
110+
}
111+
}
112+
113+
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
114+
{
115+
Serial.printf("HID device unmounted (address %d, instance %d)\n", dev_addr, instance);
116+
}
117+
118+
void turbo_button() {
119+
if (combo_active) {
120+
while (!usb_hid.ready()) {
121+
yield();
122+
}
123+
Serial.println("A");
124+
gp.buttons = GAMEPAD_BUTTON_A;
125+
usb_hid.sendReport(0, &gp, sizeof(gp));
126+
Serial.println("off");
127+
delay(2);
128+
gp.buttons = 0;
129+
usb_hid.sendReport(0, &gp, sizeof(gp));
130+
delay(2);
131+
}
132+
}
133+
134+
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) {
135+
// Known report when the combo is pressed
136+
//uint8_t combo_report[] = { 0x80, 0x7F, 0x80, 0x7F, 0x28, 0x03, 0x00, 0xFF };
137+
// Check if the incoming report matches the combo report
138+
bool combo_detected = ((report[4] == combo_report[4]) && (report[5] == combo_report[5]));// len == sizeof(combo_report)) && (memcmp(report, combo_report, sizeof(combo_report)) == 0);
139+
140+
// Manage the combo state and print messages
141+
if (combo_detected && !combo_active) {
142+
combo_active = true;
143+
Serial.println("combo!");
144+
} else if (combo_detected && combo_active) {
145+
combo_active = false;
146+
Serial.println("combo released!");
147+
}
148+
if (!(combo_active)) {
149+
if (!(report[BYTE_LEFT_STICK_X] == LEFT_STICK_X_NEUTRAL)) {
150+
int16_t leftStickX = report[BYTE_LEFT_STICK_X];
151+
Serial.print("left stick X: ");
152+
Serial.println(leftStickX);
153+
int16_t new_leftStickX = map(leftStickX, 0, 255, -127, 127);
154+
gp.x = new_leftStickX;
155+
} else {
156+
gp.x = 0;
157+
}
158+
if (!(report[BYTE_LEFT_STICK_Y] == LEFT_STICK_Y_NEUTRAL)) {
159+
int16_t leftStickY = report[BYTE_LEFT_STICK_Y];
160+
Serial.print("left stick Y: ");
161+
Serial.println(leftStickY);
162+
int16_t new_leftStickY = map(leftStickY, 0, 255, -127, 127);
163+
gp.y = new_leftStickY;
164+
} else {
165+
gp.y = 0;
166+
}
167+
if (!(report[BYTE_RIGHT_STICK_X] == RIGHT_STICK_X_NEUTRAL)) {
168+
int8_t rightStickX = report[BYTE_RIGHT_STICK_X];
169+
Serial.print("right stick X: ");
170+
Serial.println(rightStickX);
171+
int16_t new_rightStickX = map(rightStickX, 0, 255, 127, -127);
172+
gp.z = new_rightStickX;
173+
} else {
174+
gp.z = 0;
175+
}
176+
if (!(report[BYTE_RIGHT_STICK_Y] == RIGHT_STICK_Y_NEUTRAL)) {
177+
int8_t rightStickY = report[BYTE_RIGHT_STICK_Y];
178+
Serial.print("right stick Y: ");
179+
Serial.println(rightStickY);
180+
int16_t new_rightStickY = map(rightStickY, 0, 255, -127, 127);
181+
gp.rz = new_rightStickY;
182+
} else {
183+
gp.rz = 0;
184+
}
185+
if (!(report[BYTE_DPAD_BUTTONS] == DPAD_NEUTRAL)) {
186+
// D-Pad is active
187+
uint8_t buttonsSelect = report[BYTE_DPAD_BUTTONS];
188+
switch (buttonsSelect) {
189+
case BUTTON_X:
190+
Serial.println("x");
191+
gp.buttons = GAMEPAD_BUTTON_X;
192+
break;
193+
case BUTTON_A:
194+
Serial.println("a");
195+
gp.buttons = GAMEPAD_BUTTON_A;
196+
break;
197+
case BUTTON_B:
198+
Serial.println("b");
199+
gp.buttons = GAMEPAD_BUTTON_B;
200+
break;
201+
case BUTTON_Y:
202+
Serial.println("y");
203+
gp.buttons = GAMEPAD_BUTTON_Y;
204+
break;
205+
}
206+
} else {
207+
gp.hat = 0;
208+
gp.buttons = 0;
209+
}
210+
if (!(report[BYTE_DPAD_BUTTONS] == DPAD_NEUTRAL)) {
211+
// D-Pad is active
212+
uint8_t dpadDirection = report[BYTE_DPAD_BUTTONS];
213+
switch (dpadDirection) {
214+
case DPAD_UP:
215+
Serial.println("up");
216+
gp.hat = 1; // GAMEPAD_HAT_UP;
217+
break;
218+
case DPAD_UP_RIGHT:
219+
Serial.println("up/right");
220+
gp.hat = 2;
221+
break;
222+
case DPAD_RIGHT:
223+
Serial.println("right");
224+
gp.hat = 3;
225+
break;
226+
case DPAD_DOWN_RIGHT:
227+
Serial.println("down/right");
228+
gp.hat = 4;
229+
break;
230+
case DPAD_DOWN:
231+
Serial.println("down");
232+
gp.hat = 5;
233+
break;
234+
case DPAD_DOWN_LEFT:
235+
Serial.println("down/left");
236+
gp.hat = 6;
237+
break;
238+
case DPAD_LEFT:
239+
Serial.println("left");
240+
gp.hat = 7;
241+
break;
242+
case DPAD_UP_LEFT:
243+
Serial.println("up/left");
244+
gp.hat = 8;
245+
break;
246+
}
247+
} else {
248+
gp.hat = 0;
249+
}
250+
if (!(report[BYTE_MISC_BUTTONS] == MISC_NEUTRAL)) {
251+
// misc are active
252+
uint8_t miscDirection = report[BYTE_MISC_BUTTONS];
253+
switch (miscDirection) {
254+
case BUTTON_LEFT_PADDLE:
255+
Serial.println("left paddle");
256+
gp.buttons = GAMEPAD_BUTTON_TL;
257+
break;
258+
case BUTTON_RIGHT_PADDLE:
259+
Serial.println("right paddle");
260+
gp.buttons = GAMEPAD_BUTTON_TR;
261+
break;
262+
case BUTTON_LEFT_TRIGGER:
263+
Serial.println("left trigger");
264+
gp.buttons = GAMEPAD_BUTTON_TL2;
265+
break;
266+
case BUTTON_RIGHT_TRIGGER:
267+
Serial.println("right trigger");
268+
gp.buttons = GAMEPAD_BUTTON_TR2;
269+
break;
270+
case BUTTON_BACK:
271+
Serial.println("back");
272+
gp.buttons = GAMEPAD_BUTTON_SELECT;
273+
break;
274+
case BUTTON_START:
275+
Serial.println("start");
276+
gp.buttons = GAMEPAD_BUTTON_START;
277+
break;
278+
}
279+
}
280+
} else {
281+
gp.buttons = GAMEPAD_BUTTON_A;
282+
}
283+
while (!usb_hid.ready()) {
284+
yield();
285+
}
286+
usb_hid.sendReport(0, &gp, sizeof(gp));
287+
// Continue to receive the next report
288+
if (!tuh_hid_receive_report(dev_addr, instance)) {
289+
Serial.println("Error: cannot request to receive report");
290+
}
291+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries
2+
//
3+
// SPDX-License-Identifier: MIT
4+
5+
// HID reports for Logitech Gamepad F310
6+
// Update defines and combo_report for your gamepad and/or combo!
7+
8+
uint8_t combo_report[] = { 0x80, 0x7F, 0x80, 0x7F, 0x28, 0x03, 0x00, 0xFF };
9+
10+
// Byte indices for the gamepad report
11+
#define BYTE_LEFT_STICK_X 0 // Left analog stick X-axis
12+
#define BYTE_LEFT_STICK_Y 1 // Left analog stick Y-axis
13+
#define BYTE_RIGHT_STICK_X 2 // Right analog stick X-axis
14+
#define BYTE_RIGHT_STICK_Y 3 // Right analog stick Y-axis
15+
#define BYTE_DPAD_BUTTONS 4 // D-Pad and face buttons
16+
#define BYTE_MISC_BUTTONS 5 // Miscellaneous buttons (triggers, paddles, start, back)
17+
#define BYTE_UNUSED 6 // Unused
18+
#define BYTE_STATUS 7 // Status byte (usually constant)
19+
20+
// Button masks for Byte[4] (DPAD and face buttons)
21+
#define DPAD_MASK 0x07 // Bits 0-2 for D-Pad direction
22+
#define DPAD_NEUTRAL 0x08 // Bit 3 set when D-Pad is neutral
23+
24+
// D-Pad directions (use when DPAD_NEUTRAL is not set)
25+
#define DPAD_UP 0x00 // 0000
26+
#define DPAD_UP_RIGHT 0x01 // 0001
27+
#define DPAD_RIGHT 0x02 // 0010
28+
#define DPAD_DOWN_RIGHT 0x03 // 0011
29+
#define DPAD_DOWN 0x04 // 0100
30+
#define DPAD_DOWN_LEFT 0x05 // 0101
31+
#define DPAD_LEFT 0x06 // 0110
32+
#define DPAD_UP_LEFT 0x07 // 0111
33+
34+
// Face buttons (Byte[4] bits 4-7)
35+
#define BUTTON_X 0x18
36+
#define BUTTON_A 0x28
37+
#define BUTTON_B 0x48
38+
#define BUTTON_Y 0x88
39+
40+
// Button masks for Byte[5] (MISC buttons)
41+
#define MISC_NEUTRAL 0x00
42+
43+
// Miscellaneous buttons (Byte[5])
44+
#define BUTTON_LEFT_PADDLE 0x01
45+
#define BUTTON_RIGHT_PADDLE 0x02
46+
#define BUTTON_LEFT_TRIGGER 0x04
47+
#define BUTTON_RIGHT_TRIGGER 0x08
48+
#define BUTTON_BACK 0x10
49+
#define BUTTON_START 0x20
50+
51+
#define LEFT_STICK_X_NEUTRAL 0x80
52+
#define LEFT_STICK_Y_NEUTRAL 0x7F
53+
#define RIGHT_STICK_X_NEUTRAL 0x80
54+
#define RIGHT_STICK_Y_NEUTRAL 0x7F

0 commit comments

Comments
 (0)