Skip to content
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

async input() #9144

Open
kamocat opened this issue Apr 4, 2024 · 4 comments
Open

async input() #9144

kamocat opened this issue Apr 4, 2024 · 4 comments
Labels
async asyncio related enhancement
Milestone

Comments

@kamocat
Copy link

kamocat commented Apr 4, 2024

One of the many uses of async coroutines is waiting for user input while completing background tasks.
There are examples for buttons, but digitalIO.value wasn't a blocking function anyway so polling it is trivial.

My issue is that I am trying to use the serial console to interact with the device, and input() is most definitely a blocking function.
The implementations I've found seem to be platform-dependent, communicating with a separate UI thread or relying on non-standard libraries. Even the aioconsole library seems to do this.

It seems there should be a non-blocking way to read from sys.stdio, as it's a stream. There is some discussion of this in the circuitpython docs, but no methods given. The asyncio streams appear to be TCP-only.

@kamocat
Copy link
Author

kamocat commented Apr 4, 2024

I tried using sys.stdin.read() but apparently that's a blocking function too.

async def ainput():
    while(True):
        x = sys.stdin.read(100)
        if(len(x) > 0):
            return x
        await asyncio.sleep(0.1)

@dhalbert
Copy link
Collaborator

dhalbert commented Apr 4, 2024

you can use supervisor.runtime.serial_bytes_available to see if there's any input to read. Read the characters one at a time with sys.stdin.read(1) until .serial_bytes_available is false.

Right now this is a boolean. In 9.1.0 it will change to an int telling you how many characters are available. See #9141

You can also use usb_cdc.console to read the console stream as bytes. Also note that on many boards you can enable a second CDC input stream that does not have the REPL on it, which you can use solely for data. See https://learn.adafruit.com/customizing-usb-devices-in-circuitpython/circuitpy-midi-serial#usb-serial-console-repl-and-data-3096590. On Espressif there aren't enough USB endpoints available to enable it without disabling other things, but it's fine on most other chips.

@dhalbert
Copy link
Collaborator

dhalbert commented Apr 4, 2024

There are some impressively complicated ways to do this in CPython (desktop Python): https://stackoverflow.com/questions/35223896/listen-to-keypress-with-asyncio

@dhalbert dhalbert transferred this issue from adafruit/Adafruit_CircuitPython_asyncio Apr 4, 2024
@dhalbert dhalbert added this to the Long term milestone Apr 4, 2024
@dhalbert dhalbert added async asyncio related enhancement labels Apr 4, 2024
@kamocat
Copy link
Author

kamocat commented Apr 4, 2024

Interesting. So if I only have single-byte characters, I can use the count from usb_cdc like so:

async def ainput():
    usb_cdc.console.timeout = 0
    x = ""
    while(True):
        n = usb_cdc.console.in_waiting
        x += sys.stdin.read(n)
        if('\n' in x):
            return x
        await asyncio.sleep(0.1)

But then it breaks if I press a "delete" character. Also interesting is that the input isn't echoed back this way.
It will do for now, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
async asyncio related enhancement
Projects
None yet
Development

No branches or pull requests

2 participants