I'm trying to write code for the XIAO SAMD21 that will control both the joystick and the keyboard simultaneously, but I'm having trouble connecting them; they won't work at all. Both programs below work separately. Does anyone know how to connect them so they work together? My attempts end up with USB communication hanging up.
Joystick:
#include "Adafruit_TinyUSB.h"
// Obiekt HID
Adafruit_USBD_HID usb_hid;
// === Deskryptor HID: 64 przyciski + oś Z ===
uint8_t const hid_report_descriptor[] = {
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Application)
// 64 przyciski (8 bajtów)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (Button 1)
0x29, 0x40, // Usage Maximum (Button 64)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x95, 0x40, // Report Count (64)
0x75, 0x01, // Report Size (1 bit)
0x81, 0x02, // Input (Data, Variable, Absolute)
// Oś Z (hamulec)
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x32, // Usage (Z)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8 bitów)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data, Variable, Absolute)
0xC0 // End Collection
};
// Struktura raportu HID (8 bajtów przycisków + 1 bajt osi)
struct __attribute__((packed)) {
uint8_t buttons[8];
uint8_t z;
} hid_report;
void setup() {
usb_hid.setReportDescriptor(hid_report_descriptor, sizeof(hid_report_descriptor));
usb_hid.setPollInterval(2);
usb_hid.begin();
// Czekamy, aż host (PC) zainicjuje USB
while (!USBDevice.mounted()) {
delay(10);
}
}
void loop() {
static uint32_t t = 0;
// Oś "pływa" sinusoidalnie w zakresie 0–255
float value = (sin(t * 0.05) * 0.5 + 0.5) * 255.0;
hid_report.z = (uint8_t)value;
// Wszystkie przyciski niewciśnięte
memset(hid_report.buttons, 0x00, sizeof(hid_report.buttons));
// Wysłanie raportu (Report ID = domyślnie 0)
usb_hid.sendReport(0, &hid_report, sizeof(hid_report));
t++;
delay(10);
}
Keyboard:
#include <Adafruit_TinyUSB.h>
// --- konfiguracja pinów przycisków ---
#define BTN_A 9
#define BTN_Arrow 10
#define keya HID_KEY_A
// --- obiekt HID klawiatury TinyUSB ---
Adafruit_USBD_HID usb_hid_keyboard;
// --- pełny raport HID (modifier,reserved,6 keycodes) ---
uint8_t keyReport[8] = { 0 };
// --- blokady stanów przycisków ---
bool btnArrowLeftLock = false;
bool btnALock = false;
// --- standardowy deskryptor HID klawiatury (8-bajtowy raport) ---
const uint8_t keyboard_report_descriptor[] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xA1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xE0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xE7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x05, // USAGE_MAXIMUM (Kana)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved)
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Arr,Abs)
0xC0
};
void setup() {
// piny wejściowe
pinMode(BTN_A, INPUT_PULLUP);
pinMode(BTN_Arrow, INPUT_PULLUP);
// ustaw deskryptor i uruchom HID
usb_hid_keyboard.setPollInterval(2);
usb_hid_keyboard.setReportDescriptor(keyboard_report_descriptor, sizeof(keyboard_report_descriptor));
usb_hid_keyboard.begin();
// daj hostowi chwilę na enumerację
delay(1000);
}
void loop() {
// Przycisk A -> litera 'A'
if (!digitalRead(BTN_A)) {
keyboardPressShifted(keya);
btnALock = 1;
} else {
if (btnALock == 1) {
keyboardReleaseShifted(keya);
btnALock = 0;
}
}
if (!digitalRead(BTN_Arrow)) {
keyboardPress(HID_KEY_ARROW_LEFT);
btnArrowLeftLock = 1;
} else {
if (btnArrowLeftLock == 1) {
keyboardRelease(HID_KEY_ARROW_LEFT);
btnArrowLeftLock = 0;
}
}
delay(5);
}
// odpowiednik Keyboard.press(key)
void keyboardPress(uint8_t keycode) {
// ustaw modyfikatory w keyReport[0] jeśli potrzebne
// wstaw kod klawisza w pierwszy wolny slot (2..7) - tutaj używamy tylko slotu 2
keyReport[2] = keycode;
usb_hid_keyboard.sendReport(0, keyReport, sizeof(keyReport));
}
// odpowiednik Keyboard.release(key)
void keyboardRelease(uint8_t keycode) {
// jeśli puszczamy konkretny keycode, usuń go z raportu - tu upraszczamy: czyścimy slot 2
keyReport[2] = 0;
usb_hid_keyboard.sendReport(0, keyReport, sizeof(keyReport));
}
// puść wszystkie klawisze
void keyboardReleaseAll() {
memset(keyReport, 0, sizeof(keyReport));
usb_hid_keyboard.sendReport(0, keyReport, sizeof(keyReport));
}
void keyboardPressShifted(uint8_t keycode) {
keyReport[0] = KEYBOARD_MODIFIER_LEFTSHIFT; // włącz SHIFT
keyReport[2] = keycode; // wpisz literę
usb_hid_keyboard.sendReport(0, keyReport, sizeof(keyReport));
}
void keyboardReleaseShifted(uint8_t keycode) {
keyReport[0] = 0; // wyłącz SHIFT
keyReport[2] = 0; // zwolnij klawisz
usb_hid_keyboard.sendReport(0, keyReport, sizeof(keyReport));
}


