python-can-cansub 2026.7.2__tar.gz → 2026.9.24__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {python_can_cansub-2026.7.2 → python_can_cansub-2026.9.24}/.gitignore +1 -0
  2. python_can_cansub-2026.9.24/CHANGELOG.md +51 -0
  3. {python_can_cansub-2026.7.2 → python_can_cansub-2026.9.24}/PKG-INFO +153 -61
  4. {python_can_cansub-2026.7.2 → python_can_cansub-2026.9.24}/README.md +149 -59
  5. {python_can_cansub-2026.7.2 → python_can_cansub-2026.9.24}/pyproject.toml +17 -4
  6. python_can_cansub-2026.9.24/src/python_can_cansub/__init__.py +22 -0
  7. python_can_cansub-2026.9.24/src/python_can_cansub/cansub.py +1356 -0
  8. python_can_cansub-2026.9.24/src/python_can_cansub/cansub_csv.py +125 -0
  9. {python_can_cansub-2026.7.2 → python_can_cansub-2026.9.24}/src/python_can_cansub/cansub_protocol.py +6 -3
  10. python_can_cansub-2026.9.24/tests/README.md +14 -0
  11. python_can_cansub-2026.9.24/tests/bus/test_open_close.py +167 -0
  12. python_can_cansub-2026.9.24/tests/cmdline/test_cmdline.py +146 -0
  13. python_can_cansub-2026.9.24/tests/conftest.py +79 -0
  14. python_can_cansub-2026.9.24/tests/detect/test_detect.py +26 -0
  15. python_can_cansub-2026.9.24/tests/errors/test_error_frames.py +26 -0
  16. python_can_cansub-2026.9.24/tests/filters/test_filters.py +64 -0
  17. python_can_cansub-2026.9.24/tests/io/test_csv_io.py +127 -0
  18. python_can_cansub-2026.9.24/tests/periodic/test_periodic.py +65 -0
  19. python_can_cansub-2026.9.24/tests/send_recv/test_send_recv.py +110 -0
  20. python_can_cansub-2026.7.2/CHANGELOG.md +0 -26
  21. python_can_cansub-2026.7.2/private_repo/README.md +0 -1
  22. python_can_cansub-2026.7.2/private_repo/README_BUILD.md +0 -6
  23. python_can_cansub-2026.7.2/private_repo/README_DEV.md +0 -5
  24. python_can_cansub-2026.7.2/private_repo/README_PYPI.md +0 -58
  25. python_can_cansub-2026.7.2/private_repo/README_REPO.md +0 -43
  26. python_can_cansub-2026.7.2/src/python_can_cansub/__init__.py +0 -4
  27. python_can_cansub-2026.7.2/src/python_can_cansub/cansub.py +0 -975
  28. {python_can_cansub-2026.7.2 → python_can_cansub-2026.9.24}/src/python_can_cansub/cansub_root_cert.crt +0 -0
@@ -6,6 +6,7 @@
6
6
 
7
7
  # Virtual environment
8
8
  .venv/
9
+ venv/
9
10
 
10
11
  # Generated by MacOS
11
12
  .DS_Store
@@ -0,0 +1,51 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## 2026.09.24 (API 05.00)
6
+
7
+ ### Added
8
+ - `README.md` is now shipped inside the installed package (`python_can_cansub/README.md`)
9
+
10
+ ### Changed
11
+ - Supported device API version changed from `04.00` to `05.00`.
12
+
13
+ ## 2026.08.17 (API 04.00)
14
+
15
+ ### Added
16
+ - `max_device_time_skew`: device/host clock skew (s) tolerated before setting the device clock on open (`None`=never).
17
+ - `receive_own_messages`: receive the messages transmitted by the bus itself (default `False`).
18
+
19
+ ### Changed
20
+ - Supported device API version changed from `03.00` to `04.00`.
21
+ - The device hostname is now resolved only once on bus open, reducing connection time.
22
+ - `data_bitrate` is now optional. When omitted, transmissions of FD frames are rejected.
23
+ - Updated shutdown sequence (using `DELETE` on the websocket resource after `shutdown_timeout` timeout).
24
+
25
+ ### Removed
26
+ - `server_cert`: the server certificate is now always verified against the built-in CANsub root certificate.
27
+ When connecting via an IP address, certificate hostname verification is automatically disabled
28
+ (certificate chain verification remains active).
29
+
30
+ ## 2026.07.02 (API 03.00)
31
+
32
+ ### Added
33
+ - `server_cert`: path to the server certificate (`.crt` file).
34
+ - `client_cert`: tuple of paths to the client certificate and key (`.crt`, `.key`), used for TLS mutual authentication.
35
+ - Warning when devices with unsupported API versions are found.
36
+
37
+ ### Changed
38
+ - Supported device API version changed from `02.00` to `03.00`.
39
+
40
+ ## 2026.06.02 (API 02.00)
41
+
42
+ ### Added
43
+ - `CANSUB_ROOT_CERT`: path to the CANsub root certificate.
44
+
45
+ ### Changed
46
+ - Transmit sequences (on the device) are deleted on bus open.
47
+
48
+ ## 2026.06.01 (API 02.00)
49
+
50
+ ### Fixed
51
+ - `CyclicSendTask` with `duration=None` now working.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: python-can-cansub
3
- Version: 2026.7.2
3
+ Version: 2026.09.24
4
4
  Summary: CANsub python-can interface
5
5
  Project-URL: Homepage, https://csselectronics.com/
6
6
  Project-URL: Source, https://github.com/CSS-Electronics/python-can-cansub
@@ -16,6 +16,8 @@ Requires-Dist: python-can>=4.6.0
16
16
  Requires-Dist: requests>=2.32.5
17
17
  Requires-Dist: wsproto>=1.3.2
18
18
  Requires-Dist: zeroconf>=0.131.0
19
+ Provides-Extra: test
20
+ Requires-Dist: pytest>=8.0; extra == 'test'
19
21
  Description-Content-Type: text/markdown
20
22
 
21
23
  # python-can-cansub
@@ -31,11 +33,11 @@ This package registers the CANsub as a standard python-can interface, making it
31
33
  The python-can-cansub package and the CANsub device communicate over a versioned API. They are compatible when the package supports the API version used by the device firmware.
32
34
 
33
35
  - Each **python-can-cansub** release supports one API version. The supported API version for each release is listed in the [python-can-cansub changelog](https://github.com/CSS-Electronics/python-can-cansub/blob/master/CHANGELOG.md).
34
- - Each **CANsub firmware** release uses one API version. The API version for each firmware release is listed in the CANsub changelog (provided with the device).
36
+ - Each **CANsub firmware** release uses one API version. The API version for each firmware release is listed in the CANsub changelog (provided with the device documentation).
35
37
 
36
- To check compatibility, look up the API version of the package release and of the device firmware release in their respective changelogs. If they match, the two are compatible.
38
+ To check compatibility, look up the API version of the python-can-cansub release and of the device firmware release in their respective changelogs. If they match, they are compatible.
37
39
 
38
- If the package finds a connected device whose API version it does not support, it emits a warning. Update the package or the device firmware so their API versions align.
40
+ Device *auto-detection* (see *Configuration*) skips devices with an unsupported API version and emits a `UserWarning`. Opening a bus on such a device raises `can.exceptions.CanInitializationError`. In either case, update the package or the device firmware so their API versions align.
39
41
 
40
42
  ## python-can API
41
43
 
@@ -55,16 +57,16 @@ import can
55
57
 
56
58
  ### Configuration
57
59
 
58
- Python-can defines a hardware *configuration* by an `interface` and a `channel` (a single interface can have multiple channels).
60
+ In python-can, a hardware *configuration* is defined by an `interface` and a `channel` (a single interface can have multiple channels).
59
61
 
60
- The CANsub `interface` is fixed `"cansub"`. The `channel` is constructed from the device hostname (unique) and channel index.
62
+ The CANsub `interface` is always `"cansub"`. The `channel` is constructed from the device's unique hostname and the channel index.
61
63
 
62
64
  | Connection | Hostname | python-can `channel` string |
63
65
  |------------|-------------------------|-----------------------------------|
64
66
  | USB | `[DEVICE-ID]-usb.local` | `[DEVICE-ID]-usb.local@[channel]` |
65
67
  | Ethernet | `[DEVICE-ID]-eth.local` | `[DEVICE-ID]-eth.local@[channel]` |
66
68
 
67
- The device-ID is printed on the device label. Channel indexing is **1-based** - the first channel is `1`.
69
+ The `[DEVICE-ID]` is printed on the device label. Channel indexing is **1-based** - the first channel is `1`.
68
70
 
69
71
  A configuration is passed to `can.Bus` to open a bus.
70
72
 
@@ -79,7 +81,7 @@ configs = [{"interface": "cansub", "channel": "aabbccdd-usb.local@1"},
79
81
 
80
82
  #### Auto-detect
81
83
 
82
- Example of using `detect_available_configs` to automatically discover (uses mDNS) all connected CANsub devices and channels:
84
+ Example of using `detect_available_configs` to automatically discover all connected CANsub devices and channels via mDNS:
83
85
 
84
86
  ```python
85
87
  configs = can.detect_available_configs(interfaces=["cansub"])
@@ -89,73 +91,62 @@ configs = can.detect_available_configs(interfaces=["cansub"])
89
91
  # {"interface": "cansub", "channel": "11223344-eth.local@2"}]
90
92
  ```
91
93
 
92
- In the above example two CANsub devices are detected, each with two channels. One device is connected via USB and the other via Ethernet.
94
+ In the above example, two CANsub devices are detected, each with two channels. One device is connected via USB and the other via Ethernet.
93
95
 
94
- ### Opening a Bus
95
-
96
- > **Note:** `data_bitrate` is required even on a classic (non-FD) bus. On a non-FD bus, simply set it to e.g. `1_000_000` (1 Mbit/s).
96
+ > **Note:** mDNS discovery relies on inbound UDP port 5353. If a host firewall blocks it, no devices are detected (a fixed configuration still works).
97
97
 
98
- > **Tip:** Pass `listen_only=True` to `can.Bus` to monitor a bus without transmitting or acknowledging frames.
98
+ ### Opening a Bus
99
99
 
100
- > **Tip:** Pass `error_frames=True` to `can.Bus` to receive error frames.
100
+ The python-can-cansub constructor implements the required arguments for can.Bus and adds custom arguments specific to python-can-cansub. See the python-can-cansub constructor docstring for additional information.
101
101
 
102
- #### Single bus - hardcoded
102
+ A bus is opened by passing a configuration to `can.Bus`:
103
103
 
104
104
  ```python
105
105
  with can.Bus(interface="cansub", channel="aabbccdd-usb.local@1", bitrate=250_000, data_bitrate=1_000_000) as bus:
106
106
  pass
107
107
  ```
108
108
 
109
- #### Single bus - from configs
109
+ `**config` unpacks a config dict directly into `can.Bus` keyword arguments - convenient with auto-detected configs, and for opening multiple buses:
110
110
 
111
111
  ```python
112
- with can.Bus(interface=configs[0]["interface"], channel=configs[0]["channel"], bitrate=250_000, data_bitrate=1_000_000) as bus:
112
+ with (can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000) as bus1,
113
+ can.Bus(**configs[1], bitrate=250_000, data_bitrate=1_000_000) as bus2):
113
114
  pass
114
115
  ```
115
116
 
116
- #### Multiple buses - from configs
117
+ ### Device Time
118
+
119
+ On open, the device clock is compared to the host clock and set to the host time (UTC) if the two differ by more than `max_device_time_skew` seconds (default `1.0`). Pass `0.0` to always set the device clock, or `None` to never set it.
120
+
121
+ > **Note:** Setting the device clock overrides and disables the device time synchronization (PTP) for the rest of the device power cycle. When using PTP, pass `max_device_time_skew=None` to leave the synchronized device clock untouched:
117
122
 
118
123
  ```python
119
- with (can.Bus(interface=configs[0]["interface"], channel=configs[0]["channel"], bitrate=250_000, data_bitrate=1_000_000) as bus1,
120
- can.Bus(interface=configs[1]["interface"], channel=configs[1]["channel"], bitrate=250_000, data_bitrate=1_000_000) as bus2):
124
+ with can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000, max_device_time_skew=None) as bus:
121
125
  pass
122
126
  ```
123
127
 
124
- > **Tip:** `**config` unpacks a config dict directly into `can.Bus` keyword arguments:
125
- >
126
- > ```python
127
- > with can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000) as bus:
128
- > pass
129
- > ```
128
+ ### Bit Timing
130
129
 
131
- ### TLS / Certificates
132
-
133
- The data connection to the device is secured by TLS. When connecting via an IP address (where hostname verification will fail), `server_cert=None` can be used to disable certificate validation:
130
+ `bitrate` and `data_bitrate` configure the bus with a fixed sample point of 80%. For full control of the bit timing (sample point, SJW), pass a `can.BitTiming` (classic CAN) or `can.BitTimingFd` (CAN FD) as `timing` instead. The CANsub CAN clock is 80 MHz:
134
131
 
135
132
  ```python
136
- with can.Bus(interface="cansub", channel="192.168.1.10@1", server_cert=None, bitrate=250_000, data_bitrate=1_000_000) as bus:
133
+ timing = can.BitTimingFd.from_sample_point(f_clock=80_000_000,
134
+ nom_bitrate=250_000, nom_sample_point=87.5,
135
+ data_bitrate=1_000_000, data_sample_point=87.5)
136
+
137
+ with can.Bus(**configs[0], timing=timing) as bus:
137
138
  pass
138
139
  ```
139
140
 
140
- If TLS mutual authentication is enabled, `client_cert` can be used to provide a tuple of paths to the client certificate (`.crt` file) and its unencrypted private key (`.key` file), i.e. `("cert", "key")`:
141
+ ### TLS / Certificates
141
142
 
142
- ```python
143
- with can.Bus(**configs[0], client_cert=("/path/to/client.crt", "/path/to/client.key"), bitrate=250_000, data_bitrate=1_000_000) as bus:
144
- pass
145
- ```
143
+ The data connection to the device is secured by TLS, with the device certificate verified against the built-in CANsub root certificate. When connecting via an IP address (which carries no name to verify the certificate hostname against), hostname verification is automatically disabled - the certificate chain is still verified.
146
144
 
147
- ### Receive and Transmit
145
+ When TLS mutual authentication is enabled, `client_cert` can be used to provide a tuple of paths to the client certificate (`.crt` file) and its unencrypted private key (`.key` file):
148
146
 
149
147
  ```python
150
- with can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000) as bus:
151
-
152
- # Transmit
153
- msg_tx = can.Message(is_extended_id=False, arbitration_id=0x123, data=[0x01, 0x02, 0x03, 0x04])
154
- bus.send(msg_tx)
155
-
156
- # Receive with timeout
157
- msg_rx = bus.recv(timeout=1.0)
158
- print(msg_rx)
148
+ with can.Bus(**configs[0], client_cert=("/path/to/client.crt", "/path/to/client.key"), bitrate=250_000, data_bitrate=1_000_000) as bus:
149
+ pass
159
150
  ```
160
151
 
161
152
  ### Error Frames
@@ -172,6 +163,15 @@ with can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000, error_frames
172
163
  print(f"Bus error: {error_type.name}") # e.g. "Bus error: ACK"
173
164
  ```
174
165
 
166
+ ### Bus State
167
+
168
+ `bus.state` queries the current channel state: `can.BusState.ACTIVE` (error-active or error-warning), `can.BusState.PASSIVE` (error-passive), or `can.BusState.ERROR` (bus-off). Reading the state of a stopped channel (e.g. after a connection loss) or of a closed bus raises `can.exceptions.CanOperationError`:
169
+
170
+ ```python
171
+ with can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000) as bus:
172
+ print(bus.state) # e.g. "BusState.ACTIVE"
173
+ ```
174
+
175
175
  ### Filters
176
176
 
177
177
  Apply hardware filters by passing `can_filters` to `can.Bus`. Each filter specifies a `can_id`, a `can_mask`, and whether to match standard (`extended=False`) or extended (`extended=True`) frames. A frame passes if `(frame_id & can_mask) == (can_id & can_mask)`.
@@ -189,6 +189,85 @@ with can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000, can_filters=
189
189
 
190
190
  > **Tip:** Applying hardware filters reduces the network load between the CANsub and the connected client.
191
191
 
192
+ ### Receive and Transmit
193
+
194
+ A bus receives the messages transmitted by the other nodes on the CAN bus. Messages transmitted by the bus itself are not received, unless the bus is opened with `receive_own_messages=True`.
195
+
196
+ ```python
197
+ with can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000) as bus:
198
+
199
+ # Transmit
200
+ msg_tx = can.Message(is_extended_id=False, arbitration_id=0x123, data=[0x01, 0x02, 0x03, 0x04])
201
+ bus.send(msg_tx)
202
+
203
+ # Receive with timeout
204
+ msg_rx = bus.recv(timeout=1.0)
205
+ print(msg_rx)
206
+ ```
207
+
208
+ #### TX Acknowledgement
209
+
210
+ With `receive_own_messages=True`, the bus receives its own transmitted messages (marked with `is_rx=False`) once they have been acknowledged on the CAN bus. This tx-ack can be used to wait for a message to be transmitted before proceeding, e.g. transmitting the next message:
211
+
212
+ ```python
213
+ msgs = [
214
+ can.Message(is_extended_id=False, arbitration_id=0x123, data=[0x01, 0x02, 0x03, 0x04]),
215
+ can.Message(is_extended_id=False, arbitration_id=0x123, data=[0x05, 0x06, 0x07, 0x08]),
216
+ ]
217
+
218
+ with can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000, receive_own_messages=True) as bus:
219
+ for msg_tx in msgs:
220
+ # Transmit
221
+ bus.send(msg_tx)
222
+
223
+ # Wait for the tx-ack before transmitting the next message
224
+ msg_ack = bus.recv()
225
+ print(msg_ack)
226
+ ```
227
+
228
+ #### CAN FD
229
+
230
+ CAN FD frames are transmitted by setting `is_fd` (and typically `bitrate_switch`, which switches to `data_bitrate` for the payload). FD payloads can be up to 64 bytes. Transmitting an FD frame requires the bus to be opened with a `data_bitrate` (or an FD bit timing); on a classic CAN bus, FD transmission raises `can.exceptions.CanOperationError`:
231
+
232
+ ```python
233
+ with can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000) as bus:
234
+ msg_fd = can.Message(is_extended_id=False, arbitration_id=0x123,
235
+ is_fd=True, bitrate_switch=True, data=bytes(range(64)))
236
+ bus.send(msg_fd)
237
+ ```
238
+
239
+ ### Error Handling
240
+
241
+ Operations on a failed bus raise `can.exceptions.CanOperationError`: `send()` and `recv()` raise it once the connection to the device is lost (detected within seconds, also on an idle bus). `send()` additionally raises `can.exceptions.CanTimeoutError` when the transmit queue stays full for the full timeout (back-pressure from a slow or blocked CAN bus). Failures to open a bus raise `can.exceptions.CanInitializationError`.
242
+
243
+ A failed bus does not recover, and the package does not reconnect automatically. To recover from a connection loss, close the failed bus and open a new one:
244
+
245
+ ```python
246
+ try:
247
+ msg = bus.recv(timeout=1.0)
248
+ except can.CanOperationError:
249
+ # Connection lost: the bus cannot recover - close it and open a new one
250
+ bus.shutdown()
251
+ bus = can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000)
252
+ ```
253
+
254
+ ### Closing a Bus
255
+
256
+ Closing a bus (leaving the `with` block, or calling `bus.shutdown()`) waits up to `shutdown_timeout` seconds for the queued messages to be transmitted before asking the device to discard them (see the *Opening a Bus*).
257
+
258
+ Messages received up to (and during) the close remain retrievable with `recv()` after the bus is closed - drain until `None`:
259
+
260
+ ```python
261
+ with can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000) as bus:
262
+ bus.send(can.Message(is_extended_id=False, arbitration_id=0x123, data=[0x01, 0x02, 0x03, 0x04]))
263
+
264
+ # The bus is closed (all queued messages transmitted)
265
+ ```
266
+
267
+ > **Tip:** When two buses are connected to the same physical CAN bus (e.g. a transmitter and a receiver), close the transmitting bus first - closing it waits for the queued messages to be transmitted, which requires the other (acknowledging) bus to still be open.
268
+
269
+ > **Tip:** A `can.Notifier` stops dispatching the moment it is stopped - received messages not yet dispatched are discarded (python-can behavior). Stop the notifier only once the traffic has settled, or read the messages with `recv()` instead.
270
+
192
271
  ### Notifier and Listeners
193
272
 
194
273
  `bus.recv()` blocks until a frame arrives. A `can.Notifier` runs a background thread that dispatches received frames to one or more *listeners*, allowing the main program to continue other work.
@@ -209,15 +288,17 @@ with can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000) as bus:
209
288
 
210
289
  Periodic transmission jobs can be started with `bus.send_periodic()`.
211
290
 
212
- Most periodic transmission job types can be offloaded to the CANsub hardware, providing much better transmission time accuracy (compared to a host-scheduled transmission). A host-side background task is used only as a fallback when hardware transmission is not available.
291
+ Periodic transmission is offloaded to the CANsub hardware where possible, providing much better transmission time accuracy than a host-scheduled transmission. A host-side background task is used only as a fallback when hardware transmission is not available.
292
+
293
+ > **Note:** python-can requires all messages in a periodic task to share the same arbitration ID (the payload can differ per frame).
213
294
 
214
295
  ```python
215
296
  from time import sleep
216
297
 
217
298
  msgs = [
218
299
  can.Message(is_extended_id=False, arbitration_id=0x123, data=[0x01, 0x02, 0x03, 0x04]),
219
- can.Message(is_extended_id=False, arbitration_id=0x124, data=[0x05, 0x06, 0x07, 0x08]),
220
- can.Message(is_extended_id=False, arbitration_id=0x125, data=[0x09, 0x0A, 0x0B, 0x0C]),
300
+ can.Message(is_extended_id=False, arbitration_id=0x123, data=[0x05, 0x06, 0x07, 0x08]),
301
+ can.Message(is_extended_id=False, arbitration_id=0x123, data=[0x09, 0x0A, 0x0B, 0x0C]),
221
302
  ]
222
303
 
223
304
  with can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000) as bus:
@@ -229,7 +310,7 @@ with can.Bus(**configs[0], bitrate=250_000, data_bitrate=1_000_000) as bus:
229
310
  sleep(6)
230
311
  ```
231
312
 
232
- ### Replaying files
313
+ ### Replaying Files
233
314
 
234
315
  `can.MessageSync` can be used to replay messages from a log file.
235
316
 
@@ -262,9 +343,9 @@ with CanSubCSVReader("log.csv") as reader:
262
343
  print(msg)
263
344
  ```
264
345
 
265
- ## python-can tools
346
+ ## python-can Tools
266
347
 
267
- python-can includes several command line tools. All tools accept `--interface` and `--channel` to select the bus, following the same configuration as the API.
348
+ python-can includes several command-line tools. All tools accept `--interface` and `--channel` to select the bus, following the same configuration as the API.
268
349
 
269
350
  The common argument pattern for the CANsub:
270
351
 
@@ -272,7 +353,15 @@ The common argument pattern for the CANsub:
272
353
  --interface cansub --channel aabbccdd-usb.local@1 --bitrate 250000 --data-bitrate 1000000
273
354
  ```
274
355
 
275
- Note that the *filter* argument supported by some command-line tools is limited to standard (11-bit) CAN IDs. Filtering on extended (29-bit) IDs requires the python-can API.
356
+ Bus arguments without a dedicated command-line flag are passed with `--bus-kwargs key=value ...`. For example, listen-only monitoring:
357
+
358
+ ```
359
+ --interface cansub --channel 192.168.1.10@1 --bitrate 250000 --data-bitrate 1000000 --bus-kwargs listen_only=True
360
+ ```
361
+
362
+ > **Note:** `--bus-kwargs` consumes all values that follow it; place positional arguments (e.g. the `can_player` log file) before it.
363
+
364
+ Note that the *filter* argument supported by some command-line tools matches both standard (11-bit) and extended (29-bit) CAN IDs.
276
365
 
277
366
  ### can_logger
278
367
 
@@ -298,11 +387,11 @@ Live terminal viewer showing received frames, updated counts, timestamps, and by
298
387
  can_viewer --interface cansub --channel aabbccdd-usb.local@1 --bitrate 250000 --data-bitrate 1000000
299
388
  ```
300
389
 
301
- On Windows, the can_viewer requires `windows-curses` (`pip install windows-curses`).
390
+ On Windows, `can_viewer` requires `windows-curses` (`pip install windows-curses`).
302
391
 
303
392
  ### can_bridge
304
393
 
305
- Forward all frames received on one bus to another (e.g. bridge two CANsub channels):
394
+ Forward all frames received on one bus to another (e.g., to bridge two CANsub channels):
306
395
 
307
396
  ```bash
308
397
  can_bridge --bus1-interface cansub --bus1-channel aabbccdd-usb.local@1 --bus1-bitrate 250000 --bus1-data-bitrate 1000000 \
@@ -323,7 +412,7 @@ The following packages complement `python-can-cansub` and are included here as i
323
412
 
324
413
  ### cantools
325
414
 
326
- [cantools](https://github.com/cantools/cantools) is a Python package for encoding and decoding CAN messages. Encoding/decoding rules can be created or loaded from DBC (and other) database files. It works directly with `can.Message` objects from python-can.
415
+ [cantools](https://github.com/cantools/cantools) is a Python package for encoding and decoding CAN messages. Encoding/decoding rules can be created directly in code, or loaded from DBC and other database file formats. It works directly with `can.Message` objects from python-can.
327
416
 
328
417
  #### Installation
329
418
 
@@ -337,20 +426,23 @@ A database can be constructed directly in Python without a database file:
337
426
 
338
427
  ```python
339
428
  import cantools
340
-
341
- db = cantools.database.Database()
429
+ from cantools.database.conversion import LinearConversion
342
430
 
343
431
  msg_def = cantools.database.can.Message(
344
432
  frame_id=0x123,
345
433
  name="Message1",
346
434
  length=8,
347
435
  signals=[
348
- cantools.database.can.Signal(name="Signal1", start=0, length=16, scale=0.1, offset=0.0, minimum=0.0, maximum=100.0),
349
- cantools.database.can.Signal(name="Signal2", start=16, length=16, scale=0.1, offset=0.0, minimum=0.0, maximum=100.0),
436
+ cantools.database.can.Signal(name="Signal1", start=0, length=16,
437
+ conversion=LinearConversion(scale=0.1, offset=0.0, is_float=False),
438
+ minimum=0.0, maximum=100.0),
439
+ cantools.database.can.Signal(name="Signal2", start=16, length=16,
440
+ conversion=LinearConversion(scale=0.1, offset=0.0, is_float=False),
441
+ minimum=0.0, maximum=100.0),
350
442
  ]
351
443
  )
352
444
 
353
- db.add_message(msg_def)
445
+ db = cantools.database.Database(messages=[msg_def])
354
446
  ```
355
447
 
356
448
  #### Load database from DBC file