Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to switch the USB mode at runtime programmatically? #8277

Closed
1 task done
sblantipodi opened this issue Jun 1, 2023 · 12 comments
Closed
1 task done
Labels
Area: Peripherals API Relates to peripheral's APIs. Chip: ESP32-S3 Issue is related to support of ESP32-S3 Chip Type: Feature request Feature request for Arduino ESP32

Comments

@sblantipodi
Copy link
Contributor

sblantipodi commented Jun 1, 2023

Board

Lolin ESP32-S3 mini

Device Description

ESP32-S3

Hardware Configuration

no gpio in use

Version

latest master (checkout manually)

IDE Name

CLION

Operating System

Windows 11

Flash frequency

80MHz

PSRAM enabled

yes

Upload speed

115200

Description

esptool.js does not work with TinyUSB devices like S2 and S3,
espressif/esptool-js#97
this means that you cannot flash this devices using a web tool when they are in TinyUSB mode.

as you can imagine TinyUSB is much better than CDC since CDC creates lags on S3, it consumes more memory and it's not as stable as TinyUSB.

is there a way to flash a CDC firmware using the esptool.js and then switch to TinyUSB at runtime using some lines of code inside our firmware?

Sketch

no sketch

Debug Message

no debug

Other Steps to Reproduce

No response

I have checked existing issues, online documentation and the Troubleshooting Guide

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.
@sblantipodi sblantipodi added the Status: Awaiting triage Issue is waiting for triage label Jun 1, 2023
@me-no-dev
Copy link
Member

you could manually boot into download mode, which will expose the CDC/JTAG interface and allow you to flash.

@sblantipodi
Copy link
Contributor Author

@me-no-dev this is not really a solution...
most open source projects are using web installers like esp-web-tools

to let users flash the firmware on their devices and then configure the wifi connection on the same webpage before accessing the web interface of the firmware itself.

if you set the device in CDC mode and then you flash a TinyUSB firmware, you can't setup the wifi connection as described before because the improv-wifi does not work since it tries to Serial.print() using the TinyUSB method on a CDC connection.

@me-no-dev
Copy link
Member

seems that you mention a few different problems. Getting TinyUSB's CDC to reboot into download, when attempted by esptool.js is not an issue. I made a couple small modifications and got it to work. The problems come afterwards:

  • The USB post changes, because now the CDC/JTAG controller is active
  • esptool.js needs to know that it needs to connect to that new port, but that is not implemented
  • In this mode (TinyUSB -> CDC/JTAG) download mode can not automatically reboot into the new firmware, so reset must be pressed
  • If you want to talk to the new firmware, you again need to connect to the new port

@sblantipodi
Copy link
Contributor Author

sblantipodi commented Jun 1, 2023

@me-no-dev thanks for the answer.

the idea is to

  • flash a CDC firmware with the esp-web-tools + improv-wifi,
  • let the user configure the wifi using improv-wifi,
  • once the wifi is set,
  • reboot the device in Tiny USB mode programmatically since we don't need the web tools features anymore.

it would be cool to have a function like the ESP.restart() that works like
ESP.restartInCdcMode()
or
ESP.restartInTinyUsbMode()

is it possible to do this? this could be really useful since the only method that works with web tools is CDC.

@sblantipodi sblantipodi changed the title Is it possible to switch the USB mode at runtime? Is it possible to switch the USB mode at runtime programmatically? Jun 1, 2023
@me-no-dev
Copy link
Member

Switching USB controllers is not all that straight-forward. We can and should look into this in the future, but as of now is not possible.

@sblantipodi
Copy link
Contributor Author

if you agree please leave this issue open as a reminder, this feature would be really really useful for a lot of use cases in addition to the one I explained before.

@me-no-dev me-no-dev added Type: Feature request Feature request for Arduino ESP32 Chip: ESP32-S3 Issue is related to support of ESP32-S3 Chip Area: Peripherals API Relates to peripheral's APIs. and removed Status: Awaiting triage Issue is waiting for triage labels Jun 1, 2023
@me-no-dev me-no-dev added this to the 3.0.0 milestone Jun 1, 2023
@me-no-dev
Copy link
Member

done :)

@sblantipodi
Copy link
Contributor Author

thank you very much, I appreciate it. :)

@0xBERNDOG
Copy link

0xBERNDOG commented Jan 17, 2024

Related to this, is it possible to change the device type programmatically before initialising? I would like to select either CDC or MSC (depending on a user input at boot), and it's okay to reboot in order to change between them

@VojtechBartoska VojtechBartoska modified the milestones: 3.0.0, 3.1.0 Feb 20, 2024
@VojtechBartoska
Copy link
Contributor

Postponing to 3.1.0 milestone.

@RobMel98
Copy link

RobMel98 commented Jan 4, 2025

Hello, has this topic been worked on?
I also have a situation, where the USB mode should be switchable programmatically, but in my case I need only at the beginning.

I want to create a Custom USB HID Device with an update feature: When the user presses a button while the setup() function is running, the USB mode should switch to MSC Update, so a user can upload a new binary firmware file to ESP via the explorer.

I've tried that and the windows explorer also openes and shows that "MSC drive" when I press the button.
But unfourtunatly when the USBHID.h file is included, this drive is write protected, so I cannot upload any binary firmware file to the ESP.
Only when I remove the include of the USBHID.h file the MSC drive in the explorer is not write protected anymore and also the upload of a binary firmware file is possible.

Here is my code:

#ifndef ARDUINO_USB_MODE
#error This ESP32 SoC has no Native USB interface
#elif ARDUINO_USB_MODE == 1
#warning This sketch should be used when USB is in OTG mode
void setup() {}
void loop() {}
#else
#include "USB.h"
#include "USBHID.h"
#include "FirmwareMSC.h"

USBHID HID;

static const uint8_t report_descriptor[] =
{
  // 8 axis
  0x05, 0x01,  // Usage Page (Generic Desktop Ctrls)
  0x09, 0x04,  // Usage (Joystick)
  0xa1, 0x01,  // Collection (Application)
  0xa1, 0x00,  //   Collection (Physical)
  0x05, 0x01,  //   Usage Page (Generic Desktop Ctrls)
  0x09, 0x30,  //     Usage (X)
  0x09, 0x31,  //     Usage (Y)
  0x09, 0x32,  //     Usage (Z)
  0x09, 0x33,  //     Usage (Rx)
  0x09, 0x34,  //     Usage (Ry)
  0x09, 0x35,  //     Usage (Rz)
  0x09, 0x36,  //     Usage (Slider)
  0x09, 0x36,  //     Usage (Slider)
  0x15, 0x81,  //     Logical Minimum (-127)
  0x25, 0x7f,  //     Logical Maximum (127)
  0x75, 0x08,  //     Report Size (8)
  0x95, 0x08,  //     Report Count (8)
  0x81, 0x02,  //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
  0xC0,        //   End Collection
  0xC0,        // End Collection
};

class CustomHIDDevice : public USBHIDDevice
{
public:
  CustomHIDDevice(void)
  {
    static bool initialized = false;
    if (!initialized)
    {
      initialized = true;
      HID.addDevice(this, sizeof(report_descriptor));
    }
  }

  void begin(void)
  {
    HID.begin();
  }

  uint16_t _onGetDescriptor(uint8_t *buffer)
  {
    memcpy(buffer, report_descriptor, sizeof(report_descriptor));
    return sizeof(report_descriptor);
  }

  void _onOutput(uint8_t report_id, const uint8_t *buffer, uint16_t len)
  {
    // the pc sends data to the device
  }

  bool send(uint8_t *value)
  {
    return HID.SendReport(0, value, 8);
  }
};


static void usbEventCallback(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
{
  if (event_base == ARDUINO_USB_EVENTS)
  {
    arduino_usb_event_data_t *data = (arduino_usb_event_data_t *)event_data;
    switch (event_id)
    {
      case ARDUINO_USB_STARTED_EVENT: 
      {
        Serial.println("USB PLUGGED");
        break;
      }
      case ARDUINO_USB_STOPPED_EVENT:
      {
        Serial.println("USB UNPLUGGED");
        break;
      }
      case ARDUINO_USB_SUSPEND_EVENT:
      {
        Serial.printf("USB SUSPENDED: remote_wakeup_en: %u\n", data->suspend.remote_wakeup_en);
        break;
      }
      case ARDUINO_USB_RESUME_EVENT:
      {
        Serial.println("USB RESUMED");
        break;
      }

      default:
      {
        break;
      }
    }
  }
  else if (event_base == ARDUINO_FIRMWARE_MSC_EVENTS)
  {
    arduino_firmware_msc_event_data_t *data = (arduino_firmware_msc_event_data_t *)event_data;    
    switch (event_id)
    {
      case ARDUINO_FIRMWARE_MSC_START_EVENT:
      {
        Serial.println("MSC Update Start");
        break;
      }
      case ARDUINO_FIRMWARE_MSC_WRITE_EVENT:
      {
        //Serial.printf("MSC Update Write %u bytes at offset %u\n", data->write.size, data->write.offset);
        Serial.print(".");
        break;
      }
      case ARDUINO_FIRMWARE_MSC_END_EVENT:
      {
        Serial.printf("\nMSC Update End: %u bytes\n", data->end.size);
        break;
      }
      case ARDUINO_FIRMWARE_MSC_ERROR_EVENT:
      {
        Serial.printf("MSC Update ERROR! Progress: %u bytes\n", data->error.size);
        break;
      }
      case ARDUINO_FIRMWARE_MSC_POWER_EVENT:
      {
        Serial.printf("MSC Update Power: power: %u, start: %u, eject: %u", data->power.power_condition, data->power.start, data->power.load_eject);
        break;
      }

      default:
      {
        break;
      }
    }
  }
}


CustomHIDDevice Device;

const int buttonPin = 0;
int previousButtonState = HIGH;
uint8_t axis[8];

void setup()
{
  Serial.begin(115200);
  //Serial.setDebugOutput(true);
  pinMode(buttonPin, INPUT_PULLUP);

  USB.onEvent(usbEventCallback);

  uint32_t millis_start = millis();
  while (millis() - millis_start <= 3000)
  {
    if (digitalRead(buttonPin) == LOW)
    {
      FirmwareMSC MSC_Update;
      MSC_Update.onEvent(usbEventCallback);
      MSC_Update.begin();
      USB.begin();

      while (true)
      {
        // loop, run until device is rebooted (manually or after msc firmware update is done)
      }
    }
  }
  
  Device.begin();
  USB.begin();
}

void loop()
{
  int buttonState = digitalRead(buttonPin);
  if (HID.ready() && buttonState != previousButtonState)
  {
    previousButtonState = buttonState;
    if (buttonState == LOW)
    {
      Serial.println("Button Pressed");
      axis[0] = random() & 0xFF;
      Device.send(axis);
    }
    else
    {
      Serial.println("Button Released");
    }
    delay(100);
  }
}
#endif /* ARDUINO_USB_MODE */

Is that what I want to do currently possible and if so, what am I doing wrong?

@rftafas rftafas modified the milestones: 3.1.0, 3.1.1 Jan 6, 2025
@rftafas rftafas removed this from the 3.1.1 milestone Jan 14, 2025
@Parsaabasi
Copy link

Hello,

Due to the overwhelming volume of issues currently being addressed, we have decided to close the previously received tickets. If you still require assistance or if the issue persists, please don't hesitate to reopen the ticket.

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Peripherals API Relates to peripheral's APIs. Chip: ESP32-S3 Issue is related to support of ESP32-S3 Chip Type: Feature request Feature request for Arduino ESP32
Projects
Development

No branches or pull requests

7 participants