🛠 Clavier USB HID via l'USB-OTG natif (TinyUSB)
Objectives
Transform the ESP32-S3 into a USB HID peripheral (keyboard) thanks to its native USB-OTG controller on GPIO19 (D-) and GPIO20 (D+), via the TinyUSB stack integrated into the Arduino core. This is one of the major differences from the classic ESP32: no additional chip is required. You will also discover the major constraint of the S3's dual USB: the "TinyUSB (OTG)" mode and the "Hardware CDC and JTAG" mode compete for the same pins; you cannot do everything at the same time without a second physical connector.
Hardware
- 1 ESP32-S3 board with USB on GPIO19/20 (DevKitC-1 v1.1, Feather S3, XIAO in OTG mode)
- 1 push button between GPIO1 and GND
- 1 second USB port (or a second computer/tester) to reflash if the HID takes over
- Arduino IDE with a recent esp32 core
Complete code
// Clavier HID sur USB-OTG natif (GPIO19/20) via TinyUSB
// Outils Arduino : USB Mode = "TinyUSB Device" (ou "OTG"), USB CDC = Disabled
#include "Arduino.h"
#include "USB.h"
#include "USBHIDKeyboard.h"
USBHIDKeyboard Keyboard;
const uint8_t BTN = 1;
uint8_t etatPrecedent = HIGH;
unsigned long dernierBord = 0;
void setup {
pinMode(BTN, INPUT_PULLUP);
Keyboard.begin;
if (!USB.begin) {
// pas de hot-plug possible ici : le host verra le HID au prochain branchement
}
}
void loop {
uint8_t etat = digitalRead(BTN);
if (etat == LOW && etatPrecedent == HIGH && millis - dernierBord > 120) {
dernierBord = millis;
Keyboard.begin; // s'assure que le HID est pret
Keyboard.print("ESP32-S3 dit bonjour ");
Keyboard.write(KEY_RETURN);
Keyboard.end;
}
etatPrecedent = etat;
delay(10);
}
Expected verification
After uploading and reconnecting the cable, the computer detects a new "USB Keyboard" (hidusb) device without a driver. Pressing the button enters the line "ESP32-S3 says hello" followed by Enter into any active text field. The USB enumeration goes through GPIO19/20, the only pins connected to the S3's OTG controller.
Common errors
- Forgetting to switch the "USB Mode" menu to TinyUSB: with Hardware CDC+JTAG mode, USB.begin fails silently because GPIO19/20 are not routed to the OTG.
- Losing the flash port: in TinyUSB mode, the enumeration serial port disappears if the program crashes before USB.begin; plan for a BOOT/RESET button to enter ROM download mode, or keep a second cable on UART0 (GPIO43/44).
- Expecting a reconnection: many hosts do not re-enumerate hot; after a flash, unplugging/replugging the cable is often necessary.
- Keyboard layout: Keyboard.write assumes a US layout; for a French AZERTY layout, use modifiers or the KeyboardFR class depending on the core versions.
- Confusing the two USBs: the "COM" port of the console (Serial/JTAG) and the HID OTG can coexist only if the board physically wires the two controllers to two distinct connectors.
Other tutorials for this board
Code examples for this board
clignotante.inoArduinobouton-led.inoArduinorespiration.inoArduinodht22.inoArduinooled-i2c.inoArduinoserveur-web.inoArduino