#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <EEPROM.h>
// ===============================================================================
// For each #define below flagged SET, enter the correct value for your equipment
// The initial SET values below will be stored to EEPROM on the initial install
// ===============================================================================
float VersionNumber = 7.25; // The version number of this program
int SettingsChanged = 0; // SET 0 if settings never updated, 1 if updated
int StepsPerFullRotation = 200; // SET number of *full* steps per full 360 degree rotation for your motor
int MicroStepping = 1; // SET microsteps per full step; 1,2,4,8,16,etc.
int TableRatio1 = 26; // SET rotary table gear ratio, if applicable; otherwise = 1
int TableRatio2 = 103; // SET table ratio is 26+103/121 = 26.851239669
int TableRatio3 = 121; // SET
int stepdelay = 1000; // SET delay in microseconds
int Backlash = 0; // SET number of microsteps needed to cancel out backlash
// - set to 0 if no backlash correction is desired
int beep = 1; // SET beep on completion of a move (1=ON, 2=OFF)
float TableRatio = (float) TableRatio1 + ( (float) TableRatio2 / (float) TableRatio3 );
float MicroStepsPerFullRotation = (float) StepsPerFullRotation * (float) MicroStepping * TableRatio;
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'.','0','#','D'}
};
byte rowPINS[ROWS] = {11,10,9,8};
byte colPINS[COLS] = {7,6,5,4};
Keypad kpd = Keypad(makeKeymap(keys),rowPINS,colPINS, ROWS, COLS);
LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x20 for a 16 chars and 4 line display
// some displays may use 0x3F for the address
// SCL - A5, SDA - A4, VCC - +5, Gnd - Gnd
//setup vars
const int stp = 2; // connect pin 2 to step
const int dir = 3; // connect pin 3 to dir
float Degrees = 0.0; // Degrees from Serial input or number of degrees per division
float TotalDegrees = 0.0; // total number of degrees moved
float StepsPerIncrementTheoretical = 0.0; // Number of *theoretical* microsteps to move (can be a fractional number)
unsigned long StepsToMove = 0; // Actual number of (integer) steps to move
float TotalStepsTheoretical = 0.0; // Theoretical total number of steps moved
unsigned long TotalStepsActualPrevious = 0; // Previous total of steps moved
unsigned long TotalStepsActual = 0; // Cumulative total of steps moved
int Direction = 0; // Direction = the direction of rotation of the last move, either
int Fwd = 1; // forward (Fwd) or
int Rvs = -1; // reverse (Rvs)
// This information is needed for backlash correction. On initial start-up,
// the direction is undefined.
int choice = 0;
void setup()
{
EEPROM.get(0,SettingsChanged);
if (SettingsChanged == 1) //If the initial program settings were changed, load the updated values
{
EEPROM.get(2,StepsPerFullRotation);
EEPROM.get(4,MicroStepping);
EEPROM.get(6,TableRatio);
EEPROM.get(10,stepdelay);
EEPROM.get(12,Backlash);
EEPROM.get(14,beep);
}
else //If the initial program settings were not changed, store the initial values
{
EEPROM.put(0,SettingsChanged);
EEPROM.put(2,StepsPerFullRotation);
EEPROM.put(4,MicroStepping);
EEPROM.put(6,TableRatio);
EEPROM.put(10,stepdelay);
EEPROM.get(12,Backlash);
EEPROM.get(14,beep);
}
// Re-calculate the MicroStepsPerFullRotation from the updated values
MicroStepsPerFullRotation = (float) StepsPerFullRotation * (float) MicroStepping * TableRatio;
lcd.init(); // initialize the lcd
pinMode(stp, OUTPUT);
pinMode(dir, OUTPUT);
// Print welcome message to the LCD.
lcd.backlight();
lcd.setCursor(0,0);
lcd.print("Rotary Table Control");
lcd.setCursor(3,2);
lcd.print("Rev. ");lcd.print(VersionNumber,2);lcd.print(" 2019");
delay(1500);
lcd.init();
choice = 0;
char key = kpd.getKey();
MainMenu(); //display the main menu
while(choice == 0)
{
key = kpd.getKey();
switch (key)
{
case NO_KEY:
break;
case 'A':
{
String prompt = "Degrees per move";
Degrees = getnumber(prompt,0,0);
StepsPerIncrementTheoretical = Degrees / 360.0 * MicroStepsPerFullRotation;
lcd.clear();
choice = 1;
break;
}
case 'B':
{
// Get the total number of sides or gear teeth and determine the number of microsteps per division
String prompt = "Sides or Teeth";
StepsPerIncrementTheoretical = MicroStepsPerFullRotation / getnumber(prompt,0,0);
Degrees = StepsPerIncrementTheoretical / MicroStepsPerFullRotation * 360.0;
choice = 2;
break;
}
case 'C':
{
StepsPerIncrementTheoretical = getjog();
Degrees = StepsPerIncrementTheoretical / MicroStepsPerFullRotation * 360.0;
lcd.clear();
choice = 3;
break;
}
case 'D':
{
Settings();
lcd.clear();
choice=0;
MainMenu();
break;
}
} // end case
} // end while choice = 0
} // end setup
void loop() // MAIN LOOP
{
lcd.clear();
char key = kpd.getKey();
// Initialize variables
TotalDegrees = 0.0;
StepsToMove = 0;
TotalStepsTheoretical = 0.0;
TotalStepsActualPrevious = 0;
TotalStepsActual = 0;
lcd.setCursor(7,0);lcd.print("Total: ");lcd.print(TotalDegrees,2);lcd.print((char)223);
lcd.setCursor(0,2);lcd.print("Steps=");
lcd.setCursor(6,2);lcd.print(" "); // clear print field
lcd.setCursor(6,2);lcd.print(0.0,0); // print zero steps to start
lcd.print("/");
lcd.print((float) MicroStepsPerFullRotation,0);
lcd.setCursor(0,3);lcd.print("A=FWD B=RVS C=MENU");
while(key != 'C') // C will return to start menu
{
lcd.setCursor(0,0);lcd.print(abs(Degrees),2);lcd.print((char)223);
key = kpd.getKey();
if(key == 'A') // MOVE FORWARD
{
TotalDegrees = TotalDegrees + Degrees;
if (TotalDegrees > 360.0) // When making a full rotation, restart count at 360 degrees
{
TotalDegrees = TotalDegrees - 360.0;
}
TotalStepsActualPrevious = TotalStepsActual; // Save current steps total as previous total
TotalStepsTheoretical = TotalStepsTheoretical + StepsPerIncrementTheoretical;
/*
Round up to integer actual total number of steps moved.
If a fractional value is N.5 or greater, adding .5 will increase the interger portion to N+1.
Conversion to int will truncate any fractional part. Result is that any number with a fractional
part of >=.5 will be rounded up; <.5 will be rounded down.
*/
TotalStepsActual = (unsigned long) (TotalStepsTheoretical + 0.5);
StepsToMove = TotalStepsActual - TotalStepsActualPrevious;
//If we have moved through a full revolution, reset actual steps to begin counting from zero.
if ( TotalStepsTheoretical >= MicroStepsPerFullRotation ) {
TotalStepsActual = (unsigned long) (TotalStepsTheoretical - MicroStepsPerFullRotation);
TotalStepsTheoretical = TotalStepsTheoretical - MicroStepsPerFullRotation;
}
if (Direction == 0){Direction = Fwd;} //If this is the first rotation, set the direction to forward
if (Direction == Rvs) //If reversing direction, apply backlash correction and reset direction.
{
StepsToMove = StepsToMove + Backlash;
Direction = Fwd;
}
digitalWrite(dir, LOW);
printadvance();
}
if(key=='B') // MOVE REVERSE
{
TotalDegrees = TotalDegrees - Degrees;
if ( TotalDegrees < 0.0) {
TotalDegrees = TotalDegrees + 360.0;
}
/* Note:
For reverse moves theoretical reverse steps are calculated as the equivalent forward theoretical steps.
*/
TotalStepsActualPrevious = TotalStepsActual; // Save current steps total as previous total
if (TotalStepsTheoretical - StepsPerIncrementTheoretical >= 0.0) // If next move does NOT go through a full rotation
{
TotalStepsTheoretical = TotalStepsTheoretical - StepsPerIncrementTheoretical;
}
else // Next move >= full rotation, so reset
{
TotalStepsTheoretical = TotalStepsTheoretical - StepsPerIncrementTheoretical + MicroStepsPerFullRotation;
}
TotalStepsActual = (unsigned long) (TotalStepsTheoretical - 0.5); //Use rounded value
if (TotalStepsActual < TotalStepsActualPrevious)
{
StepsToMove = TotalStepsActualPrevious - TotalStepsActual;
}
else
{
StepsToMove = TotalStepsActualPrevious + (unsigned long) MicroStepsPerFullRotation - TotalStepsActual;
}
if (Direction == 0){Direction = Rvs;} //If this is the first rotation, set the direction to forward
if (Direction == Fwd) //If reversing direction, apply backlash correction and reset direction.
{
StepsToMove = StepsToMove + Backlash;
Direction = Rvs;
}
digitalWrite(dir, HIGH); // pin 13
printadvance();
}
} // end while not C loop
lcd.init();
setup();
} // end MAIN LOOP
void MainMenu()
{
lcd.setCursor(0,0);
lcd.print("A=Degrees");
lcd.setCursor(0,1);
lcd.print("B=Sides or Teeth");
lcd.setCursor(0,2);
lcd.print("C=Jog");
lcd.setCursor(0,3);
lcd.print("D=Settings");
} //end MainMenu
float getjog()
{
float steps = 0.0;
float StepA = 1.0; // If A move one step
float StepB = 0.0;
StepB = MicroStepsPerFullRotation / 360.0; // If B move 1 degree
float StepC = StepB * 5.0 ; // If C move 5 degrees
char key = kpd.getKey();
lcd.clear();
lcd.setCursor(0,0);lcd.print("Jog (1 Step, 1");lcd.print((char)223);lcd.print(", 5");lcd.print((char)223);lcd.print(")");
lcd.setCursor(0,1);lcd.print(" A=1 B= C= ");
lcd.setCursor(8,1);lcd.print(StepB,0);
lcd.setCursor(14,1);lcd.print(StepC,0);
lcd.setCursor(0,2);lcd.print("Enter Choice:");lcd.setCursor(0,3);lcd.print("#=ENTER D=CLEAR");
while(key != '#')
{
switch (key)
{
case NO_KEY:
break;
case 'A':
steps = StepA;
lcd.setCursor(14,2);lcd.print("A");
break;
case 'B':
steps = StepB;
lcd.setCursor(14,2);lcd.print("B");
break;
case 'C':
steps = StepC;
lcd.setCursor(14,2);lcd.print("C");
break;
case 'D':
steps = 0;
lcd.setCursor(14,2);lcd.print(" ");
lcd.setCursor(14,2);
break;
}
key = kpd.getKey();
}
return steps;
}
float getnumber(String prompt, int intvalue, float floatvalue) //prompt and value to get
{
float num = 0.00;
float decimal = 0.00;
float decnum = 0.00;
int counter = 0;
lcd.clear();
lcd.home();
lcd.print(prompt);
lcd.setCursor(0,1); lcd.print("Value = ");
if (floatvalue != 0)
{ lcd.print(floatvalue,4); }
else
{ lcd.print(intvalue); }
lcd.setCursor(0,3); lcd.print("#=ENTER D=CLEAR");
lcd.setCursor(8,1);
boolean decOffset = false;
char key = kpd.getKey();
while(key != '#')
{
switch (key)
{
case NO_KEY:
break;
case '.':
if(!decOffset)
{
decOffset = true; //Set decimal flag and print decimal point (but not if already printed)
lcd.print(key);
}
break;
case 'D': //Clear data entry and reset decimal point flag
num=0.00;
lcd.setCursor(8,1);
lcd.print(" "); //clear data field with spaces
lcd.setCursor(8,1);
decOffset = false;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (num == 0.0) //Clear data entry field if new entry
{
lcd.setCursor(8,1);
lcd.print(" "); //clear data field with spaces
lcd.setCursor(8,1);
}
if(!decOffset)
{
num = num * 10.0 + (key - '0');
lcd.print(key);
}
else if((decOffset) && (counter <= 4)) //Counter number = number of decimal places minus one
{
num = num * 10.0 + (key - '0');
lcd.print(key);
counter++;
}
break;
} //end case
decnum = num / pow(10, counter);
key = kpd.getKey();
} //end while not #
return decnum;
}
void Settings() //Get the basic settings for the stepper motor, gear reduction, etc.
//Display the current value for each setting
//If no new number is entered, keep the current value
{
//
lcd.clear();
//Get number of steps per full revolution of the stepper motor
float temp;
String prompt = "Full steps in 360";
prompt += char(223); //degree symbol
temp = getnumber(prompt,StepsPerFullRotation,0);
if (temp != 0) { StepsPerFullRotation = temp; } //Change value only if new number entered
//Get the micro-stepping setting
lcd.clear();
prompt = "Micro-stepping";
temp = getnumber(prompt,MicroStepping,0);
if (temp != 0) { MicroStepping = temp; } //Change value only if new number entered
//Get the table (gear) ratio
lcd.clear();
prompt = "Table Gear Ratio";
temp = getnumber(prompt,0,TableRatio);
if (temp != 0) { TableRatio = temp; } //Change value only if new number entered
//Get the step delay
lcd.clear();
prompt = "Step delay-microsec";
temp = getnumber(prompt,stepdelay,0);
if (temp != 0) { stepdelay = temp; } //Change value only if new number entered
//Get the backlash correction
lcd.clear();
prompt = "Backlash correction";
temp = getnumber(prompt,Backlash,0);
if (temp != 0) { Backlash = temp; } //Change value only if new number entered
//Set beep on or off
lcd.clear();
prompt = "Beep: 1=ON, 2=OFF";
temp = getnumber(prompt,beep,0);
if (temp != 0) { beep = temp; } //Change value only if new number entered
lcd.clear();
SettingsChanged = 1; //Set SettingsChanged flag to 1
EEPROM.put(0,SettingsChanged);
EEPROM.put(2,StepsPerFullRotation);
EEPROM.put(4,MicroStepping);
EEPROM.put(6,TableRatio);
EEPROM.put(10,stepdelay);
EEPROM.put(12,Backlash);
EEPROM.put(14,beep);
// Re-calculate the MicroStepsPerFullRotation from the updated values
MicroStepsPerFullRotation = (float) StepsPerFullRotation * (float) MicroStepping * TableRatio;
}
void printadvance() // print function
{
lcd.setCursor(6,1);lcd.print("Moving");
lcd.setCursor(0,2);lcd.print("Steps=");
/*
//debug code below: prints values to LCD for debugging
//Comment out for working program.
lcd.setCursor(0,1);lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(TotalStepsTheoretical,2);
lcd.setCursor(10,1);
lcd.print(StepsToMove);
*/
lcd.setCursor(6,2);lcd.print(" "); // clear print field
lcd.setCursor(6,2);
lcd.print(TotalStepsActual); // print total number of steps
lcd.print("/");
lcd.print((float) MicroStepsPerFullRotation,0);
lcd.setCursor(13,0);lcd.print(" "); // clear print field
lcd.setCursor(13,0);lcd.print(TotalDegrees,2);lcd.print((char)223);
rotation(StepsToMove);
lcd.setCursor(6,1);lcd.print(" ");
if (beep == 1) //Beep on completion of move if beep switched on
{
tone(13, 4000, 500); //tone(pin,frequency,duration-milliseconds)
}
//Connect speaker or piezo buzzer to pin 13 and ground
//Adjust frequency between 2000 - 5000 for desired tone
}
void rotation(unsigned long tm)
{
for(unsigned long i = 0; i < tm; i++)
{
digitalWrite(stp, HIGH);
delayMicroseconds(stepdelay);
digitalWrite(stp, LOW);
delayMicroseconds(stepdelay);
}
}
void software_Reset() // Restarts program from beginning but does not reset the peripherals and registers
{
asm volatile (" jmp 0");
}