This is a huge help. Thanks for your patience with me on this! I attempted to set up the spindle speed output as a DAC, flashing ch. 7 with the mentioned settings, redirecting the M3, M4, M5, and S commands to the CSS jog versions of the files. I didn't attempt the actual CSS file include because I got a bit confused there. I don't have an encoder on my spindle, so it didn't seem like that made sense for my setup. Long story short, I couldn't get that functioning at all, unfortunately. I did, however, review the C program files I'm currently using to control those commands and was able to hobble together some logic to control the spindle that seems to be working. I basically copied from the S file used for speed control and changed the line where it looks for an S gcode command for speed and just gave it a specific speed instead. Let me know if you think there are some issues with the way I've done it though (shown in UnloadTool and LoadNewTool code sections below).
I gave the file a try, referencing it in KmotionCNC for the M6 command, and it does attempt to do a tool change, but the motion is wrong. For reference, my router has channels 0 and 1 slaved for the Y axis, channel 2 as the X axis, and channel 3 as the Z axis. In the C file, it defines each axis (I don't really understand why since this is handled in the Init file already). I only put channel 0 for Y, not knowing if/how I could specify two axes that are slaved.
At first, I had the T1 tool bay position permanently referenced in the LoadNewTool and UnloadTool sections for testing. When given an M6 command the router would move Z up to machine coordinate 0 (as requested), then move to the given X position, but wouldn't move at all in the Y direction. Unfortunately, it would also slowly move the Z axis up any time it moved in X. This would push Z up and trigger the limit switch. If it was already close enough to the x position for the bay, it wouldn't have time to trigger the Z limit switch and could continue with the code. The rest of the Z movements seemed accurate and the spindle turned on/off in the appropriate direction/rpms when called to. I can't figure out how my code could cause the random upward Z movement during the X movement though, nor why the Y axis movement didn't occur at all.
Question 1: Do you know what could cause the Z axis to drift up during X moves?
Question 2: Do you think I need to somehow indicate in the #define AXISY section that it's a channel 0 & 1 slaved axis setup for Y instead of "#define AXISY 0" in order to get the Y axis working during M6 commands?
Next, I tried switching out the T1 tool bay position for the generic tool bay code originally in the file. This code, I assumed, would reference whatever tool was called for with the M6 command (for example; T2 M6). It seems to now be partially working for X movements, but no movement at all for Y, and the Z axis still creeps upward during X moves. I say partially for X, because movements to HOLDER_X positions 1 through 4 are accurate, however, after that point, all X movements are off, but go the same incorrect positions each time. For example, when I try slot 7, it goes to 15.2333 every time, but the value I have for HOLDER_X_7 is 16.5893. This continues for slots 5 through 16, with some being off as far as almost 10 inches (Slot 9 goes to 19.7333, but HOLDER_X_9 is set to 28.2833).
UPDATE: There was a small bit of code underneath the Eject Tool section that I had commented out (shown below question 3). Before enabling this code, the ToolPositionX commands did not work and the file could not compile and run (received an error in KmotionCNC when an M6 command was given). Enabling this code allowed the ToolPositionX lines to stop throwing errors and got the M6 commands working. However, looking closer at the code, it's designed to calculate the spacing between tool slots and seems to be overriding my HOLDER_X values after bay 4. I believe this is due to the uneven spacing of my tool changer design. The first 4 bays have even spacing, then the gap is larger between bays 4 and 5, 8 and 9, as well as 12 and 13. This is because I have 4 cassettes, each with 4 bays. I arranged them symmetrically around the tool setter, so this creates gaps between the cassettes that aren't consistent with the bay spacing within each cassette (The pictures in my first post are worth 1000 words here).
Question 3: Do you know how I could alter the code so that the ToolPositionX command just references the HOLDER_X_% value for the slot needed directly instead of trying to calculate for a consistent slot spacing?
Code: Select all
//return x position of tool holder as a function of the tool
float ToolPositionX(int tool)
{
return (HOLDER_X_2-HOLDER_X_1)*(tool-1) + HOLDER_X_1;
}
Lines 36-38 (below) reference TOOL_CHANGE_SAFE_POS_X and Y absolute positions that are "safe to move down in Z." I think this could be another section related to moving the spindle a bit in front of the tool table in Y that I don't need for my set up. It would make more sense for me to simply have a safe Z height, where X and Y travel cannot occur before it reaches that height.
Question 4: Can you see any issue with just commenting out or deleting this code below concerning tool change safe positions for XY?
Code: Select all
// absolute position to move to that is permanently unobstructed, and safe to move down in Z
#define TOOL_CHANGE_SAFE_POS_X 23.5613
#define TOOL_CHANGE_SAFE_POS_Y 30.1336
One other issue I noticed is an error for "invalid tool number" that happens when I request a tool number higher than T4. I have 16 bays on my tool changer, so I added a bunch in the code below.
UPDATE: I think I figured out the tool number issue. Under the BOOL ToolNumberValid(int tool) section in the code, I noticed it specified 4 or less tools as valid, so I'd assume that was what caused the problem. I adjusted that to 16. I also configured the Tool section in KmotionCNC to have 16 Slots setup, and this seems to allow me to pick any slot needed, though slots 5-16 do not go to the correct X location so far.
Thanks again for all of the help so far!
Here's my entire file so far:
Code: Select all
#include "KMotionDef.h"
#define TMP 10 // which spare persist to use to transfer data
#include "C:\Users\NUA_TIM\Desktop\KMotion5.4.0\C Programs\KflopToKMotionCNCFunctions.c"
#include "C:\Users\NUA_TIM\Desktop\Machine Programs\CorrectAnalogFunction.c"
#define RPM_FACTOR 22200.0 // RPM for full duty cycle (max analog out)
//-----------------------------------------
// LINEAR TOOL CHANGING
//-----------------------------------------
#define AXISX 2
#define AXISY 0
#define AXISZ 3
//---------Absolute position of tool holders
#define HOLDER_X_1 1.7333
#define HOLDER_X_2 3.9833
#define HOLDER_X_3 6.2333
#define HOLDER_X_4 8.4833
#define HOLDER_X_5 12.0893
#define HOLDER_X_6 14.3393
#define HOLDER_X_7 16.5893
#define HOLDER_X_8 18.8393
#define HOLDER_X_9 28.2833
#define HOLDER_X_10 30.5333
#define HOLDER_X_11 32.7833
#define HOLDER_X_12 35.0333
#define HOLDER_X_13 38.6393
#define HOLDER_X_14 40.8893
#define HOLDER_X_15 43.1393
#define HOLDER_X_16 45.3893
#define HOLDER_Y 30.1336
#define HOLDER_Z -1
// absolute position of the tool height setting plate
#define TOOL_HEIGHT_PLATE_X 23.5613
#define TOOL_HEIGHT_PLATE_Y 30.1336
// absolute position to move to that is permanently unobstructed, and safe to move down in Z
//#define TOOL_CHANGE_SAFE_POS_X 23.5613
//#define TOOL_CHANGE_SAFE_POS_Y 30.1336
#define TOOL_VAR 9 // Tool changer desired new tool Var
// Tool changer Last tool loaded is saved globally in this Var
#define LAST_TOOL_VAR 8 // -1=Spindle empty, 0=unknown, 1-4 Tool Slot loaded into Spindle
#define TOOL_DISK_FILE "c:\\Temp\\ToolChangerData.txt"
//#define CLAMP_TIME 1.0 // seconds to wait for the clamp/unclamp
#define TOOL_HEIGHT_BIT 29 //bit to read tool height plate (KONNECT INPUT 31)
#define SAFE_HEIGHT_Z -1 // relative distance in inch to move to clear the top of the tool taper
#define TOOL_RETRACT_SPEED_Z 5 //speed in inch/second to move spindle up after tool has been ejected
#define SlowSpeed 0.4 //inch/sec
#define CNT_PER_INCH_X 3175
#define CNT_PER_INCH_Y 3180.3
#define CNT_PER_INCH_Z 10000
// function prototypes
int DoToolChange(int ToolSlot);
int GetCurrentTool(int *tool);
int SaveCurrentTool(int tool);
BOOL ToolNumberValid(int tool);
float ToolPositionX(int tool);
int MoveXY(float x, float y, float Speed);
int MoveZ(float z, float Speed);
int UnloadTool(int CurrentTool);
int LoadNewTool(int Tool);
int EjectTool(void);
main()
{
int ToolSlot = persist.UserData[TOOL_VAR]; // Requested tool to load (value stored an integer)
if (DoToolChange(ToolSlot)) // perform Tool Change
{
// error, Halt Job
DoPC(PC_COMM_HALT);
}
}
// Perform Tool Change. Return 0=Success, 1=Failure
int DoToolChange(int ToolSlot)
{
int CurrentTool;
if (GetCurrentTool(&CurrentTool)) return 1; // -1=Spindle empty, 0=unknown, 1-4 Tool Slot loaded into Spindle
printf("Load Tool Slot %d requested, Current Tool %d\n",ToolSlot, CurrentTool);
if (!ToolNumberValid(ToolSlot)) // check if invalid
{
char s[80];
sprintf(s,"Invalid Tool Change Number %d\n",ToolSlot);
printf(s);
MsgBox(s, MB_ICONHAND | MB_OK);
return 1;
}
if (CurrentTool!=-1) // is there a tool in the Spindle??
if (UnloadTool(CurrentTool)) return 1; // yes, unload it
// Now Spindle is empty, load requested tool
if (LoadNewTool(ToolSlot)) return 1;
SaveCurrentTool(ToolSlot); // save the one that has been loaded
return 0; // success
}
// - Load new Tool (Spindle must be empty)
int LoadNewTool(int Tool)
{
//I picked a random very low Z point for testing (-5) to give some room for that upward Z drift issue to occur but not trip the limit switch so I
//could test the other actions. I also lowered the speeds to make it safer while troubleshooting.
MoveZ(-5, 2); // Move Z at 5 inch per second (300 in/minute)
MoveXY(ToolPositionX(Tool),HOLDER_Y, 3); // Move XY at 10 inch per second (600 in/minute)
MoveZ(-3, 2); // Move Z to tool bay at 5 in per second (300 in/minute) - Tool lowers into bay 1 inch above engagement with socket bearings
float speed = 1500.0; // Hardcoded speed value in RPM
SetBitDirection(26,1); // define bit as an output
FPGA(IO_PWMS_PRESCALE) = 46; // divide clock by 46 (1.4 KHz)
FPGA(IO_PWMS+1) = 1; // Enable
FPGA(IO_PWMS) = CorrectAnalog(speed/RPM_FACTOR); // Set PWM
ClearBit(0);
MoveZ(-4, 1); // Move Z at 1 inch per second (60 in/min) to engage collet nut
MoveZ(-3, 3); // Move Z at 3 inch per second (180 in/min) upward by an inch to disengage collet nut from socket
MoveZ(-4, 1); // Move Z at 1 inch per second (60 in/min) to engage/tighten collet nut
MoveZ(-3, 3); // Move Z up 1 inch to disengage collet nut from socket
SetBit(0);
MoveZ(0, 2); // Move Z at 5 inch per second (300 In/min) to machine coordinates Z zero (fully up and clear of tool bays)
// - Move to position of requested tool
// - Rapid move to absolute position of new tool only in X and Y
//if (MoveXY(ToolPositionX(Tool),HOLDER_Y,SlowSpeed)) return 1;
// - Move to tool Z position at TOOL_RETRACT_SPEED_Z
//if (MoveZ(HOLDER_Z,SlowSpeed)) return 1;
// - Engage new tool
// - CLAW_EJECT and SPINDLE_CLEAN bits are currently high from tool removal operation
// - Turn off CLAW_EJECT and SPINDLE_CLEAN bits to engage tool
//ClearBit(CLAW_EJECT);
//ClearBit(SPINDLE_CLEAN);
// - Wait for time in seconds defined by CLAMP_TIME
//Delay_sec(CLAMP_TIME);
// - Check to see if CLAW_LOOSE and TOOL_SENSE are high; if either are not,
// something has gone wrong; halt everything and display message indicating failure
// - Tool has been engaged
//if (!ReadBit(CLAW_LOOSE))
//{
// printf("Claw Still Loose Error\n");
// MsgBox("Claw Still Loose Error\n", MB_ICONHAND | MB_OK);
// return 1;
//}
//if (!ReadBit(TOOL_SENSE))
//{
// printf("Tool Sense Error\n");
// MsgBox("Tool Sense Error\n", MB_ICONHAND | MB_OK);
// return 1;
//}
// - Leave tool holder by moving Y axis by the negative value of Y_AXIS_SAFE_DISTANCE
// - Move to position of requested tool
// - Rapid move to absolute position of new tool only in X and Y
//if (MoveXY(ToolPositionX(Tool),HOLDER_Y+AXIS_SAFE_DISTANCE_Y,SlowSpeed)) return 1;
// - Rapid to Z home
//if (MoveZ(0.0,SlowSpeed)) return 1;
//return 0; //success
}
// - Remove tool in spindle by going to holder of current tool
int UnloadTool(int CurrentTool)
{
MoveZ(-5, 2); // Move Z at 5 inch per second (300 in/minute)
MoveXY(ToolPositionX(CurrentTool),HOLDER_Y,3); // Move XY at 10 inch per second (600 in/minute)
MoveZ(-3, 2); // Move Z to tool bay at 5 in per second (300 in/minute) - Tool lowers into bay 1 inch above engagement with socket bearings
float speed = 1600.0; // Hardcoded speed value in RPM
SetBitDirection(26,1); // define bit as an output
FPGA(IO_PWMS_PRESCALE) = 46; // divide clock by 46 (1.4 KHz)
FPGA(IO_PWMS+1) = 1; // Enable
FPGA(IO_PWMS) = CorrectAnalog(speed/RPM_FACTOR); // Set PWM
SetBit(45);
MoveZ(-4, 1); // Move Z at 1 inch per second (60 in/min) to engage collet nut
MoveZ(-3, 1); // Move Z at 1 inch per second (180 in/min) upward by an inch to disengage collet nut from socket
ClearBit(45);
MoveZ(0, 2); // Move Z at 5 inch per second (300 In/min) to machine coordinates Z zero (fully up and clear of tool bays)
// - Rapid to Z Home to clear any work that may be on the table
//if (MoveZ(0.0,SlowSpeed)) return 1;
// - Rapid to TOOL_CHANGE_SAFE_POS to execute a safe negative Z move
//if (MoveXY(TOOL_CHANGE_SAFE_POS_X,TOOL_CHANGE_SAFE_POS_Y,SlowSpeed)) return 1;
// - Approach tool holder by matching Z height of tool flange currently in spindle with tool holder claw
//if (MoveZ(HOLDER_Z,SlowSpeed)) return 1;
// - After matching height above, approach tool holder by moving to holder X position
//if (MoveXY(ToolPositionX(CurrentTool),TOOL_CHANGE_SAFE_POS_Y,SlowSpeed)) return 1;
// - After matching X position, match tool Y position
//if (MoveXY(ToolPositionX(CurrentTool),HOLDER_Y,SlowSpeed)) return 1;
// - Move only in Y position until current position matches tool holder position (maybe disable X) axis?)
// ???
// - Eject tool
//if (EjectTool()) return 1;
//return 0; //success
}
// - Eject tool
//int EjectTool(void)
//{
// - Turn on CLAW_EJECT bit to remove tool from spindle
//SetBit(CLAW_EJECT);
// - Turn on SPINDLE_CLEAN bit to remove any debris from taper and tools
//SetBit(SPINDLE_CLEAN);
// - Wait for time in seconds defined by CLAMP_TIME
//Delay_sec(CLAMP_TIME);
// - Read CLAW_LOOSE bit to see whether the tool is loose, to make a safe Z move without
// destroying tool holder
// - If CLAW_LOOSE bit is high, something has gone wrong;
// halt everything and display message indicating failure
//if (ReadBit(CLAW_LOOSE))
//{
// printf("Claw Loose Error\n");
// MsgBox("Claw Loose Error\n", MB_ICONHAND | MB_OK);
// return 1;
//}
// - Move Z axis up at speed defined by 'Z_TOOL_RETRACT_SPEED', to Z_SAFE_HEIGHT
//if (MoveZ(HOLDER_Z+SAFE_HEIGHT_Z,TOOL_RETRACT_SPEED_Z)) return 1;
// - Read TOOL_SENSE bit to see whether the tool has been successfully ejected from the spindle
// - If TOOL_SENSE bit is high, something has gone wrong;
// halt everything and display message indicating failure
//if (ReadBit(TOOL_SENSE))
//{
// printf("Tool Sense Release Error\n");
// MsgBox("Tool Sense Release Error\n", MB_ICONHAND | MB_OK);
// return 1;
//}
//return 0; // success
//}
//return x position of tool holder as a function of the tool
float ToolPositionX(int tool)
{
return (HOLDER_X_2-HOLDER_X_1)*(tool-1) + HOLDER_X_1;
}
// Get the last loaded tool. Parameter points to where to return tool
// First try to get from KFLOP memory
// if memory is invalid, try to read from disk
// if can't read disk then ask Operator
// returns 0 on success, 1 on fail or Operator asked to abort
int GetCurrentTool(int *ptool)
{
int success,Answer,result,tool;
float value;
tool = persist.UserData[LAST_TOOL_VAR];
success = ToolNumberValid(tool); // check if valid
if (!success) // invalid after power up, try to read from PC Disk File
{
// Try to open file
FILE *f=fopen(TOOL_DISK_FILE,"rt");
if (f) // did file open?
{
// read a line and convert it
result=fscanf(f,"%d",&tool);
fclose(f);
if (result==1 && ToolNumberValid(tool))
{
printf("Read Disk File Value of %d\n",tool);
success=TRUE; // success if one value converted
}
}
if (!success) printf("Unable to open/read file:%s\n",TOOL_DISK_FILE);
}
if (!success) // if still no success ask Operator
{
Answer = InputBox("Tool in Spindle or -1",&value);
if (Answer)
{
printf("Operator Canceled\n");
return 1;
}
else
{
tool=value;
printf("Operator Entered Value of %d\n",tool);
}
}
if (!ToolNumberValid(tool)) // check if invalid
{
char s[80];
sprintf(s,"Invalid Current Tool Number %d\n",tool);
printf(s);
MsgBox(s, MB_ICONHAND | MB_OK);
return 1;
}
printf("Current tool = %d\n",tool);
*ptool = tool; // return result to caller
return 0; //success
}
// save the tool number to KFLOP global Variable and to PC Disk file in case we loose power
int SaveCurrentTool(int tool)
{
persist.UserData[LAST_TOOL_VAR]=tool;
FILE *f=fopen(TOOL_DISK_FILE,"wt");
fprintf(f,"%d\n",tool);
fclose(f);
return 0;
}
// check if Current Tool number Valid
// -1 = no tool loaded
// 1-16 = valid tool
BOOL ToolNumberValid(int tool)
{
return tool == -1 || (tool>=1 && tool<=16);
}
// Move Axis XY at specified Speed and wait until complete
// return 0 = success, 1 if axis disabled
int MoveXY(float x, float y, float Speed)
{
MoveAtVel(AXISX, x * CNT_PER_INCH_X, Speed * CNT_PER_INCH_X);
MoveAtVel(AXISZ, y * CNT_PER_INCH_Y, Speed * CNT_PER_INCH_Y);
while (!CheckDone(AXISX) || !CheckDone(AXISY))
{
if (!chan[AXISX].Enable)
{
printf("Error X Axis Disabled\n");
MsgBox("Error X Axis Disabled\n", MB_ICONHAND | MB_OK);
return 1;
}
if (!chan[AXISY].Enable)
{
printf("Error Y Axis Disabled\n");
MsgBox("Error Y Axis Disabled\n", MB_ICONHAND | MB_OK);
return 1;
}
}
return 0; //success
}
// Move Axis Z at specified Speed and wait until complete
// return 0 = success, 1 if axis disabled
int MoveZ(float z, float Speed)
{
MoveAtVel(AXISZ, z * CNT_PER_INCH_Z, Speed * CNT_PER_INCH_Z);
while (!CheckDone(AXISZ))
{
if (!chan[AXISZ].Enable)
{
printf("Error Z Axis Disabled\n");
MsgBox("Error Z Axis Disabled\n", MB_ICONHAND | MB_OK);
return 1;
}
}
return 0; //success
}