Skip to content

Instantly share code, notes, and snippets.

@benagricola
Created March 25, 2025 21:06
Show Gist options
  • Save benagricola/4fb6a56d0d93bdf770b3fc5591ce2c7a to your computer and use it in GitHub Desktop.
Save benagricola/4fb6a56d0d93bdf770b3fc5591ce2c7a to your computer and use it in GitHub Desktop.
Safety Net Emergency Stop with BTT Scylla V1.0
; estop.g - Configure software-based latching emergency-stop
; Safety Net is a configuration for RepRapFirmware that allows the
; machine operator to verify that a machine is in a known-safe state
; before re-enabling any motors and spindles.
; Safety Net uses the integrated, IO controlled relay on the Scylla mainboard
; to act as a failsafe between the emergency stop circuit and the
; contactor that cuts power to any motors and spindles.
; This acts roughly similarly to a latching switch setup, with the
; MASSIVE caveat that this is software controlled.
; It's about as good as we can get without specifying a latching
; power and emergency stop setup.
; When the mainboard receives an emergency stop signal, it will:
; - Open the safety net relay using M81
; - Run M112 to trigger a machine halt
; On boot, the safety relay will be closed either by:
; - A user-defined M80 command closing the relay on every boot or
; - MillenniumOS, if installed, will prompt the operator to close the relay.
; The relay that is controlled by the Safety Net configuration MUST interrupt
; the physical circuit that controls the contactor (with the emergency stop
; button in series), and NOT the circuit that sends an emergency stop signal
; to the mainboard. These MUST be 2 separate circuits.
; REMEMBER - a Software Emergency Stop is NOT a replacement for a Hardware
; Emergency Stop. Please use this IN ADDITION TO a hardware emergency stop -
; NOT AS A REPLACEMENT.
; Configure emergency stop input. You must set the C"..." parameter to
; the pin identifier where your emergency stop input is connected.
M950 J1 C"extin0"
; Configure safety net output. You must set the C"..." parameter to
; the pin identifier where your safety net relay output is connected.
; We use RRFs inbuilt ATX POWER control system, as this shows some
; feedback in Duet Web Control showing the machine is 'OFF'.
; Scylla's relay is always open on boot.
; RRF assumes that ATX POWER is on, otherwise it wouldn't be able to run,
; so we configure the power port using M81 .. S1 to force the RRF and
; physical state to match.
M81 C"relayctr" S0
; Fire trigger 2 (Safety Net emergency macro) on emergency stop input.
M581 P1 T2 S1 R0
; Check e-stop is not active before continuing startup
echo {"Checking E-Stop status..."}
M582 T2
echo {"E-Stop is not activated, continuing startup"}
; M80.9.g: Allow operator to enable ATX power on boot if configured
; USAGE: M80.9
; If no ATX power port is configured, exit
if { state.atxPowerPort == null }
M99
; Otherwise, check the state and prompt the operator to enable ATX power
; if it is not already enabled.
if { !state.atxPower }
M291 P{"<b>CAUTION</b>: Machine Power is currently deactivated. Do you want to activate power to the machine?<br/>Check the machine is in a safe state before pressing <b>Activate</b>!"} R"MillenniumOS: Safety Net" S4 K{"Activate", "Cancel"} F1
if { input == 0 }
M80
echo {"MillenniumOS: Safety Net - Machine Power Activated!"}
else
M81
echo {"MillenniumOS: Safety Net - Machine Power Deactivated! Run <b>M80.9</b> to activate power."}
; mos-boot.g
;
; Load MillenniumOS.
; This involves sanity checking variables and aborting if they are not set.
; Confirm RRF is in CNC mode.
if { state.machineMode != "CNC" }
set global.mosErr = { "Machine mode must be set to CNC using M453!" }
M99
if { move.axes[2].max > 0 || move.axes[2].min >= 0 }
set global.mosErr = { "Your Z axis uses positive co-ordinates which are untested and unsupported. Please configure your Z max as 0 and Z min as a negative number." }
M99
; Remove existing probe tool so
; it can be redefined.
M4001 P{global.mosPTID}
; If we have a touch probe, make sure the relevant variables are set
if { global.mosFeatTouchProbe }
; If we have a touch probe, make sure we have the ID set
if { !exists(global.mosTPID) || global.mosTPID == null }
set global.mosErr = { "<b>global.mosTPID</b> must contain the ID of the touch probe. Configure it using M558 K<probe-id>... in config.g, then run the configuration wizard (<b>G8000</b>)." }
M99
if { !exists(global.mosTPRP) || global.mosTPRP == null }
set global.mosErr = { "<b>global.mosTPRP</b> is not set." }
M99
if { !exists(global.mosTPR) || global.mosTPR == null }
set global.mosErr = { "<b>global.mosTPR</b> is not set." }
M99
if { !exists(global.mosTPD) || global.mosTPD == null }
set global.mosErr = { "<b>global.mosTPD</b> is not set." }
M99
; Add a touch probe tool at the last index in the tool table.
; Make sure to specify deflection values for compensation.
M4000 S{"Touch Probe"} P{global.mosPTID} R{global.mosTPR} X{global.mosTPD[0]} Y{global.mosTPD[1]} I{-1}
else
if { !exists(global.mosDTR) || global.mosDTR == null }
set global.mosErr = { "<b>global.mosDTR</b> is not set." }
M99
; Add a datum tool at the last index in the tool table.
M4000 S{"Datum Tool"} P{global.mosPTID} R{global.mosDTR} I{-1}
; If we have a toolsetter, make sure the relevant variables are set
if { global.mosFeatToolSetter }
if { !exists(global.mosTSID) || global.mosTSID == null }
set global.mosErr = { "<b>global.mosTSID</b> must contain the ID of the Toolsetter probe. Configure it using M558 K<probe-id>... in config.g, then run the configuration wizard (<b>G8000</b>)." }
M99
if { !exists(global.mosTSP) || global.mosTSP == null }
set global.mosErr = { "<b>global.mosTSP</b> is not set." }
M99
if { !exists(global.mosTSR) || global.mosTSR == null }
set global.mosErr = { "<b>global.mosTSR</b> is not set." }
M99
; Make sure protected move back-off is set with touchprobe or toolsetter enabled
if { (global.mosFeatToolSetter || global.mosFeatTouchProbe) && global.mosPMBO == null }
set global.mosErr = { "<b>global.mosPMBO</b> is not set." }
M99
if { !exists(global.mosSAS) || global.mosSAS == null }
set global.mosErr = { "<b>global.mosSAS</b> is not set." }
M99
if { !exists(global.mosSDS) || global.mosSDS == null }
set global.mosErr = { "<b>global.mosSDS</b> is not set." }
M99
; Allow MOS macros to run.
set global.mosLdd = true
; Activate machine power if safety net is configured
M80.9
; Conditionally load saved WCS details and tool offsets
M501.1
if { global.mosEM }
echo { "WARNING: Expert mode is enabled! You will not be asked to confirm any actions. Be careful!" }
; trigger2.g - Safety Net emergency stop macro
; Trigger an instant reboot using M999
; The commented out lines are for a slightly less
; aggressive stop, which will open the safety net
; relay and then halt the machine, allowing the user
; to debug any machine issues before triggering the
; reboot themselves.
M999
; M81
; M112
; echo {"Safety Net activated due to emergency stop."}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment