summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorDamien George <damien@micropython.org>2024-04-30 11:33:39 +1000
committerDamien George <damien@micropython.org>2024-05-13 11:26:29 +1000
commitb2df89c417841a7db18120fb40e1dee96cf71865 (patch)
tree86e73b68ac02c48ba011ac91fc6349fee3fa2c1a /examples
parentc3301da17626663d7d6e5fc79a1010842528c9b1 (diff)
examples/usb: Add a very simple USBDevice example with host.
Signed-off-by: Damien George <damien@micropython.org>
Diffstat (limited to 'examples')
-rw-r--r--examples/usb/usb_simple_device.py144
-rwxr-xr-xexamples/usb/usb_simple_host_pyusb.py47
2 files changed, 191 insertions, 0 deletions
diff --git a/examples/usb/usb_simple_device.py b/examples/usb/usb_simple_device.py
new file mode 100644
index 000000000..0b0921f54
--- /dev/null
+++ b/examples/usb/usb_simple_device.py
@@ -0,0 +1,144 @@
+# 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)
diff --git a/examples/usb/usb_simple_host_pyusb.py b/examples/usb/usb_simple_host_pyusb.py
new file mode 100755
index 000000000..d8ac2dd9c
--- /dev/null
+++ b/examples/usb/usb_simple_host_pyusb.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+#
+# Host side of the `usb_simple_device.py` example. This must be run using standard
+# Python on a PC. See further instructions in `usb_simple_device.py`.
+
+import sys
+import usb.core
+import usb.util
+
+# VID and PID of the custom USB device.
+VID = 0xF055
+PID = 0x9999
+
+# USB endpoints used by the device.
+EP_OUT = 0x01
+EP_IN = 0x81
+
+
+def main():
+ # Search for the custom USB device by VID/PID.
+ dev = usb.core.find(idVendor=VID, idProduct=PID)
+
+ if dev is None:
+ print("No USB device found")
+ sys.exit(1)
+
+ # Claim the USB device.
+ usb.util.claim_interface(dev, 0)
+
+ # Read the device's strings.
+ for i in range(0x11, 0x17):
+ print(f"str{i}:", usb.util.get_string(dev, i))
+
+ # Test writing to the device.
+ ret = dev.write(EP_OUT, b"01234567", timeout=1000)
+ print(ret)
+
+ # Test reading from the device.
+ print(dev.read(EP_IN, 64))
+
+ # Release the USB device.
+ usb.util.release_interface(dev, 0)
+ usb.util.dispose_resources(dev)
+
+
+if __name__ == "__main__":
+ main()