8000 ESP32-S3 forcing use of USB OTG · Issue #14217 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

ESP32-S3 forcing use of USB OTG #14217

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

Closed
2 tasks done
kdschlosser opened this issue Mar 30, 2024 · 28 comments
Closed
2 tasks done

ESP32-S3 forcing use of USB OTG #14217

kdschlosser opened this issue Mar 30, 2024 · 28 comments

Comments

@kdschlosser
Copy link

Checks

  • I agree to follow the MicroPython Code of Conduct to ensure a safe and respectful space for everyone.

  • I've searched for existing issues matching this bug, and didn't find any.

Port, board and/or hardware

ESP32-S3

MicroPython version

MicroPython: 1.22.2
board: ESP32_GENERIC_S3
board_variant: SPIRAM_OCT

Reproduction

compile for the ESP32-S3 and try and use the pins for I2C. Using them results in a timeout or if doing a scan it takes the scan more than a minute to complete and returns an empty list.

Expected behaviour

for I2C to work using pins 19 and 20

Observed behaviour

I2C attached to pins 19 and 20 does not work I believe this issue is caused by the forced use of USB OTG. I have tried to turn it off by setting CONFIG_USB_OTG_SUPPORTED=n in the ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board file and also removing boards/sdkconfig.usb from the ports/esp32/boards/ESP32_GENERIC_S3/mpconfigboard.cmake file. For whatever reason CONFIG_USB_OTG_SUPPORTED still gets set to y when being compiled.

Normally this wouldn't be an issue but because of this code in MicroPython

void mp_task(void *pvParameter) {
    volatile uint32_t sp = (uint32_t)esp_cpu_get_sp();
    #if MICROPY_PY_THREAD
    mp_thread_init(pxTaskGetStackStart(NULL), MICROPY_TASK_STACK_SIZE / sizeof(uintptr_t));
    #endif
    #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
    usb_serial_jtag_init();
    #elif CONFIG_USB_OTG_SUPPORTED
    usb_init();
    #endif
    #if MICROPY_HW_ENABLE_UART_REPL
    uart_stdout_init();
    #endif
    machine_init();

Pin 19 and 20 get forced into use for OTG instead of being something that user is able to selectively turn on. I believe that the ESP-IDF is overriding the setting CONFIG_USB_OTG_SUPPORTED=n which is why I am not able to turn it off and because MicroPython is coded in a manner that forces those pins into being used for USB OTG at runtime results in my inability to use those pins for anything else.

I believe that this kind of a thing should be able to be turned on or off at the users discretion and it should be able to be done at runtime and not compile time. The CONFIG_USB_OTG_SUPPORTED is simply a setting that should be used to have code compiled and not be used to determine if it should run code. That macro as it implies if whether or not a board supports OTG not that it should be used.

Additional Information

I have not managed to grasp why the OTG is having repl data sent to it.

@dpgeorge
Copy link
Member

What hardware are you using? I'm guessing that you have a board that does not have a USB connector on GPIO19/20, and instead uses a UART REPL.

For whatever reason CONFIG_USB_OTG_SUPPORTED still gets set to y when being compiled.

It looks like this config setting is always enabled if the MCU supports USB, which is ESP32-S2 and ESP32-S3. You cannot turn this option off.

So MicroPython should probably not use this option to control whether usb_init() is called. Instead maybe it should use CONFIG_TINYUSB_CDC_ENABLED.

@kdschlosser
Copy link
Author

If the only purpose for using the USB feature is to output repl data, which is what it looks like then this being set is what should e checked.

CONFIG_ESP_CONSOLE_USB_CDC

I also put into question the complexity of the code being used because if the user is wanting the repl data output to the USB then why not use esp_tusb_init_console which redirects the output from the uart to the USB instead if having this code.

#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
    usb_serial_jtag_tx_strn(str, len);
    did_write = true;
    #elif CONFIG_USB_OTG_SUPPORTED
    usb_tx_strn(str, len);
    did_write = true;
    #endif
    #if MICROPY_HW_ENABLE_UART_REPL
    uart_stdout_tx_strn(str, len);
    did_write = true;
    #endif

Which kind of makes no sense because of this...

#ifndef MICROPY_HW_ENABLE_UART_REPL
#define MICROPY_HW_ENABLE_UART_REPL (!CONFIG_USB_OTG_SUPPORTED && !CONFIG_ESP_CONSOLE_USB_CDC && !CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG)
#endif

In reality why would someone want to occupy 4 pins that have the purpose of doing the same thing essentially?

The USB OTG should be exposed to the user in MicroPython if it is available and it is not being used for repl output. There are quite a few things it can be used for. Things like connecting a mouse, keyboard or joystick are a few examples. Just because a board has a second USB port doesn't mean that the USB should be locked to only being able to be used for the purposes of sending repl data.

@kdschlosser

This comment was marked as outdated.

@kdschlosser
Copy link
Author

The board I am working with is a Sunton display board and no it doesn't have a second USB port.

The waveshare board has a way of turning on and off the use of the second USB PORT by using an IO expander.

@vshymanskyy

This comment was marked as outdated.

@kdschlosser

This comment was marked as outdated.

@projectgus
Copy link
Contributor

I looked at this briefly as part of #15727 and agree it could use some cleanup.

If I understand correctly, there's two related things here:

Runtime USB pins

It should be possible to reconfigure a USB pin for another purpose at runtime, at least if USB isn't currently enumerated.

It's already possible to switch the UART0 pins to other functions, and the same should be possible for the USB pins as well. I think that would resolve the original issue report, while allowing us to keep shipping a single ESP32-S3 binary that works in as many places as possible.

Compile time USB enable/disable

This is almost possible at the moment, but as discussed above it's fiddly. PR #15727 is a move towards making it easier as it defines two MicroPython board-level build macros to independently enable/disable native USB and USB Serial/JTAG. An additional fix is still needed to fully disable USB on IOs 19 and 20 though (I think unless USB has been permanently disabled on the chip via efuse, in that case it might work already.)

The ESP-IDF internally has the capability to redirect the stdout to the CDC. There is no need to have micropython specifically writing the REPL to the CDC. It should continue to write it to the UART and it will redirect if the config setting has been set to do it.

Historically (and currently) MicroPython esp32 port doesn't really use ESP-IDF stdio, except for (I think) ESP-IDF logging. The Python stdin/stdout functions all work directly with the various peripherals, as noted above. Maybe we could change this, although the current approach may actually make implementing these fixes easier (especially runtime reconfiguration). It'll probably be the only way after #15108 lands and MicroPython is no longer using ESP-IDF's TinyUSB.

@kdschlosser

This comment was marked as outdated.

@projectgus
Copy link
Contributor
projectgus commented Aug 27, 2024

Question about the efuses. If the USB has not been disabled via the efuse there is still a choice whether or not to use the USB correct?

Never again on that chip. Burning the USB disable efuse would mean the two USB pins on that chip are permanently regular GPIO and can't be switched back to USB functions. I also haven't actually confirmed this allows those pins to work as normal IO in MicroPython, I just believe it would. It's absolutely not the proper fix for this issue.

@kdschlosser

This comment was marked as outdated.

@vshymanskyy
Copy link
Contributor
vshymanskyy commented Sep 26, 2024

I can confirm that after #15727 was merged, I am able to disable MICROPY_HW_USB_CDC in mpconfigboard.h of my board:

// Disable USB CDC as the same pins are used by I2C0
#define MICROPY_HW_USB_CDC                  (0)

#define MICROPY_HW_I2C0_SCL                 (19)
#define MICROPY_HW_I2C0_SDA                 (20)

After that, I can use pins 19 and 20 for I2C as usual:

>>> board.i2c().scan()
[72, 80, 81]

@kdschlosser

This comment was marked as off-topic.

@vshymanskyy

This comment was marked as off-topic.

@kdschlosser

This comment was marked as off-topic.

@andrewleech
Copy link
Contributor

on the majority of the boards USB-CDC is NOT being used. Most boards still use the UART.

On every S2 and S3 board I own, only USB CDC is wired up, UART pins are not connected to anything.
Even if I had a board that wired both I would never use the UART port, I consider it vastly inferior to the native USB in both speed and reliability. So I personally would prefer the native USB port to be enabled by default and UART opt-in only, rather than waste pins on it be default.

Keep in mind most of this code is community contributed and under active development / change. I for one have been working on improving the consistency of repl / stdio interfaces across all the ports recently with the consolidation of stdio handling - this has all been done in my own personal time around my young family needs. This is what I do when there's features that don't work the way I want them to, I raise PR's.

Regardless, the long term fix here is to use the dupterm interface/module for both CDC and UART repl, it's already designed to support dynamic attach/detach of streams to stdio so then users can decide at runtime.

This is how it works currently on the stm port and I'm in progress making it possible on all others that use TinyUSB etc.

@kdschlosser

This comment was marked as off-topic.

@kdschlosser

This comment was marked as off-topic.

@vshymanskyy
Copy link
Contributor
vshymanskyy commented Sep 27, 2024

on the majority of the boards USB-CDC is NOT being used. Most boards still use the UART. Having MicroPython shanghai 2 pins by default when the majority do not have those 2 pins connected to USB is an insane concept to me. Have a look at the ESP32-S3 devkits. They have BOTH the UART and the CDC connected. So you are going to make users plug into one port to flash the firmware and then have to unplug the device when done and plug into the second USB port in order to get the REPL to show..

I'm not sure what your comment is based on, but I think there are some inaccuracies. The REPL is available on both UART and CDC.

Additionally, it might be worth considering:
a) Creating your own board definition if the default doesn’t suit your use case—this is fairly straightforward.
b) Contributing your suggestions through a Pull Request. It could help improve things for everyone.

@kdschlosser

This comment was marked as off-topic.

@kdschlosser

This comment was marked as off-topic.

@vshymanskyy

This comment was marked as off-topic.

@kdschlosser

This comment was marked as off-topic.

@andrewleech
Copy link
Contributor
andrewleech commented Sep 27, 2024

@kdschlosser when I read your messages they sound very heated to me, even if that's not your intention. I admit I can get frustrated by some of this too.
I was presenting my experiences with many esp boards because your expectations of how it works actually sounded to me like your very singular experience.

Yes the Espressif Dev kit exposes both, as does the waveshare one - devkits often try to expose everything to let users decide what to use. The majority of other smaller esp32 boards I see on the market only expose USB, not UART.

I always use esptool to flash my S3 boards via the native USB port. There were bugs in the silicon and some other platforms that have hurt the reliability of this in the early days, but that was never the intention of Espressif and PR's I've submitted both here and to esptool have resolved some of these issues.

Regardless it's absolutely now a moot point because as @vshymanskyy said the port repl configuration can be enabled / disabled in board profile. The Dev kids with both ports wired should expose repl on both to avoid confusion, boards that only expose UART or USB can configure just that port. Unused port pins can be user configured for other goals.
If a user with a dual port board does not want repl on one of those ports they can use a different board profile or compile their own - if the repl was missing on either port by default that would be confusing for many users.

The ability to change these settings at runtime is well known to be desirable, I spoke to this in my last message, specifically that I am actually working on this already in my limited personal time! If that's too slow for you please feel free to help rather than repeat complaints that it's not already there. These things take effort and time.

Keep in mind that runtime configurability comes at a cost in both flash and ram use. Sure, many esp boards come with "lots" of external flash and ram but not all, and certainly not so boards on other ports (what I'm working on is intended to do it properly for all ports, I don't just think about esp32).
These sort of features usually come with trade offs which is why it takes even longer to build them, in an attempt to minimise negative side effects

@projectgus
Copy link
Contributor
projectgus commented Oct 1, 2024

Well said, @andrewleech. As far as I'm concerned most of this issue discussion is generating far more heat than light. Time spent here is time not spent improving any code.

Going back to the assessment I wrote in this comment then can re-summarise the status quo as follows:

Compile time USB enable/disable

This is now possible on ESP32-C3, thanks @vshymanskyy for confirming and describing how: #14217 (comment) 🎉 🎉

I'm not actually sure the same strategy works on ESP32-S*, I think it does now but it'd be good to double check. If not now then I suspect will work after @andrewleech's PR #15108 is merged, which cleans up a bunch of the esp32 port USB implementation.

Runtime USB pins

What I said in the comment linked above hasn't changed. It should be possible to reconfigure a USB pin at runtime and have it become a normal pin (maybe with a check that it's not currently being used as an enumerated USB device). However this is not currently possible.

This needs someone to do the work, submit the PR, and would resolve the remaining part of this issue.

There should be almost no behavioural difference between compile-time and runtime disabling of native USB (click for details).

  1. When any ESP32-S* chips boots from ROM then it will enable the USB port, which - without a USB host present - means USB D- pin is Hi-Z but USB D+ pin has a 1.5k pullup to 3.3V.
  2. When MicroPython starts running and USB was disabled at compile time then during MicroPython startup USB D+ switches back to Hi-Z and both USB pins are then available for normal IO use.
  3. If we enable runtime disabling then the event described in (2) is deferred until MicroPython code does something with the pin, but the behaviour will be otherwise identical. i.e. The external impact is limited to "USB D+ pin has a pullup for a time and may get confused if it sees a particular sequence of input transitions", the only thing which changes is the time period this applies for.

Preventing the situation described in (1) and disabling the USB port at all times requires burning an efuse to permanently disable USB on that chip. There's nothing MicroPython can do to change this as it's ROM bootloader behaviour.

Summary

  • Profiles for boards which have no native USB port should be able to disable native USB at compile time.
  • The generic boards will always have USB enabled so they work in the most number of places.
  • Once runtime disabling is implemented then the USB pins should be reusable on these boards, as well.

EDIT: Fixed the assessment of compile time disabling, I missed this was on C3 only so far. I thought it was linking Andrew's big tinyusb PR.

@vshymanskyy
Copy link
Contributor

After #15108 got merged, my board stopped building.
See #16035

@trikhongngu
Copy link

Any solutions for this problem? I am working on ESP32-S3 and I want to use GPIO19 20 for GPIO purpose. But I can't turn off the USB function

@andrewleech
Copy link
Contributor

Any solutions for this problem? I am working on ESP32-S3 and I want to use GPIO19 20 for GPIO purpose. But I can't turn off the USB function

As described above in #14217 (comment) / #14217 (comment)
You should be able to disable MICROPY_HW_USB_CDC in mpconfigboard.h of your board profile and recompile the firmware:

// Disable USB CDC to allow using the pins as regular gpio
#define MICROPY_HW_USB_CDC

@Ombenavides
Copy link
Ombenavides commented May 22, 2025

Hello everyone,

I designed a PCB and used ESP32C3 MIN 1U as a main MCU. I connected pins 18 and 19 as general GPIO without knowing this problem and after days of trying and trying I still cannot solve the issue. I have tried many times by changing mpconfigboard.h and recompiling the firmware like 20 times and still I get the invalid pin error.

MY mpconfigboard.h is like this:

#define MICROPY_HW_BOARD_NAME               "ESP32C3 module"
#define MICROPY_HW_MCU_NAME                 "ESP32C3"

// Enable UART REPL for modules that have an external USB-UART and don't use native USB.
#define MICROPY_HW_ENABLE_UART_REPL         (1)
#define MICROPY_HW_USB_CDC                  (0) 

As you can see I disabled the USB_CDC and also enabled the UART because I am using a CP2102 as USB 'UART converter.
I also tried to modify the sdkconfig without any good output.

If anyone knows how to solve it properly, I would be really happy.

Thank you :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants
0