One recommendation I was given when I done my lathe several years ago, was to have the E-stop circuit totally separate from the controller, as it removes the possibility of race conditions.
On my machines, I design things so the E-stop ultimately cuts all power to anything that moves. When a machine is in E-stop, the only things still getting power are the controller(s), and inputs. Anything that can move, gets it's power removed.
That means when E-stop is triggered, the servo/spindle drives lose power, and any signals the controller needs from those drives to ensure they are OK, are also lost, which is where you can quite quickly end up with a race situation trying to re-enable things.
Do you ignore drive OK signals and still re-power the drives, before triggering a reset, and hoping the drives come back online OK? If the drives don't come back online, how many seconds do you give things before triggering the E-stop again?
With that in mind, I use the SWE as part of the physical E-stop circuit (if the controller loses power, the machine will lose power). I then control any drive enables from the KFlop(or Kanalog/KStep/Konnect/...). The KFlop must see OK signals from all the drives before it enables anything (initialisation covers the need to trigger drive resets if OK signals are missing), and those OK signals are also continually monitored as part of an E-stop monitoring loop within the KFlop, and should any OK signal be lost (or any other condition that gets monitored), what I personally call a soft E-Stop gets triggered.
To give you an idea of my E-stop monitoring script, here's an old version from my current mill project, which I'd copied from my lathe (hence the big chunks of commented out code, which will eventually get tidied up).-
Code:
#include "KMotionDef.h"
estop_test() // this function monitors for any estop condition
{
int estopMes = 0; // used for message displaying after estop kill has occured (message's are blocking)
// **** Start E-Stop Monitoring ****
// if we're still initializing, then we don't check TC
/*if(!ReadBit(INIT)) {
// TC monitor - If we don't have TCOK other than when TC is active, then we have an issue
if(!ReadBit(TCOK) && !ReadBit(TCACTIVE)) {
ClearBit(SYSTEMOK);
estopMes = ESTCFAIL;
//printf("ESTOP TCOK and TCACTIVE low\n");
}
}*/
// if external estop circuit is lost, or if any other input that should be active disappears, we have an issue
if (!ReadBit(ESTOP)){
ClearBit(SYSTEMOK);
estopMes = ESTRIGGD;
//printf("External EStop\n");
}
/*
if (!ReadBit(XREADY)){
ClearBit(SYSTEMOK);
estopMes = ESXFAIL;
//printf("X not ready\n");
}
if (!ReadBit(ZREADY)){
ClearBit(SYSTEMOK);
estopMes = ESZFAIL;
//printf("Z not ready\n");
}
// if either channel is not enabled, and we're not in the homing routine which needs to disable axis, we have an issue
if ((!ch0->Enable || !ch1->Enable) && !INHOMING){
ClearBit(SYSTEMOK);
estopMes = ESXZFAIL;
//printf("X or Z not enabled\n");
}
// if spindle axis has faulted when it should be running, we need to stop things
if (!ch2->Enable){
ClearBit(SYSTEMOK);
estopMes = ESSPDL;
//printf("Spindle not enabled\n");
}*/
// if system ok, then no faults found and we can enable servos
if(!ReadBit(SYSTEMOK) && !ReadBit(ESTOPHANDLED))
{ //kill everything
//ClearBit(SERVOENABLE); // (it maybe gets enabled elsewhere, but we kill everything here, just to make sure)
DisableAxis(0);
DisableAxis(1);
DisableAxis(2);
ClearBit(XHOMED);
ClearBit(YHOMED);
ClearBit(ZHOMED);
ClearBit(COOLANT);
ClearBit(SPDLRUN);
//ClearBit(SPDLREV);
//ClearBit(INHOMING);
ClearBit(SYSTEMOK);
persist.UserData[TOOL_STATE_VAR] = T_IDLE; // set TC Idle (outputs will remain as set, so TC doesn't move after EStop)
//ClearBit(TCACTIVE);
//ClearBit(JOGALLOWED);
DoPC(PC_COMM_ESTOP); // finally we'll tell KMotionCNC to EStop
printf("PC_COMM_ESTOP requested\n");
SetBit(ESTOPHANDLED);
printf("ESTOPHANDLED set\n");
//double msgdel = Time_sec()+2;
//while(msgdel > Time_sec()){}
//printf("in estopMes\n");
switch(estopMes)
{
case(ESTCFAIL): // TC failure
{
MsgBox("ESTOP!! ToolChanger - No OK signal",MB_OK|MB_ICONEXCLAMATION);
break;
}
case(ESTRIGGD):
{
MsgBox("ESTOP!! E-stop, or Limit switch activated",MB_OK|MB_ICONEXCLAMATION);
break;
}
case(ESXZFAIL):
{
MsgBox("ESTOP!! X or Z axis disabled while not in homing",MB_OK|MB_ICONEXCLAMATION);
break;
}
case(ESXFAIL):
{
MsgBox("ESTOP!! X axis drive not ready",MB_OK|MB_ICONEXCLAMATION);
break;
}
case(ESZFAIL):
{
MsgBox("ESTOP!! Z axis drive not ready",MB_OK|MB_ICONEXCLAMATION);
break;
}
case(ESSPDL):
{
MsgBox("ESTOP!! Spindle disabled",MB_OK|MB_ICONEXCLAMATION);
break;
}
}
/*if(ReadBit(ESTOPHANDLED)){
printf("ESTOPHANDLED set\n");
} else {
printf("ESTOPHANDLED not set\n");
}*/
}
// **** End E-Stop Monitoring ****
}
That goes in it's own standalone file, and gets included in my main init.c file, and the estop_test() called in the init.c's endless loop.
I also use the following enum to make things a bit more legible-
Code:
//Enum stuff to make life easier
enum estops {ESTCFAIL, ESTRIGGD, ESXZFAIL, ESXFAIL, ESZFAIL, ESSPDL};
Having just had another scan over the estop monitor above, it's an old version that has been modified quite a bit since I copied it from the lathe. I'll have to dig out a pen drive for the latest version of the lathe code, as it will be more suitable than the mill code (lathe has analog servos, mill has steppers). I done a bit work to tidy it up the lathe code and iron out a few niggles, but the above should give you a general idea of how I handle things.