how to implement a security switch launching a program / gcode
Posted: Sat Apr 28, 2018 9:53 pm
Dear Tom,
I transfer my question and your reply to it from yahoo group to here (below), I guess and hope it will be much more convenient for you !
So... Thanks for the quick reply! Indeed, I did not really consdier all the possible events you mentioned, all very good questions..
To keep it as simple and safe as possible, I will try to program the most basic case:
stop motion, turn off spindle and dont do anything when closing the door again, thus, the user has to resume the job onscreen (KmotionCNC-button) if(!) the door is again closed.
For this, I suppose it might be sufficient to start with the first part of your suggested code and use
StopCoordinatedMotion();
ClearBit(SPINDLEBIT)
solely?
I will try asap to modify my ini-file and post it here for the pros to have a look at;)
Thanks again, talk to you soon, and kind regards,
Johannes
****************************************
****************************************
INITIAL QUESTION
****************************************
****************************************
Dear all,
I have a 4-axis-milling machine with NEMA-steppers, run by a KFLOP and KSTEP.
For security reasons we have to install a security switch in the door of the machine housing.
It should to the following:
When opening the door to the milling machine, a switch in the door is triggered that should pause (!) the gcode-program: pause the axis movement and stop the spindle (like hitting F5 and stopping the spindle).
When closing the door (and thus the switch), the spindle should start spinning again and after e.g. 5 seconds continue with the milling process.
What would you suggest is the best way to achieve this? I would like to connect the switch to one of the io of KFLOP/KSEP, since I was able to do something similar with the tool-length sensor that Iuckily turned out to work pretty well:)
In g-code language, the door switch should do something like this
open door:
M05 (stop spindle)
M0 (stop program)
close door:
M3 (start spindle CW)
G4 P5.0 (pause for spindle to catch up speed)
G1 x .....
But apart from how this would interfer with the currently running g-code, I think a little C-Program thats launched with the switch might be more appropriate or safe or intelligent. What do you think? How would a program doiing the above look like?
Really looking forward to your opinions, solutions, suggestions!
Thanks a lot in advance,
Kind regards,
Johannes
****************************************
****************************************
YOUR REPLY
****************************************
****************************************
Hi Johannes,
Sorry for the delay we've been having problems with the Yahoo Group. Please use our forum in the future:
...
You might debounce the Door switch to detect a change in the door switch to open, then issue a FeedHold, Stop Spindle, then command KMotionCNC to Halt. By issuing the Feedhold and spindle stop from within KFLOP should allow that to occur whether or not the PC is responsive.
Disclaimer: Safety is entirely your responsibility and software monitors may not be reliable enough for proper protection.
Depending on your system you may want to wait until the Feedhold completes and the system stops before turning off the Spindle.
I tried to code something like that up as a state machine. See Attached example.
You would need to modify it for your system - Spindle Control, Door Input, etc...
It turned out to be more complicated than I thought as I soon realized there are a number of special cases. Such as:
#1 - if the door is closed should the Job be started in all cases even if one wasn't active before?
#2 - what to do if the Operator starts a Job with the Door Open?
#3 - what if the Operator opens the door which pauses a job, then loads a different job, then closes the door. Should the new Job automatically start?
#4 - what if the spindle is off when the Job is halted? Should it still be turned on when re-started
#5 - How to tell KFLOP you no longer wish to resume when the door is closed?
It might be a lot safer to never resume a paused Job automatically.
You might try the attached program. But again as always it is your responsibility to fully guarantee it meets all safety requirements.
Regards
TK
#include "KMotionDef.h"
// Defines axis 0, 1, 2 as simple step dir TTL outputs for KSTEP
// enables them
// sets them as an xyz coordinate system for GCode
int main()
{
double T0, LastX=0, LastY=0, LastZ=0, Tau;
KStepPresent=TRUE; // enable KSTEP input multiplexing
FPGA(KAN_TRIG_REG)=4; // Mux PWM0 to JP7 Pin5 IO 44 for KSTEP
FPGA(STEP_PULSE_LENGTH_ADD) = 63 + 0x80; // set polarity and pulse length to 4us
ch0->InputMode=NO_INPUT_MODE;
ch0->OutputMode=STEP_DIR_MODE;
ch0->Vel=40000;
ch0->Accel=200000;
ch0->Jerk=4e+006;
ch0->P=0;
ch0->I=0.01;
ch0->D=0;
ch0->FFAccel=0;
ch0->FFVel=0;
ch0->MaxI=200;
ch0->MaxErr=1e+006;
ch0->MaxOutput=200;
ch0->DeadBandGain=1;
ch0->DeadBandRange=0;
ch0->InputChan0=0;
ch0->InputChan1=0;
ch0->OutputChan0=8;
ch0->OutputChan1=0;
ch0->MasterAxis=-1;
ch0->LimitSwitchOptions=0x0;
ch0->SoftLimitPos=1e+030;
ch0->SoftLimitNeg=-1e+030;
ch0->InputGain0=1;
ch0->InputGain1=1;
ch0->InputOffset0=0;
ch0->InputOffset1=0;
ch0->OutputGain=1;
ch0->OutputOffset=0;
ch0->SlaveGain=1;
ch0->BacklashMode=BACKLASH_OFF;
ch0->BacklashAmount=0;
ch0->BacklashRate=0;
ch0->invDistPerCycle=1;
ch0->Lead=0;
ch0->MaxFollowingError=1000000000;
ch0->StepperAmplitude=20;
ch0->iir[0].B0=1;
ch0->iir[0].B1=0;
ch0->iir[0].B2=0;
ch0->iir[0].A1=0;
ch0->iir[0].A2=0;
ch0->iir[1].B0=1;
ch0->iir[1].B1=0;
ch0->iir[1].B2=0;
ch0->iir[1].A1=0;
ch0->iir[1].A2=0;
ch0->iir[2].B0=0.000769;
ch0->iir[2].B1=0.001538;
ch0->iir[2].B2=0.000769;
ch0->iir[2].A1=1.92076;
ch0->iir[2].A2=-0.923833;
EnableAxisDest(0,0);
ch1->InputMode=NO_INPUT_MODE;
ch1->OutputMode=STEP_DIR_MODE;
ch1->Vel=40000;
ch1->Accel=200000;
ch1->Jerk=4e+006;
ch1->P=0;
ch1->I=0.01;
ch1->D=0;
ch1->FFAccel=0;
ch1->FFVel=0;
ch1->MaxI=200;
ch1->MaxErr=1e+006;
ch1->MaxOutput=200;
ch1->DeadBandGain=1;
ch1->DeadBandRange=0;
ch1->InputChan0=0;
ch1->InputChan1=0;
ch1->OutputChan0=9;
ch1->OutputChan1=0;
ch1->MasterAxis=-1;
ch1->LimitSwitchOptions=0x0;
ch1->SoftLimitPos=1e+030;
ch1->SoftLimitNeg=-1e+030;
ch1->InputGain0=1;
ch1->InputGain1=1;
ch1->InputOffset0=0;
ch1->InputOffset1=0;
ch1->OutputGain=1;
ch1->OutputOffset=0;
ch1->SlaveGain=1;
ch1->BacklashMode=BACKLASH_OFF;
ch1->BacklashAmount=0;
ch1->BacklashRate=0;
ch1->invDistPerCycle=1;
ch1->Lead=0;
ch1->MaxFollowingError=1000000000;
ch1->StepperAmplitude=20;
ch1->iir[0].B0=1;
ch1->iir[0].B1=0;
ch1->iir[0].B2=0;
ch1->iir[0].A1=0;
ch1->iir[0].A2=0;
ch1->iir[1].B0=1;
ch1->iir[1].B1=0;
ch1->iir[1].B2=0;
ch1->iir[1].A1=0;
ch1->iir[1].A2=0;
ch1->iir[2].B0=0.000769;
ch1->iir[2].B1=0.001538;
ch1->iir[2].B2=0.000769;
ch1->iir[2].A1=1.92076;
ch1->iir[2].A2=-0.923833;
EnableAxisDest(1,0);
ch2->InputMode=NO_INPUT_MODE;
ch2->OutputMode=STEP_DIR_MODE;
ch2->Vel=40000;
ch2->Accel=200000;
ch2->Jerk=4e+006;
ch2->P=0;
ch2->I=0.01;
ch2->D=0;
ch2->FFAccel=0;
ch2->FFVel=0;
ch2->MaxI=200;
ch2->MaxErr=1e+006;
ch2->MaxOutput=200;
ch2->DeadBandGain=1;
ch2->DeadBandRange=0;
ch2->InputChan0=0;
ch2->InputChan1=0;
ch2->OutputChan0=10;
ch2->OutputChan1=0;
ch2->MasterAxis=-1;
ch2->LimitSwitchOptions=0x0;
ch2->SoftLimitPos=1e+009;
ch2->SoftLimitNeg=-1e+009;
ch2->InputGain0=1;
ch2->InputGain1=1;
ch2->InputOffset0=0;
ch2->InputOffset1=0;
ch2->OutputGain=-1;
ch2->OutputOffset=0;
ch2->SlaveGain=1;
ch2->BacklashMode=BACKLASH_OFF;
ch2->BacklashAmount=0;
ch2->BacklashRate=0;
ch2->invDistPerCycle=1;
ch2->Lead=0;
ch2->MaxFollowingError=1000000000;
ch2->StepperAmplitude=20;
ch2->iir[0].B0=1;
ch2->iir[0].B1=0;
ch2->iir[0].B2=0;
ch2->iir[0].A1=0;
ch2->iir[0].A2=0;
ch2->iir[1].B0=1;
ch2->iir[1].B1=0;
ch2->iir[1].B2=0;
ch2->iir[1].A1=0;
ch2->iir[1].A2=0;
ch2->iir[2].B0=1;
ch2->iir[2].B1=0;
ch2->iir[2].B2=0;
ch2->iir[2].A1=0;
ch2->iir[2].A2=0;
EnableAxisDest(2,0);
DefineCoordSystem(0,1,2,-1);
SetBitDirection(45,1); // set Enable Signal as Output
SetBit(45); // Enable the amplifiers
// Add a small amount of Coordinated Motion Path smoothing if desired
// Tau = 0.001; // seconds for Low Pass Filter Time Constant
// KLP = exp(-TIMEBASE/Tau);
KLP=0; // force to 0 to disable
// printf("Tau=%f KLP=%f\n",Tau,KLP);
for (;;) // loop forever
{
WaitNextTimeSlice();
ServiceDoorMonitor();
// Service Amplifier disable after no activity for a while
if (ch0->Dest != LastX || ch1->Dest != LastY || ch2->Dest != LastZ)
{
// we moved - enable KStep Amplifers
SetBit(45);
T0 = Time_sec(); // record the time and position of last motion
LastX=ch0->Dest;
LastY=ch1->Dest;
LastZ=ch2->Dest;
}
else
{
if (Time_sec() > T0 + 10.0) ClearBit(45);
}
}
return 0;
}
#define StateDoorIsClosed 0
#define StateWaitingForFeedHoldComplete 1
#define StateDoorIsOpen 2
#define WaitingForSpindleSpinUp 3
#define DOOROPENBIT 46
#define SPINDLEBIT 47
#define TMP 10 // which spare persist to use to transfer data
#include "..\KflopToKMotionCNCFunctions.c"
// function prototypes for compiler
int Debounce(int n, int *cnt, int *last, int *lastsolid);
// state variables for switch debouncing
int dlast=0,dlastsolid=-1,dcount=0;
// Monitor Door called continuously
//
// Keeps track of what State we are in and watches for the appropriate event
// to perform some action and transition to a new state
void ServiceDoorMonitor(void)
{
static int JobWasActive, SpindleWasOn, PrevJobActive, State=StateDoorIsClosed;
static double SpinUpTime;
switch (State)
{
case StateDoorIsClosed:
// check if door was opened
if (Debounce(ReadBit(DOOROPENBIT),&dcount,&dlast,&dlastsolid)==1)
{
//Door was opened
printf("Door was opened\n");
StopCoordinatedMotion(); // command Feedhold
State++;
}
break;
case StateWaitingForFeedHoldComplete:
// Check if Feedhold has completed or not active
if (CS0_StoppingState == 0 || CS0_StoppingState > 2)
{
SpindleWasOn = ReadBit(SPINDLEBIT); // remember if Spindle was on
ClearBit(SPINDLEBIT); // StopSpindle
PrevJobActive = JobWasActive = JOB_ACTIVE;
printf("Door - Full Stop JobWasActive=%d SpindleWasOn=%d\n",JobWasActive,SpindleWasOn);
DoPC(PC_COMM_HALT); // Halt KMotionCNC
State++;
}
break;
case StateDoorIsOpen:
ClearBit(SPINDLEBIT); // Force Spindle Off in all cases
if (JOB_ACTIVE && !PrevJobActive) // did a Job suddenly start with door open?
{
printf("Job Started with Door Open\n");
StopCoordinatedMotion(); // command Feedhold
State=StateWaitingForFeedHoldComplete;
}
// check if door was closed
else if (Debounce(ReadBit(DOOROPENBIT),&dcount,&dlast,&dlastsolid)==0)
{
//Door was closed
printf("Door was closed\n");
if (JobWasActive && SpindleWasOn) // was Job running and Spindle on when door was opened??
{
printf("Door - Spindle Started - Time Delay Started\n");
SetBit(SPINDLEBIT); // Yes, StartSpindle
SpinUpTime = Time_sec() + 5.0; // Record Time Spindle should be up to speed
State++;
}
else
{
State=StateDoorIsClosed; //No, go directly to closed state
}
}
PrevJobActive = JOB_ACTIVE; // remember previous job state
break;
case WaitingForSpindleSpinUp:
// check if door was opened while still spinning up
if (Debounce(ReadBit(DOOROPENBIT),&dcount,&dlast,&dlastsolid)==1)
{
//Door was opened
printf("Door was opened while still spinning up\n");
State = StateWaitingForFeedHoldComplete;
}
// else, check if Time to resume Job
else if (Time_sec() > SpinUpTime)
{
printf("Door closed and Job Resumed\n");
DoPC(PC_COMM_EXECUTE);
State = StateDoorIsClosed;
}
break;
}
}
// Debounce a bit
//
// return 1 one time when first debounced high
// return 0 one time when first debounced low
// return -1 otherwise
#define DBTIME 300
int Debounce(int n, int *cnt, int *last, int *lastsolid)
{
int v = -1;
if (n == *last) // same as last time?
{
if (*cnt == DBTIME-1)
{
if (n != *lastsolid)
{
v = *lastsolid = n; // return debounced value
}
}
if (*cnt < DBTIME) (*cnt)++;
}
else
{
*cnt = 0; // reset count
}
*last = n;
return v;
}
I transfer my question and your reply to it from yahoo group to here (below), I guess and hope it will be much more convenient for you !
So... Thanks for the quick reply! Indeed, I did not really consdier all the possible events you mentioned, all very good questions..
To keep it as simple and safe as possible, I will try to program the most basic case:
stop motion, turn off spindle and dont do anything when closing the door again, thus, the user has to resume the job onscreen (KmotionCNC-button) if(!) the door is again closed.
For this, I suppose it might be sufficient to start with the first part of your suggested code and use
StopCoordinatedMotion();
ClearBit(SPINDLEBIT)
solely?
I will try asap to modify my ini-file and post it here for the pros to have a look at;)
Thanks again, talk to you soon, and kind regards,
Johannes
****************************************
****************************************
INITIAL QUESTION
****************************************
****************************************
Dear all,
I have a 4-axis-milling machine with NEMA-steppers, run by a KFLOP and KSTEP.
For security reasons we have to install a security switch in the door of the machine housing.
It should to the following:
When opening the door to the milling machine, a switch in the door is triggered that should pause (!) the gcode-program: pause the axis movement and stop the spindle (like hitting F5 and stopping the spindle).
When closing the door (and thus the switch), the spindle should start spinning again and after e.g. 5 seconds continue with the milling process.
What would you suggest is the best way to achieve this? I would like to connect the switch to one of the io of KFLOP/KSEP, since I was able to do something similar with the tool-length sensor that Iuckily turned out to work pretty well:)
In g-code language, the door switch should do something like this
open door:
M05 (stop spindle)
M0 (stop program)
close door:
M3 (start spindle CW)
G4 P5.0 (pause for spindle to catch up speed)
G1 x .....
But apart from how this would interfer with the currently running g-code, I think a little C-Program thats launched with the switch might be more appropriate or safe or intelligent. What do you think? How would a program doiing the above look like?
Really looking forward to your opinions, solutions, suggestions!
Thanks a lot in advance,
Kind regards,
Johannes
****************************************
****************************************
YOUR REPLY
****************************************
****************************************
Hi Johannes,
Sorry for the delay we've been having problems with the Yahoo Group. Please use our forum in the future:
...
You might debounce the Door switch to detect a change in the door switch to open, then issue a FeedHold, Stop Spindle, then command KMotionCNC to Halt. By issuing the Feedhold and spindle stop from within KFLOP should allow that to occur whether or not the PC is responsive.
Disclaimer: Safety is entirely your responsibility and software monitors may not be reliable enough for proper protection.
Depending on your system you may want to wait until the Feedhold completes and the system stops before turning off the Spindle.
I tried to code something like that up as a state machine. See Attached example.
You would need to modify it for your system - Spindle Control, Door Input, etc...
It turned out to be more complicated than I thought as I soon realized there are a number of special cases. Such as:
#1 - if the door is closed should the Job be started in all cases even if one wasn't active before?
#2 - what to do if the Operator starts a Job with the Door Open?
#3 - what if the Operator opens the door which pauses a job, then loads a different job, then closes the door. Should the new Job automatically start?
#4 - what if the spindle is off when the Job is halted? Should it still be turned on when re-started
#5 - How to tell KFLOP you no longer wish to resume when the door is closed?
It might be a lot safer to never resume a paused Job automatically.
You might try the attached program. But again as always it is your responsibility to fully guarantee it meets all safety requirements.
Regards
TK
#include "KMotionDef.h"
// Defines axis 0, 1, 2 as simple step dir TTL outputs for KSTEP
// enables them
// sets them as an xyz coordinate system for GCode
int main()
{
double T0, LastX=0, LastY=0, LastZ=0, Tau;
KStepPresent=TRUE; // enable KSTEP input multiplexing
FPGA(KAN_TRIG_REG)=4; // Mux PWM0 to JP7 Pin5 IO 44 for KSTEP
FPGA(STEP_PULSE_LENGTH_ADD) = 63 + 0x80; // set polarity and pulse length to 4us
ch0->InputMode=NO_INPUT_MODE;
ch0->OutputMode=STEP_DIR_MODE;
ch0->Vel=40000;
ch0->Accel=200000;
ch0->Jerk=4e+006;
ch0->P=0;
ch0->I=0.01;
ch0->D=0;
ch0->FFAccel=0;
ch0->FFVel=0;
ch0->MaxI=200;
ch0->MaxErr=1e+006;
ch0->MaxOutput=200;
ch0->DeadBandGain=1;
ch0->DeadBandRange=0;
ch0->InputChan0=0;
ch0->InputChan1=0;
ch0->OutputChan0=8;
ch0->OutputChan1=0;
ch0->MasterAxis=-1;
ch0->LimitSwitchOptions=0x0;
ch0->SoftLimitPos=1e+030;
ch0->SoftLimitNeg=-1e+030;
ch0->InputGain0=1;
ch0->InputGain1=1;
ch0->InputOffset0=0;
ch0->InputOffset1=0;
ch0->OutputGain=1;
ch0->OutputOffset=0;
ch0->SlaveGain=1;
ch0->BacklashMode=BACKLASH_OFF;
ch0->BacklashAmount=0;
ch0->BacklashRate=0;
ch0->invDistPerCycle=1;
ch0->Lead=0;
ch0->MaxFollowingError=1000000000;
ch0->StepperAmplitude=20;
ch0->iir[0].B0=1;
ch0->iir[0].B1=0;
ch0->iir[0].B2=0;
ch0->iir[0].A1=0;
ch0->iir[0].A2=0;
ch0->iir[1].B0=1;
ch0->iir[1].B1=0;
ch0->iir[1].B2=0;
ch0->iir[1].A1=0;
ch0->iir[1].A2=0;
ch0->iir[2].B0=0.000769;
ch0->iir[2].B1=0.001538;
ch0->iir[2].B2=0.000769;
ch0->iir[2].A1=1.92076;
ch0->iir[2].A2=-0.923833;
EnableAxisDest(0,0);
ch1->InputMode=NO_INPUT_MODE;
ch1->OutputMode=STEP_DIR_MODE;
ch1->Vel=40000;
ch1->Accel=200000;
ch1->Jerk=4e+006;
ch1->P=0;
ch1->I=0.01;
ch1->D=0;
ch1->FFAccel=0;
ch1->FFVel=0;
ch1->MaxI=200;
ch1->MaxErr=1e+006;
ch1->MaxOutput=200;
ch1->DeadBandGain=1;
ch1->DeadBandRange=0;
ch1->InputChan0=0;
ch1->InputChan1=0;
ch1->OutputChan0=9;
ch1->OutputChan1=0;
ch1->MasterAxis=-1;
ch1->LimitSwitchOptions=0x0;
ch1->SoftLimitPos=1e+030;
ch1->SoftLimitNeg=-1e+030;
ch1->InputGain0=1;
ch1->InputGain1=1;
ch1->InputOffset0=0;
ch1->InputOffset1=0;
ch1->OutputGain=1;
ch1->OutputOffset=0;
ch1->SlaveGain=1;
ch1->BacklashMode=BACKLASH_OFF;
ch1->BacklashAmount=0;
ch1->BacklashRate=0;
ch1->invDistPerCycle=1;
ch1->Lead=0;
ch1->MaxFollowingError=1000000000;
ch1->StepperAmplitude=20;
ch1->iir[0].B0=1;
ch1->iir[0].B1=0;
ch1->iir[0].B2=0;
ch1->iir[0].A1=0;
ch1->iir[0].A2=0;
ch1->iir[1].B0=1;
ch1->iir[1].B1=0;
ch1->iir[1].B2=0;
ch1->iir[1].A1=0;
ch1->iir[1].A2=0;
ch1->iir[2].B0=0.000769;
ch1->iir[2].B1=0.001538;
ch1->iir[2].B2=0.000769;
ch1->iir[2].A1=1.92076;
ch1->iir[2].A2=-0.923833;
EnableAxisDest(1,0);
ch2->InputMode=NO_INPUT_MODE;
ch2->OutputMode=STEP_DIR_MODE;
ch2->Vel=40000;
ch2->Accel=200000;
ch2->Jerk=4e+006;
ch2->P=0;
ch2->I=0.01;
ch2->D=0;
ch2->FFAccel=0;
ch2->FFVel=0;
ch2->MaxI=200;
ch2->MaxErr=1e+006;
ch2->MaxOutput=200;
ch2->DeadBandGain=1;
ch2->DeadBandRange=0;
ch2->InputChan0=0;
ch2->InputChan1=0;
ch2->OutputChan0=10;
ch2->OutputChan1=0;
ch2->MasterAxis=-1;
ch2->LimitSwitchOptions=0x0;
ch2->SoftLimitPos=1e+009;
ch2->SoftLimitNeg=-1e+009;
ch2->InputGain0=1;
ch2->InputGain1=1;
ch2->InputOffset0=0;
ch2->InputOffset1=0;
ch2->OutputGain=-1;
ch2->OutputOffset=0;
ch2->SlaveGain=1;
ch2->BacklashMode=BACKLASH_OFF;
ch2->BacklashAmount=0;
ch2->BacklashRate=0;
ch2->invDistPerCycle=1;
ch2->Lead=0;
ch2->MaxFollowingError=1000000000;
ch2->StepperAmplitude=20;
ch2->iir[0].B0=1;
ch2->iir[0].B1=0;
ch2->iir[0].B2=0;
ch2->iir[0].A1=0;
ch2->iir[0].A2=0;
ch2->iir[1].B0=1;
ch2->iir[1].B1=0;
ch2->iir[1].B2=0;
ch2->iir[1].A1=0;
ch2->iir[1].A2=0;
ch2->iir[2].B0=1;
ch2->iir[2].B1=0;
ch2->iir[2].B2=0;
ch2->iir[2].A1=0;
ch2->iir[2].A2=0;
EnableAxisDest(2,0);
DefineCoordSystem(0,1,2,-1);
SetBitDirection(45,1); // set Enable Signal as Output
SetBit(45); // Enable the amplifiers
// Add a small amount of Coordinated Motion Path smoothing if desired
// Tau = 0.001; // seconds for Low Pass Filter Time Constant
// KLP = exp(-TIMEBASE/Tau);
KLP=0; // force to 0 to disable
// printf("Tau=%f KLP=%f\n",Tau,KLP);
for (;;) // loop forever
{
WaitNextTimeSlice();
ServiceDoorMonitor();
// Service Amplifier disable after no activity for a while
if (ch0->Dest != LastX || ch1->Dest != LastY || ch2->Dest != LastZ)
{
// we moved - enable KStep Amplifers
SetBit(45);
T0 = Time_sec(); // record the time and position of last motion
LastX=ch0->Dest;
LastY=ch1->Dest;
LastZ=ch2->Dest;
}
else
{
if (Time_sec() > T0 + 10.0) ClearBit(45);
}
}
return 0;
}
#define StateDoorIsClosed 0
#define StateWaitingForFeedHoldComplete 1
#define StateDoorIsOpen 2
#define WaitingForSpindleSpinUp 3
#define DOOROPENBIT 46
#define SPINDLEBIT 47
#define TMP 10 // which spare persist to use to transfer data
#include "..\KflopToKMotionCNCFunctions.c"
// function prototypes for compiler
int Debounce(int n, int *cnt, int *last, int *lastsolid);
// state variables for switch debouncing
int dlast=0,dlastsolid=-1,dcount=0;
// Monitor Door called continuously
//
// Keeps track of what State we are in and watches for the appropriate event
// to perform some action and transition to a new state
void ServiceDoorMonitor(void)
{
static int JobWasActive, SpindleWasOn, PrevJobActive, State=StateDoorIsClosed;
static double SpinUpTime;
switch (State)
{
case StateDoorIsClosed:
// check if door was opened
if (Debounce(ReadBit(DOOROPENBIT),&dcount,&dlast,&dlastsolid)==1)
{
//Door was opened
printf("Door was opened\n");
StopCoordinatedMotion(); // command Feedhold
State++;
}
break;
case StateWaitingForFeedHoldComplete:
// Check if Feedhold has completed or not active
if (CS0_StoppingState == 0 || CS0_StoppingState > 2)
{
SpindleWasOn = ReadBit(SPINDLEBIT); // remember if Spindle was on
ClearBit(SPINDLEBIT); // StopSpindle
PrevJobActive = JobWasActive = JOB_ACTIVE;
printf("Door - Full Stop JobWasActive=%d SpindleWasOn=%d\n",JobWasActive,SpindleWasOn);
DoPC(PC_COMM_HALT); // Halt KMotionCNC
State++;
}
break;
case StateDoorIsOpen:
ClearBit(SPINDLEBIT); // Force Spindle Off in all cases
if (JOB_ACTIVE && !PrevJobActive) // did a Job suddenly start with door open?
{
printf("Job Started with Door Open\n");
StopCoordinatedMotion(); // command Feedhold
State=StateWaitingForFeedHoldComplete;
}
// check if door was closed
else if (Debounce(ReadBit(DOOROPENBIT),&dcount,&dlast,&dlastsolid)==0)
{
//Door was closed
printf("Door was closed\n");
if (JobWasActive && SpindleWasOn) // was Job running and Spindle on when door was opened??
{
printf("Door - Spindle Started - Time Delay Started\n");
SetBit(SPINDLEBIT); // Yes, StartSpindle
SpinUpTime = Time_sec() + 5.0; // Record Time Spindle should be up to speed
State++;
}
else
{
State=StateDoorIsClosed; //No, go directly to closed state
}
}
PrevJobActive = JOB_ACTIVE; // remember previous job state
break;
case WaitingForSpindleSpinUp:
// check if door was opened while still spinning up
if (Debounce(ReadBit(DOOROPENBIT),&dcount,&dlast,&dlastsolid)==1)
{
//Door was opened
printf("Door was opened while still spinning up\n");
State = StateWaitingForFeedHoldComplete;
}
// else, check if Time to resume Job
else if (Time_sec() > SpinUpTime)
{
printf("Door closed and Job Resumed\n");
DoPC(PC_COMM_EXECUTE);
State = StateDoorIsClosed;
}
break;
}
}
// Debounce a bit
//
// return 1 one time when first debounced high
// return 0 one time when first debounced low
// return -1 otherwise
#define DBTIME 300
int Debounce(int n, int *cnt, int *last, int *lastsolid)
{
int v = -1;
if (n == *last) // same as last time?
{
if (*cnt == DBTIME-1)
{
if (n != *lastsolid)
{
v = *lastsolid = n; // return debounced value
}
}
if (*cnt < DBTIME) (*cnt)++;
}
else
{
*cnt = 0; // reset count
}
*last = n;
return v;
}