Hello Guest it is April 18, 2024, 03:08:59 AM

Author Topic: Scripting Custom Control Panel  (Read 1732 times)

0 Members and 1 Guest are viewing this topic.

Scripting Custom Control Panel
« on: February 01, 2021, 11:19:14 AM »
So I have built a custom control panel for my Mach4/EthernetSmoothStepper lathe. It has:

1. Pushbuttons for Cycle Start, Feed Hold, and Manual Spindle (for tailstock drilling etc where the spindle needs to run but no machine motions take place)
2. An E-Stop mushroom
3. A Jog Handle, consisting of an MPG, an X/Z selector Switch, and a 4-position rotary switch for OFF/0.1/0.01/0.001
4. A Program Run LED
5. A Feed Hold LED
6. A Manual Jog Active LED

I'm now at the point where I can wire it into the controller and set up scripting.

Here are the functions if should do:

1. If Cycle Start is pressed, it should react the same as if the on-screen button has been clicked. The on-screen button should light the Program Run LED while a program is running, and turn it off when the program stops.

2. If Feed Hold is pressed when a program is running, it should react the same as if the on-screen button has been clicked. The on-screen button should light the Feed Hold LED and turn off the Program Run while a program is paused, and reverse the LEDs when the program is resumed.

3. If Manual Spindle Start is pressed, the script should check to see if a non-zero value is set in Program RPM. If yes, turn on the spindle. If no, set Program RPM to 1200 and turn on the spindle. If the spindle is already running, turn it off.

4. If EStop is activated, stop the machine.

5. If the rotary Jog Resolution switch is set to 0.1, 0.01, or 0.001, set the MPG resolution to the appropriate value, activate the axis selected by the X/Z selector switch, and turn on the Jog Handle LED. If no resolution is selected, set the MPG resolution to 0, deselect all jog axis, and turn off the Jog LED.

My initial kick at the cat is below. It is missing some functions, identified as "FIXME". Comments and suggestions welcome.

Code: [Select]
--------------------------------------
-- FNR Control Console Code --
--------------------------------------
SigLib = {
    [mc.ISIG_INPUT10] = function (state) -- Cycle Start
        RunFNR()
    end,
    [mc.ISIG_INPUT11] = function (state) -- Feed Hold
        RunFNR()
    end,
    [mc.ISIG_INPUT12] = function (state) -- Manual Spindle Start
        RunFNR()
    end,
    [mc.ISIG_INPUT13] = function (state) -- EStop
        RunFNR()
    end,
    [mc.ISIG_INPUT14] = function (state) -- Jog On, 0.001
        RunFNR()
    end,
    [mc.ISIG_INPUT15] = function (state) -- Jog On, 0.01
        RunFNR()
    end,
    [mc.ISIG_INPUT16] = function (state) -- Jog On, 0.1
        RunFNR()
    end
}
---------------------------------------------------------------
-- FNR Control Console function.
---------------------------------------------------------------
function RunFNR()
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT10) -- Is mapped to Port X Pin X *Cycle Start
    local FNRCycleStart, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT11) -- Is mapped to Port X Pin X *Feed Hold
    local FNRFeedHold, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT12) -- Is mapped to Port X Pin X *Spindle Start
    local FNRSpindleStart, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT13) -- Is mapped to Port X Pin X *EStop
    local FNREStop, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT14) -- Is mapped to Port X Pin X *.001 Select
    local FNRStep001, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT15) -- Is mapped to Port X Pin X *.010 Select
    local FNRStep010, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT16) -- Is mapped to Port X Pin X *.100 Select
    local FNRStep100, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT17) -- Is mapped to Port X Pin X *X Axis Jog Select
    local FNRXJog, rc = mc.mcSignalGetState(hSig)
    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT18) -- Is mapped to Port X Pin X *Z Axis Jog Select
    local FNRZJog, rc = mc.mcSignalGetState(hSig)
    local FNRJogLED, rc = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT10)-- Is mapped to Port X Pin X *Jog Active LED
    local FNRRunLED, rc = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT11)-- Is mapped to Port X Pin X *Program Run LED
    local FNRPauseLED, rc = mc.mcSignalGetHandle(inst, mc.OSIG_OUTPUT12)-- Is mapped to Port X Pin X *Pause LED

    -- Set active axis based on X/Z switch
    if FNRXJog == 1 then
        mc.mcMpgSetAxis(inst, 0, 0) --X Axis
        mc.mcCntlSetLastError(inst, "X Jog Selected")
    elseif FNRZJog == 1 then
        mc.mcMpgSetAxis(inst, 0, 1) --Z Axis
        mc.mcCntlSetLastError(inst, "Z Jog Selected")
    else
-- Can't get here, so there's a fault
        mc.mcMpgSetAxis(inst, 0, -1) --No Axis
        mc.mcCntlSetLastError(inst, "No Jog Axis Selected - IMPOSSIBLE - EStop triggered!")
        mc.mcSignalSetState(FNRJogLED, 0)
        mc.mcCntlEStop(inst)
    end

    -- Set step resolution and activate jog based on rotary selector switch
    if FNRStep001 == 1 then
        mc.mcMpgSetInc(inst, 0, .001)
mc.mcCntlSetLastError(inst, "MPG JOG ON - Thousandths")
        mc.mcSignalSetState(FNRJogLED, 1)
    elseif FNRStep010 == 1 then
        mc.mcMpgSetInc(inst, 0, .010)
        mc.mcCntlSetLastError(inst, "MPG JOG ON - Hundredths")
        mc.mcSignalSetState(FNRJogLED, 1)
    elseif FNRStep100 == 1 then
        mc.mcMpgSetInc(inst, 0, .100)
        mc.mcCntlSetLastError(inst, "MPG JOG ON - Tenths")
        mc.mcSignalSetState(FNRJogLED, 1)
    else -- None of the jog enable switches are closed, so ensure jog is off
        mc.mcMpgSetInc(inst, 0, 0) -- Set jog increment to 0 to shut it off. FIXME: is this valid?
        mc.mcMpgSetAxis(inst, 0, -1) -- Set jog axis to -1 to shut off jogging.
        mc.mcCntlSetLastError(inst, "MPG JOG OFF")
        mc.mcSignalSetState(FNRJogLED, 0)
    end

    -- EStop
    if FNREStop == 1 then
        mc.mcCntlEStop(inst)
    end

   -- Cycle Start
   if FNRCycleStart == 1 then
        mc.mcCntlSetLastError(inst, "Console Cycle Start")
        mc.mcSignalSetState(FNRRunLED, 1)
        -- FIXME: Need command to run "Cycle Start"
        -- FIXME: LED control should go in main cycle start command so it works with screen controls too
   end

   -- Feed Hold
   if FNRCycleStart == 1 then
        mc.mcCntlSetLastError(inst, "Console Feed Hold")
        mc.mcSignalSetState(FNRRunLED, 0)
        mc.mcSignalSetState(FNRPauseLED, 1)
        -- FIXME: Need command to run "Feed Hold"
        -- FIXME: LED control should go in main cycle start / feed hold command so it works with screen controls too
   end

   -- Manual Spindle
   if FNRSpindleStart == 1 then
        -- Check current spindle setting, if 0, set to 1200, otherwise leave alone
        -- Check if spindle running, if no, then start. If yes, then stop
        -- FIXME: need code to do this
   end
end
Re: Scripting Custom Control Panel
« Reply #1 on: February 01, 2021, 12:53:40 PM »
Why don't you send who called the function and at least make it an ifelse set of commands. Option number 2 is to make a function for each operation. One more thing that may be really calling you issues is that you are getting 2 calls for every press. You have one with the state high and one with the state low (On press and one release). You tell me where we should start because I love what your doing! Panels and machines are like peas and carrots!
Fixing problems one post at a time ;)

www.newfangledsolutions.com
www.machsupport.com
Re: Scripting Custom Control Panel
« Reply #2 on: February 01, 2021, 01:43:49 PM »
Well, I dog-robbed this code from someone else on this forum who used it to run his pendant, so I figured it was a safe start point.

> One more thing that may be really calling you issues is that you are getting 2 calls for every press. You have one with the state high and one with the state low (On press and one release).

Hm. OK, that's a problem for any of the momentary pushbuttons (Cycle Start, Feed Hold, Manual Spindle).

So... those need a separate function for each button, called by the signal. If the signal is high, do something. If the signal is low, ignore it.

Something like:

Code: [Select]
--------------------------------------
-- FNR Control Console Code --
--------------------------------------
SigLib = {
    [mc.ISIG_INPUT10] = function (state) -- Cycle Start
        RunFNRCycleStart()
    end,
    [mc.ISIG_INPUT11] = function (state) -- Feed Hold
        RunFNRFeedHold()
    end,
    [mc.ISIG_INPUT12] = function (state) -- Manual Spindle Start
        RunFNRManualSpindle()
    end,
    [mc.ISIG_INPUT13] = function (state) -- EStop
        RunFNR()
    end,
    [mc.ISIG_INPUT14] = function (state) -- Jog On, 0.001
        RunFNR()
    end,
    [mc.ISIG_INPUT15] = function (state) -- Jog On, 0.01
        RunFNR()
    end,
    [mc.ISIG_INPUT16] = function (state) -- Jog On, 0.1
        RunFNR()
    end
}

---------------------------------------------------------------
-- FNR Control Console Cycle Start function.
---------------------------------------------------------------
function RunFNRCycleStart()

    local hSig, rc = mc.mcSignalGetHandle(inst, mc.ISIG_INPUT10) -- Is mapped to Port X Pin X *Cycle Start
    local FNRCycleStart, rc = mc.mcSignalGetState(hSig)

    -- Cycle Start
   if FNRCycleStart == 1 then
        mc.mcCntlSetLastError(inst, "Console Cycle Start")
        mc.mcSignalSetState(FNRRunLED, 1)
        -- FIXME: Need command to run "Cycle Start"
        -- FIXME: LED control should go in main cycle start command so it works with screen controls too
   elsif FNRCyleStart == 0 then
        -- We get this case on button release
        -- IGNORE ME!
   end
 end

Etc.

What are the function calls to "click" on the screen cycle start, feed hold, and spindle start clockwise buttons?
Re: Scripting Custom Control Panel
« Reply #3 on: February 01, 2021, 02:22:22 PM »
your really close!

So it more like this please
Code: [Select]

[mc.ISIG_INPUT1] = function (state)
   if (state == 1) then   -- This is on press (Rising Edge)
        CycleStart()
   else
        mc.mcCntlFeedHold (0) -- This is on release (Falling Edge)
    end

end,

So if you simply look at the state when you call the function you can call it at the correct time. I think  your code will look more like this:

Code: [Select]
    [mc.ISIG_INPUT10] = function (state) -- Cycle Start
        if (state == 1) then   -- This is on press (Rising Edge)
              RunFNRCycleStart()
        end
    end,

Hope that helps !
Brian
Fixing problems one post at a time ;)

www.newfangledsolutions.com
www.machsupport.com
Re: Scripting Custom Control Panel
« Reply #4 on: February 01, 2021, 02:37:06 PM »
Well there's no real reason to call a separate function at that point - RunFNRCycleStart() should just be whatever function is executed by clicking on the "Cycle Start" UI element, and that function should have the LED management code added to it.

What is mc.mcCntlFeedHold (0) ?

Is that the built-in feed hold function?

Is there a document that details all the built-in functions?

Re: Scripting Custom Control Panel
« Reply #5 on: February 01, 2021, 02:51:43 PM »
Hey man you do you ,I am simply showing  you how to get the "state" so you can see on press and on release. The Cycle start and feedhold are out of the stock screen as an example. If you like your way better, you do you! I am only trying to show you how I do it. I don't even know what your "LED management code" is. I don't have anything to prove and if what you have is what you like, I like it.

Feedhold is the stock action at instance zero (see the docs)

Try the Mach4->docs, that is where we keep all the API docs (Mach4CoreAPI.chm)

Hope that helps, if not we will try again
Fixing problems one post at a time ;)

www.newfangledsolutions.com
www.machsupport.com
Re: Scripting Custom Control Panel
« Reply #6 on: February 01, 2021, 03:02:31 PM »
Dude, I'm not throwing rocks at you - I'm away from my Mach computer working through a problem by inference looking at other people's code.
Re: Scripting Custom Control Panel
« Reply #7 on: February 01, 2021, 03:41:56 PM »
I was thinking that you where thinking I was throwing! Sweet that's over, Anyway we will get this done...

I don't get the LED's, Where and why are you making them? We shouldn't need any script to see the state of feedhold and so on. Many ways to solve a problem just some of them will make you work more.

If you want maybe we should make some better code and show everyone how it is done ;) I can tell you I am 4th best at this!
Fixing problems one post at a time ;)

www.newfangledsolutions.com
www.machsupport.com
Re: Scripting Custom Control Panel
« Reply #8 on: February 01, 2021, 04:26:36 PM »
When I make control panels I make them as simple as possible in the Screen Script.  I make it easy on myself, I use the provided code that has been commented out and copy it to each of my inputs that I'm using for button presses. 
The Cycle Start and feedhold I use just like they are in the SigLib, I just change the input# to match what I have it mapped to.
No need to make a new function if the code is already being provided my Artsoft.  From your first post, it seems like the stock functions will do what you want them to do.
You will need to make a function to adjust your MPG handle to switch Axis, but that could be as simple as making a new function and calling the appropriate API calls to set the axis and the increments that you want.  Call those functions on the button press, just like the cylcestart and feedhold are called. 

I also made a keyboard toggle button to toggle the keyboard inputs enable/disable that way I can easily push a button and edit code or mess with CAM without having to use a mouseclick to turn off the keyboard inputs. 

Having an LED... I get the handle of the feedhold state and use that to set an output on or off based on it's state. 
To have an LED on when code is running, use the GCodeRunning output.  That's the easiest way to do that. 
That's how I utilize the stack light I put on machines.  I also run most things through a PLC, but it can all be done in Mach4.
« Last Edit: February 01, 2021, 04:28:52 PM by Cbyrdtopper »
Chad Byrd
Re: Scripting Custom Control Panel
« Reply #9 on: February 02, 2021, 08:46:59 AM »
I don't get the LED's, Where and why are you making them?

The control panel I have built has 3 LEDs:

1. Program Running (GREEN)
2. Feed Hold Engaged (ORANGE)
3. Manual Jog Handle Active (BLUE)

At some point, I want to be able to blink the orange light in response to errors, and I want to blink the green light to prompt a tool change, but that is future expansion stuff at this point.

The intent is that the LEDs change state in response to either manual button presses on the control panel OR to virtual button presses in the Mach UI. That means that for Program Run and Feed Hold I have to flip the LED signal state accordingly in the main script, not in my own code like I can with the manual jog handle code.

So last night I dumped my current screen script to text and I found the API reference and the scripting manual. I'm going through that now.

I have discovered a few interesting things:

1. Some script functions are... puzzling. For example, clicking the "Cycle Start" UI button calls btnCycleStart_Left_Up_Script(), which only calls CycleStart(), which is a script that checks to see if we are running a loaded program or are in MDI mode (fair ball) before running the appropriate state. So why the wrapper function?

2. There doesn't seem to be much in the way of error checking.... I was raised to always capture and check the result code from any function call as a minimum, and have a hook to throw an error if something doesn't work. And if you want to be doubleplus sure that the call worked, if there is a "state checker" function, test the state after the return code to be sure it matches - or throw an error. There is next to none of this in these scripts. Is that by design?

3. There's a lot of stuff commented out, with no notes as to when, why, or by whom; and

4. There's a bunch of functions that appear to be NOOP - like Mach_Timer_Script() - with no comments as to why they exist.

I feel like there's room for cleanup here.