Machsupport Forum
Mach Discussion => Mach4 General Discussion => Topic started by: beezerlm on September 26, 2016, 08:14:44 PM
-
Is there a simple way to pause motion until an input is triggered within a macro?
I would like to ensure my Z axis returns home before the macro ends because after the macro would be an X-Y move. This is a cylinder where I have output 1 activate the "up" direction and output 0 activate the "down" direction.
Something like this:
1. activate output_1 (up)
2. wait for Z home switch to trigger
3. end macro.
What is the best way to code step 2 (wait for input) in a Macro?
-
Take a look at the api docs for mcSignalWait located in the mach4 docs directory.
DazTheGas
-
Thanks. I gave it a try by adding it to the end of the macro. For some reason it does not wait for the "Z home" signal before moving on to the next line of the Gcode program ( a G0 X/Y axis positioning move ). I also tried WAIT_MODE_LOW and it did the same thing. I was hoping the machine would stop all movement until the Z axis reached the home position. Any Ideas what I am doing wrong?
function M100()
local inst = mc.mcGetInstance()
local input_20 = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT20) -- Get handle for input 20
local input_20_State = mc.mcSignalGetState(input_20) -- Get input 20 state
local z_home_sig = mc.mcSignalGetHandle(inst, mc.OSIG_ZHOME) -- Get handle for Z axis home switch
if input_20 == 0 then
wx.wxMessageBox("Could not locate signal!")
else
if input_20_State == 1 then --If input_20_State equals 1, then the signal is active.
wx.wxMessageBox("Signal is already active!")
else --If input_20_State does not equal 1, then the signal is inactive.
mc.mcSignalSetState(input_20, 1)
mc.mcSignalWait(inst, z_home_sig, WAIT_MODE_HIGH, 20)
end
end
end
if (mc.mcInEditor() == 1) then
M100()
end
-
Your trying to wait for a signal handle not a signal id like mc.OSIG_ZHOME
Try something like this but signal should be the INPUT of your Z Home
mc.mcSignalWait(inst, mc.ISIG_MOTOR0_PLUS, WAIT_MODE_HIGH, 20)
DazTheGas
-
Hmmm....I changed the signal to "mc.ISIG_MOTOR3_HOME" which is mapped to the Z axis home switch and it still just moves on to the next line in the main G-code program?
function M100()
local inst = mc.mcGetInstance()
local input_20 = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT20) -- Get handle for input 20
local input_20_State = mc.mcSignalGetState(input_20) -- Get input 20 state
local z_home_sig = mc.ISIG_MOTOR3_HOME -- Get signal for Z axis home switch
if input_20 == 0 then
wx.wxMessageBox("Could not locate signal!")
else
if input_20_State == 1 then
wx.wxMessageBox("Signal is already active!")
else --If input_20_State does not equal 1, then the signal is inactive.
mc.mcSignalSetState(input_20, 1)
mc.mcSignalWait(inst, z_home_sig, WAIT_MODE_HIGH, 20)
end
end
end
if (mc.mcInEditor() == 1) then
M100()
end
-
In the default screens of the later dev versions the ref all home buttons run the RefAllHome function as a coroutine. This was modified to provide an example for those interested in doing something similar. It will reference all axis and (only after motion is finished) pop up a message box saying referencing is complete. You may be able to do something similar to get the results your looking for.
--Button script
--RefAllHome()
wait = coroutine.create (RefAllHome) --Run the RefAllHome function as a coroutine named wait.
--Screen Load Script
---------------------------------------------------------------
-- Ref All Home() function.
---------------------------------------------------------------
function RefAllHome()
mc.mcAxisDerefAll(inst) --Just to turn off all ref leds
mc.mcAxisHomeAll(inst)
coroutine.yield() --yield coroutine so we can do the following after motion stops
----See ref all home button and plc script for coroutine.create and coroutine.resume
wx.wxMessageBox('Referencing is complete')
end
--PLC Script
-------------------------------------------------------
-- Coroutine resume
-------------------------------------------------------
if (wait ~= nil) and (machState == 0) then --wait exist and state == idle
local state = coroutine.status(wait) --Get the status of the coroutine named wait
if state == "suspended" then --Coroutine named wait is suspended
coroutine.resume(wait) --Resume coroutine named wait
end
end
-
Well a gave it a go as and I get a lua error in the macro "Error while running chunk" it doesn't seem to want to run my function (GrindCycle) in the Screenload script from within the Macro(M100)?
Here is what I have now:
Add to the ScreenLoad script:
--My GrindCycle function starts here:
function GrindCycle()
local inst = mc.mcGetInstance()
local input_20 = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT20) -- Get handle for input 20
local input_20_State = mc.mcSignalGetState(input_20) -- Get input 20 state
if input_20 == 0 then
wx.wxMessageBox("Could not locate signal!")
else
if input_20_State == 1 then --If input_20_State equals 1, then grind cycle active signal is High.
wx.wxMessageBox("Signal is already active!")
else --If input_20_State does not equal 1, then the grind cycle signal is inactive.
mc.mcSignalSetState(input_20, 1) -- set grind cycle signal active
mc.mcSignalWait(inst, z_home_sig, WAIT_MODE_HIGH, 20) -- wait for z to get home
coroutine.yield () --yield coroutine so we can do the following after motion stops
wx.wxMessageBox("Grind Cycle Complete")
end
end
end
--My GrindCycle function ends here:
In the M100 macro:
function M100()
wait = coroutine.create (GrindCycle) --Run the GrindCycle function as a coroutine named wait.
end
if (mc.mcInEditor() == 1) then
M100()
end
Added to the PLC script:
-------------------------------------------------------
-- Coroutine resume
-------------------------------------------------------
if (wait ~= nil) and (machState == 0) then --wait exist and state == idle
local state = coroutine.status(wait) --Get the status of the coroutine named wait
if state == "suspended" then --Coroutine named wait is suspended
coroutine.resume(wait) --Resume coroutine named wait
end
end
-
Cant remember of top of my head but think macros and gcode run in a different lua instance than the gui??
DazTheGas
-
Yup, that is right Daz........ my bad.
-
Is there a function to inhibit motion? I looked through the API but didn't see one. If there is, maybe I could inhibit motion somehow until the Z home signal goes high? The Z would still move home even if all motion is inhibited since it is a pneumatic cylinder and mach4 only controls up/down via output_0 and output_1.
-
The only problem I can see is if your home switch is also the limit this could cause mach4 to disable, just as a thought have you thought of using something like mc.mcCntlGcodeExecuteWait(inst, "G0 Z-1 F50") this will halt the gcode.
I have been looking into the mc.mcSignalWait and all works well when using any of the ISIG_INPUT0 to 63 etc but not working on anything else???
DazTheGas
-
Daz - I mapped the Z home signal to Input_21 and I still can't get mc.mcSignalWait to work? The API says to use the signal handle, are you using the handle? I haven't tried the others yet.
Current M100:
function M100()
local inst = mc.mcGetInstance()
local input_20 = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT20) -- Get handle for input 20
local input_20_State = mc.mcSignalGetState(input_20) -- Get input 20 state
local z_home_sig = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT21) -- Get handle for Z axis home switch
--local z_home_sig_state = mc.mcSignalGetState(z_home_sig) --Get state of signal
if input_20 == 0 then
wx.wxMessageBox("Could not locate signal!")
else
if input_20_State == 1 then --If input_20_State equals 1, then the signal is active.
wx.wxMessageBox("Signal is already active!")
else --If input_20_State does not equal 1, then the signal is inactive.
--wx.wxMessageBox("Setting input_20 high")
mc.mcSignalSetState(input_20, 1)
mc.mcSignalWait(inst, z_home_sig, WAIT_MODE_HIGH, 20)
end
end
end
if (mc.mcInEditor() == 1) then
M100()
end
-
I looked at mc.mcCntlGcodeExecuteWait. I don't know how I could use it because the Z axis is not controlled via G-code.
-
I got mcSignalWait to work. I was wrong API does not say to use handle. It worked by putting the signal ID.
-
Was just looking at that myself.
From the manual......
sigId A valid signal ID. (NOT a signal handle)
Glad you got it sorted. :)
How about post the code that worked so others will see what right looks like.
-
Here is the code that works:
function M100()
local inst = mc.mcGetInstance()
local input_20 = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT20) -- Get handle for input 20
local input_20_State = mc.mcSignalGetState(input_20) -- Get input 20 state
local z_home_sig = mc.ISIG_INPUT21 -- Get Signal ID for Z axis home switch
if input_20 == 0 then
wx.wxMessageBox("Could not locate signal!")
else
if input_20_State == 1 then --If input_20_State equals 1, then the signal is active.
wx.wxMessageBox("Signal is already active!")
else --If input_20_State does not equal 1, then the signal is inactive.
--wx.wxMessageBox("Setting input_20 high")
mc.mcSignalSetState(input_20, 1)
mc.mcSignalWait(inst, z_home_sig, 1, 0)
end
end
end
if (mc.mcInEditor() == 1) then
M100()
end
-
Nice, thanks!
Does this work as well?
function M100()
local inst = mc.mcGetInstance()
local input_20 = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT20) -- Get handle for input 20
local input_20_State = mc.mcSignalGetState(input_20) -- Get input 20 state
if input_20 == 0 then
wx.wxMessageBox("Could not locate signal!")
else
if input_20_State == 1 then --If input_20_State equals 1, then the signal is active.
wx.wxMessageBox("Signal is already active!")
else --If input_20_State does not equal 1, then the signal is inactive.
--wx.wxMessageBox("Setting input_20 high")
mc.mcSignalSetState(input_20, 1)
mc.mcSignalWait(inst, mc.ISIG_INPUT21, 1, 0)
end
end
end
if (mc.mcInEditor() == 1) then
M100()
end
Also, just wondering what this is......... mc.mcSignalSetState(input_20, 1)
Shouldn't that be turning on an output to fire the raise solenoid?
-
Actually I am using input 20 as a "In automatic Cycle" signal. The M100 is interacting with the PLC script to monitor the Z-axis encoder and change direction when necessary. I am still working on the Z axis reciprocation and automatic grinding cycle part at the moment, but as of now, yes the input 20 is setting the output_1 high in the PLC script which sends the Z axis home.
Yes your sample works also.
-
yay you lot have been busy, glad its working for you now.
DazTheGas
-
Is there a simple way to pause motion until an input is triggered within a macro?
I would like to ensure my Z axis returns home before the macro ends because after the macro would be an X-Y move. This is a cylinder where I have output 1 activate the "up" direction and output 0 activate the "down" direction.
Something like this:
1. activate output_1 (up)
2. wait for Z home switch to trigger
3. end macro.
What is the best way to code step 2 (wait for input) in a Macro?
Good, thanks for testing!
This does not do any error checking and the time it waits for the input to go high is indefinite (neither of which are very good ideas) and it does not take your "In automatic Cycle" into account but it should be all that is needed to do what you were looking for originally.
Also note the 2 M100's were changed to m100 so should be saved as m100.mcs. Early on their was some debate about whether or not M codes should be upper or lower case in the scripts themselves. The answer is lower case. It should prevent some possible issues in non Windows OS's.
I would also delete any other M100 files (M100.mcs, M100.mcc, M100.bak) each time you edit the macro and before testing. If not, your changes may not take effect until you do.
function m100()
local inst, hSig
inst = mc.mcGetInstance()
hSig = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT1) -- Get handle for output 1
mc.mcSignalSetState(hSig, 1) --turn on output 1
mc.mcSignalWait(inst, mc.ISIG_INPUT21, 1, 0) --Wait indefinitely (0) for input 21 to become active
end
if (mc.mcInEditor() == 1) then
m100()
end
-
yay you lot have been busy, glad its working for you now.
DazTheGas
Thanks Daz!
I have not had a use for the mc.mcSignalWait API call yet so hadn't used it beofre but bet I will find a use for it real soon now. :)
Love it when a plan comes together! The devil is in the details. Thankfully the details are covered pretty good in the help file.
-
This does not do any error checking and the time it waits for the input to go high is indefinite (neither of which are very good ideas) and it does not take your "In automatic Cycle" into account but it should be all that is needed to do what you were looking for originally.
Could you recommend the proper error checking for this? I have been neglecting that too much while focusing on getting the code to work.
-
This should work. I changed the time to 5 seconds but you can adjust as needed.
Note, I have it putting the first checks in a MessageBox, the last 2 checks write them to the status bar or message bar in Mach. All of them write a message even if there is no error (MERROR_NOERROR).
function m100()
local inst, hSig, rc
inst = mc.mcGetInstance()
hSig, rc = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT1) -- Get handle for output 1
if (rc == mc.MERROR_NOERROR) then
wx.wxMessageBox("MERROR_NOERROR")
elseif (rc == mc.MERROR_INVALID_INSTANCE) then
wx.wxMessageBox("MERROR_INVALID_INSTANCE")
elseif (rc == mc.MERROR_SIGNAL_NOT_FOUND) then
wx.wxMessageBox("MERROR_SIGNAL_NOT_FOUND")
end
rc = mc.mcSignalSetState(hSig, 1) --turn on output 1
if (rc == mc.MERROR_NOERROR) then
mc.mcCntlSetLastError(inst, "MERROR_NOERROR")
elseif (rc == mc.MERROR_INVALID_ARG) then
mc.mcCntlSetLastError(inst, "MERROR_INVALID_ARG")
end
rc = mc.mcSignalWait(inst, mc.ISIG_INPUT21, 1, 5) --Wait 5 seconds for input 21 to become active
if (rc == mc.MERROR_NOERROR) then
mc.mcCntlSetLastError(inst, "MERROR_NOERROR")
elseif (rc == mc.MERROR_INVALID_INSTANCE) then
mc.mcCntlSetLastError(inst, "MERROR_INVALID_INSTANCE")
elseif (rc == mc.MERROR_INVALID_ARG) then
mc.mcCntlSetLastError(inst, "MERROR_INVALID_ARG")
elseif (rc == mc.MERROR_NOT_ENABLED) then
mc.mcCntlSetLastError(inst, "MERROR_NOT_ENABLED")
elseif (rc == mc.MERROR_TIMED_OUT) then
mc.mcCntlSetLastError(inst, "MERROR_TIMED_OUT")
end
end
if (mc.mcInEditor() == 1) then
m100()
end
-
Hi Guys,
reading Robs post and I saw:
if (rc == mc.MERROR_NOERROR) then
wx.wxMessageBox("MERROR_NOERROR")
I have been writing code similar to:
if(rc==0)then
wx.wxMessageBo("MERROR_NOERROR")
ie I've been testing the numeric value of rc against a list provided by DTG but
this suggests that mc.MERROR_NOERROR is a defined quantity. Is this correct?
If so 'gotta luv C'
Craig
-
That is correct, 0 is the same as mc.MERROR_NOERROR just like in the command 1 is the same as mc.WAIT_MODE_HIGH, these have been defined in C but can be accessed by the leading mc.
DazTheGas
-
I am going to look at adding an error check module and the necessary bits to make it work in the default profiles. I have done it and tested it already and got it all working with some help from DTG. I had a very simple mistake and just kept overlooking it. :-[ Garbage in is garbage out and I'm not immune from giving it some garbage myself. I think most when they start out coding have those moments when they want to kick their own butt. Happens to us all I think so remember that when your ready to give up.
Anyway, the m100 discussed earlier in this topic would like this after its all done. I like this much better..........
function m100()
local inst, hSig, rc
inst = mc.mcGetInstance()
hSig, rc = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT1) -- Get handle for output 1
if (rc~= 0) then --Check our errors
mc.mcCntlSetLastError(inst, (mcErrorCheck[rc]))
--wx.wxMessageBox(mcErrorCheck[rc]) --popup a message box with the error
end
rc = mc.mcSignalSetState(hSig, 1) --turn on output 1
if (rc~= 0) then --Check our errors
mc.mcCntlSetLastError(inst, (mcErrorCheck[rc]))
end
rc = mc.mcSignalWait(inst, mc.ISIG_INPUT21, 1, 5) --Wait 5 seconds for input 21 to become active
if (rc~= 0) then mc.mcCntlSetLastError(inst, (mcErrorCheck[rc])) end --In line check our errors works too
end
if (mc.mcInEditor() == 1) then
m100()
end
-
Yes that looks nice and clean.