Page 1 of 1

MicroPythonic way to non-blocking read a character from the serial USB port

Posted: Fri Nov 29, 2019 12:24 pm
by hsmptg
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.

Re: MicroPythonic way to non-blocking read a character from the serial USB port

Posted: Fri Nov 29, 2019 1:51 pm
by Roberthh
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.

Code: Select all

import uselect
list = uselect.select([sys.stdin], [], [], 0.01)
if list[0]:
    byte = sys.stdin.read(1)
else:
    byte = None
uart.read() is non-blocking. In order to use it with UART 0, you have to detach REPL from the UART.

Re: MicroPythonic way to non-blocking read a character from the serial USB port

Posted: Fri Nov 29, 2019 6:54 pm
by hsmptg
Roberthh wrote:
Fri Nov 29, 2019 1:51 pm
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.

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
Thank you for your reply!
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)
Any suggestion?
Roberthh wrote:
Fri Nov 29, 2019 1:51 pm
uart.read() is non-blocking. In order to use it with UART 0, you have to detach REPL from the UART.
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

Posted: Fri Nov 29, 2019 7:16 pm
by Roberthh
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.

Re: MicroPythonic way to non-blocking read a character from the serial USB port

Posted: Fri Nov 29, 2019 9:07 pm
by hsmptg
Roberthh wrote:
Fri Nov 29, 2019 7:16 pm
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.
Well in fact I was using the default version 1.11 (esp32-idf3-20190529-v1.11.bin).
With the esp32-idf3-20191129-v1.11-611-g7f24c2977.bin it worked!!! :D
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

Posted: Fri Nov 29, 2019 9:19 pm
by Roberthh
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.

uselect.select

Posted: Sat Nov 30, 2019 7:02 am
by pythoncoder
Note that in the docs uselect.select() is deprecated in favour of the Poll mecahnism on grounds of efficiency.

Re: MicroPythonic way to non-blocking read a character from the serial USB port

Posted: Tue Dec 31, 2019 4:09 am
by jdts
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

Posted: Tue Mar 16, 2021 12:30 pm
by stanely
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

Code: Select all

spoll.unregister(sys.stdin)
and

Code: Select all

sys.stdin.close()
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...

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)

Re: MicroPythonic way to non-blocking read a character from the serial USB port

Posted: Fri Aug 19, 2022 3:51 pm
by Jibun no kage
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?