-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
[ESP8266] No non-blocking read on uart0 if dupterm is active #4849
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
Comments
Thanks for the report of the issue. Indeed the esp8266 handles this case of UART attached to REPL differently to other ports, in order to be efficient with RAM usage. The stdin ring buffer is "large" (eg so pasting code to the REPL works) and used for both UART REPL and WebREPL, so that they don't both need separate large input buffers.
Yes this is a good workaround and I don't think that inconvenient, eg use this helper function: def uart_read(n):
uart = uos.dupterm(None, 1)
data = uart.read(n)
uos.dupterm(uart, 1)
return data
Adding a check (poll) for data using the |
I don't think your workaround mimics the behavior of the UART.read() function in the older versions. With your method, any data that is received, while not inside this function would be lost because it is still forwarded to stdin. But yes I also think, adding support to use select on stdin would be the best solution to fix this problem. |
Yes, that is true. I guess the point is you want to use the UART for the REPL (output printing) and for reading in at the same time. You can do this by making a custom TX-only UART, eg: import io
class UARTHalf(io.IOBase):
def __init__(self, uart):
self.uart = uart
def write(self, data):
return self.uart.write(data)
def test():
uart = os.dupterm(None, 1)
uart_half = UARTHalf(uart)
os.dupterm(uart_half, 1)
print('testing')
while True:
data = uart.read(10)
if data:
print('got', data)
if data == b'q':
break
os.dupterm(uart, 1)
test() Type text at the prompt and press "q" to finish.
The REPL input is always active. Eg you want to be able to stop a program using ctrl-C while it's running, and that requires processing stdin during program execution. That is what makes it tricky. |
See #4859 for a proposal to do that (and example code to test it on esp8266). Note that this is not really the best solution because you can only ever read 1 char if you want to guarantee non-blocking behaviour (because select/poll can only tell if 1 or more chars is available, not the number of chars/bytes). As such, in that PR I also made (On CPython, |
That depends on the tty mode setting, e.g. in linux et. al.. If tty is set to raw mode, it does not wait for an entire line. |
I guess you'd have to reconfigure it as part of the script, and then restore configuration before the script finishes to make sure the REPL still works. In the end the question is whether to make sys.stdin.buffer non-blocking, or somehow make it configurable by the user, or leave it blocking and use something like |
That's what I do in the Linux variant of the pye editor. |
I see, thanks for the pointer. It looks like you use But note that Of course it would be good to keep things as similar to CPython as possible, but in the case of file IO and tty's it's hard. I don't think MicroPython will ever go the way of providing actual file descriptors/integers so things will always be different when it comes to these things. |
Thanks for the PR @dpgeorge, this implements exactly the functionality I was missing. Also your proposed workaround seems to be a good solution until the non-blocking buffer read gets upstream. |
It's not certain that non-blocking capability will be merged, at least not in the form given in that PR. But polling should be there and that already solves the problem of this issue. |
Polling of sys.stdin was added in c80614d |
As far as I can see, there is no non-blocking way to read the input from the uart0, if this uart is also used for REPL through dupterm.
In version 1.9 and before it is possible to use the UART.read() function for this, however since commit afd0701 the uart buffer is bypassed when UART 0 is used for REPL, so that UART.read() will never receive any data.
The input data is now directly put into the stdin buffer, which has (as far as I know) no function to check if any data is available.
The only workaround for this would be to disable dupterm for the uart0 while intending to read data. Yet, this is somewhat inconvenient, because then print() also won't output any data on the uart.
I think, the best option is to add a non-blocking read for stdin (or at least a way to check, if data is available for read).
This is not the same as a non-blocking read on the uart, but for most applications, this should be sufficient.
The text was updated successfully, but these errors were encountered: