#include <AccelStepper.h>
#include <LiquidCrystal_I2C.h>
#include <ezButton.h>
#include <EEPROM.h>
// Define LCD parameters
LiquidCrystal_I2C lcd(0x27, 16, 2); // Set the LCD address to 0x27 for a 16 chars and 2 line display
//AccelStepper
#include <AccelStepper.h>
AccelStepper feederStepper(1, 8, 9); // steps 5; direction 6
//Defining pins
ezButton Up (4); //CLK pin on the rotary encoder
ezButton Down (5); //DT pin on the rotary encoder
ezButton Enter (6); //SW pin on the rotary encoder (Button function)
ezButton LeftSwitch (10) ; //Up button which is also a start button
ezButton RightSwitch (11); //Down button which is also a stop button
const int A = 2;
const int B = 3;
//Statuses of the DT and CLK pins on the encoder
int UpNow;
int UpPrevious;
int DownNow;
int DownPrevious;
int ANow;
int APrevious;
int BNow;
int BPrevious;
int Forward = 0;
int Backward = 0;
int STP = 0;
int target = 0;
String dfe = "";
int dir = 1;
//Menu-related variables
//Values
int RotaryButtonValue;
volatile int menuCounter = 0;
//
volatile int stepperMaxSpeed = 5000; //for setMaxSpeed - steps (1 turn/sec or 60 rpm)
volatile int stepperAcceleration = 5000;
volatile float wireDiameter = 1.0;
volatile float feederSteps = 0;
byte Left[8] = {
0b00001,
0b00011,
0b01111,
0b11111,
0b01111,
0b00011,
0b00001,
0b00000
};
byte Right[8] = {
0b10000,
0b11000,
0b11110,
0b11111,
0b11110,
0b11100,
0b10000,
0b00000
};
byte Tail[8] = {
0b00000,
0b00000,
0b11111,
0b11111,
0b11111,
0b00000,
0b00000,
0b00000
};
byte Check[8] = {
0b00000,
0b00001,
0b00011,
0b10110,
0b11100,
0b01000,
0b00000,
0b00000
};
int eeAddress = 0;
int jobSize = sizeof(int) + sizeof(int) + sizeof(float) + sizeof(int) ;
struct Job {
int jobNumber;
int stepperMaxSpeed;
float pitch;
int feederMicroStepping;
};
int jobNumber;
unsigned int feederMicroStepping = 800;
volatile float pitch=2;
//booleans
bool saving_Selected = false;
bool pitch_Selected = false;
bool stepperMaxSpeed_Selected = false;
bool stepperAcceleration_Selected = false;
bool wireDiameter_Selected = false;
bool feeder_Selected = false;
bool winding_Selected = false;
bool microStepping_Selected = false;
bool menuChanged = false;
bool valueChanged = false;
bool updateValueSelection = false;
bool autoWinding_Selected = false;
bool autoWinding_enabled = false;
bool microStepping_enabled = false;
bool switched = false; //LimitSwitch Triggered
char buf[15]; //buffer for printing on the LCD (strings) holds 25 characters
char str_temp[7]; //for printing floats
float RotaryTime1; //up timing
float RotaryTime2; //enter button timing
long numberOfTurns = 0;
long rightPosition=0;
long leftPosition=0;
long switchPosition=0;
long turnsOffset;
int direction = 1; // 1 for clockwise, -1 for counterclockwise
int remaining_steps = 0;
void setup()
{
//Definition of the pins
//Rotary encoder
//pinMode(2, INPUT); //CLK
// pinMode(10, INPUT); //DT
// pinMode(4, INPUT_PULLUP); //SW
//pin 5 PUL
//pin 6 DIR
pinMode(10, INPUT_PULLUP); //Leftswitch
pinMode(11, INPUT_PULLUP); //Rightswitch
//EEPROM.begin(512);
Job retrievedJob = readJobFromEEPROM(1);
stepperMaxSpeed = retrievedJob.stepperMaxSpeed;
pitch = retrievedJob.pitch;
if (pitch != 1){
pitch=2;
}
if (stepperMaxSpeed != -1){
stepperMaxSpeed = retrievedJob.stepperMaxSpeed;
}else {
stepperMaxSpeed = 2000;
}
feederMicroStepping = retrievedJob.feederMicroStepping;
if (feederMicroStepping != -1 ){
feederMicroStepping = retrievedJob.feederMicroStepping;
}else {
feederMicroStepping = 800;
}
//Store states of the encoder pins
APrevious = digitalRead(A);
BPrevious = digitalRead(B);
//Rotary encoder interrupt for the CLK pin
// attachInterrupt(digitalPinToInterrupt(Up), MenuEncoder, CHANGE);
attachInterrupt(digitalPinToInterrupt(A), RotaryEncoder, CHANGE);
attachInterrupt(digitalPinToInterrupt(B), RotaryEncoder, CHANGE);
//Stepper speed setup
feederStepper.setMaxSpeed(stepperMaxSpeed);
feederStepper.setSpeed(stepperMaxSpeed);
feederStepper.setAcceleration(stepperAcceleration);
lcd.init(); //initialize LCD
lcd.backlight();
//
printLCD(); //print the welcome message
LeftSwitch.setDebounceTime(60);
RightSwitch.setDebounceTime(60);
Up.setDebounceTime(60);
Down.setDebounceTime(60);
Enter.setDebounceTime(60);
Up.setCountMode(COUNT_FALLING);
Down.setCountMode(COUNT_FALLING);
Enter.setCountMode(COUNT_FALLING);
Serial.begin(34800);
Serial.println("Ready");
lcd.createChar(0, Left);
lcd.createChar(1, Right);
lcd.createChar(2, Tail);
lcd.createChar(3, Check);
feederSteps = wireDiameter * feederMicroStepping / pitch;
feederStepper.setCurrentPosition(0);
feederStepper.setPinsInverted(false, false, false);
}
void loop()
{
Up.loop();
Down.loop();
Enter.loop();
LeftSwitch.loop();
RightSwitch.loop();
get_dfe();
RotaryEncoder();
MenuEncoder();
if (menuChanged == true)
{
updateMenuPosition();
}
if (updateValueSelection == true)
{
updateSelection();
updateValueSelection = false;
}
if (valueChanged == true)
{
updateValue();
valueChanged = false;
}
feederStepper.setSpeed(stepperMaxSpeed);
flipCheck() ;
CheckRotaryButton();
checkSteps();
//feederStepper.runSpeedToPosition();
}
void RotaryEncoder()
{ if (autoWinding_Selected == true)
{
ANow = digitalRead(A); //Read the state of the CLK pin
// If last and current state of CLK are different, then a pulse occurred
if (autoWinding_enabled == true) {
if (ANow != APrevious && ANow == 0) {
int BNow=digitalRead(B);
// If the DT state is different than the CLK state then the encoder is rotating in "A" direction, so we increase the value of the variable
if (digitalRead(B) == ANow) {
Forward=1;
numberOfTurns++;
} else {
Backward=1;
numberOfTurns--;
}
checkSteps();
valueChanged = true;
}
}
APrevious = ANow; // Store the recent CLK stat
}
}
void MenuEncoder()
{
//Stepper speed - defined in steps/sec
if (stepperMaxSpeed_Selected == true)
{
if (Up.isPressed())
{
stepperMaxSpeed += 100 ; //1 step size increment
valueChanged = true;
}
if (Down.isPressed())
{
if (stepperMaxSpeed == 160) //160, depends on the microstepping!
{
// Don't decrease further, it should be at least 160
// 1600/10 = 160 -> 0.1 turns/sec is the minimum speed
}
else
{
stepperMaxSpeed -= 100; //1 step size decrement
}
valueChanged = true;
}
// Store the recent CLK state
}
//----------------------------------------------------------------------------
//Stepper acceleration
else if (pitch_Selected == true)
{
if (Up.isPressed())
{
pitch = pitch + 0.1;
valueChanged = true;
}
if (Down.isPressed())
{
pitch = pitch - 0.1;
valueChanged = true;
}
}
//----------------------------------------------------------------------------
//Stepper microSteps
else if (microStepping_Selected == true)
{
if (Up.isPressed())
{
feederMicroStepping *= 2; //1 step size increment
valueChanged = true;
}
if (Down.isPressed())
{
feederMicroStepping = feederMicroStepping / 2; //1 step size decrement
if (feederMicroStepping <= 200 ) {
feederMicroStepping = 200;
}
valueChanged = true;
}
}
//----------------------------------------------------------------------------
//Wire diameter
else if (wireDiameter_Selected == true)
{
if (Up.isPressed())
{
wireDiameter = wireDiameter + 0.10; //0.001 step size increment (button allows larger jumps)
valueChanged = true;
}
else if (Down.isPressed())
{
if (wireDiameter == 0.01) //when it is = 0.01
{
// Don't decrease further, it should be at least 0.01
}
else
{
wireDiameter = wireDiameter - 0.01; //0.001 step size decrement
}
feederSteps = wireDiameter * feederMicroStepping / pitch;
valueChanged = true;
}
}
//----------------------------------------------------------------------------
else if (autoWinding_Selected == true)
{
pinMode(2, INPUT);//A encoder
pinMode(3, INPUT);//B encoder
if (Up.isPressed())
{
//ON
autoWinding_enabled = !autoWinding_enabled;
feederStepper.setCurrentPosition(0);
feederSteps = wireDiameter * feederMicroStepping / pitch;
valueChanged = true;
}
if (Down.isPressed())
{
//OFF
autoWinding_enabled = !autoWinding_enabled;
valueChanged = true;
}
}
//----------------------------------------------------------------------------
else if (saving_Selected == true)
{
if (Up.isPressed())
{
//ON
jobNumber+=1;
SAVING();
valueChanged = true;
}
}
//----------------------------------------------------------------------------
//Feeder manual stepping
else if (feeder_Selected == true)
{
if (Up.isPressed())
{
//160 step size increment relative to the current position
Forward=1;
}
if (Down.isPressed())
{ Backward=1;
}
if (RightSwitch.isPressed())
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(14, 1);
lcd.write(2);
lcd.setCursor(15, 1);
lcd.write(1);
snprintf(buf, 8, "%d", feederStepper.currentPosition());
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(buf);
}
else if (LeftSwitch.isPressed())
{ lcd.setCursor(14, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.write(0);
lcd.setCursor(1, 1);
lcd.write(2);
snprintf(buf, 8, "%d", feederStepper.currentPosition());
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(buf);
}
valueChanged = true;
}
else //MENU COUNTER----------------------------------------------------------------------------
{
if (Up.isPressed())
{
if (menuCounter < 6) //5 menu items (0, 1, 2....5)
{
menuCounter += 1;
}
else
{
menuCounter = 0;
}
menuChanged = true;
}
else if (Down.isPressed())
{
if (menuCounter > 0) //5 menu items (0, 1, 2.3..4)
{
menuCounter -= 1;
}
else
{
menuCounter = 6;
}
menuChanged = true;
}
}
}
void printLCD() //Prints a "welcome screen"
{
lcd.setCursor(2, 0);
lcd.print( "Feeder");
lcd.print( "Control");
lcd.clear();
lcd.setCursor(2, 0);
lcd.print( " "); //erase previous text
lcd.print( "Speed "); //print new text
}
void CheckRotaryButton()
{
if (Enter.isPressed())
{
RotaryTime1 = millis();
if (RotaryTime1 - RotaryTime2 > 1000) //1 second has to elapse between two button clicks to enter below ("debounce")
{
switch (menuCounter) //we check the menu position
{
case 0:
stepperMaxSpeed_Selected = !stepperMaxSpeed_Selected; //we change the status of the variable to the opposite
break;
//
case 1:
pitch_Selected = !pitch_Selected;
break;
//
case 2:
wireDiameter_Selected = !wireDiameter_Selected;
break;
//
case 3:
feeder_Selected = !feeder_Selected;
break;
//
case 4:
autoWinding_Selected = !autoWinding_Selected;
turnsOffset=0;
numberOfTurns = 0;
valueChanged = true;
break;
case 5:
microStepping_Selected = !microStepping_Selected;
break;
case 6:
saving_Selected = !saving_Selected;
break;
}
updateValueSelection = true;
RotaryTime2 = millis();
}
}
}
void updateMenuPosition()
{
lcd.clear();//delete the entire screen
switch (menuCounter) //this checks the value of the counter (0, 1, 2 or 3)
{
//MAX SPEED
case 0:
lcd.setCursor(2, 0);
lcd.print( " "); //erase previous text
lcd.print( "Speed "); //print new text
//Set another (larger) font
snprintf(buf, 8, "%d", stepperMaxSpeed); //the decimal value of the variable is in the buf
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(buf);
break;
//-------------------------------
//ACCELERATION
case 1:
lcd.setCursor(2, 0);
lcd.print( "Screw Pitch");
dtostrf(pitch, 5, 3, str_temp);
snprintf(buf, 8, "%s", str_temp);
//snprintf(buf, 8, "%d", pitch);
lcd.setCursor(6, 1);
lcd.print(" ");
lcd.setCursor(6, 1);
lcd.print(buf);
break;
//-------------------------------
//Wire diameter
case 2:
lcd.setCursor(2, 0);
lcd.print( "Wire diameter");
dtostrf(wireDiameter, 5, 3, str_temp);
snprintf(buf, 8, "%s", str_temp);
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(buf);
break;
//-------------------------------
case 3:
lcd.setCursor(2, 0);
lcd.print( "Manual Move");
snprintf(buf, 8, "%d", feederStepper.currentPosition());
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(buf);
break;
//-------------------------------
//Coil pitch
case 4:
lcd.setCursor(2, 0);
lcd.print( "AUTO Winding");
if (autoWinding_enabled == true)
{
snprintf(buf, 8, "%s", "ON");
}
else
{
snprintf(buf, 8, "%s", "OFF");
}
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(buf);
snprintf(buf, 8, "%d", numberOfTurns);
lcd.setCursor(10, 1);
lcd.print(" ");
lcd.setCursor(10, 1);
lcd.print(buf);
break;
case 5:
lcd.setCursor(2, 0);
lcd.print( "MicroSteps");
snprintf(buf, 8, "%d", feederMicroStepping);
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(buf);
break;
case 6:
lcd.setCursor(2, 0);
lcd.print( "Save Settings");
snprintf(buf, 8, "%d", jobNumber);
lcd.setCursor(5, 1);
lcd.print("# ");
lcd.setCursor(6, 1);
lcd.print(buf);
break;
}
menuChanged = false; //next loop() iteration will not enter again
}
void updateValue()
{
switch (menuCounter) //this checks the value of the counter (0, 1, 2 or 3)
{
//Max speed menu
case 0:
feederStepper.setMaxSpeed(stepperMaxSpeed);
snprintf(buf, 8, "%d", stepperMaxSpeed);
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(buf);
break;
//-------------------------------
//Acceleration menu - In this version, the acceleration does not play any role
case 1:
dtostrf(pitch, 5, 3, str_temp);
snprintf(buf, 8, "%s", str_temp);
// snprintf(buf, 8, "%d", pitch);
lcd.setCursor(6, 1);
lcd.print(" ");
lcd.setCursor(6, 1);
lcd.print(buf);
break;
//-------------------------------
//wireDiameter
case 2:
dtostrf(wireDiameter, 5, 3, str_temp);
snprintf(buf, 8, "%s", str_temp);
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(buf);
break;
//-------------------------------
//Manual Move
case 3:
if (Up.isPressed())
{
if (dir < 0) {
lcd.setCursor(14, 1);
lcd.write(2);
lcd.setCursor(15, 1);
lcd.write(1);
lcd.setCursor(0, 1);
lcd.print(" ");
} else if (dir > 0) {
lcd.setCursor(0, 1);
lcd.write(0);
lcd.setCursor(1, 1);
lcd.write(2);
lcd.setCursor(14, 1);
lcd.print(" ");
}
snprintf(buf, 8, "%d", feederStepper.currentPosition());
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(buf);
}
else if (Down.isPressed()) {
if (dir < 0) {
lcd.setCursor(14, 1);
lcd.write(2);
lcd.setCursor(15, 1);
lcd.write(1);
lcd.setCursor(0, 1);
lcd.print(" ");
} else if (dir > 0) {
lcd.setCursor(0, 1);
lcd.write(0);
lcd.setCursor(1, 1);
lcd.write(2);
lcd.setCursor(14, 1);
lcd.print(" ");
}
snprintf(buf, 8, "%d", feederStepper.currentPosition());
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(buf);
}
break;
//-------------------------------
//autoWinding
case 4:
lcd.setCursor(0, 0);
if (autoWinding_enabled == true)
{
snprintf(buf, 8, "%s", "ON");
if (dir > 0) {
lcd.setCursor(14, 1);
lcd.write(2);
lcd.setCursor(15, 1);
lcd.write(1);
lcd.setCursor(0, 1);
lcd.print(" ");
} else if (dir < 0) {
lcd.setCursor(0, 1);
lcd.write(0);
lcd.setCursor(1, 1);
lcd.write(2);
lcd.setCursor(14, 1);
lcd.print(" ");
}
}
else
{
snprintf(buf, 8, "%s", "OFF");
lcd.setCursor(1, 1);
lcd.print(" ");
lcd.setCursor(10, 1);
lcd.print(" ");
}
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(buf);
snprintf(buf, 8, "%d", numberOfTurns);
lcd.setCursor(10, 1);
lcd.print(" ");
lcd.setCursor(10, 1);
lcd.print(buf);
break;
//-------------------------------
case 5:
lcd.setCursor(2, 0);
lcd.print( "Microsteps");
snprintf(buf, 8, "%d", feederMicroStepping);
lcd.setCursor(5, 1);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(buf);
break;
case 6:
lcd.setCursor(2, 0);
lcd.print( "Save Settings");
snprintf(buf, 8, "%d", jobNumber);
lcd.setCursor(5, 1);
lcd.print("# ");
lcd.setCursor(6, 1);
lcd.print(buf);
break;
}
valueChanged = false; //next loop() iteration will not enter again
}
void updateSelection()
{
if (stepperMaxSpeed_Selected == true)
{
lcd.setCursor(0, 0);
lcd.write(3);
return;
}
else if (pitch_Selected == true)
{
lcd.setCursor(0, 0);
lcd.write(3);
return;
}
else if (wireDiameter_Selected == true)
{
lcd.setCursor(0, 0);
lcd.write(3);
return;
}
else if (feeder_Selected == true)
{
lcd.setCursor(0, 0);
lcd.write(3);
return;
}
else if (autoWinding_Selected == true)
{
lcd.setCursor(0, 0);
lcd.write(3);
return;
}
else if (microStepping_Selected == true)
{
lcd.setCursor(0, 0);
lcd.write(3);
return;
}
else if (saving_Selected == true)
{
lcd.setCursor(0, 0);
lcd.write(3);
return;
}
else //If a menu item is unselected, this else branch is performed and the ">" symbol is erased
{
snprintf(buf, 8, "%s", " ");
lcd.setCursor(0, 0);
lcd.print(buf);
return;
}
}
void flipCheck() //this function constantly polls the pins. It is not the most efficient practice...
{
if (LeftSwitch.isPressed())
{
leftPosition=feederStepper.currentPosition();
switched = !switched;
dir = -1 * dir;
turnsOffset = numberOfTurns;
valueChanged = true;
}
if (RightSwitch.isPressed())
{
rightPosition = feederStepper.currentPosition();
switched = !switched;
dir = -1 * dir;
turnsOffset = numberOfTurns;
valueChanged = true;
}
}
void get_dfe() {
if (Serial.available() > 0) {
dfe += char(Serial.read());
if (dfe.length() > 3 && dfe.substring(0, 3) != "CMD") dfe = "";
else {
if (dfe.substring((dfe.length() - 1), dfe.length()) == "?") {
String command = dfe.substring(3, 6);
String value = dfe.substring(6, dfe.length() - 1);
Serial.println(command + " : " + value);
if (command == "FON") {
Serial.println(digitalRead(A));
Serial.println(digitalRead(B));
feederStepper.moveTo(0);
feederStepper.run();
}
if (command == "FOF") {
Forward = 0;
// senddata = 1;
}
if (command == "RON") {
Backward = 1;
// senddata = 0;
}
if (command == "ROF") {
Backward = 0;
// senddata = 1;
}
dfe = "";
delay(20);
}
}
}
}
void writeJobToEEPROM(int jobNumber, int stepperMaxSpeed, float pitch, int feederMicroStepping) {
int addressToWrite = eeAddress + jobSize * (jobNumber - 1);
Job job = { jobNumber, stepperMaxSpeed, pitch, feederMicroStepping };
EEPROM.put(addressToWrite, job);
// EEPROM.commit();
}
Job readJobFromEEPROM(int jobNumber) {
int addressToRetrieve = eeAddress + jobSize * (jobNumber - 1);
Job retrievedJob;
EEPROM.get(addressToRetrieve, retrievedJob);
return retrievedJob;
}
void SAVING()
{
writeJobToEEPROM(jobNumber, stepperMaxSpeed, pitch, feederMicroStepping);
lcd.setCursor(0, 1);
lcd.write(3);
delay(1500 );
lcd.setCursor(0, 1);
lcd.print(" ");
}
void checkSteps()
{
if (Forward==1){
feederStepper.setCurrentPosition(0);
feederStepper.move(dir * feederSteps);
Forward=0;
}else
if (Backward==1){
feederStepper.setCurrentPosition(0);
feederStepper.move(-dir * feederSteps);
Backward=0;
}
while(feederStepper.distanceToGo() != 0)
{
feederStepper.setSpeed(stepperMaxSpeed);
feederStepper.runSpeedToPosition();
}
}