diff --git a/docs/File-Commands.md b/docs/File-Commands.md new file mode 100644 index 0000000..3d3d7dc --- /dev/null +++ b/docs/File-Commands.md @@ -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 + diff --git a/docs/Packet.md b/docs/Packet.md new file mode 100644 index 0000000..cbf5040 --- /dev/null +++ b/docs/Packet.md @@ -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/ \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..abc2d47 --- /dev/null +++ b/docs/README.md @@ -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) \ No newline at end of file diff --git a/docs/System-Commands.md b/docs/System-Commands.md new file mode 100644 index 0000000..0487201 --- /dev/null +++ b/docs/System-Commands.md @@ -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 + diff --git a/docs/Types.md b/docs/Types.md new file mode 100644 index 0000000..48d3914 --- /dev/null +++ b/docs/Types.md @@ -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 |