# 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