8000 'getch()' · Issue #231 · adafruit/circuitpython · GitHub
[go: up one dir, main page]

Skip to content

'getch()' #231

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

Closed
ladyada opened this issue Sep 5, 2017 · 42 comments · Fixed by #4215
Closed

'getch()' #231

ladyada opened this issue Sep 5, 2017 · 42 comments · Fixed by #4215

Comments

@ladyada
Copy link
Member
ladyada commented Sep 5, 2017

OK its odd but hear me out :)

python used to allow raw REPL keygrabs (e.g. 'press any key to continue') but deprecated it in 3 for just input() which is blocking and requires a return key

it would be useful for interactive-at-the repl scripts to have a non-input() way of getting raw characters. i mean, you can send individual chars with print("x", end='') so why not have the reverse :D

also, using input() wonks with the REPL history: if you use input() it puts that entry into the history which i think is a bit odd:
image

@tannewt
Copy link
Member
tannewt commented Sep 5, 2017

Try sys.stdin.read(1). I'm not sure if CircuitPython supports it but CPython should. We can add it if its closer to what you want.

Reference: https://docs.python.org/3/library/sys.html#sys.stdin

@ladyada
Copy link
Member Author
ladyada commented Sep 5, 2017

not really - its basically input() but it only gives you a char at a time. i wanted something where it was timeout-not-blocking AND you didnt have to hit return

8000

@tannewt
Copy link
Member
tannewt commented Sep 6, 2017

We'll have to add an new API for this. Its not well covered in CPython. More background is here: https://stackoverflow.com/questions/2408560/python-nonblocking-console-input

@tannewt tannewt added this to the 3.0 milestone Sep 6, 2017
@deshipu
Copy link
deshipu commented Sep 20, 2017

Shouldn't read() be non-blocking, returning an empty string if there is nothing in the buffer? I think it works that way for the sockets and the serial — returning at most n characters.

@dhalbert
Copy link
Collaborator

Just tried this: sys.stdin.read() in CPython 3.5 will read until EOF (ctrl-D); it's blocking. Reading a single character, sys.stdin.read(1) hangs until one character is typed (and it buffers by line, so it waits for a newline as well) or EOF is encountered.

@deshipu
Copy link
deshipu commented Sep 20, 2017

Yes, but that's not consistent with other stream-like interfaces.

@dhalbert
Copy link
Collaborator

I think we want to make the behavior consistent with CPython. Do you mean other stream-like interfaces in Python or in general? It's a file-like interface, where the reads might be really slow, because the device (the human) is slow to supply characters. I tried to find some asyncio examples for stdin, but I didn't see anything right away.

@deshipu
Copy link
deshipu commented Sep 20, 2017

Looking at the documentation at https://docs.python.org/3/library/io.html I can see there is a whole zoo of different behaviors, depending on what the object on which you call read() is and how it was created, and also what options were used. Sometimes it will block, sometimes it will return 0 bytes, sometimes None, and sometimes it will raise an exception.

It seems that there is a way to switch the stdio into a non-blocking mode, at least on Linux, using non-standard terminal libraries: https://stackoverflow.com/questions/21791621/python-taking-input-from-sys-stdin-non-blocking

@ladyada
Copy link
Member Author
ladyada commented Sep 20, 2017

there's nothing in 'standard' python that works on all OS's - windows uses a DLL and mscvt or whatnot - we have to do something custom :)

@tdicola
Copy link
tdicola commented Feb 21, 2018

IMHO the canonical python way to handle non-blocking io is with select: https://docs.python.org/3/library/select.html Like Radomir mentions though it quickly gets into platform specific optimizations for desktop though (poll, epoll, kpoll, etc.), but at the end of the day they're all the same idea of polling for availability of data from an arbitrary file descriptor with a select-like API. Adding a select API and ability to use it to poll for new chars from stdin, etc. would be handy, and it would be a natural progression to add polling for other descriptors like sockets (same way python likes to handle sockets on the desktop). With a light wrapper on top it could become a general purpose async IO engine too (i.e. your main loop is just a big poll of awaiting descriptors with select).

@deshipu
Copy link
deshipu commented Feb 22, 2018

It seems to me that being able to select or await on a DigitaIO object (or something that wraps it) would handle most of the cases for us. SPI and I2C are clocked by master, so nothing interesting happens with them without the master initiating it. AnalogIO could be selectable/awaitable also, but would require specifying a threshold. Selecting on a timer would pretty much be equivalent to doing select with a timeout, so I'm not that sure this is useful.

@tannewt
Copy link
Member
tannewt commented Feb 27, 2018

Another way to achieve this is to provide a second "raw" serial connection to the computer in addition to the input. This would allow for binary data transmission in addition to text. It could work just like a UART connection.

We'd want dynamic USB descriptors for this (mentioned in #190) so that its only on as needed.

@ladyada
Copy link
Member Author
8000
ladyada commented Feb 27, 2018

good idea! that's probably the 'right' way to do it, but yeah would have to wait until you can select the USB interfaces cauz there'd be juggling between webusb, cdc, HID.

@hybotix
Copy link
hybotix commented Apr 19, 2018

I have a situation where I need to have a Raspberry Pi 3 interact with a board running Circuit Python. I need to use Firmata between the two for two-way communication. I would need to transfer both ASCII and binary data. Being able to do this is highly desirable for some applications, like robotics.

@hybotix
Copy link
hybotix commented Apr 19, 2018

PS I am geekguy on the Adafruit forums.

@themzlab
Copy link

member hybotics/geekguy mentioned the same use case that I have for which this feature is highly desirable. At my company we use Raspberry Pi boards to host our internal test and production machines and sometimes have Arduino boards hanging on them over USB. CircuitPython boards are preferred compared to Arduino because we can stay with one language and also the instrument is self-documenting because it hold's its own code and readme files. The hardware is relatively elegant because it only requires that a USB cable be run to each remote device or sensor.

A problem is that we don't have a great way to send serial data instructions to the boards.

as a work-around is there a way to query the input buffer so that I can know if sys.stdin.read(1) would pass through or block?

@protothesis
Copy link

Hello folks!

I was directed to this thread by Scott (@tannewt ) over at the Adafruit forums, in reply to my query about "Arduino-like 'serial' with Circuit Python".

For what I'm trying to achieve, being able to send and receive serial data in this way through the USB connection would be really great.

Thanks all!

-Casey

@WRadigan
Copy link
WRadigan commented Sep 6, 2018

Ditto to @protothesis comments above... and I was similarly directed by @tannewt from the Arduino Discord... My goal would be to have something similar to the Arduino CmdMessenger. Right now the best I can come up with is using two boards: 1 for communication via the Serial Console, and 1 for Device Control with a UART between them. If anybody has already been down that road, I'd love to hear about it...

https://playground.arduino.cc/Code/CmdMessenger

@ladyada
Copy link
Member Author
ladyada commented Sep 6, 2018

an intermediate solution for now is to use the hardware UART + a usb/uart cable. elegant? not as much but it does work now :)
https://learn.adafruit.com/circuit-playground-express-serial-communications
(code works for any/all circuitpy boards)

@ATMakersBill
Copy link
Collaborator

I created a workaround for this and am sharing it via this pull request
#1193

Not sure it's the right solution, but it does solve our issues.

@bludin
Copy link
bludin commented Apr 15, 2020

Has anything changed with respect to the original request, i.e. a non-blocking input() or sys.stdin.read() function? Or is there another way for bi-directional communication via USB?

@dhalbert
Copy link
Collaborator
dhalbert commented Apr 15, 2020

@bludin Is supervisor.runtime.serial_bytes_available (doc) useful to you? It returns the number of bytes that are queued to read.

@bludin
Copy link
bludin commented Apr 16, 2020

@dhalbert yes, that is very useful. It returns a boolean, though, not the number of bytes. So a non-blocking read func has to look like

def non_blocking_read():
    i = ""
    while supervisor.runtime.serial_bytes_available:
        i += sys.stdin.read(1)
    return i

or is there a better way?

@dhalbert
Copy link
Collaborator

Yes, that's the best way for now. Sorry, it would be nicer if serial_bytes_available returned a count.

@emard
Copy link
emard commented Dec 12, 2020

Related to 2nd USB-serial channel wanted,
endpoint shortage and dynamic or static descriptor:

Perhaps we could have static descriptor with shared
endpoints for usb-serial and something-else, for example microphone.

Of course usb-serial and microphone cannot work
at the same time using the same endpoints.

but I was thinking - if in the OS the microphone driver is blacklisted,
then usb-serial may normally load and work.

@dhalbert
Copy link
Collaborator

but I was thinking - if in the OS the microphone driver is blacklisted,
then usb-serial may normally load and work.

We want to provide run-time choice of the USB devices that CircuitPython presents. That is #1015. Blacklisting might work on Linux, but we want an easier mechanism and one that is OS-independent.

@dhalbert
Copy link
Collaborator

The secondary serial channel idea mentioned in this thread now is covered in issue #3853.

@ladyada
Copy link
Member Author
ladyada commented Dec 19, 2020

kk - we'd still need a way to read individual characters from the secondary USB-serial connection

@dhalbert
Copy link
Collaborator

kk - we'd still need a way to read individual characters from the secondary USB-serial connection

Yeah, Python is still bad at that.

@tannewt
Copy link
Member
tannewt commented Dec 22, 2020

I think we should have the second CDC available as a UART compatible (aka stream). It'd be similar to how we have objects for MIDI stuff.

@ladyada
Copy link
Member Author
ladyada commented Dec 22, 2020

yes! think about interfacing with processing, unity, cnc etc. just need bidirectional byte stream (e.g. like pyserial which is the cpy equiv)

@ecasadod
Copy link

@dhalbert yes, that is very useful. It returns a boolean, though, not the number of bytes. So a non-blocking read func has to look like

def non_blocking_read():
    i = ""
    while supervisor.runtime.serial_bytes_available:
        i += sys.stdin.read(1)
    return i

or is there a better way?

I'm using that method to interface with a desktop program on my DynOSSAT-EDU. That program opens the COM port and writes bytes to it, and the CircuitPython board should receive them and act in consequence in a non-blocking way. It mostly works, but I found a problem: the moment I get a Ctrl-C or Ctrl-D byte (0x03 or 0x04) in a frame and read it using sys.stdin.read(), the program closes throwing a KeyboardInterrupt.

Just leaving this here as a warning for someone wanting to do this; if you only send ASCII-printable bytes you won't find any problem.

@dhalbert
Copy link
Collaborator

@ecasadod To get around this problem, we have near-term plans to add an optional secondary CDC channel that does not connect to the REPL: #3853.

@ladyada
Copy link
Member Author
ladyada commented Feb 20, 2021

yesss! oldest issue closed :D

@astrilchuk
Copy link

Since the usb_cdc module is on hold indefinitely, will this issue be reopened? I'm stuck on a nightly until, fingers crossed, this is available as an option in version 7.

Thanks

@ladyada
Copy link
Member Author
ladyada commented Mar 18, 2021

its not on hold indefinitely - we turned it off a few PR's ago because it was confusing some IDEs - you can build yourself and enable, or i think use nighties from over a week ago :)

@dhalbert
Copy link
Collaborator

This Guide page describes how to configure a build to turn it on: https://learn.adafruit.com/building-circuitpython/customizing-usb-devices

@astrilchuk
Copy link

Thanks @ladyada and @dhalbert for the fast response! I'll have a look at that. I was so happy when I saw it was added just a few days ago and very confused when I tried the latest nightly and it wasn't available.

@RufusVS
Copy link
RufusVS commented May 1, 2021

I've spent the evening playing with my Neo Trinkey and have had a not-fun time tracking down issues like the ones mentioned here. It's a cute little board, and I was doing some fun things like touch pads to simulate keys through the hid. Then I wanted to change colors by sending DC4F character command through the serial port. Using supervisor.runtime.serial_bytes_available was non-intuitive at best, but I found it. It would be nice if serial_bytes_available returned a count instead of a boolean (efficiency at higher baud rates or when chars are buffered), and I worry about the Ctrl-C and Ctrl-D issue mentioned with sys.stdin. I'm not crazy about rebuilding a custom circuit python if I don't need to. I'll be back.

@ladyada
Copy link
Member Author
ladyada commented May 1, 2021

you can subscribe to #4689 - when that's merged in (no ETA!) it will allow turning on/off the second CDC channel

@ScruffR
Copy link
ScruffR commented May 25, 2021

Is or will usb_cdc be supported on nRF52840 devices (i.e. Particle Xenon)?

@ladyada
Copy link
Member Author
ladyada commented May 25, 2021

yep!

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

Successfully merging a pull request may close this issue.

0