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

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
User avatar
hsmptg
Posts: 3
Joined: Tue Nov 26, 2019 5:12 pm

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

Post by hsmptg » Fri Nov 29, 2019 12:24 pm

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.

User avatar
Roberthh
Posts: 1890
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

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

Post by Roberthh » 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
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.

User avatar
hsmptg
Posts: 3
Joined: Tue Nov 26, 2019 5:12 pm

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

Post by hsmptg » Fri Nov 29, 2019 6:54 pm

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!

User avatar
Roberthh
Posts: 1890
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

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

Post by Roberthh » 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.

User avatar
hsmptg
Posts: 3
Joined: Tue Nov 26, 2019 5:12 pm

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

Post by hsmptg » Fri Nov 29, 2019 9:07 pm

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!

User avatar
Roberthh
Posts: 1890
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

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

Post by Roberthh » Fri Nov 29, 2019 9:19 pm

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.

User avatar
pythoncoder
Posts: 4035
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

uselect.select

Post by pythoncoder » Sat Nov 30, 2019 7:02 am

Note that in the docs uselect.select() is deprecated in favour of the Poll mecahnism on grounds of efficiency.
Peter Hinch

jdts
Posts: 15
Joined: Mon Dec 23, 2019 2:55 pm

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

Post by jdts » Tue Dec 31, 2019 4:09 am

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)

Post Reply