From 3ff22b2227b904933dbdb031d45d6deefa944d9a Mon Sep 17 00:00:00 2001 From: shedfly Date: Fri, 3 May 2019 03:10:19 -0700 Subject: [PATCH 01/17] adding seeedstudio usb-can-analyzer interface --- can/interfaces/__init__.py | 3 +- can/interfaces/usb_can_analyzer/__init__.py | 6 + can/interfaces/usb_can_analyzer/notes | 167 +++++++++++ .../usb_can_analyzer/usb_can_analyzer.py | 276 ++++++++++++++++++ setup.py | 3 +- 5 files changed, 453 insertions(+), 2 deletions(-) create mode 100644 can/interfaces/usb_can_analyzer/__init__.py create mode 100644 can/interfaces/usb_can_analyzer/notes create mode 100644 can/interfaces/usb_can_analyzer/usb_can_analyzer.py diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index ec79e51d6..864233b0f 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -23,7 +23,8 @@ 'vector': ('can.interfaces.vector', 'VectorBus'), 'slcan': ('can.interfaces.slcan', 'slcanBus'), 'canalystii': ('can.interfaces.canalystii', 'CANalystIIBus'), - 'systec': ('can.interfaces.systec', 'UcanBus') + 'systec': ('can.interfaces.systec', 'UcanBus'), + 'usb_can_analyzer': ('can.interfaces.usb_can_analyzer.usb_can_analyzer', 'CanAnalyzer') } BACKENDS.update({ diff --git a/can/interfaces/usb_can_analyzer/__init__.py b/can/interfaces/usb_can_analyzer/__init__.py new file mode 100644 index 000000000..ec07c4d68 --- /dev/null +++ b/can/interfaces/usb_can_analyzer/__init__.py @@ -0,0 +1,6 @@ +# coding: utf-8 + +""" +""" + +from can.interfaces.usb_can_analyzer.usb_can_analyzer import CanAnalyzer as Bus diff --git a/can/interfaces/usb_can_analyzer/notes b/can/interfaces/usb_can_analyzer/notes new file mode 100644 index 000000000..829c85173 --- /dev/null +++ b/can/interfaces/usb_can_analyzer/notes @@ -0,0 +1,167 @@ +USB-CAN Analyzer - USB to CAN Bus Serial Protocol Definition + +Posted by Wilfried Voss on September 19, 2018 - copperhilltech.com + +CAN Bus To USB Mini Converter + +The USB-CAN Analyzer, in combination with the corresponding Windows software, represents a very economical solution to run an effective CAN Bus Analyzer. It allows you to develop, test, manage, and maintain your own CAN Bus network, as well as receiving, sending, logging, and analyzing CAN Bus data. + +For the following, we need to point out that the device is manufactured in China, and documentation is sparse. And while this is a very affordable solution, there is naturally room for some improvement. In general, there is no application interface, or, to be more precise, the communication between the USB-CAN device and the host system, in this case a PC running Windows, is not documented. Consequently, there is currently no software support for Linux (e.g. SocketCAN), and it would be useful if users could write their own applications, regardless of the operating system they use. + +As a first step in this direction, we invested some efforts to analyze and document the USB communication protocol, which makes it easy developing an application interface. + +There has been a document, USB (Serial port) to CAN protocol defines, that was distributed online, claiming a documentation of the "Send and receive data packet format." However, after analyzing the protocol, we believe that the data frame architecture described therein represents the original intention. + +We believe that the architecture as described caused problems when it came to higher baud rates, specifically at 1 Mbps in combination with high busloads. Consequently, the firmware developers decided to optimize the data frame size. As a result, they cut the frame size from a static 20 bytes to a dynamic 6 to 16 bytes, depending on message ID length (11 or 29 bits) and number of data bytes. + +The following describes the serial protocol as we analyzed it, with a few unknowns remaining. However, the documentation includes all vital information for creating your own CAN Bus application. + +CAN Bus Data Frame + +The CAN Bus data frame format applies to both, send and receive. + +Both message ID lengths, 11-bit standard and 29-bit extended, use the first two bytes in the serial data frame. Depending on the message ID length, the remaining bytes will be filled differently. + +The data frame does not utilize a checksum check, most probably due to keeping the number of transmitted bytes to a minimum and cutting down on processing time. + +Byte Description + +0 0xAA = Packet Start + +1 CAN Bus Data Frame Information + + Bit 7 Always 1 + + Bit 6 Always 1 + + Bit 5 0=STD; 1=EXT + + Bit 4 0=Data; 1=Remote + + Bit 3…0 Data Length Code (DLC); 0…8 + +Standard CAN data frame continues: + +Byte Description + +2 Message ID LSB + +3 Message ID MSB + +x Data, length depending on DLC + +y 0x55 = Packet End + +Extended CAN data frame continues: + +Byte Description + +2 Message ID LSB + +3 Message ID 2ND + +4 Message ID 3RD + +5 Message ID MSB + +x Data, length depending on DLC + +y 0x55 = Packet End + +CAN Bus Data Frame Size + +Standard 11-bit 6 bytes with zero data bytes + + 14 bytes with eight data bytes + +Extended 29-bit 8 bytes with zero data bytes + + 16 bytes with eight data bytes + +CAN Bus Initialization Frame + +The CAN Bus initialization frame has a constant length of 20 bytes. + +Byte(s) Description + +0 0xAA = Frame Start Byte 1 + +1 0x55 = Frame Start Byte 2 + +2 0x12 = Initialization Message ID + +3 CAN Baud Rate (See table below) + +4 0x01=STD; 0x02=EXT; Applies only to transmitting + +5 – 8 Filter ID; LSB first, MSB last + +9 – 12 Mask ID; LSB first, MSB last + +13 Operation Mode (See table below) + +14 0x01; Purpose Unknown + +15 – 18 All 0x00 + +19 Checksum + +Note: The checksum is processed between bytes 2 and 18. + +CAN Bus baud rate + +0x01 1000k + +0x02 800k + +0x03 500k + +0x04 400k + +0x05 250k + +0x06 200k + +0x07 125k + +0x08 100k + +0x09 50k + +0x0A 20k + +0x0B 10k + +0x0C 5k + +Operation Mode + +0x00 Normal + +0x01 Loopback + +0x02 Silent (Listen-Only) + +0x03 Loopback + Silent + +CAN Controller Status Frame + +The format of the status request and status report frames are identical, where the request frame sends all zeroes between the frame header (frame start tokens plus frame ID) and the checksum, i.e. bytes 3 through 18 are 0x00. + +Byte(s) Description + +0 0xAA = Frame Start Byte 1 + +1 0x55 = Frame Start Byte 2 + +2 0x04 = Status Message ID + +3 Receive Error Counter + +4 Transmit Error Counter + +5 – 18 Unknown Purpose + In case of status report, they may be filled with data + +19 Checksum + diff --git a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py new file mode 100644 index 000000000..9fee2fc4d --- /dev/null +++ b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py @@ -0,0 +1,276 @@ +# coding: utf-8 + +""" +To Support the Seeed USB-Can analyzer interface. The device will appear +as a serial port, for example "/dev/ttyS1" or "/dev/ttyUSB0" on Linux +machines or "COM1" on Windows. +https://www.seeedstudio.com/USB-CAN-Analyzer-p-2888.html +SKU 114991193 +See protoocl: +https://copperhilltech.com/blog/usbcan-analyzer-usb-to-can-bus-serial-protocol-definition/ + +this file uses Crc8Darc checksums. +""" + + +from __future__ import absolute_import, division + +import logging +import struct +from crccheck.crc import Crc8Darc +from time import sleep, time +from can import BusABC, Message + +logger = logging.getLogger('can.CanAnalyzer') + +try: + import serial +except ImportError: + logger.warning("You won't be able to use the serial can backend without " + "the serial module installed!") + serial = None + + +class CanAnalyzer(BusABC): + """ + Enable basic can communication over a serial device. + + .. note:: See :meth:`can.interfaces.serial.CanAnalyzer._recv_internal` + for some special semantics. + + """ + BITRATE = { + 1000000: 0x01, + 800000: 0x02, + 500000: 0x03, + 400000: 0x04, + 250000: 0x05, + 200000: 0x06, + 125000: 0x07, + 100000: 0x08, + 50000: 0x09, + 20000: 0x0A, + 10000: 0x0B, + 5000: 0x0C + } + + FRAMETYPE = { + "STD":0x01, + "EXT":0x02 + } + + OPERATIONMODE = { + "normal":0x00, + "loopback":0x01, + "silent":0x02, + "loopback_and_silent":0x03 + } + + def __init__(self, channel, baudrate=2000000, timeout=0.1, rtscts=False, + frame_type='STD', operation_mode='normal', bit_rate=500000, + *args, **kwargs): + """ + :param str channel: + The serial device to open. For example "/dev/ttyS1" or + "/dev/ttyUSB0" on Linux or "COM1" on Windows systems. + + :param int baudrate: + Baud rate of the serial device in bit/s (default 115200). + + .. warning:: + Some serial port implementations don't care about the baudrate. + + :param float timeout: + Timeout for the serial device in seconds (default 0.1). + + :param bool rtscts: + turn hardware handshake (RTS/CTS) on and off + + """ + self.bit_rate = bit_rate + self.frame_type = frame_type + self.op_mode = operation_mode + self.filter_id = bytearray([0x00, 0x00, 0x00, 0x00]) + self.mask_id = bytearray([0x00, 0x00, 0x00, 0x00]) + if not channel: + raise ValueError("Must specify a serial port.") + + self.channel_info = "Serial interface: " + channel + self.ser = serial.Serial( + channel, baudrate=baudrate, timeout=timeout, rtscts=rtscts) + + super(CanAnalyzer, self).__init__(channel=channel, *args, **kwargs) + self.init_frame + + def shutdown(self): + """ + Close the serial interface. + """ + self.ser.close() + + def init_frame(self, timeout=None): + + byte_msg = bytearray() + byte_msg.append(0xAA) # Frame Start Byte 1 + byte_msg.append(0x55) # Frame Start Byte 2 + + byte_msg.append(0x12) # Initialization Message ID + + byte_msg.append(CanAnalyzer.BITRATE[self.bit_rate]) # CAN Baud Rate + byte_msg.append(CanAnalyzer.FRAMETYPE[self.frame_type]) + + for i in range(0, 4): + byte_msg.append(self.filter_id[i]) + + for i in range(0, 4): + byte_msg.append(self.mask_id[i]) + + byte_msg.append(CanAnalyzer.OPERATIONMODE[self.op_mode]) + + byte_msg.append(0x01) + + for i in range(0, 4): + byte_msg.append(0x00) + + print "byte_str = " + str(byte_msg[2:]) + + crc = Crc8Darc.calc(byte_msg[2:]) + crc_byte = struct.pack('B', crc) + + print "crc_byte: " + str(crc_byte) + " length: " + str(len(crc_byte)) + + byte_msg.append(crc_byte) + self.ser.write(byte_msg) + + def flush_buffer(self): + self.ser.flushInput() + + def status_frame(self, timeout=None): + byte_msg = bytearray() + byte_msg.append(0xAA) # Frame Start Byte 1 + byte_msg.append(0x55) # Frame Start Byte 2 + byte_msg.append(0x04) # Status Message ID + byte_msg.append(0x00) # In response packet - Rx error count + byte_msg.append(0x00) # In response packet - Tx error count + + for i in range(0, 14): + byte_msg.append(0x00) + + print "byte_str = " + str(byte_msg[2:]) + + crc = Crc8Darc.calc(byte_msg[2:]) + crc_byte = struct.pack('B', crc) + + print "crc_byte: " + str(crc_byte) + " length: " + str(len(crc_byte)) + + byte_msg.append(crc_byte) + self.ser.write(byte_msg) + + + def send(self, msg, timeout=None): + """ + Send a message over the serial device. + + :param can.Message msg: + Message to send. + + .. note:: Flags like ``extended_id``, ``is_remote_frame`` and + ``is_error_frame`` will be ignored. + + .. note:: If the timestamp is a float value it will be converted + to an integer. + + :param timeout: + This parameter will be ignored. The timeout value of the channel is + used instead. + + """ + try: + timestamp = struct.pack(' Date: Mon, 6 May 2019 06:59:03 +0000 Subject: [PATCH 02/17] replacing print with log, moving crccheck to extras_require --- .../usb_can_analyzer/usb_can_analyzer.py | 39 +++++++++---------- setup.py | 6 +-- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py index 9fee2fc4d..9a2ee0baa 100644 --- a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py +++ b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py @@ -12,12 +12,10 @@ this file uses Crc8Darc checksums. """ - from __future__ import absolute_import, division import logging import struct -from crccheck.crc import Crc8Darc from time import sleep, time from can import BusABC, Message @@ -30,6 +28,11 @@ "the serial module installed!") serial = None +try: + from crccheck.crc import Crc8Darc +except ImportError: + logger.warning("The interface requires the install option crccheck.") + class CanAnalyzer(BusABC): """ @@ -53,7 +56,7 @@ class CanAnalyzer(BusABC): 10000: 0x0B, 5000: 0x0C } - + FRAMETYPE = { "STD":0x01, "EXT":0x02 @@ -100,7 +103,7 @@ def __init__(self, channel, baudrate=2000000, timeout=0.1, rtscts=False, channel, baudrate=baudrate, timeout=timeout, rtscts=rtscts) super(CanAnalyzer, self).__init__(channel=channel, *args, **kwargs) - self.init_frame + self.init_frame() def shutdown(self): """ @@ -119,27 +122,23 @@ def init_frame(self, timeout=None): byte_msg.append(CanAnalyzer.BITRATE[self.bit_rate]) # CAN Baud Rate byte_msg.append(CanAnalyzer.FRAMETYPE[self.frame_type]) - for i in range(0, 4): - byte_msg.append(self.filter_id[i]) + byte_msg.extend(self.filter_id) - for i in range(0, 4): - byte_msg.append(self.mask_id[i]) + byte_msg.extend(self.mask_id) byte_msg.append(CanAnalyzer.OPERATIONMODE[self.op_mode]) - + byte_msg.append(0x01) for i in range(0, 4): byte_msg.append(0x00) - print "byte_str = " + str(byte_msg[2:]) - crc = Crc8Darc.calc(byte_msg[2:]) crc_byte = struct.pack('B', crc) - print "crc_byte: " + str(crc_byte) + " length: " + str(len(crc_byte)) - byte_msg.append(crc_byte) + + logger.debug("init_frm:\t" + str(byte_msg)) self.ser.write(byte_msg) def flush_buffer(self): @@ -156,16 +155,13 @@ def status_frame(self, timeout=None): for i in range(0, 14): byte_msg.append(0x00) - print "byte_str = " + str(byte_msg[2:]) - crc = Crc8Darc.calc(byte_msg[2:]) crc_byte = struct.pack('B', crc) - print "crc_byte: " + str(crc_byte) + " length: " + str(len(crc_byte)) - byte_msg.append(crc_byte) - self.ser.write(byte_msg) + logger.debug("status_frm:\t" + str(byte_msg)) + self.ser.write(byte_msg) def send(self, msg, timeout=None): """ @@ -203,6 +199,8 @@ def send(self, msg, timeout=None): for i in range(0, msg.dlc): byte_msg.append(msg.data[i]) byte_msg.append(0xBB) + + logger.debug("Sending:\t" + byte_msg) self.ser.write(byte_msg) def _recv_internal(self, timeout): @@ -229,7 +227,7 @@ def _recv_internal(self, timeout): # ser.read can return an empty string # or raise a SerialException rx_byte_1 = self.ser.read() - + except serial.SerialException: return None, False @@ -260,7 +258,7 @@ def _recv_internal(self, timeout): arbitration_id=arb_id, extended_id=is_extended, data=data) - + logger.debug("recv_msg:\t" + str(msg)) return msg, False else: @@ -268,7 +266,6 @@ def _recv_internal(self, timeout): return None, None - def fileno(self): if hasattr(self.ser, 'fileno'): return self.ser.fileno() diff --git a/setup.py b/setup.py index dcef0acb3..d4c6b7838 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,8 @@ # Dependencies extras_require = { 'serial': ['pyserial~=3.0'], - 'neovi': ['python-ics>=2.12'] + 'neovi': ['python-ics>=2.12'], + 'usb-can-analyzer': ['crccheck>=0.6'] } tests_require = [ @@ -36,8 +37,7 @@ 'codecov~=2.0', 'future', 'six', - 'hypothesis', - 'crccheck' + 'hypothesis' ] + extras_require['serial'] extras_require['test'] = tests_require From ea5d15cecf4476d3dbb232af26276557038efa53 Mon Sep 17 00:00:00 2001 From: shedfly Date: Mon, 6 May 2019 10:43:38 +0000 Subject: [PATCH 03/17] adding 'is_remote' to message, add binascii to debug logs --- .../usb_can_analyzer/usb_can_analyzer.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py index 9a2ee0baa..6f37a9066 100644 --- a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py +++ b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py @@ -16,6 +16,7 @@ import logging import struct +import binascii from time import sleep, time from can import BusABC, Message @@ -138,7 +139,7 @@ def init_frame(self, timeout=None): byte_msg.append(crc_byte) - logger.debug("init_frm:\t" + str(byte_msg)) + logger.debug("init_frm:\t" + binascii.hexlify(byte_msg)) self.ser.write(byte_msg) def flush_buffer(self): @@ -160,7 +161,7 @@ def status_frame(self, timeout=None): byte_msg.append(crc_byte) - logger.debug("status_frm:\t" + str(byte_msg)) + logger.debug("status_frm:\t" + binascii.hexlify(byte_msg)) self.ser.write(byte_msg) def send(self, msg, timeout=None): @@ -200,7 +201,7 @@ def send(self, msg, timeout=None): byte_msg.append(msg.data[i]) byte_msg.append(0xBB) - logger.debug("Sending:\t" + byte_msg) + logger.debug("Sending:\t" + binascii.hexlify(byte_msg)) self.ser.write(byte_msg) def _recv_internal(self, timeout): @@ -240,8 +241,7 @@ def _recv_internal(self, timeout): else: length = int(rx_byte_2 & 0x0F) is_extended = bool(rx_byte_2 & 0x20) - is_data = bool(rx_byte_2 & 10) - + is_remote = bool(rx_byte_2 & 0x10) if is_extended: s_3_4_5_6 = bytearray(self.ser.read(4)) arb_id = (struct.unpack(' Date: Mon, 6 May 2019 12:57:35 +0000 Subject: [PATCH 04/17] more general name 'crccheck' for install option, also update Message named args --- can/interfaces/usb_can_analyzer/usb_can_analyzer.py | 3 ++- setup.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py index 6f37a9066..b1fd95ffa 100644 --- a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py +++ b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py @@ -255,8 +255,9 @@ def _recv_internal(self, timeout): if end_packet == 0x55: msg = Message(timestamp=time_stamp, arbitration_id=arb_id, - extended_id=is_extended, + is_extended_id=is_extended, is_remote_frame=is_remote, + dlc=length, data=data) logger.debug("recv message: " + str(msg)) return msg, False diff --git a/setup.py b/setup.py index d4c6b7838..a4ae7b405 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ extras_require = { 'serial': ['pyserial~=3.0'], 'neovi': ['python-ics>=2.12'], - 'usb-can-analyzer': ['crccheck>=0.6'] + 'crccheck': ['crccheck>=0.6'] } tests_require = [ From 93ad7238633bf3f36373e094a89c325b7cc1c401 Mon Sep 17 00:00:00 2001 From: shedfly Date: Fri, 10 May 2019 12:24:49 +0000 Subject: [PATCH 05/17] implementing 'send' --- .../usb_can_analyzer/usb_can_analyzer.py | 48 ++++++++----------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py index b1fd95ffa..73def806d 100644 --- a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py +++ b/can/interfaces/usb_can_analyzer/usb_can_analyzer.py @@ -171,35 +171,32 @@ def send(self, msg, timeout=None): :param can.Message msg: Message to send. - .. note:: Flags like ``extended_id``, ``is_remote_frame`` and - ``is_error_frame`` will be ignored. - - .. note:: If the timestamp is a float value it will be converted - to an integer. - :param timeout: This parameter will be ignored. The timeout value of the channel is used instead. - """ - try: - timestamp = struct.pack(' Date: Fri, 10 May 2019 12:31:34 +0000 Subject: [PATCH 06/17] changing extras_require install option name to match interface name and adding pyserial --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a4ae7b405..388b96747 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ extras_require = { 'serial': ['pyserial~=3.0'], 'neovi': ['python-ics>=2.12'], - 'crccheck': ['crccheck>=0.6'] + 'usb_can_analyzer': ['crccheck>=0.6','pyserial>=3.0'] } tests_require = [ From 324afd0bdd61f2ab036c200f579f0f1e1a77ae25 Mon Sep 17 00:00:00 2001 From: shedfly Date: Sun, 9 Jun 2019 16:28:06 +1000 Subject: [PATCH 07/17] changing name to seeedstuido and changes for python3 --- can/interfaces/__init__.py | 2 +- can/interfaces/seeedstudio/__init__.py | 6 ++++++ .../{usb_can_analyzer => seeedstudio}/notes | 0 .../seeedstudio.py} | 12 ++++++------ can/interfaces/usb_can_analyzer/__init__.py | 6 ------ 5 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 can/interfaces/seeedstudio/__init__.py rename can/interfaces/{usb_can_analyzer => seeedstudio}/notes (100%) rename can/interfaces/{usb_can_analyzer/usb_can_analyzer.py => seeedstudio/seeedstudio.py} (95%) delete mode 100644 can/interfaces/usb_can_analyzer/__init__.py diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index 671f53cc0..c17d85823 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -24,7 +24,7 @@ 'slcan': ('can.interfaces.slcan', 'slcanBus'), 'canalystii': ('can.interfaces.canalystii', 'CANalystIIBus'), 'systec': ('can.interfaces.systec', 'UcanBus'), - 'usb_can_analyzer': ('can.interfaces.usb_can_analyzer.usb_can_analyzer', 'CanAnalyzer') + 'seeedstudio': ('can.interfaces.seeedstudio', 'CanAnalyzer') } BACKENDS.update({ diff --git a/can/interfaces/seeedstudio/__init__.py b/can/interfaces/seeedstudio/__init__.py new file mode 100644 index 000000000..32306df97 --- /dev/null +++ b/can/interfaces/seeedstudio/__init__.py @@ -0,0 +1,6 @@ +# coding: utf-8 + +""" +""" + +from can.interfaces.seeedstudio.seeedstudio import CanAnalyzer diff --git a/can/interfaces/usb_can_analyzer/notes b/can/interfaces/seeedstudio/notes similarity index 100% rename from can/interfaces/usb_can_analyzer/notes rename to can/interfaces/seeedstudio/notes diff --git a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py b/can/interfaces/seeedstudio/seeedstudio.py similarity index 95% rename from can/interfaces/usb_can_analyzer/usb_can_analyzer.py rename to can/interfaces/seeedstudio/seeedstudio.py index 73def806d..9bd38bf3d 100644 --- a/can/interfaces/usb_can_analyzer/usb_can_analyzer.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -121,7 +121,7 @@ def init_frame(self, timeout=None): byte_msg.append(0x12) # Initialization Message ID byte_msg.append(CanAnalyzer.BITRATE[self.bit_rate]) # CAN Baud Rate - byte_msg.append(CanAnalyzer.FRAMETYPE[self.frame_type]) + byte_msg.append(CanAnalyzer.FRAMETYPE[self.frame_type]) byte_msg.extend(self.filter_id) @@ -135,11 +135,11 @@ def init_frame(self, timeout=None): byte_msg.append(0x00) crc = Crc8Darc.calc(byte_msg[2:]) - crc_byte = struct.pack('B', crc) +# crc_byte = struct.pack('B', crc) - byte_msg.append(crc_byte) + byte_msg.append(crc) - logger.debug("init_frm:\t" + binascii.hexlify(byte_msg)) + logger.debug("init_frm:\t" + byte_msg.hex()) self.ser.write(byte_msg) def flush_buffer(self): @@ -161,7 +161,7 @@ def status_frame(self, timeout=None): byte_msg.append(crc_byte) - logger.debug("status_frm:\t" + binascii.hexlify(byte_msg)) + logger.debug("status_frm:\t" + byte_msg.hex()) self.ser.write(byte_msg) def send(self, msg, timeout=None): @@ -198,7 +198,7 @@ def send(self, msg, timeout=None): byte_msg.extend(msg.data) byte_msg.append(0x55) - logger.debug("Sending:\t" + binascii.hexlify(byte_msg)) + logger.debug("Sending:\t" + byte_msg.hex()) self.ser.write(byte_msg) def _recv_internal(self, timeout): diff --git a/can/interfaces/usb_can_analyzer/__init__.py b/can/interfaces/usb_can_analyzer/__init__.py deleted file mode 100644 index ec07c4d68..000000000 --- a/can/interfaces/usb_can_analyzer/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# coding: utf-8 - -""" -""" - -from can.interfaces.usb_can_analyzer.usb_can_analyzer import CanAnalyzer as Bus From aad6d6635b411fc17ce924af68e52d7665d76005 Mon Sep 17 00:00:00 2001 From: shedfly Date: Sun, 9 Jun 2019 16:36:17 +1000 Subject: [PATCH 08/17] update option name in setup.py to seeedstudio. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 03055ab18..85947c596 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ extras_require = { 'serial': ['pyserial~=3.0'], 'neovi': ['python-ics>=2.12'], - 'usb_can_analyzer': ['crccheck>=0.6','pyserial>=3.0'] + 'seeedstudio': ['crccheck>=0.6','pyserial>=3.0'] } tests_require = [ From 19287be69448db97ed26962193070e2b617d792b Mon Sep 17 00:00:00 2001 From: shedfly Date: Sun, 9 Jun 2019 17:09:49 +1000 Subject: [PATCH 09/17] tidy up merge conflicts. --- can/interfaces/__init__.py | 44 +++++++++++++++++++---------------- setup.py | 47 ++++++++++++++++---------------------- 2 files changed, 44 insertions(+), 47 deletions(-) diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index c17d85823..2f8768ed5 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -10,26 +10,30 @@ # interface_name => (module, classname) BACKENDS = { - 'kvaser': ('can.interfaces.kvaser', 'KvaserBus'), - 'socketcan': ('can.interfaces.socketcan', 'SocketcanBus'), - 'serial': ('can.interfaces.serial.serial_can','SerialBus'), - 'pcan': ('can.interfaces.pcan', 'PcanBus'), - 'usb2can': ('can.interfaces.usb2can', 'Usb2canBus'), - 'ixxat': ('can.interfaces.ixxat', 'IXXATBus'), - 'nican': ('can.interfaces.nican', 'NicanBus'), - 'iscan': ('can.interfaces.iscan', 'IscanBus'), - 'virtual': ('can.interfaces.virtual', 'VirtualBus'), - 'neovi': ('can.interfaces.ics_neovi', 'NeoViBus'), - 'vector': ('can.interfaces.vector', 'VectorBus'), - 'slcan': ('can.interfaces.slcan', 'slcanBus'), - 'canalystii': ('can.interfaces.canalystii', 'CANalystIIBus'), - 'systec': ('can.interfaces.systec', 'UcanBus'), - 'seeedstudio': ('can.interfaces.seeedstudio', 'CanAnalyzer') + "kvaser": ("can.interfaces.kvaser", "KvaserBus"), + "socketcan": ("can.interfaces.socketcan", "SocketcanBus"), + "serial": ("can.interfaces.serial.serial_can", "SerialBus"), + "pcan": ("can.interfaces.pcan", "PcanBus"), + "usb2can": ("can.interfaces.usb2can", "Usb2canBus"), + "ixxat": ("can.interfaces.ixxat", "IXXATBus"), + "nican": ("can.interfaces.nican", "NicanBus"), + "iscan": ("can.interfaces.iscan", "IscanBus"), + "virtual": ("can.interfaces.virtual", "VirtualBus"), + "neovi": ("can.interfaces.ics_neovi", "NeoViBus"), + "vector": ("can.interfaces.vector", "VectorBus"), + "slcan": ("can.interfaces.slcan", "slcanBus"), + "canalystii": ("can.interfaces.canalystii", "CANalystIIBus"), + "systec": ("can.interfaces.systec", "UcanBus"), + "seeedstudio": ("can.interfaces.seeedstudio", "CanAnalyzer"), } -BACKENDS.update({ - interface.name: (interface.module_name, interface.attrs[0]) - for interface in iter_entry_points('can.interface') -}) +BACKENDS.update( + { + interface.name: (interface.module_name, interface.attrs[0]) + for interface in iter_entry_points("can.interface") + } +) -VALID_INTERFACES = frozenset(list(BACKENDS.keys()) + ['socketcan_native', 'socketcan_ctypes']) +VALID_INTERFACES = frozenset( + list(BACKENDS.keys()) + ["socketcan_native", "socketcan_ctypes"] +) diff --git a/setup.py b/setup.py index 85947c596..71427bbfa 100644 --- a/setup.py +++ b/setup.py @@ -15,30 +15,28 @@ logging.basicConfig(level=logging.WARNING) -with open('can/__init__.py', 'r') as fd: - version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', - fd.read(), re.MULTILINE).group(1) +with open("can/__init__.py", "r") as fd: + version = re.search( + r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE + ).group(1) -with open('README.rst', 'r') as f: +with open("README.rst", "r") as f: long_description = f.read() # Dependencies -extras_require = { - 'serial': ['pyserial~=3.0'], - 'neovi': ['python-ics>=2.12'], - 'seeedstudio': ['crccheck>=0.6','pyserial>=3.0'] -} +extras_require = {"serial": ["pyserial~=3.0"], + "neovi": ["python-ics>=2.12"], + "seeedstudio": ["crccheck>=0.6","pyserial>=3.0"]} tests_require = [ - 'pytest~=4.3', - 'pytest-timeout~=1.3', - 'pytest-cov~=2.6', - 'codecov~=2.0', - 'six', - 'hypothesis' -] + extras_require['serial'] + "pytest~=4.3", + "pytest-timeout~=1.3", + "pytest-cov~=2.6", + "codecov~=2.0", + "hypothesis", +] + extras_require["serial"] -extras_require['test'] = tests_require +extras_require["test"] = tests_require setup( @@ -70,37 +68,32 @@ "Topic :: System :: Monitoring", "Topic :: System :: Networking", "Topic :: System :: Hardware :: Hardware Drivers", - "Topic :: Utilities" + "Topic :: Utilities", ], - # Code version=version, packages=find_packages(exclude=["test", "doc", "scripts", "examples"]), scripts=list(filter(isfile, (join("scripts/", f) for f in listdir("scripts/")))), - # Author author="Brian Thorne", author_email="brian@thorne.link", - # License license="LGPL v3", - # Package data package_data={ "": ["README.rst", "CONTRIBUTORS.txt", "LICENSE.txt", "CHANGELOG.txt"], "doc": ["*.*"], - "examples": ["*.py"] + "examples": ["*.py"], }, - # Installation # see https://www.python.org/dev/peps/pep-0345/#version-specifiers python_requires=">=3.6", install_requires=[ - 'wrapt~=1.10', - 'aenum', + "wrapt~=1.10", + "aenum", 'windows-curses;platform_system=="Windows"', ], setup_requires=["pytest-runner"], extras_require=extras_require, - tests_require=tests_require + tests_require=tests_require, ) From 3ded7834c24e9183c87e1757539ab6690535be7d Mon Sep 17 00:00:00 2001 From: shedfly Date: Wed, 12 Jun 2019 22:00:10 +1000 Subject: [PATCH 10/17] adding doc and cleaning up comments. Changing to Bus class name SeeedBus. --- can/interfaces/__init__.py | 2 +- can/interfaces/seeedstudio/__init__.py | 2 +- can/interfaces/seeedstudio/notes | 167 ---------------------- can/interfaces/seeedstudio/seeedstudio.py | 81 +++++------ doc/interfaces/seeedstudio.rst | 85 +++++++++++ 5 files changed, 124 insertions(+), 213 deletions(-) delete mode 100644 can/interfaces/seeedstudio/notes create mode 100644 doc/interfaces/seeedstudio.rst diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index 2f8768ed5..66e55153d 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -24,7 +24,7 @@ "slcan": ("can.interfaces.slcan", "slcanBus"), "canalystii": ("can.interfaces.canalystii", "CANalystIIBus"), "systec": ("can.interfaces.systec", "UcanBus"), - "seeedstudio": ("can.interfaces.seeedstudio", "CanAnalyzer"), + "seeedstudio": ("can.interfaces.seeedstudio", "SeeedBus"), } BACKENDS.update( diff --git a/can/interfaces/seeedstudio/__init__.py b/can/interfaces/seeedstudio/__init__.py index 32306df97..507ac873e 100644 --- a/can/interfaces/seeedstudio/__init__.py +++ b/can/interfaces/seeedstudio/__init__.py @@ -3,4 +3,4 @@ """ """ -from can.interfaces.seeedstudio.seeedstudio import CanAnalyzer +from can.interfaces.seeedstudio.seeedstudio import SeeedBus diff --git a/can/interfaces/seeedstudio/notes b/can/interfaces/seeedstudio/notes deleted file mode 100644 index 829c85173..000000000 --- a/can/interfaces/seeedstudio/notes +++ /dev/null @@ -1,167 +0,0 @@ -USB-CAN Analyzer - USB to CAN Bus Serial Protocol Definition - -Posted by Wilfried Voss on September 19, 2018 - copperhilltech.com - -CAN Bus To USB Mini Converter - -The USB-CAN Analyzer, in combination with the corresponding Windows software, represents a very economical solution to run an effective CAN Bus Analyzer. It allows you to develop, test, manage, and maintain your own CAN Bus network, as well as receiving, sending, logging, and analyzing CAN Bus data. - -For the following, we need to point out that the device is manufactured in China, and documentation is sparse. And while this is a very affordable solution, there is naturally room for some improvement. In general, there is no application interface, or, to be more precise, the communication between the USB-CAN device and the host system, in this case a PC running Windows, is not documented. Consequently, there is currently no software support for Linux (e.g. SocketCAN), and it would be useful if users could write their own applications, regardless of the operating system they use. - -As a first step in this direction, we invested some efforts to analyze and document the USB communication protocol, which makes it easy developing an application interface. - -There has been a document, USB (Serial port) to CAN protocol defines, that was distributed online, claiming a documentation of the "Send and receive data packet format." However, after analyzing the protocol, we believe that the data frame architecture described therein represents the original intention. - -We believe that the architecture as described caused problems when it came to higher baud rates, specifically at 1 Mbps in combination with high busloads. Consequently, the firmware developers decided to optimize the data frame size. As a result, they cut the frame size from a static 20 bytes to a dynamic 6 to 16 bytes, depending on message ID length (11 or 29 bits) and number of data bytes. - -The following describes the serial protocol as we analyzed it, with a few unknowns remaining. However, the documentation includes all vital information for creating your own CAN Bus application. - -CAN Bus Data Frame - -The CAN Bus data frame format applies to both, send and receive. - -Both message ID lengths, 11-bit standard and 29-bit extended, use the first two bytes in the serial data frame. Depending on the message ID length, the remaining bytes will be filled differently. - -The data frame does not utilize a checksum check, most probably due to keeping the number of transmitted bytes to a minimum and cutting down on processing time. - -Byte Description - -0 0xAA = Packet Start - -1 CAN Bus Data Frame Information - - Bit 7 Always 1 - - Bit 6 Always 1 - - Bit 5 0=STD; 1=EXT - - Bit 4 0=Data; 1=Remote - - Bit 3…0 Data Length Code (DLC); 0…8 - -Standard CAN data frame continues: - -Byte Description - -2 Message ID LSB - -3 Message ID MSB - -x Data, length depending on DLC - -y 0x55 = Packet End - -Extended CAN data frame continues: - -Byte Description - -2 Message ID LSB - -3 Message ID 2ND - -4 Message ID 3RD - -5 Message ID MSB - -x Data, length depending on DLC - -y 0x55 = Packet End - -CAN Bus Data Frame Size - -Standard 11-bit 6 bytes with zero data bytes - - 14 bytes with eight data bytes - -Extended 29-bit 8 bytes with zero data bytes - - 16 bytes with eight data bytes - -CAN Bus Initialization Frame - -The CAN Bus initialization frame has a constant length of 20 bytes. - -Byte(s) Description - -0 0xAA = Frame Start Byte 1 - -1 0x55 = Frame Start Byte 2 - -2 0x12 = Initialization Message ID - -3 CAN Baud Rate (See table below) - -4 0x01=STD; 0x02=EXT; Applies only to transmitting - -5 – 8 Filter ID; LSB first, MSB last - -9 – 12 Mask ID; LSB first, MSB last - -13 Operation Mode (See table below) - -14 0x01; Purpose Unknown - -15 – 18 All 0x00 - -19 Checksum - -Note: The checksum is processed between bytes 2 and 18. - -CAN Bus baud rate - -0x01 1000k - -0x02 800k - -0x03 500k - -0x04 400k - -0x05 250k - -0x06 200k - -0x07 125k - -0x08 100k - -0x09 50k - -0x0A 20k - -0x0B 10k - -0x0C 5k - -Operation Mode - -0x00 Normal - -0x01 Loopback - -0x02 Silent (Listen-Only) - -0x03 Loopback + Silent - -CAN Controller Status Frame - -The format of the status request and status report frames are identical, where the request frame sends all zeroes between the frame header (frame start tokens plus frame ID) and the checksum, i.e. bytes 3 through 18 are 0x00. - -Byte(s) Description - -0 0xAA = Frame Start Byte 1 - -1 0x55 = Frame Start Byte 2 - -2 0x04 = Status Message ID - -3 Receive Error Counter - -4 Transmit Error Counter - -5 – 18 Unknown Purpose - In case of status report, they may be filled with data - -19 Checksum - diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index 9bd38bf3d..390ef7e6d 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -2,25 +2,19 @@ """ To Support the Seeed USB-Can analyzer interface. The device will appear -as a serial port, for example "/dev/ttyS1" or "/dev/ttyUSB0" on Linux -machines or "COM1" on Windows. +as a serial port, for example "/dev/ttyUSB0" on Linux machines +or "COM1" on Windows. https://www.seeedstudio.com/USB-CAN-Analyzer-p-2888.html SKU 114991193 -See protoocl: -https://copperhilltech.com/blog/usbcan-analyzer-usb-to-can-bus-serial-protocol-definition/ - this file uses Crc8Darc checksums. """ -from __future__ import absolute_import, division - import logging import struct -import binascii from time import sleep, time from can import BusABC, Message -logger = logging.getLogger('can.CanAnalyzer') +logger = logging.getLogger(__name__) try: import serial @@ -32,16 +26,11 @@ try: from crccheck.crc import Crc8Darc except ImportError: - logger.warning("The interface requires the install option crccheck.") + logger.warning("The interface requires the install option seeddstudio.") - -class CanAnalyzer(BusABC): +class SeeedBus(BusABC): """ - Enable basic can communication over a serial device. - - .. note:: See :meth:`can.interfaces.serial.CanAnalyzer._recv_internal` - for some special semantics. - + Enable basic can communication over a USB-CAN-Analyzer device. """ BITRATE = { 1000000: 0x01, @@ -70,25 +59,24 @@ class CanAnalyzer(BusABC): "loopback_and_silent":0x03 } - def __init__(self, channel, baudrate=2000000, timeout=0.1, rtscts=False, - frame_type='STD', operation_mode='normal', bit_rate=500000, - *args, **kwargs): + def __init__(self, channel, baudrate=2000000, timeout=0.1, frame_type="STD", + operation_mode="normal", bit_rate=500000, *args, **kwargs): """ :param str channel: The serial device to open. For example "/dev/ttyS1" or "/dev/ttyUSB0" on Linux or "COM1" on Windows systems. - :param int baudrate: - Baud rate of the serial device in bit/s (default 115200). - - .. warning:: - Some serial port implementations don't care about the baudrate. + :param baudrate: + The default matches required baudrate :param float timeout: Timeout for the serial device in seconds (default 0.1). - :param bool rtscts: - turn hardware handshake (RTS/CTS) on and off + :param str frame_type: + STD or EXT, to select standard or extended messages + + :param operation_mode + normal, loopback, silent or loopback_and_silent. """ self.bit_rate = bit_rate @@ -101,9 +89,9 @@ def __init__(self, channel, baudrate=2000000, timeout=0.1, rtscts=False, self.channel_info = "Serial interface: " + channel self.ser = serial.Serial( - channel, baudrate=baudrate, timeout=timeout, rtscts=rtscts) + channel, baudrate=baudrate, timeout=timeout, rtscts=False) - super(CanAnalyzer, self).__init__(channel=channel, *args, **kwargs) + super(SeeedBus, self).__init__(channel=channel, *args, **kwargs) self.init_frame() def shutdown(self): @@ -113,30 +101,29 @@ def shutdown(self): self.ser.close() def init_frame(self, timeout=None): + """ + Send init message to setup the device for comms. this is called during + interface creation. + :param timeout: + This parameter will be ignored. The timeout value of the channel is + used instead. + """ byte_msg = bytearray() byte_msg.append(0xAA) # Frame Start Byte 1 byte_msg.append(0x55) # Frame Start Byte 2 - byte_msg.append(0x12) # Initialization Message ID - - byte_msg.append(CanAnalyzer.BITRATE[self.bit_rate]) # CAN Baud Rate - byte_msg.append(CanAnalyzer.FRAMETYPE[self.frame_type]) - + byte_msg.append(SeeedBus.BITRATE[self.bit_rate]) # CAN Baud Rate + byte_msg.append(SeeedBus.FRAMETYPE[self.frame_type]) byte_msg.extend(self.filter_id) - byte_msg.extend(self.mask_id) - - byte_msg.append(CanAnalyzer.OPERATIONMODE[self.op_mode]) - + byte_msg.append(SeeedBus.OPERATIONMODE[self.op_mode]) byte_msg.append(0x01) for i in range(0, 4): byte_msg.append(0x00) crc = Crc8Darc.calc(byte_msg[2:]) -# crc_byte = struct.pack('B', crc) - byte_msg.append(crc) logger.debug("init_frm:\t" + byte_msg.hex()) @@ -146,6 +133,13 @@ def flush_buffer(self): self.ser.flushInput() def status_frame(self, timeout=None): + """ + Send status message over the serial device. + + :param timeout: + This parameter will be ignored. The timeout value of the channel is + used instead. + """ byte_msg = bytearray() byte_msg.append(0xAA) # Frame Start Byte 1 byte_msg.append(0x55) # Frame Start Byte 2 @@ -157,9 +151,7 @@ def status_frame(self, timeout=None): byte_msg.append(0x00) crc = Crc8Darc.calc(byte_msg[2:]) - crc_byte = struct.pack('B', crc) - - byte_msg.append(crc_byte) + byte_msg.append(crc) logger.debug("status_frm:\t" + byte_msg.hex()) self.ser.write(byte_msg) @@ -208,7 +200,8 @@ def _recv_internal(self, timeout): :param timeout: .. warning:: - This parameter will be ignored. The timeout value of the channel is used. + This parameter will be ignored. The timeout value of the + channel is used. :returns: Received message and False (because not filtering as taken place). diff --git a/doc/interfaces/seeedstudio.rst b/doc/interfaces/seeedstudio.rst new file mode 100644 index 000000000..35cc25b93 --- /dev/null +++ b/doc/interfaces/seeedstudio.rst @@ -0,0 +1,85 @@ +.. _seeeddoc: + + +USB-CAN Analyzer +================ +...by Seeed Studio + +SKU: 114991193 + +Links: + +- https://www.seeedstudio.com/USB-CAN-Analyzer-p-2888.html +- https://github.com/SeeedDocument/USB-CAN_Analyzer +- https://copperhilltech.com/blog/usbcan-analyzer-usb-to-can-bus-serial-protocol-definition/ + +^^^^^^^^^^ + +Installation +------------ +This interface has additional dependencies which can be installed using pip and the optional extra [seeedstudio]. That will install two additional packages if not already available: + - pyserial + - crccheck + + +:: + + pip3 install python-can[seeedstudio] + + +^^^^^^^^^^ + + +Interface +--------- + +:: + + can.interfaces.seeedstudio.SeeedBus + +A bus example:: + + bus = can.interface.Bus(bustype='seeedstudio', channel='/dev/ttyUSB0', bitrate=500000) + + +^^^^^^^^^^ + +Parameters +---------- +:: + + SeeedBus(channel, + timeout=0.1, + frame_type='STD', + operation_mode='normal', + bit_rate=500000) + +ChANNEL + The serial port created by the USB device when connected. + +TIMEOUT + Only used by the underling serial port, it probably should not be changed. The serial port baudrate=2000000 and rtscts=false are also matched to the device so are not added here. + +FRAMETYPE + - "STD" + - "EXT" + +OPERATIONMODE + - "normal" + - "loopback" + - "silent" + - "loopback_and_silent" + +BITRATE + - 1000000 + - 800000 + - 500000 + - 400000 + - 250000 + - 200000 + - 125000 + - 100000 + - 50000 + - 20000 + - 10000 + - 5000 From d3ef672d4a1f287e949a91117aadcfb58c5cf1aa Mon Sep 17 00:00:00 2001 From: shedfly Date: Wed, 12 Jun 2019 22:03:17 +1000 Subject: [PATCH 11/17] changing from bit_rate to bitrate to match other interfaces and examples. --- can/interfaces/seeedstudio/seeedstudio.py | 7 +++++-- doc/interfaces/seeedstudio.rst | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index 390ef7e6d..33493ca1b 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -60,7 +60,7 @@ class SeeedBus(BusABC): } def __init__(self, channel, baudrate=2000000, timeout=0.1, frame_type="STD", - operation_mode="normal", bit_rate=500000, *args, **kwargs): + operation_mode="normal", bitrate=500000, *args, **kwargs): """ :param str channel: The serial device to open. For example "/dev/ttyS1" or @@ -78,8 +78,11 @@ def __init__(self, channel, baudrate=2000000, timeout=0.1, frame_type="STD", :param operation_mode normal, loopback, silent or loopback_and_silent. + :param bitrate + CAN bus bit rate, selected from available list. + """ - self.bit_rate = bit_rate + self.bit_rate = bitrate self.frame_type = frame_type self.op_mode = operation_mode self.filter_id = bytearray([0x00, 0x00, 0x00, 0x00]) diff --git a/doc/interfaces/seeedstudio.rst b/doc/interfaces/seeedstudio.rst index 35cc25b93..b018a6643 100644 --- a/doc/interfaces/seeedstudio.rst +++ b/doc/interfaces/seeedstudio.rst @@ -49,10 +49,11 @@ Parameters :: SeeedBus(channel, + baudrate=2000000, timeout=0.1, frame_type='STD', operation_mode='normal', - bit_rate=500000) + bitrate=500000) ChANNEL The serial port created by the USB device when connected. From fde4fdd6be690a0de2845069ec6796a762975b9c Mon Sep 17 00:00:00 2001 From: shedfly Date: Wed, 12 Jun 2019 22:06:01 +1000 Subject: [PATCH 12/17] corrected crc calc and removed crccheck package. --- can/interfaces/seeedstudio/seeedstudio.py | 24 ++++++++++------------- setup.py | 2 +- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index 33493ca1b..351ecb93f 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -6,7 +6,6 @@ or "COM1" on Windows. https://www.seeedstudio.com/USB-CAN-Analyzer-p-2888.html SKU 114991193 -this file uses Crc8Darc checksums. """ import logging @@ -14,7 +13,7 @@ from time import sleep, time from can import BusABC, Message -logger = logging.getLogger(__name__) +logger = logging.getLogger('seeedbus') try: import serial @@ -23,11 +22,6 @@ "the serial module installed!") serial = None -try: - from crccheck.crc import Crc8Darc -except ImportError: - logger.warning("The interface requires the install option seeddstudio.") - class SeeedBus(BusABC): """ Enable basic can communication over a USB-CAN-Analyzer device. @@ -121,12 +115,12 @@ def init_frame(self, timeout=None): byte_msg.extend(self.filter_id) byte_msg.extend(self.mask_id) byte_msg.append(SeeedBus.OPERATIONMODE[self.op_mode]) - byte_msg.append(0x01) + byte_msg.append(0x01) # Follows 'Send once' in windows app. - for i in range(0, 4): + for i in range(0, 4): # Manual bitrate config, details unknown. byte_msg.append(0x00) - crc = Crc8Darc.calc(byte_msg[2:]) + crc = sum(byte_msg[2:]) & 0xFF byte_msg.append(crc) logger.debug("init_frm:\t" + byte_msg.hex()) @@ -137,7 +131,8 @@ def flush_buffer(self): def status_frame(self, timeout=None): """ - Send status message over the serial device. + Send status request message over the serial device. The device will + respond but details of error codes are unknown but are logged - DEBUG. :param timeout: This parameter will be ignored. The timeout value of the channel is @@ -153,7 +148,7 @@ def status_frame(self, timeout=None): for i in range(0, 14): byte_msg.append(0x00) - crc = Crc8Darc.calc(byte_msg[2:]) + crc = sum(byte_msg[2:]) & 0xFF byte_msg.append(crc) logger.debug("status_frm:\t" + byte_msg.hex()) @@ -193,7 +188,7 @@ def send(self, msg, timeout=None): byte_msg.extend(msg.data) byte_msg.append(0x55) - logger.debug("Sending:\t" + byte_msg.hex()) + logger.debug("sending:\t" + byte_msg.hex()) self.ser.write(byte_msg) def _recv_internal(self, timeout): @@ -224,7 +219,8 @@ def _recv_internal(self, timeout): rx_byte_2 = ord(self.ser.read()) time_stamp = time() if rx_byte_2 == 0x55: - rx_msg_type = self.ser.read() + status = bytearray(self.ser.read(18)) + logger.debug("status resp:\t" + status.hex()) else: length = int(rx_byte_2 & 0x0F) diff --git a/setup.py b/setup.py index 29a50329f..ef3cc3ca0 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ extras_require = { "serial": ["pyserial~=3.0"], "neovi": ["python-ics>=2.12"], - "seeedstudio": ["crccheck>=0.6", "pyserial>=3.0"], + "seeedstudio": ["pyserial>=3.0"], } tests_require = [ From 0c0b9a1b1d18dacf50ad8de293b296f47b96a811 Mon Sep 17 00:00:00 2001 From: shedfly Date: Wed, 12 Jun 2019 22:25:29 +1000 Subject: [PATCH 13/17] remove crccheck reference from doco --- doc/interfaces/seeedstudio.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/interfaces/seeedstudio.rst b/doc/interfaces/seeedstudio.rst index b018a6643..17476a498 100644 --- a/doc/interfaces/seeedstudio.rst +++ b/doc/interfaces/seeedstudio.rst @@ -19,7 +19,6 @@ Installation ------------ This interface has additional dependencies which can be installed using pip and the optional extra [seeedstudio]. That will install two additional packages if not already available: - pyserial - - crccheck :: From fee245411c5e185ab9a4421f1bc9f3762dd87c59 Mon Sep 17 00:00:00 2001 From: shedfly Date: Sun, 23 Jun 2019 14:32:57 +1000 Subject: [PATCH 14/17] fixing pylint errors, Unused variable 'i', unused import sleep. --- can/interfaces/seeedstudio/seeedstudio.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index 351ecb93f..f4f6867ba 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -10,7 +10,7 @@ import logging import struct -from time import sleep, time +from time import time from can import BusABC, Message logger = logging.getLogger('seeedbus') @@ -117,7 +117,7 @@ def init_frame(self, timeout=None): byte_msg.append(SeeedBus.OPERATIONMODE[self.op_mode]) byte_msg.append(0x01) # Follows 'Send once' in windows app. - for i in range(0, 4): # Manual bitrate config, details unknown. + for _ in range(0, 4): # Manual bitrate config, details unknown. byte_msg.append(0x00) crc = sum(byte_msg[2:]) & 0xFF @@ -145,7 +145,7 @@ def status_frame(self, timeout=None): byte_msg.append(0x00) # In response packet - Rx error count byte_msg.append(0x00) # In response packet - Tx error count - for i in range(0, 14): + for _ in range(0, 14): byte_msg.append(0x00) crc = sum(byte_msg[2:]) & 0xFF From cf1fcb4db6c62ce27ff4ef4f5611ce67adbcefe8 Mon Sep 17 00:00:00 2001 From: shedfly Date: Sun, 23 Jun 2019 15:54:02 +1000 Subject: [PATCH 15/17] fixing pylint errors, logging string arguments --- can/interfaces/seeedstudio/seeedstudio.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index f4f6867ba..328d67649 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -123,7 +123,7 @@ def init_frame(self, timeout=None): crc = sum(byte_msg[2:]) & 0xFF byte_msg.append(crc) - logger.debug("init_frm:\t" + byte_msg.hex()) + logger.debug("init_frm:\t%s", byte_msg.hex()) self.ser.write(byte_msg) def flush_buffer(self): @@ -151,7 +151,7 @@ def status_frame(self, timeout=None): crc = sum(byte_msg[2:]) & 0xFF byte_msg.append(crc) - logger.debug("status_frm:\t" + byte_msg.hex()) + logger.debug("status_frm:\t%s", byte_msg.hex()) self.ser.write(byte_msg) def send(self, msg, timeout=None): @@ -188,7 +188,7 @@ def send(self, msg, timeout=None): byte_msg.extend(msg.data) byte_msg.append(0x55) - logger.debug("sending:\t" + byte_msg.hex()) + logger.debug("sending:\t%s", byte_msg.hex()) self.ser.write(byte_msg) def _recv_internal(self, timeout): @@ -219,8 +219,9 @@ def _recv_internal(self, timeout): rx_byte_2 = ord(self.ser.read()) time_stamp = time() if rx_byte_2 == 0x55: - status = bytearray(self.ser.read(18)) - logger.debug("status resp:\t" + status.hex()) + status = bytearray([0xAA, 0x55]) + status += bytearray(self.ser.read(18)) + logger.debug("status resp:\t%s", status.hex()) else: length = int(rx_byte_2 & 0x0F) @@ -243,7 +244,7 @@ def _recv_internal(self, timeout): is_remote_frame=is_remote, dlc=length, data=data) - logger.debug("recv message: " + str(msg)) + logger.debug("recv message: %s", str(msg)) return msg, False else: From 876e12cb79b39dd6beec9d66cb9077ba75ecfec3 Mon Sep 17 00:00:00 2001 From: shedfly Date: Sun, 23 Jun 2019 16:08:40 +1000 Subject: [PATCH 16/17] fixing format errors, ran back format tool. --- can/interfaces/seeedstudio/seeedstudio.py | 119 ++++++++++++---------- 1 file changed, 66 insertions(+), 53 deletions(-) diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index 328d67649..7e93dfeb0 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -13,48 +13,58 @@ from time import time from can import BusABC, Message -logger = logging.getLogger('seeedbus') +logger = logging.getLogger("seeedbus") try: import serial except ImportError: - logger.warning("You won't be able to use the serial can backend without " - "the serial module installed!") + logger.warning( + "You won't be able to use the serial can backend without " + "the serial module installed!" + ) serial = None + class SeeedBus(BusABC): """ Enable basic can communication over a USB-CAN-Analyzer device. """ + BITRATE = { - 1000000: 0x01, - 800000: 0x02, - 500000: 0x03, - 400000: 0x04, - 250000: 0x05, - 200000: 0x06, - 125000: 0x07, - 100000: 0x08, - 50000: 0x09, - 20000: 0x0A, - 10000: 0x0B, - 5000: 0x0C - } - - FRAMETYPE = { - "STD":0x01, - "EXT":0x02 - } + 1000000: 0x01, + 800000: 0x02, + 500000: 0x03, + 400000: 0x04, + 250000: 0x05, + 200000: 0x06, + 125000: 0x07, + 100000: 0x08, + 50000: 0x09, + 20000: 0x0A, + 10000: 0x0B, + 5000: 0x0C, + } + + FRAMETYPE = {"STD": 0x01, "EXT": 0x02} OPERATIONMODE = { - "normal":0x00, - "loopback":0x01, - "silent":0x02, - "loopback_and_silent":0x03 - } - - def __init__(self, channel, baudrate=2000000, timeout=0.1, frame_type="STD", - operation_mode="normal", bitrate=500000, *args, **kwargs): + "normal": 0x00, + "loopback": 0x01, + "silent": 0x02, + "loopback_and_silent": 0x03, + } + + def __init__( + self, + channel, + baudrate=2000000, + timeout=0.1, + frame_type="STD", + operation_mode="normal", + bitrate=500000, + *args, + **kwargs + ): """ :param str channel: The serial device to open. For example "/dev/ttyS1" or @@ -86,7 +96,8 @@ def __init__(self, channel, baudrate=2000000, timeout=0.1, frame_type="STD", self.channel_info = "Serial interface: " + channel self.ser = serial.Serial( - channel, baudrate=baudrate, timeout=timeout, rtscts=False) + channel, baudrate=baudrate, timeout=timeout, rtscts=False + ) super(SeeedBus, self).__init__(channel=channel, *args, **kwargs) self.init_frame() @@ -107,17 +118,17 @@ def init_frame(self, timeout=None): used instead. """ byte_msg = bytearray() - byte_msg.append(0xAA) # Frame Start Byte 1 - byte_msg.append(0x55) # Frame Start Byte 2 - byte_msg.append(0x12) # Initialization Message ID + byte_msg.append(0xAA) # Frame Start Byte 1 + byte_msg.append(0x55) # Frame Start Byte 2 + byte_msg.append(0x12) # Initialization Message ID byte_msg.append(SeeedBus.BITRATE[self.bit_rate]) # CAN Baud Rate byte_msg.append(SeeedBus.FRAMETYPE[self.frame_type]) byte_msg.extend(self.filter_id) byte_msg.extend(self.mask_id) byte_msg.append(SeeedBus.OPERATIONMODE[self.op_mode]) - byte_msg.append(0x01) # Follows 'Send once' in windows app. + byte_msg.append(0x01) # Follows 'Send once' in windows app. - for _ in range(0, 4): # Manual bitrate config, details unknown. + for _ in range(0, 4): # Manual bitrate config, details unknown. byte_msg.append(0x00) crc = sum(byte_msg[2:]) & 0xFF @@ -139,11 +150,11 @@ def status_frame(self, timeout=None): used instead. """ byte_msg = bytearray() - byte_msg.append(0xAA) # Frame Start Byte 1 - byte_msg.append(0x55) # Frame Start Byte 2 - byte_msg.append(0x04) # Status Message ID - byte_msg.append(0x00) # In response packet - Rx error count - byte_msg.append(0x00) # In response packet - Tx error count + byte_msg.append(0xAA) # Frame Start Byte 1 + byte_msg.append(0x55) # Frame Start Byte 2 + byte_msg.append(0x04) # Status Message ID + byte_msg.append(0x00) # In response packet - Rx error count + byte_msg.append(0x00) # In response packet - Tx error count for _ in range(0, 14): byte_msg.append(0x00) @@ -169,7 +180,7 @@ def send(self, msg, timeout=None): byte_msg = bytearray() byte_msg.append(0xAA) - m_type = 0xc0 + m_type = 0xC0 if msg.is_extended_id: m_type += 1 << 5 @@ -180,9 +191,9 @@ def send(self, msg, timeout=None): byte_msg.append(m_type) if msg.is_extended_id: - a_id = struct.pack(' Date: Sun, 23 Jun 2019 16:22:28 +1000 Subject: [PATCH 17/17] minor tidy-up --- can/interfaces/seeedstudio/seeedstudio.py | 6 ++---- doc/interfaces/seeedstudio.rst | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/can/interfaces/seeedstudio/seeedstudio.py b/can/interfaces/seeedstudio/seeedstudio.py index 7e93dfeb0..eebd07753 100644 --- a/can/interfaces/seeedstudio/seeedstudio.py +++ b/can/interfaces/seeedstudio/seeedstudio.py @@ -128,8 +128,7 @@ def init_frame(self, timeout=None): byte_msg.append(SeeedBus.OPERATIONMODE[self.op_mode]) byte_msg.append(0x01) # Follows 'Send once' in windows app. - for _ in range(0, 4): # Manual bitrate config, details unknown. - byte_msg.append(0x00) + byte_msg.extend([0x00] * 4) # Manual bitrate config, details unknown. crc = sum(byte_msg[2:]) & 0xFF byte_msg.append(crc) @@ -156,8 +155,7 @@ def status_frame(self, timeout=None): byte_msg.append(0x00) # In response packet - Rx error count byte_msg.append(0x00) # In response packet - Tx error count - for _ in range(0, 14): - byte_msg.append(0x00) + byte_msg.extend([0x00] * 14) crc = sum(byte_msg[2:]) & 0xFF byte_msg.append(crc) diff --git a/doc/interfaces/seeedstudio.rst b/doc/interfaces/seeedstudio.rst index 17476a498..5c86fa688 100644 --- a/doc/interfaces/seeedstudio.rst +++ b/doc/interfaces/seeedstudio.rst @@ -17,7 +17,7 @@ Links: Installation ------------ -This interface has additional dependencies which can be installed using pip and the optional extra [seeedstudio]. That will install two additional packages if not already available: +This interface has additional dependencies which can be installed using pip and the optional extra [seeedstudio]. That will install an additional packages if not already available: - pyserial