-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
ESP32-C3 and ESP32-S2 do not flush serial (stdout), and there's no sys.stdout.flush() mechanism we can use #9393
Comments
From here: Data Buffering
Note
This looks like the key issue: The non-blocking (default) driver and the VFS implementation will flush automatically after a newline. and suggest where we need to look to find the call we need to make to generate a hardware flush even when we're not sending a newline. I assume hardware-flush is needed, because other ESP32 devices have no such issues, so circuitpython itself might not be to blame. |
We don't use the serial to jtag converter on the S2. Only on the C3. When we do use the serial to jtag converter we do flush:
Please describe the error you are experiencing so that we have the context. |
jtag and other hardware serial do not exhibit the problem. only boards with "emulated serial" like the C3 and S2 with the usbc connected to the mcu directly. the issue is that python output (maybe also input? hard to tell) is buffered and delayed. Some folks #7716 found a workaround for this by outputting copious extra ANSI junk which has the effect of "doing nothing" visibly (I've not tested this yet myself). That makes output 1600% slower of course, since every byte needs 16 extra ones to come out to be sure it gets flushed to the terminal. |
Not sure what you mean by "Emulated Serial" - It's not emulated in any way. The C3 and C6 only have a Serial JTAG/UART peripheral. That is what is used to communicate with them when using the D+ and D- pins. The S2 and S3 also have this Serial JTAG/UART peripheral, but also have an additional native USB peripheral that is what CP leverages to show a mass storage device. Grouping the S2 and C3 together is incorrect here as by default, CP doesn't use the same peripheral on each of the boards unless an S2/S3 board definition is explicitly setup to not use the native USB peripheral and use the SerialJTAG/UART peripheral instead (which I can't understand why someone would do that) for CP. |
Notwithstanding - my QtPy and s2 mini both exhibit the same issue. TTL serial does not come out of a USB socket, so the only way to get that into an esp32 chip without hardware uart in-between is "emulated serial" - right? I don't know what you mean by "Serial JTAG/UART peripheral." - if you're talking about using a separate "usb on one side, TTL serial on the other" board (which is what the word "peripheral" means), then you're using a board which won't surface this bug. |
No, not right... the external SerialUART chip that was on original ESP32? Like the CP2104? That is built into the C3/S2/S3/C6 as a HW peripheral. It's like having an external serial UART chip in the same IC as the ESP32... it presents JUST like an external SerialUART chip would via USB connector. The C3 ONLY has this peripheral. The S2 has that and native USB. When you plug your S2 mini in, does it present a USB mass storage drive for you to work on? If so, it's using a totally different peripheral (bit of HW inside the ESP32) than what the C3 is using, and therefor a completely different code path. |
OK - I see the confusion - espressif switched from using the term "module" to "peripheral" in their documentation (to be fair - they're using both at once to mean the same thing, not a full switch) - I guess English is not the first language of many people building these things; pity they got that one so wrong eh? Peripheral literally means external. I'm a software engineer, so if it was my job to add OTG and Storage and UART to an MCU which already does wifi etc, to expose all that as a USB-connectable solution, I'd do it the same way the wifi was done - use MCU resources, i.e. to "bake in" the least amount (if even any!) any unnecessary extra hardware - so the concept of calling that a "peripheral" never even crossed my mind. There's a "bus" involved the manual mentions, so something extra does seem to have gone in somehow. Anyhow, exactly how espressif exposed this seems irrelevant: circuitpython is still not flushing the (probably internal hardware 16byte fifo) buffer of it prior to any newline in there, which appears to be the cause of the problem.
@bill88t has the same issues on their C3 as me, so it's probably the same buffer issue from the same (or very similar) on-chip component. |
A "peripheral" in microcontroller parlance is a chunk of hardware that's part of the microcontroller chip that connects the CPU core to something in the outside world. So there are GPIO peripherals, PWM peripherals, I2S peripherals, USB peripherals, etc. Often, especially for USB and other more complicated peripherals, the microcontroller manufacturer will license a hardware design created by another company, and maybe pay to have it specialized slightly. The licensed design is often know as "IP", since it's licensed intellectual property. The TinyUSB library used in CircuitPython ( The S2, S3, C3, and C6 have circuitry that is a self-contained USB-serial peripheral. That design may have been licensed from elsewhere. As mentioned, we don't normally use this peripheral in the S2 and S3 CircuitPython implementations, and it is not supported by TinyUSB. The S2 and S3 also have a full USB peripheral, which I believe Espressif licensed from someone else. Recent S2 and S3 development boards (DevKitC, etc.) have two USB connectors, exposing both peripherals. Most other boards, including ours, don't do this. @tannewt has noted that we flush the USB-serial peripheral. Maybe something about that code is in error. But that's what we should concentrate on. |
I would like to replicate this. Could you give a small specific example of the non-flushing behavior? Could be a small program or even in the REPL. |
Python source is here: https://github.com/gitcnd/cpy_shell The REPL does not seem to be affected. Maybe related - I'm not sure - seems to be way worse after having uploaded a file using the drag-drop method (to the point of needing a hardware reset sometimes; not sure if this might be some other bug? maybe file-uploads while a python program is running cause problems?) Video of the problem is here: Hard to convey intermittent when you can't see me typing, but, you can get the general idea by keeping in mind that I was not delaying when I typed, but it comes out all delayed and blocky. Might possibly be an issue with my code - except for the fact that other people have the same problems? I've never noticed any problem on other non-S2 non-C3 ESP32s running identical code. A basic test program in python would be to accept typing, and echo it back in real time. My sh.py does that, but with a lot of extra wrapping around things. It might instead be an issue on the input side? maybe not noticing a character is available, when one is? I can't explain how sometimes it needs more than a few "enter" keys to get it to do what I just typed in. |
Here's a minimal program exhibiting the issue: #test S2/C3 serial buffering probmlems.
import supervisor
import sys
nbuf=''
while True:
if supervisor.runtime.serial_bytes_available:
nbuf += sys.stdin.read(supervisor.runtime.serial_bytes_available)
if len(nbuf)>0:
sys.stdout.write(nbuf)
nbuf=""
# exec(open("kio.py").read()) |
Sorry I didn't post this before - I was planning to find and fix this myself and didn't realize others might want to help. |
I was testing now on my S2 (has problems). I just tried my C3 - it is working at this moment. |
I've reproduced something similar to what you report. I modified the test program and it gives me more information. Running on My program # test S2/C3 serial buffering problems.
import sys
import supervisor
while True:
n = supervisor.runtime.serial_bytes_available
if n:
print(n)
print(repr(sys.stdin.read(1))) On my system, the number of bytes reported available is usually 2, not 1:
I don't know if the supervisor routine |
(I am typing just one character at a time, not 2) |
also @gitcnd thank you for the very small reproducer script and instructions. |
In 9.0.x, serial_bytes_available returned a bool and tud_cdc_available was harmlessly checked twice for any characters. When the routine was changed to return an int, the double checking led to over-reporting the number of characters available. In code that would attempt to read this many bytes from sys.stdin, this made the read call block since only 1 byte was actually available. This behavior came up in the discussion of adafruit#9393. I don't mark this bug as closing that one, because that issue seems to be reporting multiple things that this change would not address, such as delays in `sys.stdout.write()` or problems seen while using webserial.
@jepler - brilliant idea to sleuth Thinking about the odd behavior of that code, it matches up nicely with the idea of a boolean instead of an int coming back.
The most striking thing I noticed, is that if I type "pwd" (which has me typing the "wd" part very fast, since the keys are beside each other and use 2 fingers) - the "d" is almost always missing. |
In case this is useful to know - the following equivalent code on the same physical S2_MINI in micropython runs fine (no buffering issues): import sys,select
nbuf = ''
while True:
if select.select([sys.stdin], [], [], 0)[0]:
nbuf += sys.stdin.read(1)
if len(nbuf) > 0:
sys.stdout.write(nbuf)
nbuf = "" MicroPython v1.24.0-preview.98.g4d16a9cce on 2024-07-05; LOLIN_S2_MINI with ESP32-S2FN4R2 |
@dhalbert sorry, "real work" got in the way - I've not tried that yet. |
This fix is also in 9.1.0-beta.4. |
sys.stdin is select()able in CircuitPython as well, I just checked. |
CircuitPython version
Code/REPL
Behavior
AttributeError: 'FileIO' object has no attribute 'flush'
Description
Both the serial (RS232) and the websocket are not properly responsive with delayed/buffered IO making it all but unusable.
Additional information
Fixing this might provide a workaround for this: #7716
The text was updated successfully, but these errors were encountered: