ESP32-S2 WIFI media controller does not work

Hi there, I'm trying to do a project for controlling windows media keys over wifi. When i try to control with push button everything works well but when i do this over wifi it does not work even with the push button. How can i solve this and what is the issue?

#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <Adafruit_TinyUSB.h>

// Wi-Fi 
const char* ssid = "ssid";
const char* password = "password";

// Web 
AsyncWebServer server(80);

// HID Media Report descriptor 
uint8_t const desc_hid_report[] = {
  TUD_HID_REPORT_DESC_CONSUMER() 
};

// USB HID 
Adafruit_USBD_HID usb_hid;

const char htmlPage[] PROGMEM = R"rawliteral(
  <!DOCTYPE HTML>
  <html>
  <head>
    <meta charset="UTF-8">
    <title>ESP32 HID Media Controller</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      body { font-family: Arial, sans-serif; text-align: center; background-color: #f4f4f4; }
      h1 { color: #333; }
      button { padding: 10px 20px; font-size: 16px; color: white; background-color: #007BFF; border: none; border-radius: 5px; cursor: pointer; margin: 5px; }
      button:hover { background-color: #0056b3; }
    </style>
    <script>
      function sendKey(key) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "/" + key, true);
        xhr.send();
      }
    </script>
  </head>
  <body>
    <h1>ESP32 HID Media Controller</h1>
    <button onclick="sendKey('playPause')">Play/Pause</button>
    <button onclick="sendKey('nextTrack')">Next Track</button>
    <button onclick="sendKey('previousTrack')">Previous Track</button>
  </body>
  </html>)rawliteral";

bool playpauseon = false;

void setup() {
  // Wi-Fi 
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
  }
  
  pinMode(15, OUTPUT);
  pinMode(0, INPUT_PULLUP);

  // TinyUSB HID 
  usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
  usb_hid.begin();

  
  while (!TinyUSBDevice.mounted()) {
    delay(10);
  }

  
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", htmlPage);
  });

  // Play/Pause 
  server.on("/playPause", HTTP_GET, [](AsyncWebServerRequest *request){
    playpauseon = true; 
    request->send(200, "text/plain", "Play/Pause Gönderildi");
  });

  // Next 
  server.on("/nextTrack", HTTP_GET, [](AsyncWebServerRequest *request){
    uint16_t mediaKey = HID_USAGE_CONSUMER_SCAN_NEXT;
    usb_hid.sendReport(0, &mediaKey, sizeof(mediaKey));
    request->send(200, "text/plain", "Next Track Gönderildi");
  });

  // Previous 
  server.on("/previousTrack", HTTP_GET, [](AsyncWebServerRequest *request){
    uint16_t mediaKey = HID_USAGE_CONSUMER_SCAN_PREVIOUS;
    usb_hid.sendReport(0, &mediaKey, sizeof(mediaKey));
    request->send(200, "text/plain", "Previous Track Gönderildi");
  });

  // Web server
  server.begin();
}

void loop() {
  
  if (playpauseon || digitalRead(0) == LOW) {
    uint16_t mediaKey = HID_USAGE_CONSUMER_PLAY_PAUSE;
    usb_hid.sendReport(0, &mediaKey, sizeof(mediaKey));
    digitalWrite(15, HIGH);
    delay(300);
    digitalWrite(15, LOW);
    playpauseon = false; 
  }
  delay(50); 
}

Possibly due to the context in which server.on() runs. It might not work well calling usb_hid.sendReport() in that context.
I've got a project using the AsyncWebServer which works very reliably now.
When I was developing it I had some problems which went away when I moved some complex code from server.on() into loop(). I just set a flag in server.on() to indicate to loop that it needed to execute the code that I had moved to loop() from server.on().
IIRC I didn't ever discover what context server.on() runs in. I don't know if it's a callback from an ISR, a callback from some other thread, or a callback using some other mechanism.

I tought same thing exactly like you and i moved usb_hid.sendReport() into the loop funtion (you can see this in the given code) but it didn't change anything at all. By the way when i press the button (pulling GPIO 0 to LOW) or click the button from Web page LED lights up but usb_hid.sendReport() does not work.

Hi @alprnatmc

welcome to the arduino-forum.

Well done to post your code as a code-section in your first posting.

With what code did you test that usb_hid.sendReport() works at all?
You should always test one thing at a time.

What exact type of ESP32-S2-board?
ESP32-S2 mini?

You will have to provide much more detail-information if somebody should analyse your system.

The code you are using is the most rudimental code for that I have seen so far.
You should add serial printing in many places to narrow down what works and what not

Thanks a lot, the board is wemos ESP32-S2 mini. The reason that i didn't use serial printing is board is native usb and i couldn't make sure to usb serial and hid works together.
The working code without wifi is:

#include <Arduino.h>
#include <Adafruit_TinyUSB.h>


uint8_t const desc_hid_report[] = {
  TUD_HID_REPORT_DESC_CONSUMER() 
};


Adafruit_USBD_HID usb_hid;


const int buttonPin = 0; 


void setup() {
 
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(15, OUTPUT);

 
  usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
  usb_hid.begin();


  while (!TinyUSBDevice.mounted()) {
    delay(10);
  }

 
}
bool has_key = false;
void loop() {

  int buttonState = digitalRead(buttonPin);
  if (buttonState == LOW) {
   

 
    uint16_t mediaKey = HID_USAGE_CONSUMER_PLAY_PAUSE;
    usb_hid.sendReport(0, &mediaKey, sizeof(mediaKey));
    digitalWrite(15 , HIGH);
    has_key = true;
    
    delay(200); 
  }

  if (buttonState == HIGH){
   
    if (has_key) {
      
      usb_hid.keyboardRelease(0);
      digitalWrite(15 , LOW);
    }
    has_key = false;
  }
 

  delay(50); 
}

I'm just a beginner so how can i improve the code with wifi? What should i do for serial printing?

I haven't done anything with the Adafruit_TinyUSB.h-library myself.

Doing a simple test if serial printing works in parallel to using usb-hid.

Using some additional leds with current-limiting resistors to indicate what code is executed.

Using a library like ESPUI for creating the website stuff.
ESPUI hides a way all the html-stuff "under the hood" you only deal with call-back-functions.

I am unable to compile your code because I get multiple compiler-errors like
usbd.c:153:10: error: 'usbd_class_driver_t' {aka 'const struct '} has no member named 'name'

anyway here a code-version with serial debug-printing

#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <Adafruit_TinyUSB.h>

// Wi-Fi 
const char* ssid = "ssid";
const char* password = "password";

// Web 
AsyncWebServer server(80);

// HID Media Report descriptor 
uint8_t const desc_hid_report[] = {
  TUD_HID_REPORT_DESC_CONSUMER() 
};

// USB HID 
Adafruit_USBD_HID usb_hid;

const char htmlPage[] PROGMEM = R"rawliteral(
  <!DOCTYPE HTML>
  <html>
  <head>
    <meta charset="UTF-8">
    <title>ESP32 HID Media Controller</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      body { font-family: Arial, sans-serif; text-align: center; background-color: #f4f4f4; }
      h1 { color: #333; }
      button { padding: 10px 20px; font-size: 16px; color: white; background-color: #007BFF; border: none; border-radius: 5px; cursor: pointer; margin: 5px; }
      button:hover { background-color: #0056b3; }
    </style>
    <script>
      function sendKey(key) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "/" + key, true);
        xhr.send();
      }
    </script>
  </head>
  <body>
    <h1>ESP32 HID Media Controller</h1>
    <button onclick="sendKey('playPause')">Play/Pause</button>
    <button onclick="sendKey('nextTrack')">Next Track</button>
    <button onclick="sendKey('previousTrack')">Previous Track</button>
  </body>
  </html>)rawliteral";

bool playpauseon = false;

const byte myLEDPin = 15;
const byte myButtonPin = 0;

void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  // Wi-Fi 
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
  }
  Serial.println("connected to WiFi");
  
  pinMode(myLEDPin, OUTPUT);
  pinMode(myButtonPin, INPUT_PULLUP);

  // TinyUSB HID 
  usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
  usb_hid.begin();
  Serial.println("usb_hid.begin() done");
  
  while (!TinyUSBDevice.mounted()) {
    delay(10);
  }
  Serial.println("TinyUSBDevice.mounted()");

  
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", htmlPage);
  });

  // Play/Pause 
  server.on("/playPause", HTTP_GET, [](AsyncWebServerRequest *request){
    playpauseon = true; 
    request->send(200, "text/plain", "Play/Pause Gönderildi");
  });

  // Next 
  server.on("/nextTrack", HTTP_GET, [](AsyncWebServerRequest *request){
    uint16_t mediaKey = HID_USAGE_CONSUMER_SCAN_NEXT;
    bool result = usb_hid.sendReport(0, &mediaKey, sizeof(mediaKey));
    if (result) {
      Serial.println("nextTrack usb_hid.sendReport success");
    }
    else {
      Serial.println("nextTrack usb_hid.sendReport failed");      
    }
    request->send(200, "text/plain", "Next Track Gönderildi");
  });

  // Previous 
  server.on("/previousTrack", HTTP_GET, [](AsyncWebServerRequest *request){
    uint16_t mediaKey = HID_USAGE_CONSUMER_SCAN_PREVIOUS;
    bool result = usb_hid.sendReport(0, &mediaKey, sizeof(mediaKey));
    if (result) {
      Serial.println("previousTrack usb_hid.sendReport success");
    }
    else {
      Serial.println("previousTrack usb_hid.sendReport failed");      
    }
    request->send(200, "text/plain", "Previous Track Gönderildi");
  });

  // Web server
  server.begin();
  Serial.println("server.begin() done");
}

void loop() {
  
  if (playpauseon || digitalRead(myButtonPin) == LOW) {
    if (playpauseon) {
      Serial.println("playpauseon is true");
    }
    if (digitalRead(myButtonPin) == LOW) {
      Serial.println("myButtonPin is LOW");
    }
    
    uint16_t mediaKey = HID_USAGE_CONSUMER_PLAY_PAUSE;
    usb_hid.sendReport(myButtonPin, &mediaKey, sizeof(mediaKey));
    digitalWrite(myLEDPin, HIGH);
    delay(300);
    digitalWrite(myLEDPin, LOW);
    playpauseon = false; 
  }
  delay(50); 
}

I solved the problem, it was calling wifi function before usbhid. In the setup() i called "usb_hid.begin();" first and it solved.

@alprnatmc
As a thank you to the community please post the working sketch as a code-section

Thank you all. This is the working code:

#include <Arduino.h>
#include <Adafruit_TinyUSB.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>

uint8_t const desc_hid_report[] = {
  TUD_HID_REPORT_DESC_CONSUMER() 
};


Adafruit_USBD_HID usb_hid;

const char* ssid = "SSID";
const char* password = "password";
// Web 
AsyncWebServer server(80);

const int buttonPin = 0; 

const char htmlPage[] PROGMEM = R"rawliteral(
  <!DOCTYPE HTML>
  <html>
  <head>
    <meta charset="UTF-8">
    <title>ESP32 HID Media Controller</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      body { font-family: Arial, sans-serif; text-align: center; background-color: #f4f4f4; }
      h1 { color: #333; }
      button { padding: 10px 20px; font-size: 16px; color: white; background-color: #007BFF; border: none; border-radius: 5px; cursor: pointer; margin: 5px; }
      button:hover { background-color: #0056b3; }
    </style>
    <script>
      function sendKey(key) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "/" + key, true);
        xhr.send();
      }
    </script>
  </head>
  <body>
    <h1>ESP32 HID Media Controller</h1>
    <button onclick="sendKey('playPause')">Play/Pause</button>
    <button onclick="sendKey('nextTrack')">Next Track</button>
    <button onclick="sendKey('previousTrack')">Previous Track</button>
  </body>
  </html>)rawliteral";

bool playpauseon = false; 
bool next_track = false;
bool previous_track =false;

void setup() {
 
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(15, OUTPUT);

 
  usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report));
  usb_hid.begin();



  while (!TinyUSBDevice.mounted()) {
    delay(10);
  }

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
  }

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", htmlPage);
  });

  // Play/Pause 
  server.on("/playPause", HTTP_GET, [](AsyncWebServerRequest *request){
    playpauseon = true; 
   
    request->send(200, "text/plain", "Play/Pause Gönderildi");
  });

  // Next 
  server.on("/nextTrack", HTTP_GET, [](AsyncWebServerRequest *request){
    next_track = true;
    request->send(200, "text/plain", "Next Track Gönderildi");
  });

  // Previous 
  server.on("/previousTrack", HTTP_GET, [](AsyncWebServerRequest *request){
    previous_track = true;
    request->send(200, "text/plain", "Previous Track Gönderildi");
  });

  // Web server
  server.begin();
 
}
bool has_key = false;
void loop() {

  int buttonState = digitalRead(buttonPin);
  if (buttonState == LOW || playpauseon == true ) {
   

 
    uint16_t mediaKey = HID_USAGE_CONSUMER_PLAY_PAUSE;
    usb_hid.sendReport(0, &mediaKey, sizeof(mediaKey));
    digitalWrite(15 , HIGH);
    has_key = true;
    playpauseon = false; 

    delay(200); 

  }else if(next_track == true){
        uint16_t mediaKey = HID_USAGE_CONSUMER_SCAN_NEXT;
    usb_hid.sendReport(0, &mediaKey, sizeof(mediaKey));
    next_track = false;
    has_key = true;
    digitalWrite(15 , HIGH);
    delay(200);
  }
  else if(previous_track == true){
        uint16_t mediaKey = HID_USAGE_CONSUMER_SCAN_PREVIOUS;
    usb_hid.sendReport(0, &mediaKey, sizeof(mediaKey));
    previous_track = false;
    has_key = true;
    digitalWrite(15 , HIGH);
    delay(200);
  }

  if (buttonState == HIGH){
   
    if (has_key) {
      
      usb_hid.keyboardRelease(0);
      digitalWrite(15 , LOW);
    }
    has_key = false;
  }
 

  delay(50); 
}

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.