From 718c3eef803f742123972cda346fa983e5d34cf6 Mon Sep 17 00:00:00 2001 From: Jonas Sabaliauskas Date: Wed, 21 Sep 2022 14:41:02 +0300 Subject: [PATCH 1/3] filtering in usb2can.dll added --- can/interfaces/usb2can/usb2canInterface.py | 47 ++++++++++++++---- .../usb2can/usb2canabstractionlayer.py | 5 ++ examples/receive_all_korlan.py | 14 ++++++ receive_all.py | 34 +++++++++++++ usb2can.dll | Bin 0 -> 30208 bytes 5 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 examples/receive_all_korlan.py create mode 100644 receive_all.py create mode 100644 usb2can.dll diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index e51d485cd..663baba08 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -3,8 +3,8 @@ """ import logging -from ctypes import byref -from typing import Optional +import ctypes +from typing import Optional, cast from can import BusABC, Message, CanInitializationError, CanOperationError from .usb2canabstractionlayer import Usb2CanAbstractionLayer, CanalMsg, CanalError @@ -14,6 +14,7 @@ IS_ID_TYPE, ) from .serial_selector import find_serial_devices +from can.typechecking import CanFilters, CanFilterExtended # Set up logging log = logging.getLogger("can.usb2can") @@ -87,6 +88,9 @@ class Usb2canBus(BusABC): If both `serial` and `channel` are set, `serial` will be used and channel will be ignored. + :param can_filters (optional): + See :meth:`can.BusABC.set_filters`. + """ def __init__( @@ -96,9 +100,10 @@ def __init__( flags=0x00000008, *_, bitrate=500000, + can_filters: Optional[CanFilters] = None, **kwargs, ): - + self._is_filtered = False self.can = Usb2CanAbstractionLayer(dll) # get the serial number of the device @@ -119,15 +124,15 @@ def __init__( connector = f"{device_id}; {baudrate}" self.handle = self.can.open(connector, flags) - super().__init__(channel=channel, **kwargs) + super().__init__(channel=channel, can_filters=can_filters, **kwargs) def send(self, msg, timeout=None): tx = message_convert_tx(msg) if timeout: - status = self.can.blocking_send(self.handle, byref(tx), int(timeout * 1000)) + status = self.can.blocking_send(self.handle, ctypes.byref(tx), int(timeout * 1000)) else: - status = self.can.send(self.handle, byref(tx)) + status = self.can.send(self.handle, ctypes.byref(tx)) if status != CanalError.SUCCESS: raise CanOperationError("could not send message", error_code=status) @@ -137,11 +142,11 @@ def _recv_internal(self, timeout): messagerx = CanalMsg() if timeout == 0: - status = self.can.receive(self.handle, byref(messagerx)) + status = self.can.receive(self.handle, ctypes.byref(messagerx)) else: time = 0 if timeout is None else int(timeout * 1000) - status = self.can.blocking_receive(self.handle, byref(messagerx), time) + status = self.can.blocking_receive(self.handle, ctypes.byref(messagerx), time) if status == CanalError.SUCCESS: rx = message_convert_rx(messagerx) @@ -154,7 +159,31 @@ def _recv_internal(self, timeout): else: raise CanOperationError("could not receive message", error_code=status) - return rx, False + return rx, self._is_filtered + + def _apply_filters(self, can_filters: Optional[CanFilters]) -> None: + CAN_EXTENDED_FLAG = 0x80000000 + if can_filters is None: + # Pass all messages + # can_filters = [{"can_id": 0, "can_mask": 0}] + self._is_filtered = False + return + filter_data = [] + for can_filter in can_filters: + can_id = can_filter["can_id"] + can_mask = can_filter["can_mask"] + if "extended" in can_filter: + can_filter = cast(CanFilterExtended, can_filter) + # Match on either 11-bit OR 29-bit messages instead of both + can_mask |= CAN_EXTENDED_FLAG + if can_filter["extended"]: + can_id |= CAN_EXTENDED_FLAG + filter_data.append(can_id) + filter_data.append(can_mask) + filter_len =len(filter_data) + + self.can.set_filters(self.handle,ctypes.c_int(filter_len), (ctypes.c_ulong * filter_len)(*filter_data)) + self._is_filtered = True def shutdown(self): """ diff --git a/can/interfaces/usb2can/usb2canabstractionlayer.py b/can/interfaces/usb2can/usb2canabstractionlayer.py index 8a3ae34ca..edd1272aa 100644 --- a/can/interfaces/usb2can/usb2canabstractionlayer.py +++ b/can/interfaces/usb2can/usb2canabstractionlayer.py @@ -197,3 +197,8 @@ def get_library_version(self): def get_vendor_string(self): with error_check("Failed to get vendor string"): return self.__m_dllBasic.CanalGetVendorString() + + def set_filters(self, handle, len, filters_arr): + with error_check("Failed to set filters"): + return CanalError(self.__m_dllBasic.CanalSetFilters(handle, len, filters_arr)) + diff --git a/examples/receive_all_korlan.py b/examples/receive_all_korlan.py new file mode 100644 index 000000000..fc51c2e27 --- /dev/null +++ b/examples/receive_all_korlan.py @@ -0,0 +1,14 @@ +import can + +filters= [{"can_id": 0x1, "can_mask": 0x3, "extended": False}, + {"can_id": 0x2, "can_mask": 0x3, "extended": True}, + {"can_id": 0x3, "can_mask": 0x3}] +with can.interface.Bus(bustype="usb2can", channel="cde7e976", bitrate=1000000, dll='./usb2can.dll', can_filters=filters) as bus: + try: + while True: + msg = bus.recv(1) + if msg is not None: + print(msg) + except KeyboardInterrupt: + pass # exit normally + diff --git a/receive_all.py b/receive_all.py new file mode 100644 index 000000000..7ff532079 --- /dev/null +++ b/receive_all.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +""" +Shows how the receive messages via polling. +""" + +import can +from can.bus import BusState + + +def receive_all(): + """Receives all messages and prints them to the console until Ctrl+C is pressed.""" + + with can.interface.Bus( + bustype="pcan", channel="PCAN_USBBUS1", bitrate=250000 + ) as bus: + # bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) + # bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) + + # set to read-only, only supported on some interfaces + bus.state = BusState.PASSIVE + + try: + while True: + msg = bus.recv(1) + if msg is not None: + print(msg) + + except KeyboardInterrupt: + pass # exit normally + + +if __name__ == "__main__": + receive_all() diff --git a/usb2can.dll b/usb2can.dll new file mode 100644 index 0000000000000000000000000000000000000000..e77b2ddbdf481bddfc9bcfaa07b5edb68ad41923 GIT binary patch literal 30208 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

O^i%P@%vh9RFi!78+5A9*;G1k*Kq#kO| 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 Date: Wed, 21 Sep 2022 16:38:16 +0300 Subject: [PATCH 2/3] updated DLL build --- usb2can.dll | Bin 30208 -> 30208 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/usb2can.dll b/usb2can.dll index e77b2ddbdf481bddfc9bcfaa07b5edb68ad41923..478fcc0543f7f04e4255f69d12dbb6cb00a06f23 100644 GIT binary patch delta 113 zcmZp8!`SeKaf1LOGq14rW?@D_Z)+eUnE?oxfVdur9kPHp03^b|0F&bZvPFRS08s7# oLT+-rk0jWjYM+zL0zslg&1V1gud70FXc$5C8xG delta 113 zcmZp8!`SeKaf1LOvx}0(W?@D_Z)+eUnE?oxfVdur9kPHp03^b|0F&bZvPFRS08s7# nLT+-rk0jWjYM+zL0u3g!0uOZ9yR3YFac#SCL-*#8fHkTB+HxAZ From baee0ee4bd272a85fa002b5c6023f014b51e4dd1 Mon Sep 17 00:00:00 2001 From: Jonas Sabaliauskas Date: Fri, 23 Sep 2022 17:30:51 +0300 Subject: [PATCH 3/3] pyusb, libusb support added --- .DS_Store | Bin 0 -> 8196 bytes can/.DS_Store | Bin 0 -> 8196 bytes can/interfaces/.DS_Store | Bin 0 -> 8196 bytes can/interfaces/__init__.py | 1 + can/interfaces/usb2can_libusb/__init__.py | 4 + .../usb2can_libusb/can_8dev_usb_device.py | 182 +++++++++++++++ .../usb2can_libusb/can_8dev_usb_utils.py | 221 ++++++++++++++++++ .../usb2can_libusb/usb2can_libusb_bus.py | 114 +++++++++ examples/libusb_receive_all.py | 14 ++ examples/pyusblist.py | 11 + examples/send_one_korlan.py | 18 ++ 11 files changed, 565 insertions(+) create mode 100644 .DS_Store create mode 100644 can/.DS_Store create mode 100644 can/interfaces/.DS_Store create mode 100644 can/interfaces/usb2can_libusb/__init__.py create mode 100644 can/interfaces/usb2can_libusb/can_8dev_usb_device.py create mode 100644 can/interfaces/usb2can_libusb/can_8dev_usb_utils.py create mode 100644 can/interfaces/usb2can_libusb/usb2can_libusb_bus.py create mode 100644 examples/libusb_receive_all.py create mode 100644 examples/pyusblist.py create mode 100644 examples/send_one_korlan.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..32b4d8d2dbb0830c04a1272127f8949e72e8b754 GIT binary patch literal 8196 zcmeHM&2G~`5S~p^;t(j2pi+f|WQl7CO@XS2OGq0a5>TlwZ~zqYmxW@>bD$>1>B1XV4xDqk6mloWE^^ zq2D>YZg-w~?xH9&ttkz4SGmxkQLfVTTom9Hfg~N;H`nRLF?3{Iz7QvQ;sr?&H9t<(iHX@ zMslEgK<{Xvdq#Xu@pIIJpQAJ`!>dZ_$pRNrS063&HHEUsh0Zosy^Gj!Dbn%Tm0#ii zb%a!xZkFxkrHJ@w!gCGy*+DziS8}g?O6;{FJ(BU&qVo~sKTduUsTDj%N;&q&uNV>QX0liauR1mjV5Py?(u z!i;@6XJvtj-67*SIZ_^+E=Kd96?|Q_izKfgc5S>j+>ogQ9zSIknC9YLzKvY6pLRt=734#Nl5XWw7)%8}f(@<5Fa)$rF zU*O6gf&an@-i)`E?byH#s_sU+&#~w2df&{hGY%1{?$~b=trC%g%DB=(Q&IRj*O}5V zXYK$M_(UP4)W>I?4ri?G39Eorz$#!BunJfO{s#r{%;w_MJomG!wzUda1umrmyg#_8 zj2(m13}x#;BPjrI9;cbMkkfg zNtuzA$xxJx4xTIKq&gYe)+%5Xm{ovt_m<@BA;pFK{rGvLhhC&TSn#5Hin{(&3XX`D z$ussT@BlTYUCI2*%r7P8O)y8KX+S;=(I$9nZ~%-^cPYR%et%HvMcpm+vVdO3;8xjw zD%mb$oRp5yYXHU=$00?P%pWt%fIfg7fXgh6yR00uv0GwZ6-82+&kknBeN^0Q<6DWh zRKZJ%zL?wk8;&wbO)n1{OzUI zk#}}H@Lq+%q`z`&OULOTjDwL7LO+1X+t*?2>q%FSV?PmYFT3D0oo0V!_2lI7gEhC^ z-aK7%PulJF!!>ua{pj?x>D<0||H+H~esmD)kC+FFGMhSLs#w^Mm3kcYMscKL)||Nw z_DoSDiPz7$cx}Psp~PAMAJ#+}ajfQ9%V23lX$Rl>^{bXWrmp2YcKv*`A^Hm7gMC>WwYyA#z4+U8j@_hS-J~PY zZ&Ig-g0~6`2xN;@-~uZ!m)-kn3;3-5{r?4?p}lphfK}j63aI(*-gXyOXFtQr1kSZx z)c2@d*lwDkP|!#^4wQ5pc>aeWj$N2Cr( E0SR4$ApigX literal 0 HcmV?d00001 diff --git a/can/interfaces/.DS_Store b/can/interfaces/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..63b599ffeb74faa77f4016a1d6b0f9456771c8a1 GIT binary patch literal 8196 zcmeHML2uJA6n^fyHeI!82aqNqq)1$+u?{h5;!?VC;7So30F|VvMzk!hnsnVX4fPEF zfxp0&KLY=S6MWD1Se#}%BE;;-es1!8@$>g=cdsKN(VIpcq75SQP}#1x(Nq+E&UK;G z%$<9%0(_#FavI>;qVWRS-Eayx1)Ks-0jGdd;9pPx-`QNeYrgmOQ`b5LoC3E}0X`pm zRJL7X=h~{H1C2@nVC%T83i`+c6vsEVYwTQGMa4UH_8<&Zm=r^3I-dIshwU0W*H+U> zXgUcqvM?EnP@{wAD>#X+wyt#wI0Y6J;M~2fa`u=~D}O(Gp2%UC$PftLR8LX2eyM^U z(JBoo04Jb?&L~6XALHW8zs&qvVy+lw4(<@V3XZs6;?!vbWI&nimH1r3wdfGO3}}ir ztbFs&1x5@nn3G}*NhcKBW7G~y3=i|n%p59wuYE7!mw`XU6;N)!d1=3dY|s(L$1LH= z#KtQ9zZCRu8h?4F(7%Lc&5LMHMOR0x!e^y-5#LhyQ#D3)gjo3^qmIa{_uHNb*1L#n z(*!Dqs5xet;HvOPX>PMv6TTwMH%ELUs4DHTl=4MZLn{d+R;i6u+841Nu*vmNW&Ook zLH`!;68IzdlvmB15Ce;`vdEyw_KM?Rad-xLca3OR!Xg3|c2%h|#!5a$FM(IQvUxR? z)*Z*hVm8fw*{PZb$HGFd*=UTHV5CsrwS82oeJrSUBR8C7V{_ts3&%;E<=fjoL~W_Q z+-Q1DukF3-e~`0&9^})k7fjyprO@FuZ_~N4F-F@)z$&0~Ja-7Of zSj{NH{278%&@Q|yZk*#uf08CL$q;yU~VycKz@FR|MB{3OEJ+I|W4jpnuTAv@XBY$}{9#+edwm%7t-r rZ50KLO2=VUIu85iABO1rfHJ3R>|9&iLHX-H1i0^irMqixw*tQb!o1j@ literal 0 HcmV?d00001 diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index 755e8675c..dff255d1e 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -12,6 +12,7 @@ "serial": ("can.interfaces.serial.serial_can", "SerialBus"), "pcan": ("can.interfaces.pcan", "PcanBus"), "usb2can": ("can.interfaces.usb2can", "Usb2canBus"), + "usb2can_libusb": ("can.interfaces.usb2can_libusb", "Usb2CanLibUsbBus"), "ixxat": ("can.interfaces.ixxat", "IXXATBus"), "nican": ("can.interfaces.nican", "NicanBus"), "iscan": ("can.interfaces.iscan", "IscanBus"), diff --git a/can/interfaces/usb2can_libusb/__init__.py b/can/interfaces/usb2can_libusb/__init__.py new file mode 100644 index 000000000..53e84b325 --- /dev/null +++ b/can/interfaces/usb2can_libusb/__init__.py @@ -0,0 +1,4 @@ +""" +""" + +from .usb2can_libusb_bus import Usb2CanLibUsbBus diff --git a/can/interfaces/usb2can_libusb/can_8dev_usb_device.py b/can/interfaces/usb2can_libusb/can_8dev_usb_device.py new file mode 100644 index 000000000..c01121daf --- /dev/null +++ b/can/interfaces/usb2can_libusb/can_8dev_usb_device.py @@ -0,0 +1,182 @@ +import logging +import queue +from threading import Thread +from time import sleep +from importlib import reload + +from .can_8dev_usb_utils import * + +logger = logging.getLogger(__name__) + +try: + import usb.core + import usb.util +except ImportError: + logger.warning( + "The PyUSB module is not installed. Install it using `python3 -m pip install pyusb`" + ) + + +class Can8DevUSBDevice: + cmd_rx_ep: usb.core.Endpoint + cmd_tx_ep: usb.core.Endpoint + data_rx_ep: usb.core.Endpoint + data_tx_ep: usb.core.Endpoint + serial_number: str + _close: bool + _rx_queue: queue.Queue + _recv_thread: Thread + + def __init__(self, serial_number=None): + if serial_number is not None: + dev = usb.core.find( + idVendor=USB_8DEV_VENDOR_ID, + idProduct=USB_8DEV_PRODUCT_ID, + serial_number=serial_number, + ) + else: + dev = usb.core.find( + idVendor=USB_8DEV_VENDOR_ID, idProduct=USB_8DEV_PRODUCT_ID + ) + + if dev is None: + raise ValueError( + "8Devices CAN interface not found! Serial number provided: %s" + % serial_number + ) + + self.serial_number = dev.serial_number + + for i in range(3): + try: + dev.reset() + #self.send_command(Can8DevCommandFrame(Can8DevCommand.USB_8DEV_RESET)) + print("Pavyko reset.") + break + except Exception as x: + print(f"nepavyko reset- {i} {x}") + reload(usb.core) + reload(usb.util) + reload(usb) + if serial_number is not None: + dev = usb.core.find( + idVendor=USB_8DEV_VENDOR_ID, + idProduct=USB_8DEV_PRODUCT_ID, + serial_number=serial_number, + ) + print("Serial_number found - ",serial_number) + else: + dev = usb.core.find( + idVendor=USB_8DEV_VENDOR_ID, idProduct=USB_8DEV_PRODUCT_ID + ) + print("no serial number------------------------") + #print(dev) + #sleep(7) + + + # set the active configuration. With no arguments, the first + # configuration will be the active one + dev.set_configuration() + + # get an endpoint instance + cfg = dev.get_active_configuration() + intf = cfg[(0, 0)] + + self.cmd_rx_ep: usb.core.Endpoint = usb.util.find_descriptor( + intf, bEndpointAddress=USB_8DEV_ENDP_CMD_RX + ) + self.cmd_tx_ep: usb.core.Endpoint = usb.util.find_descriptor( + intf, bEndpointAddress=USB_8DEV_ENDP_CMD_TX + ) + self.data_rx_ep: usb.core.Endpoint = usb.util.find_descriptor( + intf, bEndpointAddress=USB_8DEV_ENDP_DATA_RX + ) + self.data_tx_ep: usb.core.Endpoint = usb.util.find_descriptor( + intf, bEndpointAddress=USB_8DEV_ENDP_DATA_TX + ) + + if ( + self.cmd_rx_ep is None + or self.cmd_tx_ep is None + or self.data_rx_ep is None + or self.data_tx_ep is None + ): + raise ValueError("Could not configure 8Devices CAN endpoints!") + + self._rx_queue = queue.Queue(MAX_8DEV_RECV_QUEUE) + + def _recv_thread_loop(self): + while True: + byte_buffer = bytes() + try: + # We must read the full possible buffer size each iteration or we risk a buffer overrun exception losing data. + byte_buffer = self.data_rx_ep.read(512, 0).tobytes() + except Exception: + pass + for i in range(0, len(byte_buffer), 21): + # We could have read multiple frames in a single bulk xfer + self._rx_queue.put(Can8DevRxFrame(byte_buffer[i : i + 21])) + if self._close: + return + + def _start_recv_thread(self): + self._close = False + self._recv_thread = Thread(target=self._recv_thread_loop, daemon=True) + self._recv_thread.start() + + def _stop_recv_thread(self): + self._close = True + + def send_command(self, cmd: Can8DevCommandFrame): + self.cmd_tx_ep.write(cmd.to_bytes()) + return Can8DevCommandFrame.from_bytes(self.cmd_rx_ep.read(16)) + + def open( + self, + phase_seg1: int, + phase_seg2: int, + sjw: int, + brp: int, + loopback: bool = False, + listenonly: bool = False, + oneshot: bool = False, + ): + self.send_command(Can8DevCommandFrame(Can8DevCommand.USB_8DEV_RESET)) + open_command = can_8dev_open_frame( + phase_seg1, phase_seg2, sjw, brp, loopback, listenonly, oneshot + ) + if self.send_command(open_command).opt1 == 0: + self._start_recv_thread() + return True + else: + return False + + def close(self): + self._stop_recv_thread() + close_command = Can8DevCommand.USB_8DEV_CLOSE + self.send_command(Can8DevCommandFrame(Can8DevCommand.USB_8DEV_RESET)) #reset + self.send_command(Can8DevCommandFrame(close_command)) + + + def recv(self, timeout=None): + try: + return self._rx_queue.get(True, timeout=timeout / 1000) + except queue.Empty: + return None + + def send(self, tx_frame: Can8DevTxFrame, timeout=None): + self.data_tx_ep.write(tx_frame.to_bytes(), timeout) + + def get_version(self): + cmd_response = self.send_command( + Can8DevCommandFrame(Can8DevCommand.USB_8DEV_GET_SOFTW_HARDW_VER) + ) + version = int.from_bytes(cmd_response.data[0:4], byteorder="big") + return version + + def get_firmware_version(self): + version = self.get_version() + return "%d.%d" % ((version >> 24) & 0xFF, (version >> 16) & 0xFF) + + def get_serial_number(self): + return self.serial_number diff --git a/can/interfaces/usb2can_libusb/can_8dev_usb_utils.py b/can/interfaces/usb2can_libusb/can_8dev_usb_utils.py new file mode 100644 index 000000000..b6836303d --- /dev/null +++ b/can/interfaces/usb2can_libusb/can_8dev_usb_utils.py @@ -0,0 +1,221 @@ +from enum import Enum + +MAX_8DEV_RECV_QUEUE = 128 # Maximum number of slots in the recv queue + +USB_8DEV_VENDOR_ID = ( + 0x0483 +) # Unfortunately this is actually the ST Microelectronics Vendor ID +USB_8DEV_PRODUCT_ID = 0x1234 # Unfortunately this is pretty bogus +USB_8DEV_PRODUCT_STRING = "USB2CAN converter" # So we use this instead. Not great. + +USB_8DEV_ABP_CLOCK = 32000000 + +# USB Bulk Endpoint identifiers + +USB_8DEV_ENDP_DATA_RX = 0x81 +USB_8DEV_ENDP_DATA_TX = 0x2 +USB_8DEV_ENDP_CMD_RX = 0x83 +USB_8DEV_ENDP_CMD_TX = 0x4 + +# Open Device Options + +USB_8DEV_SILENT = 0x01 +USB_8DEV_LOOPBACK = 0x02 +USB_8DEV_DISABLE_AUTO_RESTRANS = 0x04 +USB_8DEV_STATUS_FRAME = 0x08 + +# Command options +USB_8DEV_BAUD_MANUAL = 0x09 +USB_8DEV_CMD_START = 0x11 +USB_8DEV_CMD_END = 0x22 + +USB_8DEV_CMD_SUCCESS = 0 +USB_8DEV_CMD_ERROR = 255 + +USB_8DEV_CMD_TIMEOUT = 1000 + +# Framing definitions +USB_8DEV_DATA_START = 0x55 +USB_8DEV_DATA_END = 0xAA + +USB_8DEV_TYPE_CAN_FRAME = 0 +USB_8DEV_TYPE_ERROR_FRAME = 3 + +USB_8DEV_EXTID = 0x01 +USB_8DEV_RTR = 0x02 +USB_8DEV_ERR_FLAG = 0x04 + +# Status messages +USB_8DEV_STATUSMSG_OK = 0x00 +USB_8DEV_STATUSMSG_OVERRUN = 0x01 # Overrun occured when sending */ +USB_8DEV_STATUSMSG_BUSLIGHT = 0x02 # Error counter has reached 96 */ +USB_8DEV_STATUSMSG_BUSHEAVY = 0x03 # Error count. has reached 128 */ +USB_8DEV_STATUSMSG_BUSOFF = 0x04 # Device is in BUSOFF */ +USB_8DEV_STATUSMSG_STUFF = 0x20 # Stuff Error */ +USB_8DEV_STATUSMSG_FORM = 0x21 # Form Error */ +USB_8DEV_STATUSMSG_ACK = 0x23 # Ack Error */ +USB_8DEV_STATUSMSG_BIT0 = 0x24 # Bit1 Error */ +USB_8DEV_STATUSMSG_BIT1 = 0x25 # Bit0 Error */ +USB_8DEV_STATUSMSG_CRC = 0x27 # CRC Error */ + +USB_8DEV_RP_MASK = 0x7F # Mask for Receive Error Bit */ + +# Available Commands + + +class Can8DevCommand(Enum): + USB_8DEV_RESET = 1 # Reset Device + USB_8DEV_OPEN = 2 # Open Port + USB_8DEV_CLOSE = 3 # Close Port + USB_8DEV_SET_SPEED = 4 + USB_8DEV_SET_MASK_FILTER = ( + 5 + ) # Unfortunately unknown parameters and supposedly un-implemented on early firmwares + USB_8DEV_GET_STATUS = 6 + USB_8DEV_GET_STATISTICS = 7 + USB_8DEV_GET_SERIAL = 8 + USB_8DEV_GET_SOFTW_VER = 9 + USB_8DEV_GET_HARDW_VER = 0xA + USB_8DEV_RESET_TIMESTAMP = 0xB + USB_8DEV_GET_SOFTW_HARDW_VER = 0xC + + +class Can8DevTxFrame: + flags: int + id: int + dlc: int + data: bytes + + def __init__( + self, can_id: int, dlc: int, data: bytes, is_ext: bool, is_remote: bool + ): + self.can_id = can_id + self.dlc = dlc + self.data = data + self.flags = 0 + if is_ext: + self.flags |= USB_8DEV_EXTID + if is_remote: + self.flags |= USB_8DEV_RTR + + def _pad_data(self, data: bytes): + data_bytes = bytearray(8) + for i in range(0, 7): + if i < len(data): + data_bytes[i] = data[i] + return bytes(data_bytes) + + def to_bytes(self): + cmd_buf = bytearray() + cmd_buf.append(USB_8DEV_DATA_START) + cmd_buf.append(self.flags) + id_bytes = self.can_id.to_bytes(4, byteorder="big") + cmd_buf.extend(id_bytes) + cmd_buf.append(self.dlc) + cmd_buf.extend(self._pad_data(self.data)) + cmd_buf.append(USB_8DEV_DATA_END) + return bytes(cmd_buf) + + +class Can8DevRxFrame: + data: bytes + id: int + dlc: int + timestamp: int + ext_id: bool + is_error: bool + is_remote: bool + + def __init__(self, bytes_in: bytes): + if len(bytes_in) != 21: + raise ValueError("Did not receive 21 bytes for 8Dev Data Frame") + if bytes_in[0] != USB_8DEV_DATA_START: + raise ValueError("Did not receive a valid 8Dev Data Frame") + if bytes_in[1] == USB_8DEV_TYPE_CAN_FRAME: + self.data = bytes_in[8:16] + self.dlc = bytes_in[7] + self.ext_id = bytes_in[2] & USB_8DEV_EXTID + self.is_remote = bytes_in[2] & USB_8DEV_RTR + self.id = int.from_bytes(bytes_in[3:7], byteorder="big") + self.timestamp = int.from_bytes(bytes_in[16:20], byteorder="big") + self.is_error = False + elif bytes_in[1] == USB_8DEV_TYPE_ERROR_FRAME: + self.is_error = True + self.data = bytes_in[7:15] + self.timestamp = int.from_bytes(bytes_in[16:20], byteorder="big") + else: + raise ValueError("8Dev Data Frame with Unknown Type") + + +class Can8DevCommandFrame: + command: Can8DevCommand + opt1: int + opt2: int + data: bytes + + def __init__(self, command, data=bytes(), opt1=0, opt2=0): + self.command = command + self.data = data + self.opt1 = opt1 + self.opt2 = opt2 + + def _pad_data(self, data: bytes): + data_bytes = bytearray(10) + for i in range(0, 9): + if i < len(data): + data_bytes[i] = data[i] + return bytes(data_bytes) + + def to_bytes(self): + cmd_buf = bytearray() + cmd_buf.append(USB_8DEV_CMD_START) + cmd_buf.append(0) # Supposedly could be a channel value, but unknown + cmd_buf.append(self.command.value) + cmd_buf.append(self.opt1) + cmd_buf.append(self.opt2) + cmd_buf.extend(self._pad_data(self.data)) + cmd_buf.append(USB_8DEV_CMD_END) + return bytes(cmd_buf) + + def from_bytes(byte_input: bytes): + if len(byte_input) != 16: + raise ValueError("Did not receive 16 bytes for 8Dev Command Frame") + return Can8DevCommandFrame( + Can8DevCommand(byte_input[2]), + byte_input[5:15], + byte_input[3], + byte_input[4], + ) + + +def can_8dev_open_frame( + phase_seg1: int, + phase_seg2: int, + sjw: int, + brp: int, + loopback: bool = False, + listenonly: bool = False, + oneshot: bool = False, +) -> Can8DevCommandFrame: + open_command = Can8DevCommand.USB_8DEV_OPEN + opt1 = USB_8DEV_BAUD_MANUAL + flags = 0 + if loopback: + flags |= USB_8DEV_LOOPBACK + if listenonly: + flags |= USB_8DEV_SILENT + if oneshot: + flags |= USB_8DEV_DISABLE_AUTO_RESTRANS + flags_bytes = flags.to_bytes(4, "big") + brp_bytes = brp.to_bytes(2, "big") + data = bytearray(10) + data[0] = phase_seg1 + data[1] = phase_seg2 + data[2] = sjw + data[3] = brp_bytes[0] + data[4] = brp_bytes[1] + data[5] = flags_bytes[0] + data[6] = flags_bytes[1] + data[7] = flags_bytes[2] + data[8] = flags_bytes[3] + return Can8DevCommandFrame(open_command, data, opt1) diff --git a/can/interfaces/usb2can_libusb/usb2can_libusb_bus.py b/can/interfaces/usb2can_libusb/usb2can_libusb_bus.py new file mode 100644 index 000000000..e8932d010 --- /dev/null +++ b/can/interfaces/usb2can_libusb/usb2can_libusb_bus.py @@ -0,0 +1,114 @@ +""" +This interface requires LibUSB and `pyusb` to be installed on your system. +The interface will bind by default to the first device with VID +""" + +import logging +from ctypes import byref + +from can import BusABC, Message, BitTiming +from .can_8dev_usb_utils import * + +# Set up logging +log = logging.getLogger("can.usb2can_libusb") + +try: + from .can_8dev_usb_device import * +except NameError: + log.warning( + "The PyUSB module is not installed, but it is required for USB2Can_LibUSB support. Install it using `python3 -m pip install pyusb`" + ) + + +def message_convert_tx(msg): + """convert message from PythonCAN Message to 8Devices frame""" + return Can8DevTxFrame( + can_id=msg.arbitration_id, + dlc=msg.dlc, + data=msg.data, + is_ext=msg.is_extended_id, + is_remote=msg.is_remote_frame, + ) + + +def message_convert_rx(message_rx: Can8DevRxFrame): + """convert message from 8Devices frame to PythonCAN Message""" + + if message_rx.is_error: + return Message( + timestamp=message_rx.timestamp / 1000, + is_error_frame=message_rx.is_error, + data=message_rx.data, + ) + + return Message( + timestamp=message_rx.timestamp / 1000, + is_remote_frame=message_rx.is_remote, + is_extended_id=message_rx.ext_id, + is_error_frame=message_rx.is_error, + arbitration_id=message_rx.id, + dlc=message_rx.dlc, + data=message_rx.data[: message_rx.dlc], + ) + + +class Usb2CanLibUsbBus(BusABC): + """Interface to an 8Devices USB2CAN Bus. + + This device should work on any platform with a working LibUSB and PyUSB. It was tested with a "Korlan USB2Can" but should work with the older module as well. + + Hardware filtering is not provided, if anyone knows how the 8Devices filtering command works, this would be valuable. + + Based on the in-tree Linux kernel SocketCAN driver for USB2CAN. + + :param str channel (optional): + The device's serial number. If not provided, the first matching VID/DID will match (WARNING: 8Devices reuse a random ST VID/DID, so other devices may match!) + + :param int bitrate (optional): + Bitrate of channel in bit/s. Values will be limited to a maximum of 1000 Kb/s. + Default is 500 Kbs + + :param int flags (optional): + Flags to directly pass to open function of the usb2can abstraction layer. + """ + + def __init__(self, channel=None, *args, bitrate=500000, **kwargs): + + self.can = Can8DevUSBDevice(channel) + + # convert to kb/s and cap: max rate is 1000 kb/s + baudrate = min(int(bitrate // 1000), 1000) + + self.channel_info = "USB2CAN LibUSB device {}".format( + self.can.get_serial_number() + ) + + connector = "{}; {}".format("USB2Can_LibUSB", baudrate) + + timing = BitTiming( + tseg1=6, tseg2=1, sjw=1, bitrate=bitrate, f_clock=USB_8DEV_ABP_CLOCK + ) + self.can.open(timing.tseg1, timing.tseg2, timing.sjw, timing.brp) + + super().__init__(channel=channel, bitrate=bitrate, *args, **kwargs) + + def send(self, msg, timeout=None): + tx = message_convert_tx(msg) + if timeout is not None: + timeout *= 1000 + self.can.send(tx, timeout) + + def _recv_internal(self, timeout): + if timeout is not None: + timeout *= 1000 + messagerx = self.can.recv(timeout) + rx = None + if messagerx is not None: + rx = message_convert_rx(messagerx) + return rx, False + + def shutdown(self): + """ + Shuts down connection to the device. + """ + self.can.close() diff --git a/examples/libusb_receive_all.py b/examples/libusb_receive_all.py new file mode 100644 index 000000000..ac505d648 --- /dev/null +++ b/examples/libusb_receive_all.py @@ -0,0 +1,14 @@ +import can + +filters= [{"can_id": 0x1, "can_mask": 0x3, "extended": False}, + {"can_id": 0x2, "can_mask": 0x3, "extended": True}, + {"can_id": 0x3, "can_mask": 0x3}] +with can.interface.Bus(bustype="usb2can_libusb", channel="C454B93C", bitrate=1000000, can_filters=filters) as bus: + try: + while True: + msg = bus.recv(1) + if msg is not None: + print(msg) + except KeyboardInterrupt: + pass # exit normally + diff --git a/examples/pyusblist.py b/examples/pyusblist.py new file mode 100644 index 000000000..c6bf83a8c --- /dev/null +++ b/examples/pyusblist.py @@ -0,0 +1,11 @@ +#!/usr/bin/python +import sys +import usb.core + +# find USB devices +dev = usb.core.find(find_all=True) +# loop through devices, printing vendor and product ids in decimal and hex + +for cfg in dev: + sys.stdout.write('Decimal VendorID=' + str(cfg.idVendor) + ' ProductID=' + str(cfg.idProduct) + ' serial=' + str(cfg.serial_number)+'\n') + sys.stdout.write('Hexadecimal VendorID=' + hex(cfg.idVendor) + ' ProductID=' + hex(cfg.idProduct) + '\n\n') \ No newline at end of file diff --git a/examples/send_one_korlan.py b/examples/send_one_korlan.py new file mode 100644 index 000000000..d604a9be4 --- /dev/null +++ b/examples/send_one_korlan.py @@ -0,0 +1,18 @@ +import can +from time import sleep +from importlib import reload + +# +for j in range(1): + with (can.interface.Bus(bustype="usb2can_libusb", channel="CDE7E976", bitrate=1000000)) as bus: + for i in range(200): + msg = can.Message(arbitration_id=0x1, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=False) + try: + bus.send(msg) + #print(f"Message {i} sent on {bus.channel_info}") + except can.CanError: + print("Message NOT sent") + bus.shutdown() + print(f"closing after series {j+1}") + #sleep(1) + #reload(can)