Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Comments

Serial (CDC) module for USB Host#10283

Open
rianadon wants to merge 1 commit intoadafruit:mainfrom
rianadon:cdc-host
Open

Serial (CDC) module for USB Host#10283
rianadon wants to merge 1 commit intoadafruit:mainfrom
rianadon:cdc-host

Conversation

@rianadon
Copy link

This is a module wrapping TinyUSB'sclass/cdc/cdc_host library into a pyserial-like API so that a CircuitPython board with a USB Host can talk to other devices with CDC interfaces over USB.

It is possible to talk CDC using only theusb.core.Device.read/.write methods. However, this only works in blocking mode. I tried setting up asynchronous communication by supplying a timeout to.read(), but when I tested my code I missed data.
As I understand, CDC reads are performed by sending anIN packet, then waiting for the device to eventually send aDATA packet back. This transaction cannot be cancelled halfway through, so even if CircuitPython times out and moves onto other things, the data transfer is going to happen regardless.

I think the only way to build an asynchronous API around sending data through theusb.core library is to use FIFOs so that even if CircuitPython times out and moves on, the data is still appended to the FIFO and can be read back later. This is how TinyUSB'sEndpoint Stream API works. Theclass/cdc/cdc_host library I am wrapping internally uses Endpoint Streams for the rx and tx endpoints.

I was debating building a module around the cdc host or endpoint streams and decided against endpoint streams since they are only exposed in TinyUSB's private headers. They don't require a significant amount of code to implement though, so if you think that it is better to build a lower-level library than a higher-level library I could submit that as a PR instead. I have most of the code written, but it does not work yet.

Copy link
Member

@tannewttannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I really like the pyserial approach! I'd go 100% into it and match APIs exactly and then call itserial. That way code will just work in CPython too.

You'll want to conditionalize enabling it so that builds without USB Host don't enable it. That should fix the CI.

msgstr ""

#: shared-bindings/usb/cdc_host/__init__.c
msgid "device must be a usb.core.Device object"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Instead of adding a new message, reuse"%q must be of type %q, not %q". (I'd search for where it is used to copy it's example.)

Comment on lines +788 to +789
usb/cdc_host/Serial.c \
usb/cdc_host/__init__.c \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Likely needs to match the other whitespace.

static const mp_rom_map_elem_t usb_cdc_host_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_usb_dot_cdc_host) },
{ MP_ROM_QSTR(MP_QSTR_Serial), MP_ROM_PTR(&usb_cdc_host_serial_type) },
{ MP_ROM_QSTR(MP_QSTR_find), MP_ROM_PTR(&usb_cdc_host_find_obj) },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@rianadon
Copy link
Author

Thanks for the feedback!

I was thinking some more about the tradeoffs between implementing the low level endpoint API vs high level serial API, and I changed my mind and am leaning more towards the EndpointStream for a few reasons:

  • An API that dynamically allocates a fifo for reading from an endpoint would support infinite endpoints. However, with TinyUSB's CDC class, the number of USB communication ports supported on the host depends onCFG_TUH_CDC. You can increase it to deal with devices that spawn many CDC ports, but this comes at the expense of memory.
  • EndpointStream solves non-blocking USB for any device, not just serial. This might be useful forkeyboards,midi, etc. The pyserial-compatible API could be provided by a Python module wrapping the EndpointStream.
  • It requires fewer functions to implement.

Curious to hear your thoughts. I can close this and open a new PR.

@tannewt
Copy link
Member

It's up to you what you'd like to implement.

I'd lean against EndpointStream because it'd be a new unique API. This higher level has the advantage of matching the existing pyserial api. The low level core APIs match pyusb in CPython.

Is there a pyusb version of EndpointStream?

@rianadon
Copy link
Author

Good point. There's nothing existing, and it seems pyusb is blocking only. You'd need to use threads to do USB communication in the background. There is a PR to pyusb to make it async and add asubmit_read/submit_write that returnasyncio.Future objects, but it's not very far along. It seems by far the simplest and more Pythonic, but from my understanding there's very few modules in CircuitPython leveraging asyncio, so maybe it's not a good fit here.

I can continue to work on the pyserial approach.

@tannewt
Copy link
Member

Thanks! I think pyserial is best. It feels like the defacto way to do host serial.

@dhalbert
Copy link
Collaborator

Worth looking at#9365 and#10019. pyserial has some idiosyncrasies in its API which are not the greatest, so I ended up doing something slightly different forusb_cdc.

@jramboz
Copy link

Forgive me if this is a silly question, but does this enable USB Host support, or does it still require theusb_host module?

@samblenny
Copy link

Forgive me if this is a silly question, but does this enable USB Host support, or does it still require theusb_host module?

This PR is about adding better support for USB serial devices when they are connected to the USB host port of a CircuitPython board with USB host capability. The existing way of doing USB host stuff is to use theusb module, which mostly follows the PyUSB API. There are also some higher level CircuitPython helper modules that make it easier to use keyboards, mice, gamepads, and such.

Despite the name which suggests otherwise, theusb_host module is mostly not important for interacting with USB host devices in CircuitPython. Its purpose is for changing USB host settings that usually don't need to be changed.

If you want to learn about writing CircuitPython code for USB host stuff, the best places to ask questions are theAdafruit Forums or the#help-with-circuitPython channel onAdafruit's Discord. You could also search for "USB host" on theAdafruit Learning System or theAdafruit Playground.

@jramboz
Copy link

Thank you for the response! I've asked on the Discord and hopefully I'll get a response. I won't derail the thread here, but essentially I'm trying to use a Seeed XIAO ESP32S3 as a USB host to connect to a device that uses serial communication. And I'm having a horrible time trying to figure out how to do it (every time I try to dousb.core.find(find_all=True), I getRuntimeError: No usb host port initialized).

Something like you've proposed here would probably make my life much easier once I can actually communicate with the device :)

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@tannewttannewttannewt requested changes

Requested changes must be addressed to merge this pull request.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

5 participants

@rianadon@tannewt@dhalbert@jramboz@samblenny

[8]ページ先頭

©2009-2026 Movatter.jp