Skip to content

Commit 16cdef1

Browse files
committed
Add README, fix constructor failure case
1 parent 92189fa commit 16cdef1

2 files changed

Lines changed: 38 additions & 2 deletions

File tree

README.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,38 @@ Unofficial Python userspace driver for the low cost USB analyzer "Canalyst-II".
44

55
Uses [pyusb](https://pyusb.github.io/pyusb/) library for USB support, should work on Windows, MacOS and Linux.
66

7-
This driver is based on black box reverse engineering and the original python-can canalystii source.
7+
This driver is based on black box reverse engineering and the original python-can canalystii source. It's mostly intended for use with python-can, but can also be used standalone.
88

9+
## Usage
10+
11+
```py
12+
import canalystii
13+
14+
# Connect to the Canalyst-II device
15+
dev = canalystii.CanalystDevice(bitrate=500*1000)
16+
17+
# Receive all pending messages on channel 0
18+
for msg in dev.receive(0):
19+
print(msg)
20+
21+
# The canalystii.Message class is a ctypes Structure, to minimize overhead
22+
new_message = canalystii.Message(can_id=0x300,
23+
remote=False,
24+
extended=False,
25+
data_len=8,
26+
data=(0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08))
27+
# Send one copy to channel 1
28+
dev.send(1, new_message)
29+
# Send 3 copies to channel 0 (argument can be any iterable, or single instance of canalystii.Message)
30+
dev.send(0, [new_message] * 3)
31+
```
32+
33+
## Performance
34+
35+
Because the Canalyst-II USB protocol requires polling, there is a trade-off between CPU usage and both latency and maximum receive throughput. The host needs to constantly poll the device to request any new CAN messages.
36+
37+
The hardware seems able to buffer 1000-2000 messages (possibly a little more) per channel. The maximum number seems to depend on relative timing of the messages. Therefore, a 1Mbps (maximum speed) CAN channel receiving the maximum possible ~7800 messages/second should call `receive()` at least every 100ms in order to avoid lost messages. The USB protocol doesn't provide any way to tell if any messages in the hardware buffer were lost.
38+
39+
Testing Linux CPython 3.9 on an older i7-6500U CPU, calling `receive()` in a tight loop while receiving maximum message rate (~7800 messages/sec) on both channels (~15600 messages/sec total) uses approximately 40% of a single CPU. Adding a 50ms delay `time.sleep(0.05)` in the loop drops CPU usage to around 10% without losing any messages. Longer sleep periods in the loop reduce CPU usage further but some messages are dropped. See the `tests/can_spammer_test.py` file for the test code.
40+
41+
In systems where the CAN message rate is lower than the maximum, `receive()` can be called less frequently without losing messages. In systems where the Python process may be pre-empted, it's possible for messages to be lost anyhow.

canalystii/device.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,10 @@ def __del__(self):
7272
# In theory pyusb should manage this, but in order to allow a new device
7373
# object to be created later (in the same process) it seems the device needs to be reset (which
7474
# calls dispose internally)
75-
self._dev.reset()
75+
try:
76+
self._dev.reset()
77+
except AttributeError:
78+
pass
7679

7780
def clear_rx_buffer(self, channel):
7881
"""Clears the device's receive buffer for the specified channel.

0 commit comments

Comments
 (0)