Skip to content

Commit

Permalink
Docs: Add docs for SACP
Browse files Browse the repository at this point in the history
  • Loading branch information
parachvte committed Aug 11, 2023
1 parent 4adb0a5 commit e8abc45
Show file tree
Hide file tree
Showing 5 changed files with 342 additions and 0 deletions.
86 changes: 86 additions & 0 deletions docs/File-Commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# File Commands

## 0xb0 0x02 File Transfer Result

| Command Set | Command ID |
|-------------|------------|
| 0xb0 | 0x10 |

Request Payload:

| Payload | Data Type | Description |
|---------|-----------|-------------|
| Result | UInt8 | result = 0 means success |

ACK Payload:

| Payload | Data Type | Description |
|---------|-----------|-------------|
| Result | UInt8 | result = 0 received |

results:

- 0: file transfer completed
- 1: failed to validate the file
- 2: timeout
- 12: hardware error

Usually, the Controller/Screen peer uses 0xb0 0x02 to notify software that file transfer has been done. The software only needs to receive the message and send ACK back.

## 0xb0 0x10 Start Large File Transfer

| Command Set | Command ID |
|-------------|------------|
| 0xb0 | 0x10 |

Request Payload:

| Payload | Data Type | Description |
|---------|-----------|-------------|
| File Name | String | File name, or file path |
| File Length | UInt32 | total bytes |
| File Chunk Count | Uint32 | number of chunks |
| MD5 | String | file content MD5 |

ACK Payload:

| Payload | Data Type | Description |
|---------|-----------|-------------|
| Result | UInt8 | result = 0 means success |


results:

- 0: Ok, starts file transfer
- 6: Wrong parameters
- 9: Resource unavailable (maybe file size too big)
- 12: Hardware error
- 13: Machine state unavailable (current machine state forbits file transfer)

## 0xb0 0x11 Request/Send for a file chunk

| Command Set | Command ID |
|-------------|------------|
| 0xb0 | 0x11 |

Request Payload:

| Payload | Data Type | Description |
|---------|-----------|-------------|
| reserved field | String | reserved feild, unused |
| Chunk Index | UInt32 | chunk index, starts from 0 |

ACK Payload:

| Payload | Data Type | Description |
|---------|-----------|-------------|
| MD5 | String | MD5 of that chunk (optional) |
| Chunk Index | UInt32 | chunk index, starts from 0 |
| Chunk Content | String | chunk content |

## Note: Procedure of file transfer

1. Software sends 0xb0 0x10 to start a large file transfer job
2. The controller sends 0xb0 0x11 to request file chunks, the software responses with 0xb0 0x11 ACK including file chunk data
3. The controller sends 0xb0 0x02 to notify file transfer result

92 changes: 92 additions & 0 deletions docs/Packet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# SACP Packet

## Packet Format

| Byte(s) | Data Type | Description |
| ------- | --------- | ----------- |
| 0 - 1 | fixed bytes | SOF, it's always 0xAA 0x55 |
| 2 - 3 | UInt16 | N - 7, which is total bytes from byte 7 to byte N |
| 4 | UInt8 | Version of protocol, defaults to 0x01 |
| 5 | UInt8 | Receiver ID |
| 6 | UInt8 | CRC8 checksum of bytes 0-5 |
| 7 | UInt8 | Sender ID |
| 8 | UInt8 | Attribute |
| 9 - 10 | UInt16 | Sequence |
| 11 | UInt8 | Command Set |
| 12 | UInt8 | Command ID |
| 13 - (N - 2) | vary | Payload |
| (N-1) - N | UInt16 | TCP/IP checksum of byte 7 to byte (N - 2) |

SACP has a 13-byte header and variable length of payload.

### Byte 0 - Byte 1: SOF

SACP uses `0xAA 0x55` as its SOF, which indicates the start of an packet.

### Byte 2 - Byte 3: N - 7

SACP has a CRC8 checksum on byte 0 to byte 6, which means first 7 bytes of the packet can detectmine if received buffer is a SACP packet or not.

Once checksum is validated, then later bytes (from byte 7 to byte N) can be determined from the length.

### Byte 4: Version

Protocol version, now we only uses 0x01.

### Byte 5: Receiver ID

| Peer ID | Peer |
|---------| -----|
| 0 | Software |
| 1 | Controller |
| 2 | Screen (Snapmaker J1 & Artisan) |


### Byte 6: CRC8 checksum of byte 0 to byte 5

CRC8 checksum, see https://en.wikipedia.org/wiki/Cyclic_redundancy_check

### Byte 7: Sender ID

See Byte 5.

### Byte 8: Attribute

type: UInt8

* 0 - Request
* 1 - ACK

### Byte 9 - Byte 10: Sequence

type: UInt16

Unique number of sequence (reuse circularly), response will have the same sequence number as the request. This makes it possible to identify the 1 - 1 relation of request and response.

In our implementation, we have a internal counter, every time we are sending a new request packet, we increase the counter as the new sequence.

### Byte 11: Command Set

type: UInt8

Command set the command belongs to.

We group related commands into the same command set.

### Byte 12: Command ID

type: UInt8

Command ID the command has.

By unique command set and commnad ID, we specify a command.

### Byte 13 - Byte (N - 2): Payload

Payload format is defined by specific command.

### Byte (N - 1) - Byte N: Checksum

type: UInt16

TCP/IP checksum of byte 7 to byte (N - 2), see https://locklessinc.com/articles/tcp_checksum/
77 changes: 77 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Overview

## Background

G-code (RS-274) is widely used in CNC and 3D printing. As G-code is a plaintext protocol, it compromises some flexibility in exchange for readability.

We drafted a protocol "SACP" for our use throughout Snapmaker products, which provides:

- Sequence Number: When receiving a response, you can know which request it responses to. This is G-code can not provide.
- Subscription: Be able to subscribe to machine status, receiving periodical heartbeats from the machine.
- Multiple Peers: Besides the software and machine, there could be other peers act as communcation end.
- Active Messages: The controller can report error messsages actively, without a request from software side.

## Discovery of Snapmaker machines

We use two approaches to discover networked Snapmaker machines:

| Machine | Broadcast | mDNS |
|---------|-----------|------|
| Snapmaker 2.0 series | yes | no |
| Snapmaker J1 | yes | no |
| Snapmaker Artisan | yes | no |
| Snapmaker Ray | no | yes |


- Broadcast: send 'discover' to broadcast address, machines will response with format '{Name}@{IP}|model:{model}|SACP:{SACP version}'

- model values: "Snapmaker A150", "Snapmaker A250", "Snapmaker A350", "Snapmaker J1", "Snapmaker Artisan"
- [source code](https://github.com/Snapmaker/Luban/blob/main/src/server/services/machine/network-discover/BroadcastMachineFinder.ts)

- mDNS: Use "PTR" queries to discover services with `service_type = "printer"` and `proto = udp & proto = tcp`.

- Note that, with mDNS service discover, we may not know about what machine model is when we discover a "printer" service.
- all available [services types](http://www.dns-sd.org/serviceTypes.html)
- [source code](https://github.com/Snapmaker/Luban/blob/main/src/server/services/machine/network-discover/MulticastDNSMachineFinder.ts)


## Supported protocols of Snapmaker machines

With serial port connection:

| Machine | G-code (plaintext) | SACP |
|---------|--------------------|------|
| Snapmaker 2.0 series | yes | no |
| Snapmaker J1 | yes | no |
| Snapmaker Artisan | yes | yes |
| Snapmaker Ray | partial | yes |

With networked connection

| Machine | HTTP | SACP (over TCP) | SACP (over UDP) |
|---------|------|-----------------|-----------------|
| Snapmaker 2.0 series | yes | no | no |
| Snapmaker J1 | no | yes | no |
| Snapmaker Artisan | no | yes | no |
| Snapmaker Ray | no | no | yes |


## SACP (Snapmaker Application Communication Protocol)

There are two implementation of SACP, one over TCP connection, another over UDP packats.

- SACP over TCP: Establish a TCP long-lived connection, and send/receive SACP packets over the connection. Uses port 8888.
- SACP over UDP: Without connection establish, send/receive SACP packets via UDP packets. Users port 2016 (temporarily)

SACP defines as follows:

- [Data Types](Types.md) defines basic data types.
- [SACP Packet](Packet.md) defines a SACP packet should look like.

Avaible commands (editing):

- [System Commands](System-Commands.md)
- [File Commands](File-Commands.md)
- 3D printing commands (TODO)
- Laser commands (TODO)
- CNC commands (TODO)
57 changes: 57 additions & 0 deletions docs/System-Commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# System Commands

## 0x01 0x02 Execute G-code

| Command Set | Command ID |
|-------------|------------|
| 0x01 | 0x02 |

Request Payload:

| Payload | Data Type | Description |
|---------|-----------|-------------|
| G-code | String | G-code to be executed |

ACK Payload:

| Payload | Data Type | Description |
|---------|-----------|-------------|
| Result | UInt8 | result = 0 means success |


## 0x01 0x20 Get Module Info List

TODO

## 0x01 0x21 Get Machine Info

| Command Set | Command ID |
|-------------|------------|
| 0x01 | 0x21 |

Request Payload:

| Payload | Data Type | Description |
|---------|-----------|-------------|
| - | - | - |

ACK Payload:

| Payload | Data Type | Description |
|---------|-----------|-------------|
| Result | UInt8 | result = 0 means success |
| Machine Model ID | UInt8 | See machine model ID list below |
| Hardware Version | UInt8 | hardware version |
| Hardware Serial Number | UInt32 | machine serial number (internal use) |
| Firmware Version | String | firmware version |
| Serial Number | String | machine serial number (SN Code) for warrenty support |

Machine Modal IDs:

- 0: Snapmaker A150
- 1: Snapmaker A250
- 2: Snapmaker A350
- 3: Snapmaker Artisan
- 4: Snapmaker J1
- 5: Snapmaker Ray

30 changes: 30 additions & 0 deletions docs/Types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Data Types

Note: We use little endian as the byte order.

## Boolean

Size: 1 Byte
Values: 0 or 1, 1 for true, 0 for false

## UInt8, UInt16, UInt32

Unsigned intergers in Little Endian.

## Int8, Int16, Int32

Signed intergers in Little Endian.

Used when a negative value is required.

## Float

Floating number, store value * 1000 in UInt32 format.

## String

length in UInt16 format, then followed by the string content.

| Byte 0 - Byte 1 | Byte 2 - Byte N |
| ----------------| ----------------|
| length in UInt16 | string content |

0 comments on commit e8abc45

Please sign in to comment.