Anycubic Photon 3d-printer network control
Why
Because putzing with the USB disk is annoying. Because the connector tends to wear.
How
The printer has an Ethernet interface on its controller board. In older versions of firmware
it is unused. More modern (4.2.19 for certain) allow setting the IP address (fixed one manually
from screen, or more comfortably via DHCP) and communicate over a simple UDP-based protocol.
The communication is based on G-code. UDP packet is sent to printer, response is sent back.
The printer listens on UDP port 3000.
There is no password or other security. Use a port-based VLAN and an authorization proxy if this is a concern.
The G-code was partially observed partially read partially reverse-engineered (especially the
file upload/download state machine) from Universal Photon Network Controller;
that one did not work well on a Windows laptop, the connection kept crashing, the GUI couldn't be used in scripts nor in a terminal. (GUI? Phooey!)
A simple Python script was written to facilitate the communication.
It works for Anycubic Photon printer, CBD motherboard v1.4.1, firmware version V4.2.19.3_LCD.
It should work for many CBD-Tech based SLA 3D printers (Anycubic Photon, ChiTu printers...).[ref]
If the machine autodetects, it's likely it will respond to the rest of the commands.
For a suite of utilities to control FDM printers via OctoPrint see sw_8control.
Usage
The general command syntax is
photon.py [connection parameters] <command> [filename] [parameters]
The connection parameters are:
- -a for autodetecting the printer
- -n <name or IP address> for explicitly specifying the printer (default is "photon1")
Only one can be specified.
The commands are:
- detect - detects printers on the local network; synonyms: scan, find
- ver - shows printer version; synonym: version
- pos - shows current head position; synonym: position
- stat - shows current printing status (position in current file vs its full size, or error that the machine is not printing now); synonym: status
- ls - lists files on the printer; synonym: dir
- rm <filename> - deletes a file from the printer; synonym: del <filename>
- put <filename> - sends a file to the printer; synonyms: post <filename>, send <filename>, upload <filename>
- get <filename> - downloads a file from the printer
- print <filename> - starts printing file; synonyms: run <filename>, exec <filename> (CAUTION: if previous printout is not removed from plate, the printer can attempt to push it through the display when zeroing!)
- pause - pauses current print job
- resume - resumes current print job; synonym: continue
- abort - stops/aborts current print job; synonyms: stop, cancel
- STOP - immediately stops/aborts current print job (LED off, no head movements); synonym: estop
- g "<code>" - sends raw G-code (converted to uppercase, for numeric values) to the printer, shows response (potentially dangerous); use quotes if code contains spaces
- G "<code>" - sends raw G-code (preserved case, for filenames) to the printer, shows response (potentially dangerous); use quotes if code contains spaces
Some commands have several common synonyms so the operator doesn't have to precisely remember the correct one.
The other parameters are:
- -l <filename> - override local filename
- -r <filename> - override remote filename
- -I <interface IP> - bind UDP port to explicit interface (default "0.0.0.0", all of them); may be needed for machines with multiple network interfaces in unusual conditions
- -v, -vv, -vvv - verbose operation, more verbose, even more verbose; shows all the network communication attempts, useful for debugging
(Connection parameter is checked for and parsed first. Then command and its parameter is checked. The rest of the line is then parsed by argparse.)
Printer's response to received and processed command is ok N:0, response to received but unrecognize command is ok.
Examples of use
list printers on the network
photon.py detect
send file printout.photon
photon.py put printout.photon
send file printout.photon, with in-printer name obj.photon
photon.py put printout.photon -r obj.photon
delete remote file obj.photon
photon.py rm obj.photon
get list of files on the printer
photon.py ls
get list of files on the printer, autodetect it on the network
photon.py -a ls
get list of files on the printer named "photon2"
photon.py -n photon2 ls
set photon commands for two different printers:
alias photon1="photon.py -n photon_no_1"
alias photon2="photon.py -n photon_no_2"
then list files on printer 1, check status on printer 2
photon1 ls
photon2 stat
Downloads
Configuration
By default, the printer is accessed via its hardcoded network name, photon1.
The IP address can be configured on unixes in /etc/hosts file, or its windows equivalent.
It can also be provided by local DNS or other means.
Supervision via MQTT
For various dashboards, data feed via MQTT is common.
The printer does not have facilities for reporting its status on its own, but can be periodically polled.
Practical results are achieved with running the polling script from cron, once per minute or two.
Printing:
photon.py stat
SD printing byte 7675284/9740462
Percent: 78.8
Not printing:
photon.py stat
Error:It's not printing now!
mqtt_PhotonMonitor.sh
#!/bin/bash
TOPIC=shop/photon/progress
STATE=`photon.py stat|tail -n 1`
if [ "${STATE:0:8}" == "Percent:" ]; then
mosquitto_pub -m ${STATE:10} -t $TOPIC
else
mosquitto_pub -m "-" -t $TOPIC
fi
/etc/crontab line
*/1 * * * * user /script/location/mqtt_PhotonMonitor.sh
Protocol
The machine's board is based on the Chitu-class motherboard. These boards are common in 3d printers both FDM and SLA.[ref]
G-codes
<filename> refers to relative path in currently selected directory, :<filename> refers to absolute path
- printer status queries
- M27 - get printing job percentage ("Error:It's not printing now!\r\nok N:0")
- M105 - get temperatures ("ok T:184 /0 B:184 /0 @:0 B@:0")
- M114 - get head position ("ok C: X:0.400050 Y:0.000000 Z:1.200000 E:0.000000")
- M115 - get firmware date ("ok CBD make it.Date:Jul 17 2019 Time:18:00:16")
- M4000 - get job status ("ok B:186/0 E1:186/0 E2:194/0 X:0.400 Y:0.000 Z:0.150 F:256/256 D:254395/15020812/0 T:267"); Z=head height, F=fan speed, D=printfile offset/total size/0, T=total print job time in seconds (incl. pauses)
- M4001 - get per-motor stepper settings ("ok X:0.011430 Y:0.011430 Z:0.000625 E:0.001340 T:0/0/0/155/1 U:'GBK' B:1")
- M4002 - get printer version ("ok V4.2.19.3_LCD")
- M4006 - get currently selected file ("ok '_EbHook.photon'")
- M99999 - detect printer (sent by broadcast)
- printer process control
- M24 - start currently selected, or resume; returns "Error:Cann't start print" if can't resume, otherwise returns "ok N:0"
- M25 - pause - wait until the end of layer exposure, then raise head by 20(?) mm
- M29 - stop print or cancel file upload (stop printing doesn't work?)
- M33 I5 - abort print - immediately stop printing and raise head (I5 parameter is important, bare M33 will keep the LED running and needs a touch on the user interface to finish the abort)
- M112 - emergency stop - immediately stop printing, LED off, no furtner movements of head
- M300 - beep - double beep, no control of pitch nor duration
- M6030 '<filename>' - start printing the listed file
- M6032 '<filename>' - select file to print by M24 or by M6045
- M6040 I100 - reboot; I=delay before reboot (TODO: try)
- M6045 I4000 "M24" - wait for 4000 ms(?), then start printing
- printer head movement, direct control
- G0 Z<mm> F<mm/min> - head move
- G1 Z<mm> F<mm/min> - head move (both G0 and G1 seem to always require the F parameter)
- G4 P<sec> - pause
- G21 - set units to millimeters
- G28 Z0 - homing to bottom endstop switch
- G90 - absolute positioning
- G91 - relative positioning (positive values move upwards, negative downwards)
- file operations
- M22 - close file
- M28 <filename> - upload file to printer
- M30 <filename> - delete file from printer
- M3000 - get next file chunk
- M3001 I<offset> - request resend from arbitrary offset from start
- M6032 '<filename>' - download file from printer
- operations config, common
- M8015 P2.000 - z-axis slow speed, for slow-motion peel (default 2)
- M8016 P3.000 - z-axis fast speed, for remain of the total distance and descent to another layer (default 3)
- M8016 D10 - delay (milliseconds) between rising and lowering z-axis
- M8070 S3 - z-axis lift, slow-motion/release distance (default 3)
- M8070 Z6 - z-axis lift, total (slow+fast) distance (default 6)
- M8084 Z0 - limit position offset in mm, usually 0 or a positive number (for M8083 I1); set manually from panel when leveling; useful for exchangeable buildplate holders, get from file by M8512; delta-z offset in FDM firmwares
- operations config, less common
- M8008 I700 - acceleration; the higher the value the higher the speedup but also noise and risk of losing steps (default 600) (used for z-axis?)
- M8015 I3 - z-axis zeroing speed, first pass
- M8016 I2 - z-axis zeroing speed, second pass
- M8029 C0 - after zeroing: 0=go to z=0 position, 1=stay in the limit-switch position
- M8030 C0 - after zeroing: 0=go to z=0 position, 1=stay in the limit-switch position (duplicate in saved config)
- M8030 I-2 - LED fan control; 0=always off, -1=always on, -2=on when printing
- M8030 T-1 - motherboard fan control; 0=always off, 1=during exposure, -1=always on, -2=when printing
- M8083 I1 - limit position; 0=limit switch position is z=0, 1=z-offset set by M8084 Zx (autoleveling on/off in FDM firmwares)
- M8093 I1 - enable M8093 to dump debug to a file, "Machine_Errer_code.status" (only works in recent firmwares (how recent?))
- M8489 I256 - set max fan speed (default 256) (todo: test)
- M8489 P3 - after completing printing: 0=turn off motors, 1=no action, 2=return to zero then turn off motors (DO NOT USE, crushes printout through display), 3=move to top and turn off motors (default 3)
- M9005 "networkname","password" - wifi configuration
- operations config, irrelevant (holdovers from FDM)
- M8006 I30 - maximum speed limit (holdover from FDM printer config?)
- M8007 I15 - max jerk speed - for reciprociating motion (holdover from FDM printer config?)
- appearance
- M7506 I636264 - user interface colors; T0=normal, T1=inverted, T2=flip EEPROM saved mode
- M8085 I5000 - boot logo duration; min.100(?), max.6000 milliseconds
- M8085 T0 - screensaver time in seconds; 0=screensaver off
- M9093 "ZWLF" - printer name, default "ZWLF" (shows in "detect" response); doesn't seem to have any effect
- config store/commit - use after M8xxx commands to store changes or use them without storing
- M8500 - store and use config
- M8510 - use config immediately but don't store (for testing)
- M8511 - revert to stored configuration
- M8512 '<filename.g>' - dumps the current EEPROM config data into a file[ref]
- M8513 - reset printer config to factory defaults
- hardware config (for modifications, do not touch if machine is working)
- M8004 I-1 - z-axis stepper direction; -1=ccw, 1=cw (M8002 for x-axis, M8003 for y-axis, M8005 for e-axis, for FDM)
- M8005 Z0 - z-axis manual control direction; 0=normal (head moves), 1=reversed (platform moves) (X0/X1 and Y0/Y1 for other axes, for FDM)
- M8010 S0.000625 - mm per step (16 microsteps per step, 2mm lead screw, 1.8-deg/step (200 steps/rotation); default lead/((360/1.8)*16)=0.000625; can be used for fine calibration of z-axis, different leadscrew pitch or different microstepping
- M8013 I10 - z-axis max speed (mm/s); seems max reasonable value without skipping steps is 10
- M8026 I155 - z-axis maximum position in mm
- M8029 I0 - z-axis limit switch type: 0=unilateral (only Zmin), 2=bilateral (Zmin and Zmax)
- M8029 T0 - z-axis limit switch type: 0=normally open (H when open, L when triggered), 1=normally closed (L when open, H when triggered)
- M8029 S0 - 0=limit switch for molding support close to the platform, Zmin end 1=Zmax end
- M8030 S4 - LED light control; 4=LED on only when exposing image is on the screen
- M8034 I1 - SD card folder support; 0=disabled, 1=enabled
- M8070 T0 - projector warm-up time in seconds, for DLP (0 for LED under LCD)
- M8070 I9600 - projector UART communication speed, for DLP
- M8087 I0 T0 - for external stepper driver; I=direction signal settling time (nanoseconds), T=minimum time for pulse (nanoseconds); I0 T0 for no driver board, I100000 T0 for THB7128 and TB6600, I40000 T0 for TB6560
Movements
The printer head can be moved directly. The G0/G1, G28, G90/G91 codes are supported.
During printing, the printer is in relative mode (G91). This leads some to believe that absolute/relative mode is not supported. It is at least in 4.2.19.
The G0/G1 requires both the Z and F parameter. Without it the head doesn't want to move. The F-speed is clamped by the M8013 Ixx max speed limit.
The limit is in mm/s (default value can be 5 but steps aren't being lost up to 10), the speed is in mm/min; the corresponding limit speed to I10 is therefore F600.
Higher values will be treated like the limit value.
Repeated reciprociated movements of the printer head can be used for stirring the resin - many resins contain fillers or pigments which tend to settle
on the bottom of the vat. The buildplate moves back and forth in the resin bath, causing turbulent flow that stirs the sediment back into
suspension.
G28 Z0 will move the head to the bottom endstop.
See also:
Printer autodetection
- send M99999 to network broadcast address
- each printer responds with a packet containing line of text:
- ok MAC:XX:XX:XX:XX:XX:XX IP:X.X.X.X VER:V1.4.1 ID:XX,XX,XX,XX,XX,XX,XX,XX NAME:ZWLF
BROADCAST: b'M99999'
RECV: b'ok MAC:00:e0:4c:35:02:15 IP:192.168.1.231 VER:V1.4.1 ID:15,00,42,00,25,55,41,28 NAME:ZWLF\r\n'
Printer status
send M27 to printer, receive text string with printer status and position in current file as response
SEND: b'M27'
RECV: b"Error:It's not printing now!\r\n"
send M114 to printer, receive current head position (only Z: is relevant)
SEND: b'M114'
RECV: b'ok C: X:0.400050 Y:0.000000 Z:151.899994 E:0.000000\r\n'
Files
List files
send M20, receive several UDP packets in sequence
SEND: b'M20'
RECV: b'Begin file list\r\n'
RECV: b'<filename> <filesize>\r\n'
RECV: b'_cookiecut.photon 6429960\r\n'
RECV: <rest of files...>
RECV: b'End file list\r\n'
RECV: b'ok L:<num of files>\r\n'
Remove file
SEND: b'M30 nonexistent.filename'
RECV: b'Delete failed :nonexistent.filename\r\n'
RECV: b'ok N:0\r\n'
Send/receive file protocol
- start operation
- send file packet, receive ok; or send file packet request, receive packet; repeat until all chunks transferred
- end operation
G-code commands:
- M22 - close file
- M28 <filename> - upload file to printer
- M30 <filename> - delete file from printer
- M6032 <filename> - download file from printer
- M3000 - get another chunk
- M3001 I<position> - request resend
File packets contain payload and appended 6 bytes of a "tailer". (Like a header but on the tail.)
The tailer has format [O3][O2][O1][O0][XX][MM] , where
- O3 O2 O1 O0 is offset position of the packet in the file (big endian, O3 LSB and O0 MSB, 0x12345678 == [78][56][34][12])
- XX is a checksum, all bytes of the packet including offset (and excluding the checksum and magic constant) XORed together
- MM is constant 0x83, a magic byte
The software always sends M22 before starting the file operation, in order to close eventual hanging-open file (e.g. if an earlier transaction was interrupted).
Communication is described in python syntax for byte arrays.
Except the tailer structure where the binary bytes are written as [hex] numbers.
send small file (single packet)
SEND: b'M28 <filename>'
RECV: b'ok N:0\r\n'
SEND: b'<binary data packet><tailer:[00][00][00][00][XX][83]>'
RECV: b'ok\r\n'
SEND: b'M29'
RECV: b'Done saving file!\r\n// <filename>\r\n'
RECV: b'ok N:0\r\n'
send larger file (multiple packets)
send send-file request with filename
SEND: b'M28 <filename>'
RECV: b'ok N:0\r\n'
send first packet, offset 0 (0x0000-0000)
SEND: b'<binary data packet 1><tailer:[00][00][00][00][XX][83]>'
RECV: b'ok\r\n'
send second packet, offset 1280 (0x0000-0500)
SEND: b'<binary data packet 2><tailer:[00][05][00][00][XX][83]>'
RECV: b'ok\r\n'
send third packet, offset 2560 (0x0000-0A00)
SEND: b'<binary data packet 3><tailer:[00][0a][00][00][XX][83]>'
RECV: b'ok\r\n'
send third packet, offset 3840 (0x0000-0F00)
SEND: b'<binary data packet 4><tailer:[00][0f][00][00][XX][83]>'
RECV: b'ok\r\n'
we sent it all, so close file
SEND: b'M29'
RECV: b'Done saving file!\r\n// <filename>\r\n'
RECV: b'ok N:0\r\n'
receive small file (single packet)
SEND: b"M6032 \'<filename>\'"
RECV: b'ok L:825\r\n'
SEND: b'M3000'
RECV: b'<binary data packet><tailer:[00][00][00][00][XX][83]>'
SEND: b'M22'
RECV: b'ok N:0\r\n'
receive larger file (multiple packets)
send receive-file request in filename in quotes
SEND: b"M6032 \'<filename>\'"
receive that file exists, and its length
RECV: b'ok L:4239\r\n'
send chunk request
SEND: b'M3000'
receive binary chunk at offset 0 (0x0000-0000)
RECV: b'<binary data packet 1><tailer:[00][00][00][00][XX][83]>'
send chunk request
SEND: b'M3000'
...and error happens...
<timeout>
...so we send retry request, with explicit offset (1280 bytes)
SEND: b'M3001 I1280'
...and retry works, receive binary chunk at offset 1280 (0x0000-0500)
RECV: b'<binary data packet 2><tailer:[00][05][00][00][XX][83]>'
send chunk request
SEND: b'M3000'
receive binary chunk at offset 2560 (0x0000-0A00)
RECV: b'<binary data packet 3><tailer:[00][0a][00][00][XX][83]>'
send chunk request
SEND: b'M3000'
receive binary chunk at offset 3840 (0x0000-0F00)
RECV: b'<binary data packet 4><tailer:[00][0f][00][00][XX][83]>'
we received enough bytes to match the length from earlier, so close file
SEND: b'M22'
RECV: b'ok N:0\r\n'
read nonexistent file
SEND: b"M6032 \'<filename>\'"
RECV: b"Error,Cann't open file:<filename>\r\n"
Sample config file for Anycubic Photon
obtained by M8512 'cfg.g', then machine-translated (the original comments are in Chinese) and reformatted by adding spaces to align comments
;; Version: V4.2.19.3_LCD.0 /1440x2560 /F2.9';' followed by a comment
M8513; Clear the previous configuration parameters, restore the parameters to the factory settings, and reconfigure the parameters
M8004 I-1; Z motor direction (-1=ccw)
M8005 Z0; Z axis 0: extrusion head movement 1: platform movement
M8006 I30; starting speed (speed limit)
M8007 I15; track bending speed (jerk)
M8008 I900; acceleration
M8010 S0.000625; z mm per step
M8013 I5; Z maximum speed
M8015 I3; Z first zero speed
M8016 I3; Z second zero speed
M8015 P2.000000; Z slow rising speed
M8016 P3.000000; Z rapid rise and fall speed
M8016 D10; Number of milliseconds to wait after Z rises
M8030 T-1; Fan 2 control, 1: follow exposure 0: normally closed -1: normally open
M8030 S4; LED control, 1: normally open 0: normally closed 2: follow the model to print
M8070 S3.000000; the moving distance of each Z slow ascent
M8071 X1440 Y2560; projection resolution
M8070 T0; projector warm-up time
M8070 Z6.000000; the distance moved each time Z rises
M8070 I9600; projector baud rate
M8026 I155.000000; Z maximum stroke
M8029 I0; XY limit 0: minimum limit 1: maximum limit 2: bilateral limit
M8029 T0; limit wiring 0: limit normally open 1: limit normally closed
M8029 S0; Z limit type 0: minimum limit 1: maximum limit
M8029 C0; XYZ moves after homing 0: return to zero (0,0,0) 1: stop at the limit position
M8030 C0; XYZ moves after homing 0: Return to zero (0,0,0) 1: Stop at the limit position
M8030 I-2; fan self-starting temperature
M8034 I1; folder support or not
M8083 I1; whether to enable auto leveling
M8084 Z1.300000; Delta Z offset, 0: Offset is prohibited, the stroke is determined by setting Z to zero, non-zero, the stroke is triggered by the leveling trigger position + offset value
M8085 I5000; duration of boot logo, minimum 100ms, maximum 6000ms
M8085 T0; screen saver standby wait time, in seconds
M8087 I0 T0; I: time from direction valid to pulse valid (ns), T: shortest pulse hold time (ns). If there is no external drive, please set all to 0
M9003 "ZWLF"
M8500; save configuration
M8084 is set manually by leveling, remove if it's too off to avoid collision of buildplate to display.
Todo
- variable retries are for autodetect only; file up/download has two retries hardcoded, other operations are single-attempt only, so make them all variable-retry
Also, for FDM printers with the same controller board kind:
Suggested printer modifications
- common
- better z-axis - several mods available out there
- black/white display instead of color
- such displays are rare, but they lose much less light in open-pixels due to absence of color filters on subpixels
- bigger display/tank, or denser display resolution
- potential problems with LED power
- proposed
- microlens array sheet instead of a "dumb" sheet of cover plastic over the display
- focus diverging light from pixels to parallel rays, or even focus a little above the top level of the foil
- head on load cell
- continuous readout of pull-off force on head, monitoring of printing progress and potential tearoffs
- protection against plate or workpiece crash onto display
If you have any comments or questions about the topic, please let me know here: |