3 Attachment(s)
Re: Journeyman 325 retrofit
After a few hundred tries tweaking and re-tweaking; This is what I have for the axes...
[ATTACH=CONFIG]342228[/ATTACH] [ATTACH=CONFIG]342230[/ATTACH] [ATTACH=CONFIG]342232[/ATTACH]
I wasn't able to get the Y or Z quite as accurate as the X.
I assume this is because of the extra mass / inertia on the Y axis and the asymmetrical force of gravity on the Z axis.
It's still a total error of half a mil, I think it will be fine.
Re: Journeyman 325 retrofit
Hi Rick,
Looks very good to me. Thanks for posting back.
Of course where the encoder says the table is and where it actually is are two different things.
Regards
Re: Journeyman 325 retrofit
Another question regarding the spindle.
I have a 5hp spindle motor with a Yakasawa drive.
The drive uses +-10vdc to turn the spindle forward and reverse, It also ouputs a spindle_0_speed and spindle_at_speed bit to kanalog.
The motor has feedback to the drive, but not to the Kflop / Kanalog, so I was going to run it open loop.
I have a handheld tachometer, and have manually set DAC4 in the console to test the spindle operation and measure the actual RPM.
This all works fine.
I have configured channel 4 to no input and channel 4 DAC.
I need to set the parameters for the spindle using the step response screen, but this will spin it one direction and immediately the other direction.
Is there a way to have it test only one direction?
I understand I need to use the FF setting to drive the motor, but how should this be calculated?
Also, I would like to have a program intercept spindle commands, so that a calculation can be made to scale the speed setting to the DAC value.
Assuming it's linear, DAC values of 0 to 2047 = speeds of 0 to 6250 RPM. a multiplier of 3.05.
Thanks,
Rick
Re: Journeyman 325 retrofit
Hi Rick,
For simple open loop DAC control you should be able to use the basic configuration described here:
[url=http://dynomotion.com/Help/KMotionCNC/SpindleControl.htm#Basic_Configuration]KMotionCNC Spindle Control[/url]
But actually that won't allow negative DAC Voltage so if that is needed you should follow the Advanced Configuration using C Programs controlling an Axis. See:
[url=http://dynomotion.com/Help/KMotionCNC/SpindleControl.htm#User_Program_Configuration]KMotionCNC Spindle Control[/url].
HTH
Regards
Re: Journeyman 325 retrofit
Tom,
I think I have this configured correctly, but I wanted to run it past you first.
The original Dynapath Delta 20 controller did not take encoder feedback from the spindle.
I discovered that the Yakasawa spindle drive has an encoder output, so I have wired this to Channel 4 / Input Channel 4 on the Kanalog encoder input.
This is the spindle channel on my setup.
Channel 4 is configured open loop as the instructions direct, with the parameters from them on your last post.
The spindle drive also has output bits which indicate spindle at 0 speed and spindle at commanded speed.
I have inserted while loops to monitor these bits in the attached code.
Please let me know if you think this is necessary or redundant.
Aside from these modifications, the files are unchanged.
Also, I could not find these files in the KMotion CSS folder. I downloaded them from the CNCZone forum on another thread.
Thanks,
Rick
*** MySpindleDefs.c
[CODE]#define SPINDLEAXIS 4 // Axis Channel to Jog to rotate Spindle
#define FACTOR (1/6090) // to convert RPM to counts/sec (counts/rev / 60.0sec)
#define SPINDLECW_BIT // bit to activate to cause CW rotation
#define SPINDLECCW_BIT // bit to activate to cause CCW rotation
#define SPEEDVAR 99 // global persistant variable to store latest speed
#define STATEVAR 98 // global persistant variable to store latest state (-1=CCW,0=off,1=CW)
#define KMVAR PC_COMM_CSS_S 113 // variable KMotionCNC will pass speed parameter (113)
#define USE_POS_NEG_VOLTAGE 1 // 0 = output Magnitude, 1 = output positive and negative speed
#define J325_SPINDLE_CHANGING_SPEED 140 // Bit goes low when spindle is at command speed, 1 when changing
#define J325_SPINDLE_0_SPEED 139 // Bit goes high when spindle is at 0 speed[/CODE]
*** Offjog.c
[CODE]#include "KMotionDef.h"
#include "MySpindleDefs.h"
// desired speed is passed from KMotionCNC in variable KMVAR
// save in user variable STATEVAR whether it was off, CW, or CCW (0,1,-1)
// save in user variable SPEEDVAR the last desired speed
main()
{
// spin down
//ClearBit(SPINDLECW_BIT);
//ClearBit(SPINDLECCW_BIT);
Jog(SPINDLEAXIS,0);
// Wait for spindle to stop
while (!J325_SPINDLE_0_SPEED);
printf("Jogging Spindle Stop\n");
persist.UserData[STATEVAR] = 0; // remember we are Off
while (!CheckDone(SPINDLEAXIS)) ;
}[/CODE]
*** CSSjog.c
[CODE]
// Handle CSS (Constant Surface Speed) messages from KMotionCNC)
//
// This code assumes you have an Axis Channel Configured to control
// Spindle speed and Jog Calls can be made to control speed
//
// Include this function into your main Init Thead code and call it
// continuously from a forever loop similar to that shown here
//#include "KMotionDef.h"
//#include "MySpindleDefs.h"
//#include "CSSJog.c"
//main()
//{
// for (;;)
// {
// WaitNextTimeSlice();
// ServiceCSS();
// }
//}
int *css_mode = &persist.UserData[PC_COMM_CSS_MODE]; // Mode 1=Normal RPM mode. 2=CSS
float *css_xoff = &persist.UserData[PC_COMM_CSS_X_OFFSET]; // X axis counts for Radius zero
float *css_xfactor = &persist.UserData[PC_COMM_CSS_X_FACTOR]; // X axis factor to convert counts to inches
float *css_s = &persist.UserData[PC_COMM_CSS_S]; // S speed setting in inches/sec
float *css_max_rpm = &persist.UserData[PC_COMM_CSS_MAX_RPM]; // Limit max RPM to this value as Radius approaches zero
double css_T=0; // update only every so often
#define CSS_UPDATE_DT 0.05
void ServiceCSS(void)
{
float rpm;
double T=Time_sec();
if (*css_mode == 2 && T > css_T) // check if we are in CSS mode and it is time to update
{
css_T=T+CSS_UPDATE_DT; // determine next time to update
// convert axis position to distance from center in inches
float radius = fast_fabs((chan[CS0_axis_x].Dest - *css_xoff) * *css_xfactor);
if (radius > 0.0f)
rpm = *css_s / (radius * (TWO_PI_F/60.0f));
else
rpm = *css_max_rpm;
if (rpm > *css_max_rpm) rpm = *css_max_rpm;
if (persist.UserData[STATEVAR]!=0) // if spindle is already on, ramp to new speed
{
if (USE_POS_NEG_VOLTAGE)
{
Jog(SPINDLEAXIS,rpm * FACTOR * persist.UserData[STATEVAR]);
// Wait for spindle to reach commanded speed
while (J325_SPINDLE_CHANGING_SPEED);
}
else
Jog(SPINDLEAXIS,rpm * FACTOR);
}
// printf("xoff=%f radius= %f xfactor=%f s=%f(ips) maxrpm=%f rpm=%f\n",*css_xoff,radius,*css_xfactor,*css_s,*css_max_rpm,rpm);
}
}[/CODE]
*** Spindlejog.c
[CODE]
#include "KMotionDef.h"
#include "MySpindleDefs.h"
int *css_mode = &persist.UserData[PC_COMM_CSS_MODE]; // Mode 1=Normal RPM mode. 2=CSS
// desired speed is passed from KMotionCNC in variable KMVAR
// save in user variable STATEVAR whether it was off, CW, or CCW (0,1,-1)
// save in user variable SPEEDVAR the last desired speed
main()
{
float speed = *(float *)&persist.UserData[KMVAR]; // value stored is actually a float
float LastState = persist.UserData[STATEVAR]; // get last state
persist.UserData[SPEEDVAR] = persist.UserData[KMVAR]; // Always save the last desired speed
if (LastState==0 || *css_mode == 2)
{
// if spindle is off (or CSS mode) and User Changes the speed
// just save the desired speed
return 0;
}
// spindle is already on, so ramp to new speed
if (USE_POS_NEG_VOLTAGE)
{
Jog(SPINDLEAXIS,speed * FACTOR * LastState);
// Wait for spindle to reach commanded speed
while (J325_SPINDLE_CHANGING_SPEED);
}
else
Jog(SPINDLEAXIS,speed * FACTOR);
printf("Jogging Spindle %f counts/sec\n",speed * FACTOR);
}[/CODE]
*** OnCW.c
[CODE]#include "KMotionDef.h"
#include "MySpindleDefs.h"
int *css_mode = &persist.UserData[PC_COMM_CSS_MODE]; // Mode 1=Normal RPM mode. 2=CSS
// desired speed is passed from KMotionCNC in variable KMVAR
// save in user variable STATEVAR whether it was off, CW, or CCW (0,1,-1)
// save in user variable SPEEDVAR the last desired speed
main()
{
float speed = *(float *)&persist.UserData[SPEEDVAR]; // value stored is actually a float
float LastState = persist.UserData[STATEVAR]; // get last state
if (LastState==-1)
{
// if spindle was CCW now we want CW
// spin down
//ClearBit(SPINDLECW_BIT);
//ClearBit(SPINDLECCW_BIT);
Jog(SPINDLEAXIS,0);
while (!CheckDone(SPINDLEAXIS)) ;
// Wait for spindle to stop
while (!J325_SPINDLE_0_SPEED);
}
// turn spindle on CW and ramp to new speed
SetBit(SPINDLECW_BIT);
if (*css_mode != 2)
{
// spindle is already on, so ramp to new speed
if (USE_POS_NEG_VOLTAGE)
{
Jog(SPINDLEAXIS,speed * FACTOR * LastState);
// Wait for spindle to reach commanded speed
while (J325_SPINDLE_CHANGING_SPEED);
}
else
Jog(SPINDLEAXIS,speed * FACTOR);
printf("Jogging Spindle %f counts/sec\n",speed * FACTOR);
}
persist.UserData[STATEVAR] = 1; // remember we are CW
}
[/CODE]
*** OnCCW.c
[CODE]
#include "KMotionDef.h"
#include "MySpindleDefs.h"
int *css_mode = &persist.UserData[PC_COMM_CSS_MODE]; // Mode 1=Normal RPM mode. 2=CSS
// desired speed is passed from KMotionCNC in variable KMVAR
// save in user variable STATEVAR whether it was off, CW, or CCW (0,1,-1)
// save in user variable SPEEDVAR the last desired speed
main()
{
float speed = *(float *)&persist.UserData[SPEEDVAR]; // value stored is actually a float
float LastState = persist.UserData[STATEVAR]; // get last state
if (LastState==1)
{
// if spindle was CW now we want CCW
// spin down
//ClearBit(SPINDLECW_BIT);
//ClearBit(SPINDLECCW_BIT);
Jog(SPINDLEAXIS,0);
while (!CheckDone(SPINDLEAXIS)) ;
// Wait for spindle to stop
while (!J325_SPINDLE_0_SPEED);
}
// turn spindle on CCW and ramp to new speed
SetBit(SPINDLECCW_BIT);
if (*css_mode != 2)
{
// spindle is already on, so ramp to new speed
if (USE_POS_NEG_VOLTAGE)
{
Jog(SPINDLEAXIS,speed * FACTOR * LastState);
// Wait for spindle to reach commanded speed
while (J325_SPINDLE_CHANGING_SPEED);
}
else
Jog(SPINDLEAXIS,speed * FACTOR);
printf("Jogging Spindle %f counts/sec\n",speed * FACTOR);
}
persist.UserData[STATEVAR] = -1; // remember we are CCW
}
[/CODE]
Re: Journeyman 325 retrofit
The CSS sample files are in the SpindleUsingJogs directory in C Programs, however why are you using CSS on mill?
If you're using the KFlop to close the loop, then the spindle at speed and stopped inputs could be handled via some C code, but what you've done looks simpler.
Re: Journeyman 325 retrofit
Tom,
The files I found in the C Programs/SpindleUsingJogs directory don't have the USE_POS_NEG_VOLTAGE parameter.
These were the only files I found with that parameter in them.
I can do some editing on those files and add it in.
Also, when I put these files into the configuration screen for KMotionCNC, does it download these files at startup, or do I also need to have them saved to the Kflop?
Thanks,
Rick
Re: Journeyman 325 retrofit
I'm not Tom ;-)
The example files supplied with KMotion, are the more common options. Generally if you're running a closed loop spindle, it'll be some form of servo that uses +/-10V, so you just need to rely on setting a Jog command along with channel enable/disable, and the direction takes care of itself, which is what the included files are aimed at.
If, like me, you're running a VFD controlled spindle, which wouldn't usually have any encoder feedback, you have to modify the examples to suit.
Just a little note, MySpindleDefs.c should be MySpindleDefs.h (I suspect it's a typo in your post, but just highlighting it in case anybody else sees it and wonders why they're getting an error).
Regarding what goes where. If you want to add the CSS code, you need to copy the ServiceCSS() call to your init.c main loop, so it get's continually called.
Off/OnCW/CWW should be set to run in their own thread, and configured via the relevant M3/4/5 actions, with the SpindleJog set as an action for S.
All the On/Off/Jog functions can normally share the same thread, however I'm not sure if that will still work, given the while loops. Normally the On threads would simply activate the required output and quit, at which point the Jog function would run to set the speed, however I'm not sure what will happen if the Jog file gets downloaded when the On file is still stuck in a while loop.
Re: Journeyman 325 retrofit
OK, I think I understand the code a little better after spending a few hours studying it.
I have re-written the M3, M4, M5, and S programs to eliminate the CSS code and add code for the +/- voltage direction output
I notice that in all of the example code files, the while (!CheckDone(SPINDLEAXIS)); command is issued when spinning down the spindle, but not for getting it up to speed. Is the spinup time usually handled by dwell in the G code, or should we delay until the spindle is up to speed?
*** SpindleOff.c
[CODE]#include "KMotionDef.h"
// Spindle Off function with +/- voltage used for direction
#define SPINDLEAXIS 4 // Axis Channel to Jog to rotate Spindle
#define FACTOR (1/6090) // to convert RPM to counts/sec (counts/rev / 60.0sec)
#define SPEEDVAR 99 // global persistant variable to store latest speed
#define STATEVAR 98 // global persistant variable to store latest state (-1=CCW,0=off,1=CW)
#define KMVAR 1 // variable KMotionCNC will pass speed parameter (113)
#define J325_SPINDLE_CHANGING_SPEED 140 // Bit goes low when spindle is at command speed, 1 when changing
#define J325_SPINDLE_0_SPEED 139 // Bit goes high when spindle is at 0 speed
// desired speed is passed from KMotionCNC in variable KMVAR
// save in user variable STATEVAR whether it was off, CW, or CCW (0,1,-1)
// save in user variable SPEEDVAR the last desired speed
main()
{
float speed = *(float *)&persist.UserData[KMVAR]; // value stored is actually a float
float LastSpeed = *(float *)&persist.UserData[SPEEDVAR];// get last speed setting
float LastState = persist.UserData[STATEVAR]; // get last state
{
// if spindle was CW or CCW
// spin down
Jog(SPINDLEAXIS, 0);
while (!CheckDone(SPINDLEAXIS));
// Wait for spindle to stop
while (!J325_SPINDLE_0_SPEED);
// Set LastSpeed to new speed
LastSpeed = 0.0;
// Save the new speed
*(float *)&persist.UserData[SPEEDVAR] = LastSpeed;
// Save new state
persist.UserData[STATEVAR] = 0;
// Print new speed
printf("Spindle OFF\n");
}
}[/CODE]
*** SpindleSpeedJog.c
[CODE]#include "KMotionDef.h"
// Spindle Jog function with +/- voltage used for direction
#define SPINDLEAXIS 4 // Axis Channel to Jog to rotate Spindle
#define FACTOR (1/6090) // to convert RPM to counts/sec (counts/rev / 60.0sec)
#define SPEEDVAR 99 // global persistant variable to store latest speed
#define STATEVAR 98 // global persistant variable to store latest state (-1=CCW,0=off,1=CW)
#define KMVAR 113 // variable KMotionCNC will pass speed parameter (113)
#define J325_SPINDLE_CHANGING_SPEED 140 // Bit goes low when spindle is at command speed, 1 when changing
#define J325_SPINDLE_0_SPEED 139 // Bit goes high when spindle is at 0 speed
// This function is called from the S command
// desired speed is passed from KMotionCNC in variable KMVAR
// save in user variable STATEVAR whether it was off, CW, or CCW (0,1,-1)
// save in user variable SPEEDVAR the last desired speed
main()
{
float speed = *(float *)&persist.UserData[KMVAR]; // value stored is actually a float
float LastSpeed = *(float *)&persist.UserData[SPEEDVAR];// get last speed setting
float LastState = persist.UserData[STATEVAR]; // get last state
// Set last desired speed to new desired speed
persist.UserData[SPEEDVAR] = persist.UserData[KMVAR];
if (LastState==0)
{
// spindle is off and User Changes the speed
// just save the desired speed
return 0;
}
// spindle is already on, so ramp to new speed
// Set new speed. Direction (LastState) should be the same
Jog(SPINDLEAXIS, speed * FACTOR * LastState);
// Wait for spindle to reach commanded speed
while (J325_SPINDLE_CHANGING_SPEED);
// Save new speed
*(float *)&persist.UserData[SPEEDVAR] = speed;
// Print new speed
printf("Jogging Spindle %f counts/sec\n",speed * FACTOR * LastState);
}[/CODE]
*** SpindleCWJog.c
[CODE]
#include "KMotionDef.h"
// Spindle CW Jog function with +/- voltage used for direction
#define SPINDLEAXIS 4 // Axis Channel to Jog to rotate Spindle
#define FACTOR (1/6090) // to convert RPM to counts/sec (counts/rev / 60.0sec)
#define SPEEDVAR 99 // global persistant variable to store latest speed
#define STATEVAR 98 // global persistant variable to store latest state (-1=CCW,0=off,1=CW)
#define KMVAR 1 // variable KMotionCNC will pass speed parameter (113)
#define J325_SPINDLE_CHANGING_SPEED 140 // Bit goes low when spindle is at command speed, 1 when changing
#define J325_SPINDLE_0_SPEED 139 // Bit goes high when spindle is at 0 speed
// M03 command passes no arguments
// desired speed is read from user variable SPEEDVAR
// save in user variable STATEVAR whether it was off, CW, or CCW (0,1,-1)
// save in user variable SPEEDVAR the last desired speed
main()
{
float speed = *(float *)&persist.UserData[SPEEDVAR]; // get speed setting
float LastState = persist.UserData[STATEVAR]; // get last state
if (LastState==-1)
{
// spindle was CCW now we want CW
// spin down
Jog(SPINDLEAXIS,0);
while (!CheckDone(SPINDLEAXIS));
// Wait for spindle to stop
while (!J325_SPINDLE_0_SPEED);
}
// Set spindle to new speed and direction
Jog(SPINDLEAXIS, speed * FACTOR * LastState);
// Wait for spindle to reach commanded speed
while (J325_SPINDLE_CHANGING_SPEED);
// Save the new state
persist.UserData[STATEVAR] = 1;
// Print status
printf("Spindle ON CW\n");
}
[/CODE]
*** SpindleCCWJog.c
[CODE]#include "KMotionDef.h"
// Spindle CCW Jog function with +/- voltage used for direction
#define SPINDLEAXIS 4 // Axis Channel to Jog to rotate Spindle
#define FACTOR (1/6090) // to convert RPM to counts/sec (counts/rev / 60.0sec)
#define SPEEDVAR 99 // global persistant variable to store latest speed
#define STATEVAR 98 // global persistant variable to store latest state (-1=CCW,0=off,1=CW)
#define KMVAR 1 // variable KMotionCNC will pass speed parameter (113)
#define J325_SPINDLE_CHANGING_SPEED 140 // Bit goes low when spindle is at command speed, 1 when changing
#define J325_SPINDLE_0_SPEED 139 // Bit goes high when spindle is at 0 speed
// M04 command passes no arguments
// desired speed is read from user variable SPEEDVAR
// save in user variable STATEVAR whether it was off, CW, or CCW (0,1,-1)
// save in user variable SPEEDVAR the last desired speed
main()
{
float speed = *(float *)&persist.UserData[SPEEDVAR]; // get speed setting
float LastState = persist.UserData[STATEVAR]; // get last state
if (LastState==1)
{
// if spindle was CW now we want CCW
// spin down
Jog(SPINDLEAXIS,0);
while (!CheckDone(SPINDLEAXIS)) ;
// Wait for spindle to stop
while (!J325_SPINDLE_0_SPEED);
}
// Set spindle to new speed and direction
Jog(SPINDLEAXIS, speed * FACTOR * LastState);
// Wait for spindle to reach commanded speed
while (J325_SPINDLE_CHANGING_SPEED);
// Save the new state
persist.UserData[STATEVAR] = -1;
// Print status
printf("Spindle ON CCW\n");
}
[/CODE]
Re: Journeyman 325 retrofit
[QUOTE=rbickle;1979782]I notice that in all of the example code files, the while (!CheckDone(SPINDLEAXIS)); command is issued when spinning down the spindle, but not for getting it up to speed. Is the spinup time usually handled by dwell in the G code, or should we delay until the spindle is up to speed?
[/QUOTE]
CheckDone checks to see a motion command has completed, so it won't work as a check for if the spindle is up to speed.
To check for the spindle being at speed, you need to add your own monitoring, such as continually monitor steps over a fixed time period and compare it to the commanded jog rate.
One way of handling waiting for the spindle to get to speed, would be to implement a feedhold until the speed is reached. You could also code things so that if a speed change has just been commanded, a feedhold is issued until the requested speed is reached, but if speed drops at a later time I.e. the spindle stalls, you command a stop.
Re: Journeyman 325 retrofit
I have been trying to get the spindle code to have the axes pause until the spindle is up to speed.
I'm not having any luck. Even though I have a while loop that pauses until the spindle is up to speed, the next G code command executes anyway.
As a test, I put a Delay_sec(5) command in the code, and that doesn't pause either. The next G code executes immediately.
I then tried adding commands to set feed hold until the spindle is up to speed. These commands also don't pause.
Please enlighten me on this....
Here's a copy of the code I'm running for M3 command.
[CODE]
#include "KMotionDef.h"
// Spindle CW Jog function with +/- voltage used for direction
#define SPINDLEAXIS 4 // Axis Channel to Jog to rotate Spindle
#define FACTOR (1.0/6090.0) // to convert RPM to counts/sec (counts/rev / 60.0sec)
#define SPEEDVAR 99 // global persistant variable to store latest speed
#define STATEVAR 98 // global persistant variable to store latest state (-1=CCW,0=off,1=CW)
#define KMVAR 1 // variable KMotionCNC will pass speed parameter (113)
#define SPINDLE_CHANGING_SPEED 140 // Bit goes low when spindle is at command speed, 1 when changing
#define SPINDLE_0_SPEED 139 // Bit goes high when spindle is at 0 speed
#define SPINDLE_ENABLE 153 // Bit to enable spindle drive
int splast=0,splastsolid=-1,spcount=0;
int result;
// M03 command passes no arguments
// desired speed is read from user variable SPEEDVAR
// save in user variable STATEVAR whether it was off, CW, or CCW (0,1,-1)
// save in user variable SPEEDVAR the last desired speed
main()
{
float speed = *(float *)&persist.UserData[SPEEDVAR]; // get speed setting
float LastState = persist.UserData[STATEVAR]; // get last state
if (LastState==-1)
{
// spindle was CCW now we want CW
// spin down
Jog(SPINDLEAXIS,0);
while (!CheckDone(SPINDLEAXIS));
// Wait for spindle to stop
while (ReadBit(SPINDLE_0_SPEED) == 0);
}
// Set the new state
LastState = 1;
// Turn on Spindle
SetBit (SPINDLE_ENABLE);
// Set spindle to new speed and direction
Jog(SPINDLEAXIS, speed * FACTOR * LastState);
// Wait for spindle to reach set speed
// SPINDLE_CHANGING_SPEED bit only goes low at speed
StopCoordinatedMotion();
printf ("Feed Hold ON\n");
result = 1;
//while (result != 0)
{
result = ReadBit(SPINDLE_CHANGING_SPEED)==1;
printf ("%d,",result);
}
Delay_sec(5);
ResumeCoordinatedMotion();
printf ("Feed hold OFF\n");
// Save the new state
LastState = 1;
persist.UserData[STATEVAR] = 1;
// Print status
printf("M3 Spindle CW, Spindle speed %f, FACTOR %f, State %f\n", speed, FACTOR, LastState);
}
[/CODE]
Re: Journeyman 325 retrofit
Well, I think I just answered my own question.
I have to set the command type in KmotionCNC from "Execute" to "Execute/Wait" to make it wait for the code to finish before running the next command.