Partly as an excuse to play with stepper motors, I would like to build an Arduino controlled indexing head for use on my Mill. Having never used a stepper motor before, so playing is required. Alongside is the stepper/mount/vice combination I plan to use. At present this is driven from a micro-stepper which is driven from the Arduino, but that might not be the best plan. I don't need the fine control between steppers that the micro-stepper can provide and there is a risk of it missing steps. A simple bridge driver for this 4 wire stepper may be a better bet. Still, it works and the torque is huge.Project completed! Rare to actually say that, but this is done.Code was originally from Gary Liming's website : http://www.liming.org/millindexCode:
#define Version "StepIndexer V1.3"#include <Arduino.h>#include <LiquidCrystal_I2C.h>// Define your parameters about your motor and gearing#define StepsPerRevolution 200 // Change this to represent the number of steps// it takes the motor to do one full revolution// 200 is the result of a 1.8 degree per step motor#define Microsteps 2#define AngleIncrement 5 // Set how much a keypress changes the angle setting#define CW LOW // Define direction of rotation -#define CCW HIGH // If rotation needs to be reversed, swap HIGH and LOW here#define DefaultMotorSpeed 0 // zero here means fast, as in no delay// define menu screen ids#define mainmenu 0#define stepmode 1#define anglemode 2#define runmode 3#define jogmode 4#define numModes 4 // number of above menu items to choose, not counting main menu#define moveangle 10#define movesteps 11// Define the pins that the driver is attached to.// Once set, this are normally not changed since they correspond to the wiring.#define motorSTEPpin 32 // output signal to step the motor#define motorDIRpin 28 // output siagnal to set direction#define motorENABLEpin 22 // output pin to power up the motor#define en1 26 // Enable -#define cw1 30 // CW -#define clk1 34 // Clock -#define button1 43#define button2 45#define button3 47#define button4 49#define button5 51#define button6 53#define SinkTempPin 1 // temp sensor for heatsink is pin A1#define MotorTempPin 2 // temp sensor for motor is pin A2#define pulsewidth 10 // length of time for one step pulse (microseconds)#define ScreenPause 800 // pause after screen completion#define ADSettleTime 10 // ms delay to let AD converter "settle" i.e., discharge#define SpeedDelayIncrement 5 // change value of motor speed steps (delay amount)#define JogStepsIncrement 1 // initial value of number of steps per keypress in test mode#define TSampleSize 7 // size of temperature sampling to average// create lcd display constructLiquidCrystal_I2C lcd(0x3f, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);// define LCD/Keypad buttons#define NO_KEY 0#define SELECT_KEY 1#define LEFT_KEY 2#define UP_KEY 3#define DOWN_KEY 4#define RIGHT_KEY 5// create global variablesint cur_mode = mainmenu;int mode_select = stepmode;int current_menu;float cur_angle = 0;int cur_key;int num_divisions = 0;int numjogsteps = JogStepsIncrement;unsigned long stepsperdiv;int cur_pos = 0;int cur_dir = CW;int motorspeeddelay = DefaultMotorSpeed;unsigned long motorSteps;void setup(){pinMode(button1, INPUT_PULLUP);pinMode(button2, INPUT_PULLUP);pinMode(button3, INPUT_PULLUP);pinMode(button4, INPUT_PULLUP);pinMode(button5, INPUT_PULLUP);pinMode(button6, INPUT_PULLUP);// begin program#define gearRatio 32motorSteps = Microsteps * StepsPerRevolution * gearRatio;lcd.begin(16, 2);lcd.clear();lcd.setCursor(0,0); // display flash screenlcd.print(Version);lcd.setCursor(0,1);delay(1500); // wait a few secslcd.clear();pinMode(motorSTEPpin, OUTPUT);pinMode(motorDIRpin, OUTPUT);pinMode(motorENABLEpin, OUTPUT);// These three -ve inputs to the stepper driver could all be wired high// rather than using Arduino pinspinMode(en1, OUTPUT);pinMode(cw1, OUTPUT);pinMode(clk1, OUTPUT);digitalWrite(en1, HIGH);digitalWrite(cw1, HIGH);digitalWrite(clk1, HIGH);digitalWrite(motorENABLEpin,HIGH); // power up the motor and leave it ondisplayscreen(cur_mode); // put up initial menu screen}void loop(){int exitflag;int numjogsteps;displayscreen(cur_mode); // display the screencur_key = get_real_key(); // grab a keypressswitch(cur_mode) // execute the keystroke{case mainmenu: // main menuswitch(cur_key){case UP_KEY:mode_select++;if (mode_select > numModes) // wrap around menumode_select = 1;break;case DOWN_KEY:mode_select--;if (mode_select < 1) // wrap around menumode_select = numModes;break;case LEFT_KEY: // left and right keys do nothing in main menubreak;case RIGHT_KEY:break;case SELECT_KEY: // user has picked a menu itemcur_mode = mode_select;break;}break;// case tempmode: // call temps// dotempmode(cur_key);// cur_mode = mainmenu;// break;case stepmode: // call stepsdostepmode(cur_key);cur_mode = mainmenu;break;case anglemode: // call anglesdoanglemode(cur_key);cur_mode = mainmenu;break;case runmode: // call rundorunmode(cur_key);cur_mode = mainmenu;break;case jogmode: // call jogdojogmode(cur_key);cur_mode = mainmenu;break;// case ratiomode: // call ratio// doratiomode(cur_key);// cur_mode = mainmenu;// break;} // end of mode switch} // end of main loopvoid dostepmode(int tmp_key){int breakflag = 0;displayscreen(stepmode);while (breakflag == 0){switch(tmp_key){case UP_KEY:num_divisions++;break;case DOWN_KEY:if (num_divisions > 0)num_divisions--;break;case LEFT_KEY:cur_dir = CCW;stepsperdiv = (motorSteps / num_divisions);move_motor(stepsperdiv, cur_dir, movesteps);delay(ScreenPause); //pause to inspect the screencur_pos--;if (cur_pos == (-num_divisions))cur_pos = 0;break;case RIGHT_KEY:cur_dir = CW;stepsperdiv = (motorSteps / num_divisions);move_motor(stepsperdiv, cur_dir, movesteps);delay(ScreenPause); // pause to inspect the screencur_pos++;if (cur_pos == num_divisions)cur_pos = 0;break;case SELECT_KEY:cur_pos = 0; // reset positionnum_divisions = 0; // reset number of divisionsreturn;break;}displayscreen(stepmode);tmp_key = get_real_key();}return;}void doanglemode(int tmp_key){int breakflag = 0;displayscreen(anglemode);while (breakflag == 0){switch(tmp_key){case UP_KEY:if ((cur_angle+AngleIncrement) < 361)cur_angle += AngleIncrement;break;case DOWN_KEY:if ((cur_angle-AngleIncrement) > -1)cur_angle -= AngleIncrement;break;case LEFT_KEY:cur_dir = CCW;stepsperdiv = ((motorSteps * cur_angle) / 360);move_motor(stepsperdiv, cur_dir,moveangle);delay(ScreenPause); //pause to inspect the screenbreak;case RIGHT_KEY:cur_dir = CW;stepsperdiv = ((motorSteps * cur_angle) / 360);move_motor(stepsperdiv, cur_dir,moveangle);delay(ScreenPause); //pause to inspect the screenbreak;case SELECT_KEY:cur_angle = 0; // reset angle to default of zeroreturn;break;}displayscreen(anglemode);tmp_key = get_real_key();}return;}int isKeyPressed(){int retval = 0;if (digitalRead(button1) == LOW)retval = 1;if (digitalRead(button2) == LOW)retval = 1;if (digitalRead(button3) == LOW)retval = 1;if (digitalRead(button4) == LOW)retval = 1;if (digitalRead(button5) == LOW)retval = 1;if (digitalRead(button6) == LOW)retval = 1;return retval;}void dorunmode(int tmp_key){int breakflag = 0;delay(100); // wait for keybounce from user's selectioncur_dir = CW; // initially, clockwisedisplayscreen(runmode); // show the screenwhile (breakflag == 0) // cycle until Select Key sets flag{move_motor(1,cur_dir,0); // move motor 1 stepif (isKeyPressed() == 1) // if a keypress is present {{cur_key = get_real_key(); // then get itswitch(cur_key) // and honor it{case UP_KEY: // bump up the speedif (motorspeeddelay >= SpeedDelayIncrement) {motorspeeddelay -= SpeedDelayIncrement;displayscreen(runmode);}break;case DOWN_KEY: // bump down the speedmotorspeeddelay += SpeedDelayIncrement;displayscreen(runmode);break;case LEFT_KEY: // set directioncur_dir = CCW;displayscreen(runmode);break;case RIGHT_KEY: // set other directioncur_dir = CW;displayscreen(runmode);break;case SELECT_KEY: // user wants to stopmotorspeeddelay = DefaultMotorSpeed; // reset speedbreakflag = 1; // fall through to exit}}}}void dotempmode(int test_key){while (1){if (test_key == SELECT_KEY) return; // all we do here is wait for a select keytest_key = get_real_key();}return; // to exit}void dojogmode(int tmp_key){int breakflag = 0;numjogsteps = JogStepsIncrement;for (;breakflag==0;){switch(tmp_key){case UP_KEY: // bump the number of stepsnumjogsteps += JogStepsIncrement;break;case DOWN_KEY: // reduce the number of stepsif (numjogsteps > JogStepsIncrement)numjogsteps -= JogStepsIncrement;break;case LEFT_KEY: // step the motor CCWmove_motor(numjogsteps,CCW,0);break;case RIGHT_KEY: // step the motor CWmove_motor(numjogsteps,CW,0);break;case SELECT_KEY: // user want to quitbreakflag = 1;break;}if (breakflag == 0){displayscreen(jogmode);tmp_key = get_real_key();}}numjogsteps = JogStepsIncrement;cur_mode = mainmenu; // go back to main menureturn;}void displayscreen(int menunum) // screen displays are here{lcd.clear();lcd.setCursor(0,0);switch (menunum){case mainmenu:lcd.print("Select Mode");lcd.setCursor(0,1);lcd.print("Mode = ");lcd.setCursor(7,1);switch(mode_select){case(stepmode):lcd.print("Step");break;// case(tempmode):// lcd.print("Temp");// break;case(anglemode):lcd.print("Angle");break;case(runmode):lcd.print("Run");break;case(jogmode):lcd.print("Jog");break;}break;case stepmode:lcd.print("Divisions:");lcd.setCursor(10,0);lcd.print(num_divisions);lcd.setCursor(0,1);lcd.print(" Position:");lcd.setCursor(10,1);lcd.print(cur_pos);break;case movesteps:lcd.print("Steps/Div:");lcd.setCursor(10,0);lcd.print(stepsperdiv);lcd.setCursor(0,1);lcd.print("Move ");lcd.setCursor(5,1);if (cur_dir == CW)lcd.write(">");elselcd.write("<");lcd.setCursor(7,1);lcd.print("to:");lcd.setCursor(10,1);lcd.print(cur_pos);break;case anglemode:lcd.print("Move ");lcd.setCursor(5,0);lcd.print((int)cur_angle);lcd.setCursor(8,0);lcd.print(" degrees");break;case moveangle:lcd.print("Move ");lcd.setCursor(5,0);if (cur_dir == CW)lcd.write(">");elselcd.write("<");lcd.setCursor(12,0);lcd.print("steps");lcd.setCursor(0,1);lcd.print("to ");lcd.setCursor(3,1);lcd.print((int)cur_angle);lcd.setCursor(7,1);lcd.print("degrees");break;case runmode:lcd.print("Continuous");lcd.setCursor(11,0);if (cur_dir == CW)lcd.write(">");elselcd.write("<");lcd.setCursor(0,1);lcd.print("Speed = ");lcd.setCursor(8,1);lcd.print((int)motorspeeddelay);break;case jogmode:lcd.print("Jog ");lcd.setCursor(0,1);lcd.print("Steps/jog: ");lcd.setCursor(11,1);lcd.print(numjogsteps);break;}return;}void move_motor(unsigned long steps, int dir, int type){unsigned long i;//steps = steps * 16;if (type == movesteps)displayscreen(movesteps);if (type == moveangle)displayscreen(moveangle);for (i=0; i < steps; i++){digitalWrite(motorDIRpin,dir);digitalWrite(motorSTEPpin,HIGH); //pulse the motordelayMicroseconds(pulsewidth);digitalWrite(motorSTEPpin,LOW);if (type == movesteps) // in this mode display progress{lcd.setCursor(10,1);lcd.print(i);}if (type == moveangle) // in this mode display progress{lcd.setCursor(7,0);lcd.print(i);}delayMicroseconds(motorspeeddelay * 128); // wait betweeen pulses}return;}int gettemp(int device){float temperature=0.0;float tCelsius=0.0;int i;analogRead(device); // first time to let adc settle downdelay(50); // throw first reading awayfor (i=0; i<TSampleSize; ++i) // take several readings{tCelsius = ((tCelsius-32.0)/1.8) ; // just use different formula for celsiustemperature += tCelsius;delay(ADSettleTime);}return (int)(temperature/TSampleSize); // return the average}int get_real_key(void) // routine to return a valid keystroke{int trial_key = 0;while (trial_key < 1){trial_key = read_LCD_button();}delay(200); // 200 millisec delay between user keysreturn trial_key;}int read_LCD_button() // routine to read the LCD's buttons{if (digitalRead(button1) == LOW){return SELECT_KEY;}if (digitalRead(button2) == LOW){return RIGHT_KEY;}if (digitalRead(button3) == LOW){return UP_KEY;}if (digitalRead(button4) == LOW){return DOWN_KEY;}if (digitalRead(button5) == LOW){return LEFT_KEY;}if (digitalRead(button6) == LOW){return NO_KEY;}}