MicroPythonic way to non-blocking read a character from the serial USB port
MicroPythonic way to non-blocking read a character from the serial USB port
I would like to read the characters received through the serial USB connection of an ESP32-Pico-Kit board inside my main.py. I already used the sys.stdin.read(1) to read a single character but it halts the program execution while the user do not send that character. It is possible to do something similar but in an non-blocking way? Maybe MicroPython has a way to know if the character is available before I can call the read().
I already found several posts in this forum about similar issues but never saw a definite and working solution. Since some of those posts are quite old maybe the current version of MicroPython already has a solution.
Note that I am a little biased with the way these kind of things are done in C, where we have no REPL and the use of the serial port connected to the USB is more straightforward.
Thanks for any help you can give me.
I already found several posts in this forum about similar issues but never saw a definite and working solution. Since some of those posts are quite old maybe the current version of MicroPython already has a solution.
Note that I am a little biased with the way these kind of things are done in C, where we have no REPL and the use of the serial port connected to the USB is more straightforward.
Thanks for any help you can give me.
Re: MicroPythonic way to non-blocking read a character from the serial USB port
There is still a simple function missing, which tells, whether and how many characters are available at sys.stdin. You can however use uselect.select() with a short timeout, e.g.
uart.read() is non-blocking. In order to use it with UART 0, you have to detach REPL from the UART.
Code: Select all
import uselect
list = uselect.select([sys.stdin], [], [], 0.01)
if list[0]:
byte = sys.stdin.read(1)
else:
byte = None
Re: MicroPythonic way to non-blocking read a character from the serial USB port
Thank you for your reply!Roberthh wrote: ↑Fri Nov 29, 2019 1:51 pmThere is still a simple function missing, which tells, whether and how many characters are available at sys.stdin. You can however use uselect.select() with a short timeout, e.g.
Code: Select all
import uselect import sys list = uselect.select([sys.stdin], [], [], 0.01) if list[0]: byte = sys.stdin.read(1) else: byte = None
I tried your code but when I ran it I got a "OSError: stream operation not supported" in the line:
Code: Select all
list = uselect.select([sys.stdin], [], [], 0.01)
Can you explain or give me a link where that "detach" of UART0 is explained?
Many thanks once again!
Re: MicroPythonic way to non-blocking read a character from the serial USB port
Before posting, I tried that code on an ESP32 board with the actual firmware. You may have to update the firmware. I am using:
MicroPython v1.11-611-g7f24c2977-dirty on 2019-11-29; ESP32 module (spiram) with ESP32
Detaching UART 0 does not seem to work like it is for the ESP8266.
MicroPython v1.11-611-g7f24c2977-dirty on 2019-11-29; ESP32 module (spiram) with ESP32
Detaching UART 0 does not seem to work like it is for the ESP8266.
Re: MicroPythonic way to non-blocking read a character from the serial USB port
Well in fact I was using the default version 1.11 (esp32-idf3-20190529-v1.11.bin).Roberthh wrote: ↑Fri Nov 29, 2019 7:16 pmBefore posting, I tried that code on an ESP32 board with the actual firmware. You may have to update the firmware. I am using:
MicroPython v1.11-611-g7f24c2977-dirty on 2019-11-29; ESP32 module (spiram) with ESP32
Detaching UART 0 does not seem to work like it is for the ESP8266.
With the esp32-idf3-20191129-v1.11-611-g7f24c2977.bin it worked!!!
I used the GENERIC bin (not the GENERIC-SPIRAM) because my ESP32 Pico Kit board does not have SPI RAM.
Now I don't have the undesirable blocking behaviour but still have to wait during the tiny specified timeout.
But I presume this solution is the best we can get for now, right?
I wonder how this is only achievable with this last build. No one had this necessity before?!?
Thanks @Roberthh for your help!
Re: MicroPythonic way to non-blocking read a character from the serial USB port
The select feature on sys.stdin is rather new. I do not exactly remember when it was introduced, but surely with the switch to treat the console I/O as a stream device. You can try how short you can set the timeout. The value I used is just an example.
Edit: I tested it and you can set it to 0.
Edit: I tested it and you can set it to 0.
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
uselect.select
Note that in the docs uselect.select() is deprecated in favour of the Poll mecahnism on grounds of efficiency.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: MicroPythonic way to non-blocking read a character from the serial USB port
So:
Code: Select all
import sys,uselect
spoll=uselect.poll()
spoll.register(sys.stdin,uselect.POLLIN)
def read1():
return(sys.stdin.read(1) if spoll.poll(0) else None)
Re: MicroPythonic way to non-blocking read a character from the serial USB port
Thanks for this code. It works well but has an unusual feature I don't understand. When my code exits, the stdin port is left in a raw REPL state, instead of returning to normal REPL. Is that how it's supposed to work?
I tried different things including and to no avail. On exit, REPL is connected in raw mode and I have to ^B from the keyboard to exit. Is there a way to clean things up in my code and end raw REPL mode when my code exits?
Here's the simple test program...
I tried different things including
Code: Select all
spoll.unregister(sys.stdin)
Code: Select all
sys.stdin.close()
Here's the simple test program...
Code: Select all
#test polling
#test polling
import sys
import uselect
# Function to read a character from USB serial or return None.
def kbhit():
spoll=uselect.poll() # Set up an input polling object.
spoll.register(sys.stdin,uselect.POLLIN) # Register polling object.
kbch = sys.stdin.read(1) if spoll.poll(0) else None
spoll.unregister(sys.stdin)
return(kbch)
# MAIN ---------------------------------
print('Input characters: ', end='')
while True:
new_ch = kbhit()
if new_ch == None:
continue
elif new_ch == 'q':
break
else:
print(new_ch, end='', sep='')
sys.stdin.close()
print(sys.version)
sys.exit(0)
-
- Posts: 144
- Joined: Mon Jul 25, 2022 9:45 pm
Re: MicroPythonic way to non-blocking read a character from the serial USB port
Odd, the above code, I can't seem to get to work. It is blocking. Only then a input is received, is the loop cycled. I thought the purpose of the code was to be a non-blocking UART input example, no?