Skip to content

Commit e381a64

Browse files
authored
Merge pull request #905 from jxltom/feature/gs-usb-support
The gs_usb interface support
2 parents 72c4ac9 + 17f05d7 commit e381a64

File tree

5 files changed

+173
-0
lines changed

5 files changed

+173
-0
lines changed

can/interfaces/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"systec": ("can.interfaces.systec", "UcanBus"),
2626
"seeedstudio": ("can.interfaces.seeedstudio", "SeeedBus"),
2727
"cantact": ("can.interfaces.cantact", "CantactBus"),
28+
"gs_usb": ("can.interfaces.gs_usb", "GsUsbBus"),
2829
}
2930

3031
BACKENDS.update(

can/interfaces/gs_usb.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
from typing import cast, Any, Iterator, List, Optional, Sequence, Tuple, Union
2+
3+
from gs_usb.gs_usb import GsUsb
4+
from gs_usb.gs_usb_frame import GsUsbFrame
5+
from gs_usb.constants import CAN_ERR_FLAG, CAN_RTR_FLAG, CAN_EFF_FLAG, CAN_MAX_DLC
6+
import can
7+
import usb
8+
import logging
9+
10+
11+
logger = logging.getLogger(__name__)
12+
13+
14+
class GsUsbBus(can.BusABC):
15+
def __init__(self, channel, bus, address, bitrate, can_filters=None, **kwargs):
16+
"""
17+
:param channel: usb device name
18+
:param bus: number of the bus that the device is connected to
19+
:param address: address of the device on the bus it is connected to
20+
:param can_filters: not supported
21+
:param bitrate: CAN network bandwidth (bits/s)
22+
"""
23+
gs_usb = GsUsb.find(bus=bus, address=address)
24+
if not gs_usb:
25+
raise can.CanError("Can not find device {}".format(channel))
26+
self.gs_usb = gs_usb
27+
self.channel_info = channel
28+
29+
self.gs_usb.set_bitrate(bitrate)
30+
self.gs_usb.start()
31+
32+
super().__init__(channel=channel, can_filters=can_filters, **kwargs)
33+
34+
def send(self, msg: can.Message, timeout: Optional[float] = None):
35+
"""Transmit a message to the CAN bus.
36+
37+
:param Message msg: A message object.
38+
:param timeout: timeout is not supported.
39+
The function won't return until message is sent or exception is raised.
40+
41+
:raises can.CanError:
42+
if the message could not be sent
43+
"""
44+
can_id = msg.arbitration_id
45+
46+
if msg.is_extended_id:
47+
can_id = can_id | CAN_EFF_FLAG
48+
49+
if msg.is_remote_frame:
50+
can_id = can_id | CAN_RTR_FLAG
51+
52+
if msg.is_error_frame:
53+
can_id = can_id | CAN_ERR_FLAG
54+
55+
# Pad message data
56+
msg.data.extend([0x00] * (CAN_MAX_DLC - len(msg.data)))
57+
58+
frame = GsUsbFrame()
59+
frame.can_id = can_id
60+
frame.can_dlc = msg.dlc
61+
frame.timestamp_us = int(msg.timestamp * 1000000)
62+
frame.data = list(msg.data)
63+
64+
try:
65+
self.gs_usb.send(frame)
66+
except usb.core.USBError:
67+
raise can.CanError("The message can not be sent")
68+
69+
def _recv_internal(
70+
self, timeout: Optional[float]
71+
) -> Tuple[Optional[can.Message], bool]:
72+
"""
73+
Read a message from the bus and tell whether it was filtered.
74+
This methods may be called by :meth:`~can.BusABC.recv`
75+
to read a message multiple times if the filters set by
76+
:meth:`~can.BusABC.set_filters` do not match and the call has
77+
not yet timed out.
78+
79+
:param float timeout: seconds to wait for a message,
80+
see :meth:`~can.BusABC.send`
81+
0 and None will be converted to minimum value 1ms.
82+
83+
:return:
84+
1. a message that was read or None on timeout
85+
2. a bool that is True if message filtering has already
86+
been done and else False. In this interface it is always False
87+
since filtering is not available
88+
89+
:raises can.CanError:
90+
if an error occurred while reading
91+
"""
92+
frame = GsUsbFrame()
93+
94+
# Do not set timeout as None or zero here to avoid blocking
95+
timeout_ms = round(timeout * 1000) if timeout else 1
96+
if not self.gs_usb.read(frame=frame, timeout_ms=timeout_ms):
97+
return None, False
98+
99+
msg = can.Message(
100+
timestamp=frame.timestamp,
101+
arbitration_id=frame.arbitration_id,
102+
is_extended_id=frame.can_dlc,
103+
is_remote_frame=frame.is_remote_frame,
104+
is_error_frame=frame.is_error_frame,
105+
channel=self.channel_info,
106+
dlc=frame.can_dlc,
107+
data=bytearray(frame.data)[0 : frame.can_dlc],
108+
is_rx=True,
109+
)
110+
111+
return msg, False
112+
113+
def shutdown(self):
114+
"""
115+
Called to carry out any interface specific cleanup required
116+
in shutting down a bus.
117+
"""
118+
self.gs_usb.stop()

doc/interfaces.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ The available interfaces are:
2727
interfaces/canalystii
2828
interfaces/systec
2929
interfaces/seeedstudio
30+
interfaces/gs_usb
3031

3132
Additional interfaces can be added via a plugin interface. An external package
3233
can register a new interface by using the ``can.interface`` entry point in its setup.py.

doc/interfaces/gs_usb.rst

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
.. _gs_usb:
2+
3+
CAN driver for Geschwister Schneider USB/CAN devices and bytewerk.org candleLight USB CAN interfaces
4+
==================================================================================================================
5+
6+
Windows/Linux/Mac CAN driver based on usbfs or WinUSB WCID for Geschwister Schneider USB/CAN devices and candleLight USB CAN interfaces.
7+
8+
Install: ``pip install "python-can[gs_usb]"``
9+
10+
Usage: pass ``bus`` and ``address`` to open the device. The parameters can be got by ``pyusb`` as shown below:
11+
12+
::
13+
14+
import usb
15+
import can
16+
17+
dev = usb.core.find(idVendor=0x1D50, idProduct=0x606F)
18+
bus = can.Bus(bustype="gs_usb", channel=dev.product, bus=dev.bus, address=dev.address, bitrate=250000)
19+
20+
21+
Supported devices
22+
-----------------
23+
24+
Geschwister Schneider USB/CAN devices and bytewerk.org candleLight USB CAN interfaces such as candleLight, canable, cantact, etc.
25+
26+
27+
Supported platform
28+
------------------
29+
30+
Windows, Linux and Mac.
31+
32+
Note: Since ``pyusb`` with ```libusb0``` as backend is used, ``libusb-win32`` usb driver is required to be installed in Windows.
33+
34+
35+
Supplementary Info on ``gs_usb``
36+
-----------------------------------
37+
38+
The firmware implementation for Geschwister Schneider USB/CAN devices and candleLight USB CAN can be found in `candle-usb/candleLight_fw <https://github.com/candle-usb/candleLight_fw>`_.
39+
The Linux kernel driver can be found in `linux/drivers/net/can/usb/gs_usb.c <https://github.com/torvalds/linux/blob/master/drivers/net/can/usb/gs_usb.c>`_.
40+
41+
The ``gs_usb`` interface in ``PythonCan`` relys on upstream ``gs_usb`` package, which can be found in `https://pypi.org/project/gs-usb/ <https://pypi.org/project/gs-usb/>`_ or `https://github.com/jxltom/gs_usb <https://github.com/jxltom/gs_usb>`_.
42+
The ``gs_usb`` package is using ``pyusb`` as backend, which brings better crossplatform compatibility.
43+
44+
Note: The bitrate ``10K``, ``20K``, ``50K``, ``83.333K``, ``100K``, ``125K``, ``250K``, ``500K``, ``800K`` and ``1M`` are supported in this interface, as implemented in the upstream ``gs_usb`` package's ``set_bitrate`` method.
45+
46+
Note: Message filtering is not supported in Geschwister Schneider USB/CAN devices and bytewerk.org candleLight USB CAN interfaces.
47+
48+
Bus
49+
---
50+
51+
.. autoclass:: can.interfaces.gs_usb.GsUsbBus
52+
:members:

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"serial": ["pyserial~=3.0"],
3131
"neovi": ["python-ics>=2.12"],
3232
"cantact": ["cantact>=0.0.7"],
33+
"gs_usb": ["gs_usb>=0.2.1"],
3334
}
3435

3536
setup(

0 commit comments

Comments
 (0)