# SANPO SPI to RS485 SANPO SPI to RS485 lets a Jetson, Raspberry Pi, or another host controller operate RS485 motors and devices periodically. The host places a raw device command in a fixed-length `RT` message and reads feedback during the current or a later SPI transfer. ## SPI Settings and Length | Item | SHORT | LONG | | --- | ---: | ---: | | Total transfer | 23 bytes | 73 bytes | | Message area | 22 bytes | 72 bytes | | RS485 data area | 18 bytes | 68 bytes | | Final byte | CRC-8 | CRC-8 | Both modes use SPI Mode 0 and MSB First. CRC-8 uses polynomial `0x07`, initial value `0x00`, and no final XOR. Factory default is SHORT. For more than 18 bytes of RS485 data, send over USB serial: ```text AT+SPIMODE=LONG ``` After receiving `OK`, wait at least 10 ms and begin 73-byte transfers. The setting is saved to Flash. ## Select an RS485 Channel | SPI chip select | Channel | Target | | --- | --- | --- | | First MCU | `0x01` | RS485-1 | | First MCU | `0x02` | RS485-2 | | Second MCU | `0x03` | RS485-3 | | Second MCU | `0x04` | RS485-4 | | Either chip select | `0x00` | Both RS485 interfaces on the current MCU | Internally, `1/3` select the first local interface and `2/4` select the second. Replies use local Channel `1/2`. ## SHORT Message Transmit and receive use the same layout: | Byte position | Content | | --- | --- | | 0 to 1 | Fixed `52 54` (`RT`) | | 2 | Channel | | 3 | RS485 data length `0` to `18` | | 4 to 21 | 18-byte RS485 data area; fill unused bytes with `00` | | 22 | CRC-8 over the first 22 bytes | For a 17-byte Unitree command on Channel 2, the message area before CRC is: ```text 52 54 02 11 FE EE 11 00 00 00 00 00 00 00 00 00 00 00 00 18 7C 00 ``` Append CRC-8 over these 22 bytes. ## LONG Message | Byte position | Content | | --- | --- | | 0 to 1 | Fixed `52 54` (`RT`) | | 2 | Channel | | 3 | RS485 data length `0` to `68` | | 4 to 71 | 68-byte RS485 data area; fill unused bytes with `00` | | 72 | CRC-8 over the first 72 bytes | The CRC algorithm is shown in [SANPO SPI to CAN](spi_can). ## Read RS485 Feedback The board places received RS485 data into the same `RT` layout: - Byte 2 identifies the first or second local RS485 interface. - Byte 3 is the length of this feedback block. - Data begins at byte 4. - Unused data bytes are `00`. SPI is full duplex. Data read while sending a command may be older feedback or all `00`. If the target feedback is not available, continue sending all-zero frames of the current length. One RS485 receive event is not guaranteed to contain exactly one complete device-protocol frame. Use Channel to separate buses, then reassemble data by the device protocol's header, length, and checksum. ## Unitree GO-M8010 Example Example: [SPI to RS485 Unitree motor](https://gitcode.com/sanpo/robot/blob/unified-main/products/spine/v6/demo/rs485/spi2rs485_unitree_GM8010_demo_v6.py) ```bash sudo python3 spi2rs485_unitree_GM8010_demo_v6.py \ --spibus 0 \ --cs 0 \ --motors 1 \ --channel 1 ``` Use the current script help for exact options. For the first run, reduce motion amplitude and prepare an immediate power disconnect. ## Troubleshooting | Symptom | Action | | --- | --- | | Received data is all `00` | No feedback is queued; wait briefly and send another empty frame of the current length | | Reply Channel is 1/2 | Replies use local numbering; this is normal | | A 17-byte command works in SHORT but a longer one fails | SHORT supports at most 18 bytes; switch to LONG | | CRC fails | Check Mode 0, MSB First, fixed length, SPI clock, and chip-select timing | | Device does not reply | Check chip select, Channel, RS485 settings, device ID, checksum, A/B wiring, and power | | Feedback is split into blocks | Reassemble it by the device protocol; do not treat one SPI read as exactly one motor reply |