Add readme
This commit is contained in:
189
README.md
Normal file
189
README.md
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
# Cassini
|
||||||
|
|
||||||
|
[ELEGOO](https://www.elegoo.com/) has a number of printers, such as the
|
||||||
|
[Saturn 3 Ultra](https://www.elegoo.com/products/elegoo-saturn-3-ultra-resin-3d-printer-12k)
|
||||||
|
that support file transfer and printing over WiFi. As far as I could find, only Chitubox and
|
||||||
|
Voxeldance Tango support this, but there was no way to print from Lychee or [UVTools](https://github.com/sn4k3/UVtools).
|
||||||
|
|
||||||
|
*Cassini* is a command-line tool to allow getting the status of your printer(s),
|
||||||
|
transferring files to them, and starting a print. It has only been tested with a Saturn 3 Ultra,
|
||||||
|
but may work with other ChiTu-mainboard printers with WiFi.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Python 3 is required. There are no other prerequisites.
|
||||||
|
|
||||||
|
### Printer status
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./cassini.py status
|
||||||
|
0: Saturn3Ultra (ELEGOO Saturn 3 Ultra) -- 192.168.x.x
|
||||||
|
Status: 4 Layers: 27/1002
|
||||||
|
```
|
||||||
|
|
||||||
|
### File transfer
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./cassini.py [--target printer_id] put-file [--start-print] MyFile.goo
|
||||||
|
```
|
||||||
|
|
||||||
|
### Start a print (of an existing file)
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./cassini.py [--target printer_id] start-print Myfile.goo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Protocol Description
|
||||||
|
|
||||||
|
The protocol is pretty simple. (There is no encryption or anything that I could find.)
|
||||||
|
|
||||||
|
There is a UDP discovery, status, and MQTT connection protocol. When the UDP command to
|
||||||
|
connect to a MQTT server is given, the printer connects to a MQTT server, and can be
|
||||||
|
controlled via MQTT messages. A HTTP server is also needed for file downloads.
|
||||||
|
|
||||||
|
Note: this seems nicely set up to be able to control multiple printers from one central
|
||||||
|
system. Cassini doesn't implement this, though it does allow you to choose which printer
|
||||||
|
to send commands to.
|
||||||
|
|
||||||
|
(SDCP might be a standard protocol on top of MQTT, but I didn't investigate too deeply.)
|
||||||
|
|
||||||
|
### UDP Status and Discovery
|
||||||
|
|
||||||
|
A UDP broadcast to port 3000 with `M99999` will cause all printers to send back a JSON status
|
||||||
|
blob via UDP to the sending port. This response looks like this. `LASTHOST` below refers
|
||||||
|
to the last MQTT/SDCP server this printer was connected to.
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"Id": "0a69ee780fbd40d7bfb95b312250bf46",
|
||||||
|
"Data": {
|
||||||
|
"Attributes": {
|
||||||
|
"Name": "Saturn3Ultra",
|
||||||
|
"MachineName": "ELEGOO Saturn 3 Ultra",
|
||||||
|
"ProtocolVersion": "V1.0.0",
|
||||||
|
"FirmwareVersion": "V1.4.2",
|
||||||
|
"Resolution": "11520x5120",
|
||||||
|
"MainboardIP": "192.168.7.128",
|
||||||
|
"MainboardID": "ABCD1234ABCD1234",
|
||||||
|
"SDCPStatus": 0,
|
||||||
|
"LocalSDCPAddress": "tcp://LASTHOST:33288",
|
||||||
|
"SDCPAddress": "",
|
||||||
|
"Capabilities": [
|
||||||
|
"FILE_TRANSFER",
|
||||||
|
"PRINT_CONTROL"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Status": {
|
||||||
|
"CurrentStatus": 0,
|
||||||
|
"PreviousStatus": 1,
|
||||||
|
"PrintInfo": {
|
||||||
|
"Status": 16,
|
||||||
|
"CurrentLayer": 310,
|
||||||
|
"TotalLayer": 310,
|
||||||
|
"CurrentTicks": 3222039,
|
||||||
|
"TotalTicks": 3218949,
|
||||||
|
"ErrorNumber": 0,
|
||||||
|
"Filename": "ResinXP2-ValidationMatrix.goo"
|
||||||
|
},
|
||||||
|
"FileTransferInfo": {
|
||||||
|
"Status": 0,
|
||||||
|
"DownloadOffset": 0,
|
||||||
|
"CheckOffset": 0,
|
||||||
|
"FileTotalSize": 0,
|
||||||
|
"Filename": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Sending a UDP message to port 3000 of a specific printer with the content `M66666 12345` causes the
|
||||||
|
printer to make a MQTT client connection to the server that sent the message on port 12345.
|
||||||
|
|
||||||
|
The `Id` from this message is repeated in every MQTT message below, published by either the client
|
||||||
|
or the server.
|
||||||
|
|
||||||
|
### MQTT/SDCP Control
|
||||||
|
|
||||||
|
When connected via MQTT, the printer subscribes to a request topic for the printer: `/sdcp/request/ABCD1234ABCD1234`.
|
||||||
|
The id is the `MainboardID` from the original discovery. Each payload
|
||||||
|
|
||||||
|
The printer publishes messages to three topics:
|
||||||
|
|
||||||
|
`/sdcp/status/ABCD1234ABCD1234`: Status messages, same format as the `"Status"` content of the broadcast message: `{"Id":"f25273b12b094c5a8b9513a30ca60049","Data":{"Status":{"CurrentStatus":0,"PreviousStatus":0,"PrintInfo":{"Status":0,"CurrentLayer":0,"TotalLayer":0,"CurrentTicks":0,"TotalTicks":0,"ErrorNumber":0,"Filename":""},"FileTransferInfo":{"Status":0,"DownloadOffset":0,"CheckOffset":0,"FileTotalSize":0,"Filename":""}},"MainboardID":"ABCD1234ABCD1234","TimeStamp":8629636}}`
|
||||||
|
|
||||||
|
`/sdcp/attributes/ABCD1234ABCD1234`: Unclear what this is used for, as it seems to repeat the Status information: `{"Id":"f25273b12b094c5a8b9513a30ca60049","Data":{"Attributes":{"CurrentStatus":0,"PreviousStatus":0,"PrintInfo":{"Status":0,"CurrentLayer":0,"TotalLayer":0,"CurrentTicks":0,"TotalTicks":0,"ErrorNumber":0,"Filename":""},"FileTransferInfo":{"Status":0,"DownloadOffset":0,"CheckOffset":0,"FileTotalSize":0,"Filename":""}},"MainboardID":"ABCD1234ABCD1234","TimeStamp":8629737}}`
|
||||||
|
|
||||||
|
`/sdcp/response/ABCD1234ABCD1234`: Responses to `/sdcp/request/XXX` messages. The `Cmd` inside Data (if present) and `RequestID` values will match what was sent in the request. Example: `{"Id":"f25273b12b094c5a8b9513a30ca60049","Data":{"Cmd":1,"Data":{"Ack":0},"RequestID":"130fdded918e4276a47e504f554bed54","MainboardID":"ABCD1234ABCD1234","TimeStamp":8567213}}`
|
||||||
|
|
||||||
|
The printer subscribes to a request topic specific to its mainboard ID `/sdcp/request/ABCD1234ABCD1234`, with payloads looking like:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"Data": {
|
||||||
|
"Cmd": 1,
|
||||||
|
"Data": null,
|
||||||
|
"From": 0,
|
||||||
|
"MainboardID": "ABCD1234ABCD1234",
|
||||||
|
"RequestID": "3676747651dd44b0bdbd630f38b61754",
|
||||||
|
"TimeStamp": 1693671336726
|
||||||
|
},
|
||||||
|
"Id": "0a69ee780fbd40d7bfb95b312250bf46"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Commands discovered:
|
||||||
|
|
||||||
|
| ID | Description | Data |
|
||||||
|
===========================
|
||||||
|
| 0 | Unknown. Sent by CHITUBOX first. | None |
|
||||||
|
| 1 | Unknown. Sent by CHITUBOX after 0. | None |
|
||||||
|
| 64 | Maybe a disconnect? | None |
|
||||||
|
| 128 | Start printing. | See below. |
|
||||||
|
| 256 | Upload file. | See below. |
|
||||||
|
| 512 | Set some kind of time period. | `{ "TimePeriod": 5000 }` |
|
||||||
|
|
||||||
|
#### 128: Start Printing
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"Data": {
|
||||||
|
"Cmd": 128,
|
||||||
|
"Data": {
|
||||||
|
"Filename": "_ResinXP2-ValidationMatrix_v2.goo",
|
||||||
|
"StartLayer": 0
|
||||||
|
},
|
||||||
|
"From": 0,
|
||||||
|
"MainboardID": "ABCD1234ABCD1234",
|
||||||
|
"RequestID": "b353f511680d40278c48602821a9e6ec",
|
||||||
|
"TimeStamp": 1693671341990
|
||||||
|
},
|
||||||
|
"Id": "0a69ee780fbd40d7bfb95b312250bf46"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 256: Upload file
|
||||||
|
|
||||||
|
Note: in the URL, `${ipaddr}` seems to be replaced by the IP address that the printer
|
||||||
|
is connected to. The file needs to be accessible at the specified URL.
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"Data": {
|
||||||
|
"Cmd": 256,
|
||||||
|
"Data": {
|
||||||
|
"Check": 0,
|
||||||
|
"CleanCache": 1,
|
||||||
|
"Compress": 0,
|
||||||
|
"FileSize": 3541068,
|
||||||
|
"Filename": "_ResinXP2-ValidationMatrix_v2.goo",
|
||||||
|
"MD5": "205abc8fab0762ad2b0ee1f6b63b1750",
|
||||||
|
"URL": "http://${ipaddr}:58883/f60c0718c8144b0db48b7149d4d85390.goo"
|
||||||
|
},
|
||||||
|
"From": 0,
|
||||||
|
"MainboardID": "ABCD1234ABCD1234",
|
||||||
|
"RequestID": "5b72361a76774a96b73f091bf5f79590",
|
||||||
|
"TimeStamp": 1693671336846
|
||||||
|
},
|
||||||
|
"Id": "0a69ee780fbd40d7bfb95b312250bf46"
|
||||||
|
}```
|
Reference in New Issue
Block a user