Python package for communicating with USB / CDC devices on windows via the WinUsb driver
Project description
WinUsbCDC
WinUsbCDC is a python package for communicating with USB / CDC devices on windows via the built-in WinUsb driver.
It’s intended for communicating with USB CDC (virtual com port) devices on windows without using the built in usbserial.sys driver as it is rather unstable and has data loss issues on Win 10 - https://wiki.segger.com/CDC#CDC-ACM_issues_on_Windows_10 - https://community.nxp.com/thread/458640 - https://social.msdn.microsoft.com/Forums/sqlserver/en-US/671638cd-0aed-4cae-80f2-860c1c551347/usb-cdc-acm-data-lost?forum=wdk
The standard means of usage is via the winusbcdc.ComPort(name, vid, pid) class which roughly matches the pyserial.Serial class api.
This module is based directly on WinUsbPy and as such also exposes the same WinUSB api: - A 1:1 wrapper over WinUsb which allows calling C++ functions directly from involved dlls. - A high level api which simplifies a lot of C++/windll/ctypes messy interactions offering just a bunch of easy methods.
Install WinUsbCDC
pip install winusbcdc
python setup.py install
USB CDC Device
Ensure your device has the WinUSB driver configured. This can be done with Zadig, libusbk InfWizard or similar.
from winusbcdc import ComPort
p = ComPort("My USB Device") # friendly name as shown in device manager
# or
p = ComPort(vid=0xF055, pid=0x9800)
p.open()
p.write(b'foo')
print(p.read())
p.close()
Low Level WinUsbPy Api
Low level api offers three methods for invoking functions from three different dlls.
#args: arguments of the C++ function called
def exec_function_winusb(self, function_name, *args):
def exec_function_kernel32(self, function_name, *args):
def exec_function_setupapi(self, function_name, *args):
if we need to call SetupDiGetClassDevs which presents this prototype:
c++ HDEVINFO SetupDiGetClassDevs(_In_opt_ const GUID *ClassGuid,_In_opt_ PCTSTR Enumerator,_In_opt_ HWND hwndParent,_In_ DWORD Flags);
from winusbpy import *
from ctypes import *
from ctypes.wintypes import *
from winusbclasses import DIGCF_DEVICE_INTERFACE, DIGCF_PRESENT
api = WinUSBApi()
byte_array = c_byte * 8
guid = GUID(0xA5DCBF10L, 0x6530, 0x11D2, byte_array(0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED))
flags = DWORD(DIGCF_DEVICE_INTERFACE | DIGCF_PRESENT)
hdev_info = api.exec_function_setupapi("SetupDiGetClassDevs", byref(guid), None, None, flags)
Good resources of WinUsb if you develop using this low level layer
High Level WinUsbPy Api
Built on top of the low level wrapper is a more usable api to perform common USB operations. Here it is list of defined functions:
# Possible keyword arguments: default, present, allclasses, profile, deviceinterface (Boolean), Usually called as follows list_usb_devices(deviceinterface=True, present=True)
def list_usb_devices(self, **kwargs):
# vid and pid must be str, returns True if device was correctly initialized and False otherwise
def init_winusb_device(self, vid, pid):
# path must be str, returns True if device was correctly initialized and False otherwise
# example init_winusb_device_with_path("\\\\?\\usb#vid_9999&pid_0102#3555303335351909000b0#{a5dcbf10-6530-11d2-901f-00c04fb951ed}")
def init_winusb_device_with_path(self, path):
# Returns True if device was correctly closed and False otherwise.
def close_winusb_device(self):
# Returns last error code. See http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382%28v=vs.85%29.aspx
def get_last_error_code(self):
# Returns information for a open device (0x03:High Speed, 0x01:full-speed or lower), query=1 in order to get USB speed.
def query_device_info(self, query=1):
# Returns a UsbInterfaceDescriptor object with information about a specified interface
def query_interface_settings(self, index):
# Change current interface, Winusb opens first interface (0 index) when a device is initialized
def change_interface(self, index):
# Returns a PipeInfo object with information of a specified pipe within current interface
def query_pipe(self, pipe_index):
# Send a control requesto to open device, setup_packet is a UsbSetupPacket object.
# buff = None implies no data is going to be transferred besides setup packet
# buff = [0] create a buffer of length 1. Buffer could be IN or OUT, direction is defined in setup packet
# it returns a dict with the response and with the buffer under the keywords 'result' and 'buffer'
def control_transfer(self, setup_packet, buff=None):
#Send Bulk data to the Usb device, write_buffer must be a str buffer
def write(self, pipe_id, write_buffer):
#Read Bulk data from the Usb device, Returns of a buffer not greater than length_buffer length
def read(self, pipe_id, length_buffer):
Let’s say hello to our device:
from winusbpy import *
vid = "vid_device" # for example: VID:067b PID:2303
pid = "pid_device"
api = WinUsbPy()
result = api.list_usb_devices(deviceinterface=True, present=True)
if result:
if api.init_winusb_device(pl2303_vid, pl2303_pid):
api.write(0x02, "hello")
Real examples
If you run winusbcdc as a module it should act like a basic serial terminal, see __main__.py for the code used.
python -m winusbcdc --name "My USB Device"
In “Examples” folder there are two WinUsbPy examples configuring a PL2303 serial usb device, listing characteristics and sending data.
version: 1.5
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
File details
Details for the file WinUsbCDC-1.5-py3-none-any.whl
.
File metadata
- Download URL: WinUsbCDC-1.5-py3-none-any.whl
- Upload date:
- Size: 18.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.11.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 074d9929e423262579076b76f100b5ee727cedd6f40157103f61ba80fda52653 |
|
MD5 | 7511a46e6f0cd869da4369e7e28e49af |
|
BLAKE2b-256 | f7c1587e050b6c1cc0a0f230ceb842951d7050f14a6af09d60c93e37d4313771 |