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

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