You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

144 lines
4.1 KiB

# Implementation of a very simple, custom USB device in Python. The device has an
# IN and OUT endpoint, accepts up to 64 bytes of data on the OUT endpoint, and echos
# that data back to the IN endpoint.
#
# To run, just execute this file on a device with machine.USBDevice support. The device
# will then change to the custom USB device.
#
# For example, use `mpremote` (the `--no-follow` option starts the script running
# without waiting for a response, because there won't be a response after the device
# changes its USB mode):
#
# $ mpremote run --no-follow usb_simple_device.py
#
# Then, run the host side of the example on your PC using:
#
# $ sudo python usb_simple_host_pyusb.py
#
# You'll need to have `pyusb` installed via `pip install pyusb` to run the host PC code.
# And `sudo` is most likely needed to access the custom USB device.
#
# Once you have finished running the code, you will need to reset or unplug the USB
# device to stop it.
import machine
# VID and PID of the USB device.
VID = 0xF055
PID = 0x9999
# USB endpoints used by the device.
EP_OUT = 0x01
EP_IN = 0x81
# USB device descriptor.
_desc_dev = bytes(
[
0x12, # bLength
0x01, # bDescriptorType: Device
0x00,
0x02, # USB version: 2.00
0xFF, # bDeviceClass: vendor
0x00, # bDeviceSubClass
0x01, # bDeviceProtocol
0x40, # bMaxPacketSize
VID & 0xFF,
VID >> 8 & 0xFF, # VID
PID & 0xFF,
PID >> 8 & 0xFF, # PID
0x00,
0x01, # bcdDevice: 1.00
0x11, # iManufacturer
0x12, # iProduct
0x13, # iSerialNumber
0x01, # bNumConfigurations: 1
]
)
# USB configuration descriptor.
_desc_cfg = bytes(
[
# Configuration Descriptor.
0x09, # bLength
0x02, # bDescriptorType: configuration
0x20,
0x00, # wTotalLength: 32
0x01, # bNumInterfaces
0x01, # bConfigurationValue
0x14, # iConfiguration
0xA0, # bmAttributes
0x96, # bMaxPower
# Interface Descriptor.
0x09, # bLength
0x04, # bDescriptorType: interface
0x00, # bInterfaceNumber
0x00, # bAlternateSetting
0x02, # bNumEndpoints
0xFF, # bInterfaceClass
0x03, # bInterfaceSubClass
0x00, # bInterfaceProtocol
0x15, # iInterface
# Endpoint IN1.
0x07, # bLength
0x05, # bDescriptorType: endpoint
EP_IN, # bEndpointAddress
0x03, # bmAttributes: interrupt
0x40,
0x00, # wMaxPacketSize
0x0A, # bInterval
# Endpoint OUT1.
0x07, # bLength
0x05, # bDescriptorType: endpoint
EP_OUT, # bEndpointAddress
0x02, # bmAttributes: bulk
0x40,
0x00, # wMaxPacketSize
0x00, # bInterval
]
)
# USB strings.
_desc_strs = {
0x11: b"iManufacturer",
0x12: b"iProduct",
0x13: b"iSerial",
0x14: b"iInterface",
0x15: b"iInterface",
0x16: b"Extra information",
}
# USB callback for when our custom USB interface is opened by the host.
def _open_itf_cb(interface_desc_view):
print("_open_itf_cb", bytes(interface_desc_view))
# Prepare to receive first data packet on the OUT endpoint.
usbd.submit_xfer(EP_OUT, usbd_buf)
# USB callback for when a data transfer (IN or OUT) has completed.
def _xfer_cb(ep_addr, result, xferred_bytes):
print("_xfer_cb", ep_addr, result, xferred_bytes)
if ep_addr == EP_OUT:
# Received data packet from the host, print it out.
print(usbd_buf)
# And then echo the data back to the host.
usbd.submit_xfer(EP_IN, memoryview(usbd_buf)[:xferred_bytes])
elif ep_addr == EP_IN:
# Host got our data, prepare to receive the next data packet.
usbd.submit_xfer(EP_OUT, usbd_buf)
# USB data buffer, for IN and OUT transfers.
usbd_buf = bytearray(64)
# Switch the USB device to our custom USB driver.
usbd = machine.USBDevice()
usbd.builtin_driver = usbd.BUILTIN_NONE
usbd.config(
desc_dev=_desc_dev,
desc_cfg=_desc_cfg,
desc_strs=_desc_strs,
open_itf_cb=_open_itf_cb,
xfer_cb=_xfer_cb,
)
usbd.active(1)