How to use code fields on NanoDLP.

There are many code fields on both NanoDLP’s machine settings and resin profile. Some of the most important ones are:

  • Boot-up - will be run once just after NanoDLP executed
  • Start / Stop / Resume print
  • Before / After layer
  • During cure time - run once cure starts
  • Shutdown - run before shutdown button presses
  • Shutter on / off - run if shutter enabled, consider possible GCodes will be send async, so it may cause issue for synchronization mechanisms

There are other fields that only support JS, variable and math functionalities:

  • Dynamic lift
  • Dynamic speed
  • Dynamic cure time
  • Dynamic wait

How it works?

These fields being processed on-fly during printing, it could contains many NanoDLP keywords, gcode for your RAMPS or even Javascript codes. After these fields processed result will be send to your controller board such as RAMPS and Trinamics.

Using these codes you can do many advanced things such as:

  • Send command (eg. gcode) to RAMPS, projector or other controllers to move
  • Synchronize movements
  • Communicate with other programs
  • Calculate many parameters using current status of the print and printer

Async codes

Shutter codes being processed in parallel, it is very important to consider synchronization mechanism before using async gcode inputs. As it may cause issue on synchronization as it does not guaranteed, when these codes being reached controller board, which may cause printer stalls. It is better to move shutter open/close codes to before/after layer code inputs on profile level.


Gcode is a language being used by major controller board firmware such as Marlin, klipper and GRBL. It helps do many different hardware actions. For more information on main commands and compatibility check RepRap gcode page.



Layer thickness in millimeter

Absolute layer position in millimeter

Z axis height in millimeter

Current position in millimeter

Absolute stop position from zero level

Amount of lift after layer print in millimeter. The result of dynamic lifting will be used.


Current layer number

Total number of the job layers

Current layer’s total solid area in mm^2

Current layer’s largest solid area in mm^2

Current layer’s smallest solid area in mm^2

Current layer’s total number of solid areas


Wait before exposure in second.

Wait after exposure in second.

Wait after printer lifts second.


Cure/exposure time in second. It calculated based on normal layer cure time, Support layer cure time and dynamic cure times.

Number of support layers.

Speed in mm/min, calculated from startup/min/max/slow section speeds in setup page. The result of dynamic speed will be used.

Retract speed in mm/min, calculated from startup/min/max/slow section speeds in setup page. The result of dynamic speed will be used.

Lift speed in mm/min, calculated from startup/min/max/slow section speeds in setup page. The result of dynamic speed will be used.

Return current status/action of the printer: MoveTo, GcodeBefore, WaitBefore, DisplayLayer, WaitAfter, GcodeAfter, MoveToWait, WaitAfterList

Total number of layers skipped since the last printed layer

The projector’s lamp hours

The current job name

Return job ID

Calculate amount of resin based on detected resin level, vat size and expected consumption for the object.


If current layer is burn-in layer returns 1

Return 1 if layer movement for galvo happens in filled areas

Laser movement in X axis for galvo laser in mm

Laser movement in Y axis for galvo laser in mm

Action keywords


[[LayerChange layer]]
Change current layer eg. [[LayerChange 22.1]]

[[PositionSet mm]]
Update current absolute position eg. [[PositionSet 22.1]]

[[PositionChange mm]]
Update relative position update eg. [[PositionChange -2.1]]

[[DisplayLayer LayerID MultiCurePart ReturnCureTime]]
Display layer 10th from current the current job eg. [[DisplayLayer 10]] Display layer 20, part two of the multi-cure print and return value [[DisplayLayer 20 2 true]]

Make screen blank

[[LayerSkip Value]]
For the positive values it skips a layer eg. [[LayerSkip 1]] It skips any remaining gcode and layer tasks, and starts the next layer. The more advanced use case example: [[LayerSkip ((100>[[TotalSolidArea]])*(1-[[SkippedLayers]])*([[LayerNumber]]>3))]] The gcode above would cause layers to be skipped after the first 3 layers if a layer before the current one has not been skipped and the difference between the previous and the current layer is less than 100 pixels.

[[Delay Seconds]]
Put delay eg. [[Delay 1.23]]

Split gcode into chunks and process, each part will be processed after complete processing of the previous one. For example if a single chunk contain [[Delay]] keyword, it will delay processing of the next chunks. It is not functional on dynamic calculation fields such as dynamic speed.

Pause the printer, requires resume from dashboard to continue.


NanoDLP when used with shield, could not detect how much time it would takes to finish a movement. There are a couple of ways to solve this problem.

Checkout Troubleshoot Issues for more delays on possible problems could be caused by wrong synchronization.

Using [[MoveWait n]] and [[MoveCounterSet n]] keyword

By using marlin or any other NanoDLP compatible firmware you can synchronize movements. After this keyword nanoDLP will wait for Z_move_comp response from the shield.

[[MoveWait n]]
Wait for number of Z_move_comp messages from RAMPS or similar boards.

[[MoveCounterSet n]]
Set n as current move counter

[[MoveCounterSet 0]]
G1 Z10 F200
[[MoveWait 1]]

Using [[GPIOWaitForLow]] keyword

To synchronize movements nanoDLP waits for pin to be LOW. At the start of movement, controllers / drivers must make this pin HIGH and at the end of movement make it LOW. Maximum delay for detection is 1ms.

G1 Z1.1

Using [[ResponseCheck ExpectedOK BufferSize]] keyword

If the firmware you are using on the controller board send ok response after execution of each command, you can use this command to synchronize movements.

[[ResponseCheck ExpectedOK BufferSize]]
Helps synchronize fast moving SLS and laser SLA systems. It limits how many gcode will be send to RAMPS before waiting for response from RAMPS. It requires ok response from RAMPS after commands get completed and not after receiving them. ExpectedOK integer indicates how many OK will be received from RAMPS board. BufferSize indicates how many command should be sent out before receiving OK responses. eg. [[ResponseCheck 1 3]]

Reset OK response counter to zero

G1 X1.1
[[ResponseCheck 1 1]]

Using [[WaitForDoneMessage]] keyword

By using marlin or any other NanoDLP compatible firmware you can achieve sync movements. After this keyword nanoDLP will wait for Z_move_comp response from the shield. Not suitable for fast moving printers.

[[WaitForDoneMessage n]]
Wait for Z_move_comp message from RAMPS or wait for n second. Which happens earlier.

G1 Z1.1

Using [[Delay Seconds]] keyword

It is possible to put delay after each movement. It is the most widely used method.

G1 Z1.1
[[Delay 1.5]]


[[Net URL]]
Send get request to the specified URL without waiting for the result eg. [[Net]]

[[NetReturn URL]]
Send get request to the specified URL and send the result to RAMPS eg. [[NetReturn[[LayerNumber]]]]

[[Exec Command]]
Run external command without waiting for the result eg. [[Exec python]]

[[ExecReturn Command]]
Run external command and send the result to RAMPS eg. [[ExecReturn python]]


Wait for “wait pin” GPIO to be LOW

[[Projector Command]]
Send any command to the projector eg. [[Projector \x01\x02\x03 On]]

Makes any GPIO HIGH eg. [[GPIOHigh 12]]

Makes any GPIO Low

[[Notification Type Duration Text]]
Display a notification on the dashboard and log it. The type can be log, error, or notice. A duration of zero means display the notification until the user closes it. If the duration is positive, the notification will automatically close after that many seconds. The text parameter is the message content to display in the notification.

Type could be default, info, warning, danger and modal, except modal all other types display alert.

Javascript support

You can use Javascript (ES5) to handle advanced requirements.

Javascript need to start with [JS] and end with [/JS]. The Javascript code inside will be interpreted and any result saved on variable called output will be replaced with [JS]…[/JS].

For example:

var distance = [[LayerThickness]]-[[ZLiftDistance]]*[[LayerNumber]];
var xmlHttp = new XMLHttpRequest();"GET", ""+distance, false);
output = "G1 Z"+distance+" F"+xmlHttp.responseText;


G1 Z22.25 F100

Accessing NanoDLP context

You are able to access NanoDLP internal variables using Javascript functionality.

For example:

var nano = nanodlpContext();
output = nano["Config"]["Name"];


Your printer name

Full list of available variables on the context

    Name                string
    Lang                string
    Email               string
    PrinterID           int
    Port                int
    PrinterType         uint8
    ZAxisPin            uint8
    DirectionPin        uint8
    ReverseDirectionPin uint8
    LimitPin            uint8
    LimitPinB           uint8
    LimitPinMode        uint8
    LimitPinReset       uint8
    WaitPin             uint8
    EnablePin           uint8
    EnablePinState      uint8
    EnablePinMode       uint8
    FaultPin            uint8
    FaultPinState       uint8
    ShutterPin          uint8
    ShutterType         uint8
    ShutterMode         uint8
    ShutterOpen         uint
    ShutterClose        uint
    ShutterOpenGcode    string
    ShutterCloseGcode   string
    ShutterSignalLength uint
    FanOnGcode          string
    FanOffGcode         string
    MaxSpeed            int64
    MinSpeed            int64
    StartupSpeed        int64
    StopPositionMm      float64
    ResinDistanceMm     float64
    ZAxisHeight         int32
    MotorDegree         float64
    MicroStep           float64
    LeadscrewPitch      float64
    LCDType             uint8
    LCDAdress           uint8
    LCDPath             string
    ShieldType          uint8
    ShieldEncoding      uint8
    ShieldI2CAddress    uint8
    ShieldUSBAddress    string
    ShieldSpeed         int
    ShieldBootup        string
    ShieldShutdown      string
    ShieldStart         string
    ShieldResume        string
    ShieldPause         string
    ShieldUnpause       string
    ShieldFinish        string
    ShieldForceStop     string
    ShieldAxisMode      uint8 // 0 Zero bottom 1 top bottom
    ShieldPositioning   uint8 // 0 Relative  1 Absolute
    ManualMoveGcode     string
    TopGcode            string
    BottomGcode         string
    CalibrationGcode    string
    CameraFrequency     int8
    CameraStore         int8
    CameraCommand       string
    ShutdownPin         uint8
    ProjectorWidth      int
    ProjectorHeight     int
    ProjectorType       uint8
    ProjectorPowerCycle uint8
    ProjectorSpeed      int
    ProjectorAddress    string
    ProjectorOn         string
    ProjectorOff        string
    ProjectorLampQuery  string
    ProjectorLampEffect float32
    ProjectorOnSyscall  string
    ProjectorOffSyscall string
    ProjectorWarmup     float64
    DisplayController   uint8
    ImageMirror         uint8
    LightOutputFormula  string
    BarrelFactor        float64
    BarrelX             int
    BarrelY             int
    FBPath              string
    XYRes               float64
    YRes                float64
    Mute                uint8
    DisplayID           uint32
    DefaultProfile      int
    SpeedFormula        string
    RemoteSlicer        string
    PressureType        uint8
    PressureAddress     string
    PressureSpeed       int
    PressureRegex       string
    PressureDebug       bool
    ScaleClockPin       uint8
    ScaleDataPin        uint8
    OpenScaleAddress    string
    Theme               uint8
    CustomValues        map[string]string
    AutoSlice           uint8
    PreviewGenerate     uint8
    PreviewWidth        int
    PreviewHeight       int
    USBDisplayAddress   string

	PlateID        int
	LayerID        int
	ShieldAxisMode uint8

	PlateID               int
	ProfileID             int
	CreatedDate           time.Time
	StopLayers            string
	Path                  string
	LowQualityLayerNumber int
	AutoCenter            uint8
	Updated               uint64
	LastPrint             uint64
	PrintTime             int64
	PrintEst              int64
	ImageRotate           uint8
	MaskEffect            float32
	XRes                  float64
	YRes                  float64
	ZRes                  float64
	MultiCure             string
	MultiThickness        string
	CureTimes             []float64
	DynamicThickness      []float32
	Offset                float32
	OverHangs             []int
	Risky                 bool
	IsFaulty              bool
	Repaired              bool
	FaultyLayers          []int
	Corrupted             bool             `form:"-"`
	TotalSolidArea        float32          `form:"-"`
	FillAreas             []area.LayerInfo `json:"-" form:"-"`
	BlackoutData          string           `form:"-"`
	LayersCount           int              `form:"-"`
	Rectangles            RectangleStruct  `json:"-" form:"-"`
	Processed             bool             `form:"-"`
	CustomDir             string           `json:"-" form:"-"`
	Status                uint8            `json:"-" form:"-"`
	Feedback              bool
	PrintID               int64
	MC                    format.MultiCure

	ResinID                int
	ProfileID              int
	Title                  string
	Desc                   string
	ResinPrice             float32
	ZStepWait              int64
	SlowSectionHeight      float32
	SlowSectionStepWait    int64
	TopWait                float32
	WaitHeight             float32
	CureTime               float32
	DynamicWaitAfterLift   string
	DynamicCureTime        string
	DynamicSpeed           string
	DynamicLift            string
	Depth                  float32
	WaitBeforePrint        float32
	WaitAfterPrint         float32
	JumpHeight             float32
	JumpPerLayer           int
	SupportTopWait         float32
	SupportWaitHeight      float32
	SupportLayerNumber     int
	SupportCureTime        float32
	SupportDepth           float32
	SupportWaitBeforePrint float32
	SupportWaitAfterPrint  float32
	AdaptSlicing           uint8
	AdaptSlicingMin        float32
	AdaptSlicingMax        float32
	YRes                   float64
	ZResPerc               float32
	SupportOffset          float32
	Offset                 float32
	ShutterOpenGcode       string
	ShutterCloseGcode      string
	FillColor              string
	BlankColor             string
	ShieldBeforeLayer      string
	ShieldAfterLayer       string
	PixelDiming            uint8
	DimAmount              float32
	DimWall                int
	DimSkip                uint
	TransitionalLayer      uint8
	ElephantMidExposure    uint8
	ElephantType           uint8
	ElephantAmount         float32
	ElephantWall           int
	ElephantLayers         int
	HatchingType           uint8
	HatchingWall           int
	HatchingGap            int
	HatchingOuterWall      int
	HatchingTopCap         int
	HatchingBottomCap      int
	AntiAlias              uint8
	AntiAlias3D            uint8
	AntiAlias3DDistance    float32
	AntiAlias3DMin         uint8
	AntiAliasThreshold     float32
	MultiCureGap           float32
	ImageRotate            uint8
	IgnoreMask             uint8
	ShieldStart            string
	ShieldResume           string
	ShieldFinish           string
	LaserCode              string
	Updated                uint64   

	PlateID         int
	SlicingPlateID  int
	Path            string
	LayerID         int
	ResumeID        int
	LayerSinceStart int
	LayersCount     int
	PlateHeight     float32
	CurrentHeight   int32
	LayerTime       int64
	PrevLayerTime   int64
	LayerStartTime  int64
	Camera          int8
	LampHours       float32
	Panicked        bool `json:"-"`
	PanicRow        int
	Serial          string `json:"-"`
	ip              string
	State           Mode   `json:"-"`
	Build           string `json:"-"`
	Wifi            string `json:"-"`
	Version         int    `json:"-"`
	StartAfterSlice int    `json:"-"`
	Covered         bool
	Halted          bool `json:"-"`
	ForceStop       bool `json:"-"`
	Printing        bool `json:"-"`
	Paused          bool `json:"-"`
	AutoShutdown    bool `json:"-"`
	Cast            bool `json:"-"`

    TotalCureTime       float32
	TotalPrintTime      float32
	TotalLayers         int64
	TotalPlatePrints    int
	TotalCompletePrints int

Dynamic fields

The result of equations will be used as a value for relevant operation. It could includes JS (including external calls), equations and etc.

  • Using dynamic fields, NanoDLP will ignore any static value.
  • For negative values, static values will be used.

For more details about how to use dynamic values, you can check out dynamic value guide.

Crash recovery

Using the right settings is the key to crash recovery and to benefit from nanoDLP in full potential. A way to do this is to delegate positioning to nanoDLP.

Follow the sample codes below to understand how to delegate positioning to nanoDLP. By doing so in addition to crash recovery, printer stop and other functions also will start working.

Start of Print

G90 ; Put positioning in absolute mode
[[MoveCounterSet 0]]
G28 ; Homing
[[MoveWait 1]] ; Wait until movement is completed, it requires firmwares to get patched for Z_move_comp, if you do not want to use patched firmware you can use [[Delay n.n]] instead
[[PositionSet 0]] ; Set current position on nanodlp so it could be recovered in case of failure

Before Layer

[[MoveCounterSet 0]]
G1 Z[[LayerPosition]] ; Move to layer position
[[MoveWait 1]] ; Wait for the movement to get finished
[[PositionSet [[LayerPosition]]]] ; Save layer position as the current position

After Layer

[[MoveCounterSet 0]]
G1 Z{[[LayerPosition]]+[[ZLiftDistance]]} ; Lift to wait position
[[MoveWait 1]] ; Wait for the movement to get finished
[[PositionChange [[ZLiftDistance]]]] ; Again update position

Resume Print

G90 ; Put positioning in absolute mode
G92 Z[[CurrentPosition]] Y0 X0 ; System crashed so we need to recover current position from NanoDLP and set it on RAMP
G1 Z[[LayerPosition]] ; Move to layer position


You can find advanced examples of code capabilities.

Accurate cure times

As displays getting higher resolution (eg. 8K), cure time may get less accurate as large amount of data should be transferred to GPU. You can use following trick to remove any inaccuracy by delegating cure time duration to shield.

Assume you are using M104 to turn uv light on and M105 to off. Use below code on during cure code input on profile. Better to use zero as cure time (burn-in, normal and dynamic), as it will be controlled by below code, additional cure time will be added on top.

[[MoveCounterSet 0]]
G4 P100; 100ms wait to make sure resin settled
M104; Turn on the UV LED
G4 P18000; 18 seconds wait
M105; Turn off the UV LED
[[MoveWait 2]] ; Wait until shield returns Z_move_done which indicate G4 P18000 finished

Turn on/off display

Following commands are suitable for sending command through HDMI-CEC. You can find some example, they may not compatible with your display.

[[Exec /opt/vc/bin/tvservice -o]]; Cut the signal
[[Exec /opt/vc/bin/tvservice -p]]; Restore the signal
[[Exec echo "standby 0" | cec-client -s]]; Turn off display
[[Exec echo "on 0" | cec-client -s]]; Turn on display

Light output

Light output of LED and projector lamps decrease by time. To keep output same for every prints you need to adjust light output.

It is specially important for sensitive dental resins.

This modifier’s unit is percentage and it effect color inputs on all profiles.

0 means no change to output. 80 means 80% increase in light output. -80 means 80% decrease in light output.

For example if profile render color is total white (#FFFFFF), -100% modifier make it total black (#000000). It only calculated once before slicing of a job and only effects newly generated jobs from source files.

You should use Light Output Formula input on machine settings page.

Edit this page on GitHub