// Wokwi: https://wokwi.com/projects/443714371674366977
// For: https://forum.arduino.cc/t/automatic-wire-cutter-with-servo-and-stepper-motor/1037774
//
// WARNING : The circuit is made by guessing.
// Even the Fritzing schematic was unreadable.
//
// LCD syntax: LiquidCrystal(rs, enable, d4, d5, d6, d7)
// Pin 14 is A0 ?
// Pin 15 is A1 ?
//
// Changes by ec2021
//------------------------------- librarys ----------------------------------
#include <LiquidCrystal.h>
#include <Servo.h>
//------------------------------- lcd ----------------------------------
LiquidCrystal lcd(12, 11, 2, 3, 4, 5);
//------------------------------- stepper ----------------------------------
constexpr uint8_t stepPin {7};
constexpr uint8_t dirPin {8};
//------------------------------- servo ----------------------------------
Servo snippers;
constexpr uint8_t servo {10};
constexpr uint8_t openAngle {180};
constexpr uint8_t closedAngle {0};
//------------------------------- input ----------------------------------
constexpr uint8_t leftButton {14};
constexpr uint8_t rightButton {9};
constexpr uint8_t upButton {15};
constexpr uint8_t downButton {6};
//------------------------------- user settings ----------------------------------
unsigned int wireLength = 0;
unsigned int wireQuantity = 0;
//------------------------------- system settings ----------------------------------
constexpr unsigned long debouncingDelay {30}; // ms
constexpr unsigned long buttonRepeat {300}; // ms
enum States {HOME, LENGTH, QUANTITY, CONFIRM, CUTTING, FINISHED };
States state = HOME;
int incrementSpeed = 1;
int previousWireLength = 0;
int previousWireQuantity = 0;
float mmPerStep = 0.18096;
boolean displayChange = true;
class buttonClass {
private:
uint8_t pin;
uint8_t state;
uint8_t lastState;
boolean contPressed = false;
unsigned long lastChange = 0;
unsigned long changeToLow = 0;
public:
void init(uint8_t aPin) {
pin = aPin;
pinMode(pin, INPUT_PULLUP);
};
boolean continuousPressed() {
boolean cPress = contPressed;
contPressed = false;
return cPress;
}
boolean pressed() {
uint8_t actState = digitalRead(pin);
if (actState != lastState) {
lastChange = millis();
lastState = actState;
}
if (state != actState && millis() - lastChange > debouncingDelay) {
state = actState;
if (!state) {
changeToLow = lastChange;
} else {
changeToLow = 0;
contPressed = false;
}
return !state;
}
if (state == LOW && millis() - changeToLow > buttonRepeat) {
changeToLow = millis();
contPressed = true;
}
return false;
}
} buttonLeft, buttonRight, buttonUp, buttonDown;
void setup() {
Serial.begin(9600);
lcd.begin(16, 2); //LCD columns and rows
buttonLeft.init(leftButton);
buttonRight.init(rightButton);
buttonUp.init(upButton);
buttonDown.init(downButton);
pinMode(stepPin, OUTPUT);
pinMode(dirPin, OUTPUT);
snippers.attach(servo);
snippers.write(openAngle);
delay(1000);
changeStateTo(HOME);
}
void loop() {
switch (state) {
case HOME:
homeScreen();
onButtonChangeTo(HOME, LENGTH);
break;
case LENGTH:
chooseWireLength();
onButtonChangeTo(HOME, QUANTITY);
break;
case QUANTITY:
chooseWireQuantity();
onButtonChangeTo(LENGTH, CONFIRM);
break;
case CONFIRM:
confirm();
onButtonChangeTo(QUANTITY, CUTTING);
break;
case CUTTING:
currentlyCutting();
break;
case FINISHED:
finishedCutting();
onButtonChangeTo(HOME, CONFIRM);
if (state == CONFIRM) {
wireLength = previousWireLength;
wireQuantity = previousWireQuantity;
}
break;
}
}
void clearDisplay() {
lcd.clear();
displayChange = true;
}
void changeStateTo(States nextState) {
state = nextState;
clearDisplay();
}
void onButtonChangeTo(States left, States right) {
if (buttonLeft.pressed()) {
changeStateTo(left);
};
if (buttonRight.pressed()) {
changeStateTo(right);
};
}
void homeScreen() {
if (displayChange) {
displayChange = false;
lcd.setCursor(0, 0);
lcd.print("CUTTER");
lcd.setCursor(11, 1);
lcd.print("NEXT>");
}
}
void chooseWireLength() {
wireLength = changeValue(wireLength);
//clear LCD if required
if (previousWireLength != wireLength) {
previousWireLength = wireLength;
clearDisplay();
}
//Display information on LCD
if (displayChange) {
displayChange = false;
lcd.setCursor(0, 0);
lcd.print("LENGTH: ");
lcd.print(wireLength);
lcd.print(" mm");
displayNavigation();
}
}
void chooseWireQuantity() {
wireQuantity = changeValue(wireQuantity);
//clear LCD if required
if (previousWireQuantity != wireQuantity) {
clearDisplay();
previousWireQuantity = wireQuantity;
}
//Display information on LCD
if (displayChange) {
displayChange = false;
lcd.setCursor(0, 0);
lcd.print("QUANTITY: ");
lcd.print(wireQuantity);
displayNavigation();
}
}
int changeValue(int currentValue) {
if (buttonUp.pressed() || buttonUp.continuousPressed()) {
currentValue += incrementSpeed;
}
if (buttonDown.pressed() || buttonDown.continuousPressed()) {
if (currentValue >= incrementSpeed) {
currentValue -= incrementSpeed;
}
else {
currentValue = 0;
}
}
if (buttonDown.pressed() && buttonUp.pressed()) {
incrementSpeed = 1;
}
return currentValue;
}
void displayNavigation() {
lcd.setCursor(0, 1);
lcd.print("<BACK");
lcd.setCursor(11, 1);
lcd.print("NEXT>");
}
void confirm() {
if (wireLength == 0) {
printError("LENGTH = 0 mm");
changeStateTo(LENGTH);
return;
}
if (wireQuantity == 0) {
printError("QUANTITY = 0");
changeStateTo(QUANTITY);
return;
}
if (displayChange) {
displayChange = false;
lcd.setCursor(0, 0);
lcd.print(wireLength);
lcd.print(" mm x ");
lcd.print(wireQuantity);
lcd.print(" pcs");
lcd.setCursor(0, 1);
lcd.print("<BACK");
lcd.setCursor(10, 1);
lcd.print("START>");
}
}
void printError(const char *txt) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ERROR!");
lcd.setCursor(0, 1);
lcd.print(txt);
delay(2000);
}
void currentlyCutting() {
lcd.setCursor(0, 0);
lcd.print(0);
lcd.print("/");
lcd.print(wireQuantity);
lcd.setCursor(0, 1);
lcd.print("? s");
int stepsToTake = (int)wireLength / mmPerStep;
for (int i = 0; i < wireQuantity; i++) {
unsigned long timeForOneCycle = millis();
digitalWrite(dirPin, HIGH);
for (int x = 0; x < stepsToTake; x++) {
digitalWrite(stepPin, HIGH);
delayMicroseconds(500);
digitalWrite(stepPin, LOW);
delayMicroseconds(500);
}
lcd.setCursor(0, 0);
lcd.print(i + 1);
lcd.print("/");
lcd.print(wireQuantity);
snippers.write(closedAngle);
delay(500);
snippers.write(openAngle);
delay(500);
lcd.setCursor(0, 1);
unsigned long timeRemaining = ((millis() - timeForOneCycle) * (wireQuantity - (i + 1))) / 1000;
lcd.print(timeRemaining);
lcd.print(" s ");
}
wireLength = 0;
wireQuantity = 0;
changeStateTo(FINISHED);
}
void finishedCutting() {
if (displayChange) {
displayChange = false;
lcd.setCursor(0, 0);
lcd.print("REPEAT CUTTING?");
lcd.setCursor(0, 1);
lcd.print("<NO");
lcd.setCursor(11, 1);
lcd.print("YES>");
}
}