# SPI to RS485
- 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->RS485) | RX Protocol (RS485->SPI) |
| --- | --- | --- | --- |
| RS485 motors (fixed 23-byte frames) | CS1 controls RS485-1/RS485-2; CS2 controls RS485-3/RS485-4 | Transparent, fixed length 23 bytes. Last 3 bytes reserved: last is CRC, second last is valid data length, third last is Channel. | Transparent, fixed length 23 bytes. Last 3 bytes reserved: last is CRC, second last is valid data length, third last is Channel. |
- The first two bytes of RS485 frames must not be the reserved headers 0x45 0x54, 0x41 0x54, or 0x53 0x54
### Frame Examples
TX frame
| RS485 data passthrough (pad to 20 bytes with 0x00) | Channel | Data length | CRC (1 byte) |
| --- | --- | --- | --- |
| 0x22 0x33 0x00 0x53 0x54 0x00 0x53 0x54 0x00 0x53 0x54 0x00 0x53 0x54 0x00 0x53 0x54 0x00 0x00 0x00 | 0x00 | 0x17 | 0x0F |
RX frame
| RS485 feedback passthrough | Channel | Data length | CRC (1 byte) |
| --- | --- | --- | --- |
| 0x22 0x33 0x00 0x53 0x54 0x00 0x53 0x54 0x00 0x53 0x54 0x00 0x53 0x54 0x00 0x53 0x00 0x00 0x00 0x00 | 0x00 | 0x16 | 0x0F |
### Code Snippet (SPI to RS485, Python)
Full example: spi_unitree_GO_M8010_demo_v4.py
```python
# TX: motor command data (<=20 bytes, pad with 0x00) + Channel + Data length + CRC
def build_spi_rs485_frame(cmd: bytes, channel: int) -> bytes:
data_bytes = bytearray(cmd[:20])
data_len = len(data_bytes)
if data_len < 20:
data_bytes.extend([0x00] * (20 - data_len))
payload = bytes(data_bytes) + bytes([channel & 0xFF, data_len])
crc = crc8_calculator.calculate(payload)
return payload + bytes([crc]) # fixed length 23 bytes
tx = build_spi_rs485_frame(cmd, channel=0x00)
rx = spi.xfer2(list(tx)) # fixed length 23 bytes
# RX parse: CRC check + read valid data length
data_part = rx[:22]
crc_ok = crc8_calculator.calculate(data_part) == rx[22]
data_len = rx[21]
motor_data = rx[:data_len]
```
## Examples
- SPI to RS485 example (Python): Download
- SPI uses fixed 23-byte frames by default