# SPI to CAN - The following protocol applies to firmware V4.1 or later - Send the AT+VER serial command to check the firmware version; if no version is returned or it shows V33, please update to the latest firmware - The SANPO Motor Debug Tool can generate test code quickly and supports CAN/RS485 motors ## Protocol Overview | Use Case | Wiring | TX Protocol (SPI->CAN) | RX Protocol (CAN->SPI) | | --- | --- | --- | --- | | CAN extended frame motors (fixed 23-byte frames) | CS1 controls CAN1/CAN2; CS2 controls CAN3/CAN4 | Header 2 bytes (0x45 0x54) + Channel 1 byte + CANID ext 4 bytes + CAN data length (1 byte) + CAN data (fixed 8 bytes, pad with 0x00) + tail 2 bytes (0x0D 0x0A) + reserved 4 bytes (0x00 0x00 0x00 0x12) + CRC (1 byte) | Header 2 bytes (0x45 0x54) + Channel 1 byte + CANID ext 4 bytes + CAN data length (1 byte) + CAN data (fixed 8 bytes, pad with 0x00) + tail 2 bytes (0x0D 0x0A) + reserved 4 bytes (0x00 0x00 0x00 0x12) + CRC (1 byte) | | CAN standard frame motors (fixed 23-byte frames) | CS1 controls CAN1/CAN2; CS2 controls CAN3/CAN4 | Header 2 bytes (0x53 0x54) + Channel 1 byte + fixed 2 bytes (0x00 0x00) + CANID std 2 bytes + CAN data length (1 byte) + CAN data (fixed 8 bytes, pad with 0x00) + tail 2 bytes (0x0D 0x0A) + reserved 4 bytes (0x00 0x00 0x00 0x12) + CRC (1 byte) | Header 2 bytes (0x53 0x54) + Channel 1 byte + fixed 2 bytes (0x00 0x00) + CANID std 2 bytes + CAN data length (1 byte) + CAN data (fixed 8 bytes, pad with 0x00) + tail 2 bytes (0x0D 0x0A) + reserved 4 bytes (0x00 0x00 0x00 0x12) + CRC (1 byte) | ### CAN Extended Frame Example TX frame | Header (2 bytes) | Channel (1 byte) | CANID (4 bytes) | Data length (1 byte) | Data (fixed 8 bytes) | Tail (2 bytes) | Reserved 4 bytes | CRC (1 byte) | | --- | --- | --- | --- | --- | --- | --- | --- | | 0x45 0x54 | 0x00 | 0x00 0x00 0xFD 0x01 | 0x08 | 0x48 0x45 0x4C 0x4F 0x00 0x00 0x00 0x00 | 0x0D 0x0A | 0x00 0x00 0x00 0x12 | 0x0F | - SPI (CS1) controls CAN1/CAN2; SPI (CS2) controls CAN3/CAN4 (see the label on the back of the board) - Channel can be 0x01/0x02/0x03/0x04; 0x00 broadcasts to all CAN channels on the current SPI CS - Extended CANID is 29-bit, stored in the low 29 bits of the 4-byte CANID field RX frame | Header (2 bytes) | Channel (1 byte) | CANID (4 bytes) | Data length (1 byte) | Data (fixed 8 bytes) | Tail (2 bytes) | Reserved 4 bytes | CRC (1 byte) | | --- | --- | --- | --- | --- | --- | --- | --- | | 0x45 0x54 | 0x00 | 0x00 0x00 0x01 0xFE | 0x08 | 0x48 0x45 0x4C 0x4F 0x01 0x02 0x03 0x04 | 0x0D 0x0A | 0x00 0x00 0x00 0x12 | 0x0F | ### Code Snippet (SPI to CAN Extended Frame, Python) Full example: spi_cybergear_demo_v4.py ```python FRAME_HEADER = [0x45, 0x54] FRAME_TAIL = [0x0D, 0x0A] RESERVED = [0x00, 0x00, 0x00, 0x12] # TX (header 2 bytes + channel 1 byte + CANID 4 bytes + DLC 1 byte + data + tail 2 bytes + reserved 4 bytes + CRC) def build_spi_ext_frame(channel: int, arbitration_id: int, data: list[int]) -> list[int]: ext_id = [ (arbitration_id >> 24) & 0xFF, (arbitration_id >> 16) & 0xFF, (arbitration_id >> 8) & 0xFF, arbitration_id & 0xFF, ] data_bytes = (data + [0x00] * 8)[:8] payload = ( FRAME_HEADER + [channel & 0xFF] + ext_id + [len(data[:8])] + data_bytes + FRAME_TAIL + RESERVED ) crc = crc8_calculator.calculate(payload) return payload + [crc] tx = build_spi_ext_frame(channel=0x00, arbitration_id=0x0000FD01, data=[0x01] + [0x00] * 7) rx = spi.xfer2(tx) # fixed length 23 bytes # RX parse (header 2 bytes + channel 1 byte + CANID 4 bytes + DLC 1 byte + data + tail 2 bytes + reserved 4 bytes + CRC) data_part = rx[:22] crc_ok = crc8_calculator.calculate(data_part) == rx[22] ``` ### CAN Standard Frame Example TX frame | Header (2 bytes) | Channel (1 byte) | Reserved 2 bytes | CANID (2 bytes) | Data length (1 byte) | Data (fixed 8 bytes) | Tail (2 bytes) | Reserved 4 bytes | CRC (1 byte) | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | 0x53 0x54 | 0x00 | 0x00 0x00 | 0x01 0x42 | 0x08 | 0x48 0x45 0x4C 0x4F 0x01 0x02 0x03 0x04 | 0x0D 0x0A | 0x00 0x00 0x00 0x12 | 0x0F | - SPI (CS1) controls CAN1/CAN2; SPI (CS2) controls CAN3/CAN4 - Channel can be 0x01/0x02/0x03/0x04; 0x00 broadcasts to all CAN channels on the current SPI CS - Standard CANID is 11-bit, stored in the low 11 bits of the 2-byte CANID field RX frame | Header (2 bytes) | Channel (1 byte) | Reserved 2 bytes | CANID (2 bytes) | Data length (1 byte) | Data (fixed 8 bytes) | Tail (2 bytes) | Reserved 4 bytes | CRC (1 byte) | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | 0x53 0x54 | 0x00 | 0x00 0x00 | 0x01 0x42 | 0x08 | 0x48 0x45 0x4C 0x4F 0x01 0x02 0x03 0x04 | 0x0D 0x0A | 0x00 0x00 0x00 0x12 | 0x0F | ## Examples