Trouble with keypad and loops...

Hello all I am a total noob and here looking for some advice on where to go next. My project is a wire length cutter, got the idea from a few examples online.

hardware:
Arduino mega 1260
big easy driver for stepper motor
I2C LCD (16,2)
servo for cutters
4x4 membrane keypad for input

theory:
user inputs wire length and wire quantity via keypad.
stepper feeds wire.
servo cuts wire.
repeat to desired quantity

simple in theory, but I cant quite get all the parts to collaborate with each other.

this is what I have so far:
(please excuse the mess i'm still learning):

//------------------------------- librarys ----------------------------------
#include <Wire.h>
#include <Servo.h>
#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
#include <kissStepper.h>
//------------------------------- motors ------------------------------------
static const uint8_t PIN_MS1 = 47;
static const uint8_t PIN_MS2 = 48;
static const uint8_t PIN_MS3 = 49;
static const uint8_t PIN_DIR = 52;
static const uint8_t PIN_STEP = 53;
static const uint8_t PIN_ENABLE = 46;

#define servo 11
#define openAngle 0
#define closedAngle 90
#define Go 12
#define Back 13

//------------------------------- system settings ----------------------------------
const byte numRows = 4;
const byte numCols = 4;
char keymap[numRows][numCols] =
{
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[numRows] = {44, 42, 40, 38};
byte colPins[numCols] = {39, 41, 43, 45};
Keypad kpd = Keypad(makeKeymap(keymap), rowPins, colPins, numRows, numCols);
char key = kpd.getKey();
//char waitForKey();
//int loopcount = 0;

LiquidCrystal_I2C lcd(0x27, 16, 2);
//int incomingByte = 0;
int state = 0;
int previousWireLength = 0;
int previousWireQuantity = 0;
float cmPerStep = 0.017115;
unsigned int wireLength = 0;
unsigned int wireQuantity = 0;
const int stepsPerRevolution = 200;//
const int stepsToTake = (int)wireLength / cmPerStep;

static const uint8_t DRIVE_MODE = 4; // drive mode (number of microsteps taken, eg 1/8th stepping = 8)
static const uint16_t REVOLUTION_FULL_STEPS = 200; // number of full steps in one revolution of the test motor (see your motor's specs/datasheet)
static const uint32_t REVOLUTION_PULSES = REVOLUTION_FULL_STEPS * DRIVE_MODE; // number of microsteps in one revolution of the test motor
kissStepper mot(PIN_DIR, PIN_STEP, PIN_ENABLE);
Servo snippers;

void setup() {

  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  kpd.begin(9600);
  Wire.begin();
  mot.begin();

  pinMode(Go, INPUT_PULLUP);
  pinMode(Back, INPUT_PULLUP);
  pinMode(PIN_MS1, OUTPUT);
  pinMode(PIN_MS2, OUTPUT);
  pinMode(PIN_MS3, OUTPUT);
  pinMode(PIN_DIR, OUTPUT);
  pinMode(PIN_STEP, OUTPUT);
  pinMode(PIN_ENABLE, OUTPUT);

  lcd.setCursor(0, 0);
  lcd.print("CUTMEISTER!");
  snippers.attach(servo);
  snippers.write(openAngle);
  delay(1000);
}

void loop()
{

  if (!digitalRead(Go)) {
    if (state == 5) {
      state = 0;
    }
    else {
      state += 1;
    }
    delay(200);
    lcd.clear();
  }
  if (!digitalRead(Back) && state > 0 && state < 4) {
    state -= 1;
    delay(200);
    lcd.clear();
  }

  switch (state) {
    case 0:
      homeScreen();
      break;
    case 1:
      chooseWireLength();
      break;
    case 2:
      chooseWireQuantity();
      break;
    case 3:
      confirm();
      break;
    case 4:
      currentlyCutting();
      break;
    case 5:
      finishedCutting();
      break;
  }
}

void homeScreen() {
  lcd.setCursor(0, 0);
  lcd.print("CUTMEISTER!");
  lcd.setCursor (7, 1);
  lcd.print("PRESS GO>");
  delay(100);
}

void chooseWireLength() {


  //clear LCD if required
  if (previousWireLength != wireLength) {
    lcd.clear();
    previousWireLength = wireLength;
  }
  wireLength = 12;
//---"wireLength = int changeValue();" will go here in place of number once keypad works.---


  {
    //Display information on LCD
    lcd.setCursor(0, 0);
    lcd.print("LENGTH:" + (String)wireLength + "cm");
    displayNavigation();
  }
}

void chooseWireQuantity() {

  wireQuantity = 5;
//---"wireQuantity = int changeValue();" will go here in place of number once keypad works.---

  //clear LCD if required
  if (previousWireQuantity != wireQuantity) {
    lcd.clear();
    previousWireQuantity = wireQuantity;
  }
  //Display information on LCD
  lcd.setCursor(0, 0);
  lcd.print("QUANTITY:" + (String)wireQuantity);
  Serial.println("QUANTITY:" + (String)wireQuantity);
  displayNavigation();
}
void confirm() {
  lcd.setCursor(0, 0);
  lcd.print((String)wireLength + "cm x " + (String)wireQuantity + "pcs");
  Serial.println((String)wireLength + "cm x " + (String)wireQuantity + "pcs");
  lcd.setCursor(0, 1);
  lcd.print("<BACK");
  lcd.setCursor(10, 1);
  lcd.print("START>");
  delay(100);
}
void currentlyCutting() {
  lcd.setCursor(0, 0);
  lcd.print((String)0 + "/" + (String)wireQuantity);
  lcd.setCursor(0, 1);
  lcd.print("???s");
  Serial.println("WireLength =" + (String)wireLength);
  Serial.println("TotalSteps =" + (String)stepsToTake);

  //  int stepsToTake = (int)wireLength / cmPerStep;
  unsigned long timeForOneCycle = millis();
  int i = 0;

  while (i < wireQuantity) {
    feed();
    Cut();
    i ++;
  }
  lcd.setCursor(0, 0);
  lcd.print((String)(i + 1) + "/" + (String)wireQuantity);
  lcd.setCursor(0, 1);
  unsigned long timeRemaining = ((millis() - timeForOneCycle) * (wireQuantity - (i + 1))) / 1000;
  lcd.print((String)timeRemaining + "s    ");

  if (i >= wireQuantity) {
    mot.disable();
    state = 5;
  }
  else {
    feed();
    Cut();
  }
}

void finishedCutting() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("CUTTING COMPLETE");
  lcd.setCursor(10, 1);
  lcd.print("RESET>");
  mot.stop();
  delay(100);
}

int changeValue() { //not fully functional and yes I know there should be more here..

key = kpd.getKey();

}

void displayNavigation() {
  lcd.setCursor(0, 1);
  lcd.print("<BACK");
  lcd.setCursor(10, 1);
  lcd.print("NEXT>");
  delay(100);
}

void feed() {
  int stepsToTake = (int)wireLength / cmPerStep;

  mot.enable();
  mot.setMaxSpeed(REVOLUTION_PULSES);
  mot.setAccel(0);
  mot.isMovingForwards();
  mot.prepareMove(stepsToTake);
  while (mot.move() != STATE_STOPPED);
  mot.move();
}

void Cut() {
  snippers.write(closedAngle);
  delay(600);
  snippers.write(openAngle);
  delay(600);
}

I'm sure its an easy fix that I just don't have the experience to spot.

My main problem:
having trouble getting a value from the keypad to be stored/saved and used later.

And, the "feed(); cut(); loop doesn't function as expected.. it should feed wirelength then cut the wire and repeat until "i >= wireQuantity". the best I have gotten is one cycle of "feed/cut". or it will feed once and get stuck cutting until "i >= wireQuantity".

Everythig else works great.
if anyone has some advice it would be greatly appreciated.

Why int vs unsigned int? Make the same.

int previousWireLength = 0;
int previousWireQuantity = 0;
unsigned int wireLength = 0;
unsigned int wireQuantity = 0;

There is something I don't understand about your currentlyCutting function. Each iteration of the following loop should take care of cutting a wire length with the number of times through the loop taking care of the quantity.

  int i = 0;

  while (i < wireQuantity) {
    feed();
    Cut();
    i ++;
  }

Therefore I am confused by the following code because you have already cut the desired quanity of wire so i should be equal to wireQuantity and therefore you don't need the test. Also, since i is equal to wireQuantity the else will never be executed.

  if (i >= wireQuantity) {
    mot.disable();
    state = 5;
  }
  else {
    feed();
    Cut();
  }

Try this for your currentlyCutting function:

void currentlyCutting() {
  lcd.setCursor(0, 0);
  lcd.print((String)0 + "/" + (String)wireQuantity);
  lcd.setCursor(0, 1);
  lcd.print("???s");
  Serial.println("WireLength =" + (String)wireLength);
  Serial.println("TotalSteps =" + (String)stepsToTake);

  unsigned long timeForOneCycle = millis();

  for (int i = 0; i < wireQuantity; i++) {
    feed();
    Cut();
    lcd.setCursor(0, 0);
    lcd.print((String)(i + 1) + "/" + (String)wireQuantity);
    lcd.setCursor(0, 1);
    unsigned long timeRemaining = ((millis() - timeForOneCycle) * (wireQuantity - (i + 1))) / 1000;
    lcd.print((String)timeRemaining + "s    ");
  }
  mot.disable();
}

In your changeValue() function you should be aware that getKey() is non-blocking. In other words, it does not wait for a key. if no key is pressed it returns the value NO_KEY.

If you don't care about doing any other processing while waiting for key presses I would use waitForKey(). You could collect keypresses until the '#' key is pressed which would let you know the user is done with the input. You can then convert the string into an integer. You will want to make sure key presses are only the digits '0'-'9' or '#'.

ToddL1962, thank you for your advice and fast response! When I get back to my computer I’ll make some changes, test, and report back!

Tried your suggestion and now the counter works to track the cycles and stop the loop at wireQuantity but it keeps hanging on the cut(); function. it feeds once then cuts until the counter reaches the limit but wont feed again. I thought it might be in the cut function, maybe the delays? I also tried to adapt the blinkwithoutdelay into the cut function as follows:

void cut() {

  
  if (currentMillis - previousServoMillis >= servoInterval) {
        
    previousServoMillis += servoInterval;
    
    servoPosition = servoPosition + servoDegrees; 
    }

    if ((servoPosition >= servoMaxDegrees) || (servoPosition <= servoMinDegrees))  {
         
      servoDegrees = - servoDegrees;
         
      servoPosition = servoPosition + servoDegrees; 
    }
    
    snippers.write(servoPosition);
       
  }

doing this closes the servo but does not open it.

9fingerednick:
Tried your suggestion and now the counter works to track the cycles and stop the loop at wireQuantity but it keeps hanging on the cut(); function. it feeds once then cuts until the counter reaches the limit but wont feed again.

I don't think it is your cut function from what you have stated. Since it cuts until the proper wire quantity is met that means it is properly entering and exiting the cut function. There must some issue with your feed function. I recommend the following:

  1. Remove the mot.enable, mot.setMaxSpeed, mot.setAccel, and mot.isMovingForwards calls from the feed() function. The mot.isMovingForwards() call does nothing but return a Boolean indicating whether the motor is moving or not. The other calls can be called once from setup().

  2. Remove the last mot.move() call from the feed() function. The motor has completely moved by this point and has already stopped.

  3. Remove the mot.stop() call from the finishedCutting() function. It is redundant since the motor has already stopped.

  4. Remove the mot.disable() call from the currentlyCutting() function. The motor will be enabled during setup() and left enabled. Once you get the feed working properly you can enable and disable the motor dynamically.

  5. Remove the following lines from setup(). The stepper library should be configuring the pins.

  pinMode(PIN_DIR, OUTPUT);
  pinMode(PIN_STEP, OUTPUT);
  pinMode(PIN_ENABLE, OUTPUT);
  1. Add these lines to the setup() function after mot.begin():
  mot.enable();
  mot.setMaxSpeed(REVOLUTION_PULSES);
  mot.setAccel(0);

With these recommendations your setup() function should like this:

void setup() {

  pinMode(Go, INPUT_PULLUP);
  pinMode(Back, INPUT_PULLUP);
  pinMode(PIN_MS1, OUTPUT);
  pinMode(PIN_MS2, OUTPUT);
  pinMode(PIN_MS3, OUTPUT);

  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  kpd.begin(9600);
  Wire.begin();

  mot.begin();
  mot.setMaxSpeed(REVOLUTION_PULSES);
  mot.setAccel(0);
  mot.enable();

  lcd.setCursor(0, 0);
  lcd.print("CUTMEISTER!");
  snippers.attach(servo);
  snippers.write(openAngle);
  delay(1000);
}

Your feed() function should look like this:

void feed() {
  int stepsToTake = (int)wireLength / cmPerStep;

  mot.prepareMove(stepsToTake);
  while (mot.move() != STATE_STOPPED);
}

Please modify your code, try this, and post your updated code. I will try to help you further if I can.

@ToddL1962
Thanks I really appreciate your help!

updated code as follows:

//------------------------------- librarys ----------------------------------
#include <Wire.h>
#include <Servo.h>
#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
#include <kissStepper.h>
//------------------------------- motors ------------------------------------
static const uint8_t PIN_MS1 = 47;
static const uint8_t PIN_MS2 = 48;
static const uint8_t PIN_MS3 = 49;
static const uint8_t PIN_DIR = 52;
static const uint8_t PIN_STEP = 53;
static const uint8_t PIN_ENABLE = 46;

#define servo 11
#define openAngle 0
#define closedAngle 90
#define Go 12
#define Back 13

//------------------------------- system settings ----------------------------------
const byte numRows = 4;
const byte numCols = 4;
char keymap[numRows][numCols] =
{
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[numRows] = {44, 42, 40, 38};
byte colPins[numCols] = {39, 41, 43, 45};
Keypad kpd = Keypad(makeKeymap(keymap), rowPins, colPins, numRows, numCols);
char key = kpd.getKey();


LiquidCrystal_I2C lcd(0x27, 16, 2);
int state = 0;
unsigned int previousWireLength = 0;
unsigned int previousWireQuantity = 0;
float cmPerStep = 0.017115;
unsigned int wireLength = 0;
unsigned int wireQuantity = 0;
const int stepsPerRevolution = 200;//
const int stepsToTake = (int)wireLength / cmPerStep;

static const uint8_t DRIVE_MODE = 4; // drive mode (number of microsteps taken, eg 1/8th stepping = 8)
static const uint16_t REVOLUTION_FULL_STEPS = 200; // number of full steps in one revolution of the test motor (see your motor's specs/datasheet)
static const uint32_t REVOLUTION_PULSES = REVOLUTION_FULL_STEPS * DRIVE_MODE; // number of microsteps in one revolution of the test motor
kissStepper mot(PIN_DIR, PIN_STEP, PIN_ENABLE);

Servo snippers;
const int servoMinDegrees = 0; // the limits to servo movement
const int servoMaxDegrees = 90;
int servoPosition = 0;     // the current angle of the servo - starting at 90.
int servoInterval = 10;
int servoDegrees = 2;
unsigned long currentMillis = 0;
unsigned long previousServoMillis = 0;

void setup() {

  pinMode(Go, INPUT_PULLUP);
  pinMode(Back, INPUT_PULLUP);
  pinMode(PIN_MS1, OUTPUT);
  pinMode(PIN_MS2, OUTPUT);
  pinMode(PIN_MS3, OUTPUT);

  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  kpd.begin(9600);
  Wire.begin();

  mot.begin();
  mot.setMaxSpeed(REVOLUTION_PULSES);
  mot.setAccel(0);
  mot.enable();

  lcd.setCursor(0, 0);
  lcd.print("CUTMEISTER!");
  snippers.attach(servo);
  snippers.write(openAngle);
  delay(1000);
}

void loop()
{

  if (!digitalRead(Go)) {
    if (state == 5) {
      state = 0;
    }
    else {
      state += 1;
    }
    delay(200);
    lcd.clear();
  }
  if (!digitalRead(Back) && state > 0 && state < 4) {
    state -= 1;
    delay(200);
    lcd.clear();
  }

  switch (state) {
    case 0:
      homeScreen();
      break;
    case 1:
      chooseWireLength();
      break;
    case 2:
      chooseWireQuantity();
      break;
    case 3:
      confirm();
      break;
    case 4:
      currentlyCutting();
      break;
    case 5:
      finishedCutting();
      break;
  }
}

void homeScreen() {
  lcd.setCursor(0, 0);
  lcd.print("CUTMEISTER!");
  lcd.setCursor (7, 1);
  lcd.print("PRESS GO>");
  delay(100);
}

void chooseWireLength() {

//int input[4] = {0};
//for (unsigned cursor = 0; cursor < 4; ++cursor) {// ignored for now. playing with other methods//
//    input[cursor] = kpd.getKey();
//}

  //clear LCD if required
  if (previousWireLength != wireLength) {
    lcd.clear();
    previousWireLength = wireLength;
  }
  wireLength = 12;

  {
    //Display information on LCD
    lcd.setCursor(0, 0);
    lcd.print("LENGTH:" + (String)wireLength + "cm");
    displayNavigation();
  }
}

void chooseWireQuantity() {

  wireQuantity = 5;

  //clear LCD if required
  if (previousWireQuantity != wireQuantity) {
    lcd.clear();
    previousWireQuantity = wireQuantity;
  }
  //Display information on LCD
  lcd.setCursor(0, 0);
  lcd.print("QUANTITY:" + (String)wireQuantity);
  Serial.println("QUANTITY:" + (String)wireQuantity);
  displayNavigation();
}
void confirm() {
  lcd.setCursor(0, 0);
  lcd.print((String)wireLength + "cm x " + (String)wireQuantity + "pcs");
  Serial.println((String)wireLength + "cm x " + (String)wireQuantity + "pcs");
  lcd.setCursor(0, 1);
  lcd.print("<BACK");
  lcd.setCursor(10, 1);
  lcd.print("START>");
  delay(100);
}
void currentlyCutting() {
  lcd.setCursor(0, 0);
  lcd.print((String)0 + "/" + (String)wireQuantity);
  lcd.setCursor(0, 1);
  lcd.print("???s");
  Serial.println("WireLength =" + (String)wireLength);
  Serial.println("TotalSteps =" + (String)stepsToTake);

  unsigned long timeForOneCycle = millis();

  for (int i = 0; i < wireQuantity;) {
    feed();
    cut();
    i++;
    lcd.setCursor(0, 0);
    lcd.print((String)(i + 1) + "/" + (String)wireQuantity);
    lcd.setCursor(0, 1);
    unsigned long timeRemaining = ((millis() - timeForOneCycle) * (wireQuantity - (i + 1))) / 1000;
    lcd.print((String)timeRemaining + "s    ");


    if (i == wireQuantity) {
      state = 5;
    }

  }

}

void finishedCutting() {
  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("CUTTING COMPLETE");
  lcd.setCursor(10, 1);
  lcd.print("RESET>");
  delay(100);
}

//int changeValue() { //not fully functional


void displayNavigation() {
  lcd.setCursor(0, 1);
  lcd.print("<BACK");
  lcd.setCursor(10, 1);
  lcd.print("NEXT>");
  delay(100);
}

void feed() {
  int stepsToTake = (int)wireLength / cmPerStep;

  mot.prepareMove(stepsToTake);
  while (mot.move() != STATE_STOPPED);
}

void cut() {
  snippers.write(closedAngle);
  delay(600);
  snippers.write(openAngle);
  delay(600);
}

still getting the same result, feed once and cut until the count reaches the limit set by wireQuantity.

This is not uncommon in the world of debugging. When faced with such a situation it is usually best to narrow down the problem by only executing the portion of code you suspect is faulty. This is where commenting out code comes in handy. There is no reason to gut your entire program but if it were me I would modify my loop() function to only call the feed() function. Also in the feed function I would include a print statement to ensure that the function is actually executing. Keep in mind there is a possibility the error could be in the library you are using, maybe the result of the order you are calling library functions. You never know. Either way you have to narrow it down.

The following is your sketch which simply calls the feed function once per second and prints a string indicating the feed function is called:

//------------------------------- librarys ----------------------------------
#include <Wire.h>
#include <Servo.h>
#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
#include <kissStepper.h>
//------------------------------- motors ------------------------------------
static const uint8_t PIN_MS1 = 47;
static const uint8_t PIN_MS2 = 48;
static const uint8_t PIN_MS3 = 49;
static const uint8_t PIN_DIR = 52;
static const uint8_t PIN_STEP = 53;
static const uint8_t PIN_ENABLE = 46;

#define servo 11
#define openAngle 0
#define closedAngle 90
#define Go 12
#define Back 13

//------------------------------- system settings ----------------------------------
const byte numRows = 4;
const byte numCols = 4;
char keymap[numRows][numCols] =
{
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[numRows] = {44, 42, 40, 38};
byte colPins[numCols] = {39, 41, 43, 45};
Keypad kpd = Keypad(makeKeymap(keymap), rowPins, colPins, numRows, numCols);
char key = kpd.getKey();


LiquidCrystal_I2C lcd(0x27, 16, 2);
int state = 0;
unsigned int previousWireLength = 0;
unsigned int previousWireQuantity = 0;
float cmPerStep = 0.017115;
unsigned int wireLength = 0;
unsigned int wireQuantity = 0;
const int stepsPerRevolution = 200;//
const int stepsToTake = (int)wireLength / cmPerStep;

static const uint8_t DRIVE_MODE = 4; // drive mode (number of microsteps taken, eg 1/8th stepping = 8)
static const uint16_t REVOLUTION_FULL_STEPS = 200; // number of full steps in one revolution of the test motor (see your motor's specs/datasheet)
static const uint32_t REVOLUTION_PULSES = REVOLUTION_FULL_STEPS * DRIVE_MODE; // number of microsteps in one revolution of the test motor
kissStepper mot(PIN_DIR, PIN_STEP, PIN_ENABLE);

Servo snippers;
const int servoMinDegrees = 0; // the limits to servo movement
const int servoMaxDegrees = 90;
int servoPosition = 0;     // the current angle of the servo - starting at 90.
int servoInterval = 10;
int servoDegrees = 2;
unsigned long currentMillis = 0;
unsigned long previousServoMillis = 0;

void setup() {

  pinMode(Go, INPUT_PULLUP);
  pinMode(Back, INPUT_PULLUP);
  pinMode(PIN_MS1, OUTPUT);
  pinMode(PIN_MS2, OUTPUT);
  pinMode(PIN_MS3, OUTPUT);

  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  kpd.begin(9600);
  Wire.begin();

  mot.begin();
  mot.setMaxSpeed(REVOLUTION_PULSES);
  mot.setAccel(0);
  mot.enable();

  lcd.setCursor(0, 0);
  lcd.print("CUTMEISTER!");
  snippers.attach(servo);
  snippers.write(openAngle);
  delay(1000);
}

void loop()
{
  wireLength = 12;
  feed();
  delay(1000);

/*
  if (!digitalRead(Go)) {
    if (state == 5) {
      state = 0;
    }
    else {
      state += 1;
    }
    delay(200);
    lcd.clear();
  }
  if (!digitalRead(Back) && state > 0 && state < 4) {
    state -= 1;
    delay(200);
    lcd.clear();
  }

  switch (state) {
    case 0:
      homeScreen();
      break;
    case 1:
      chooseWireLength();
      break;
    case 2:
      chooseWireQuantity();
      break;
    case 3:
      confirm();
      break;
    case 4:
      currentlyCutting();
      break;
    case 5:
      finishedCutting();
      break;
  }
  */
}

void homeScreen() {
  lcd.setCursor(0, 0);
  lcd.print("CUTMEISTER!");
  lcd.setCursor (7, 1);
  lcd.print("PRESS GO>");
  delay(100);
}

void chooseWireLength() {

//int input[4] = {0};
//for (unsigned cursor = 0; cursor < 4; ++cursor) {// ignored for now. playing with other methods//
//    input[cursor] = kpd.getKey();
//}

  //clear LCD if required
  if (previousWireLength != wireLength) {
    lcd.clear();
    previousWireLength = wireLength;
  }
  wireLength = 12;

  {
    //Display information on LCD
    lcd.setCursor(0, 0);
    lcd.print("LENGTH:" + (String)wireLength + "cm");
    displayNavigation();
  }
}

void chooseWireQuantity() {

  wireQuantity = 5;

  //clear LCD if required
  if (previousWireQuantity != wireQuantity) {
    lcd.clear();
    previousWireQuantity = wireQuantity;
  }
  //Display information on LCD
  lcd.setCursor(0, 0);
  lcd.print("QUANTITY:" + (String)wireQuantity);
  Serial.println("QUANTITY:" + (String)wireQuantity);
  displayNavigation();
}
void confirm() {
  lcd.setCursor(0, 0);
  lcd.print((String)wireLength + "cm x " + (String)wireQuantity + "pcs");
  Serial.println((String)wireLength + "cm x " + (String)wireQuantity + "pcs");
  lcd.setCursor(0, 1);
  lcd.print("<BACK");
  lcd.setCursor(10, 1);
  lcd.print("START>");
  delay(100);
}
void currentlyCutting() {
  lcd.setCursor(0, 0);
  lcd.print((String)0 + "/" + (String)wireQuantity);
  lcd.setCursor(0, 1);
  lcd.print("???s");
  Serial.println("WireLength =" + (String)wireLength);
  Serial.println("TotalSteps =" + (String)stepsToTake);

  unsigned long timeForOneCycle = millis();

  for (int i = 0; i < wireQuantity; i++) {
    feed();
    cut();
    lcd.setCursor(0, 0);
    lcd.print((String)(i + 1) + "/" + (String)wireQuantity);
    lcd.setCursor(0, 1);
    unsigned long timeRemaining = ((millis() - timeForOneCycle) * (wireQuantity - (i + 1))) / 1000;
    lcd.print((String)timeRemaining + "s    ");
  }
  state = 5;
}

void finishedCutting() {
  
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("CUTTING COMPLETE");
  lcd.setCursor(10, 1);
  lcd.print("RESET>");
  delay(100);
}

//int changeValue() { //not fully functional


void displayNavigation() {
  lcd.setCursor(0, 1);
  lcd.print("<BACK");
  lcd.setCursor(10, 1);
  lcd.print("NEXT>");
  delay(100);
}

void feed() {
  int stepsToTake = (int)wireLength / cmPerStep;
  Serial.print("Entering feed(): stepsToTake = ");
  Serial.println(stepsToTake);
  mot.prepareMove(stepsToTake);
  while (mot.move() != STATE_STOPPED);
  Serial.println("Exiting feed()");
}

void cut() {
  snippers.write(closedAngle);
  delay(600);
  snippers.write(openAngle);
  delay(600);
}

unfortunately it runs once with the loop you provided.. so possibly in the stepper library? I used the kissStepper library because I didn't know how to use the stepper library included with the IDE combined with the big easy and kissStepper was made for it.

this is the loop in the monitor,

01
21:56:14.413 -> Exiting feed()
21:56:15.373 -> Entering feed(): stepsToTake = 701
21:56:15.413 -> Exiting feed()
21:56:16.373 -> Entering feed(): stepsToTake = 701
21:56:16.413 -> Exiting feed()
21:56:17.413 -> Entering feed(): stepsToTake = 701
21:56:17.413 -> Exiting feed()
21:56:18.413 -> Entering feed(): stepsToTake = 701
21:56:18.413 -> Exiting feed()
21:56:19.413 -> Entering feed(): stepsToTake = 701
21:56:19.453 -> Exiting feed()
21:56:20.413 -> Entering feed(): stepsToTake = 701
21:56:20.453 -> Exiting feed()
21:56:21.453 -> Entering feed(): stepsToTake = 701
21:56:21.453 -> Exiting feed()

so it is looping its just not resetting the movement.
I also tried to use only the feed loop a couple ways with the same consistent results.

#include <kissStepper.h>
#include <Wire.h>

static const uint8_t PIN_MS1 = 47;
static const uint8_t PIN_ENABLE = 46;
static const uint8_t PIN_MS2 = 48;
static const uint8_t PIN_MS3 = 49;
static const uint8_t PIN_DIR = 52;
static const uint8_t PIN_STEP = 53;



unsigned int previousWireLength = 0;
unsigned int previousWireQuantity = 0;
float cmPerStep = 0.017115;
unsigned int wireLength = 0;
unsigned int wireQuantity = 0;
const int stepsPerRevolution = 200;//
const int stepsToTake = (int)wireLength / cmPerStep;

static const uint8_t DRIVE_MODE = 4; // drive mode (number of microsteps taken, eg 1/8th stepping = 8)
static const uint16_t REVOLUTION_FULL_STEPS = 200; // number of full steps in one revolution of the test motor (see your motor's specs/datasheet)
static const uint32_t REVOLUTION_PULSES = REVOLUTION_FULL_STEPS * DRIVE_MODE; // number of microsteps in one revolution of the test motor
kissStepper mot(PIN_DIR, PIN_STEP, PIN_ENABLE);

void setup() {

  pinMode(PIN_MS1, OUTPUT);
  pinMode(PIN_MS2, OUTPUT);
  pinMode(PIN_MS3, OUTPUT);

  Serial.begin(9600);
  Wire.begin();
  mot.begin();
  mot.setMaxSpeed(REVOLUTION_PULSES);
  mot.setAccel(0);
  mot.enable();
}

void loop()
{
  /*
  wireLength = 12;
  feed();
  delay(1000);

 
}

void feed() {
*/
  wireLength = 12;
  int stepsToTake = (int)wireLength / cmPerStep;
  Serial.print("Entering feed(): stepsToTake = ");
  Serial.println(stepsToTake);
  mot.prepareMove(stepsToTake);
  while (mot.move() != STATE_STOPPED);
  Serial.println("Exiting feed()");
  delay(1000);
}

Maybe this will help? I have attached the .H text for the library im using.

maybe im just not seing the right keyword in all the lines.. and once again your help is greatly apprecated! :smiley:
and the keywords:

#######################################
# Syntax Coloring Map For kissStepper
#######################################

#######################################
# Datatypes (KEYWORD1)
#######################################

kissStepperNoAccel	KEYWORD1
kissStepper	KEYWORD1
kissState_t	KEYWORD1

#######################################
# Methods and Functions (KEYWORD2)
#######################################

begin	KEYWORD2
enable	KEYWORD2
disable	KEYWORD2
setMaxSpeed	KEYWORD2
getMaxSpeed	KEYWORD2
setAccel	KEYWORD2
getAccel	KEYWORD2
getCurSpeed	KEYWORD2
prepareMove	KEYWORD2
move	KEYWORD2
getState	KEYWORD2
decelerate	KEYWORD2
stop	KEYWORD2
setPos	KEYWORD2
getPos	KEYWORD2
getTarget	KEYWORD2
isEnabled	KEYWORD2
isMovingForwards	KEYWORD2
calcMaxAccelDist	KEYWORD2
calcDecelDist	KEYWORD2
getAccelDist	KEYWORD2
getRunDist	KEYWORD2
getDecelDist	KEYWORD2
getDistRemaining	KEYWORD2
setForwardLimit	KEYWORD2
getForwardLimit	KEYWORD2
setReverseLimit	KEYWORD2
getReverseLimit	KEYWORD2

#######################################
# Constants (LITERAL1)
#######################################

STATE_STOPPED	LITERAL1
STATE_STARTING	LITERAL1
STATE_SLEW	LITERAL1
STATE_ACCEL	LITERAL1
STATE_DECEL	LITERAL1

stepperlibrary.txt (10.2 KB)

I also tried this to use my Go button as a loop trigger with the same results.. first cycle triggers, feeds, cuts. then all other triggers only cut.

#include <kissStepper.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>

#define Go 12
#define servo 11
#define openAngle 0
#define closedAngle 90
Servo snippers;
LiquidCrystal_I2C lcd(0x27, 16, 2);
static const uint8_t PIN_MS1 = 47;
static const uint8_t PIN_ENABLE = 46;
static const uint8_t PIN_MS2 = 48;
static const uint8_t PIN_MS3 = 49;
static const uint8_t PIN_DIR = 52;
static const uint8_t PIN_STEP = 53;



unsigned int previousWireLength = 0;
unsigned int previousWireQuantity = 0;
float cmPerStep = 0.017115;
unsigned int wireLength = 0;
unsigned int wireQuantity = 0;
const int stepsPerRevolution = 200;//
const int stepsToTake = (int)wireLength / cmPerStep;

static const uint8_t DRIVE_MODE = 4; // drive mode (number of microsteps taken, eg 1/8th stepping = 8)
static const uint16_t REVOLUTION_FULL_STEPS = 200; // number of full steps in one revolution of the test motor (see your motor's specs/datasheet)
static const uint32_t REVOLUTION_PULSES = REVOLUTION_FULL_STEPS * DRIVE_MODE; // number of microsteps in one revolution of the test motor
kissStepperNoAccel mot(PIN_DIR, PIN_STEP, PIN_ENABLE);

void setup() {

  pinMode(PIN_MS1, OUTPUT);
  pinMode(PIN_MS2, OUTPUT);
  pinMode(PIN_MS3, OUTPUT);
  pinMode(Go, INPUT_PULLUP);

  Serial.begin(9600);
  Wire.begin();
  lcd.init();
  lcd.backlight();
  mot.begin();
  mot.setMaxSpeed(REVOLUTION_PULSES);
  snippers.attach(servo);
  snippers.write(openAngle);
  delay(1000);

}

void loop()
{

  wireLength = 25;
  lcd.setCursor (0, 0);
  lcd.print("CUTTING AT:" + (String)wireLength + "cm");
  lcd.setCursor (7, 1);
  lcd.print("PRESS GO>");
  delay(100);

  if (!digitalRead(Go)) {
    lcd.print("CUTTING!");lcd.print("LENGTH:" + (String)wireLength + "cm");
    feed();
    cut();
    delay(1000);

  }
}


void feed() {

  int stepsToTake = (int)wireLength / cmPerStep;
  mot.move();
  Serial.print("Entering feed(): stepsToTake = ");
  mot.enable();
  Serial.println(stepsToTake);
  mot.prepareMove(stepsToTake);
  while (mot.move() != STATE_STOPPED);
  Serial.println("Exiting feed()");

}

void cut() {
  Serial.println("Entering Cut()");
  snippers.write(closedAngle);
  delay(600);
  snippers.write(openAngle);
  Serial.println("Exiting Cut()");
  delay(600);
}

any recommendations for a better stepper library?

I looked at the code in the library and I'm pretty sure I know what your problem is. It is with the prepareMove() function. The parameter the prepareMove() function takes is a position, not number of steps. The library is checking to see if the stepper is already at the position you are moving it to, and if so will not move the motor!

Try replacing:

  mot.prepareMove(stepsToTake);
  long newTarget = mot.getPos() + stepsToTake;
  mot.prepareMove(newTarget);

your advice got me on the right track and the motor loop is now working great! I can set length and quantity and it loops correctly.

this is what I came up with:

//------------------------------- librarys ----------------------------------
#include <Wire.h>
#include <Servo.h>
#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
#include <kissStepper.h>
//------------------------------- motors ------------------------------------
static const uint8_t PIN_MS1 = 47;
static const uint8_t PIN_ENABLE = 46;
static const uint8_t PIN_MS2 = 48;
static const uint8_t PIN_MS3 = 49;
static const uint8_t PIN_DIR = 52;
static const uint8_t PIN_STEP = 53;


#define servo 11
#define openAngle 0
#define closedAngle 90
#define Go 12
#define Back 13

//------------------------------- system settings ----------------------------------
const byte numRows = 4;
const byte numCols = 4;
char keymap[numRows][numCols] =
{
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[numRows] = {44, 42, 40, 38};
byte colPins[numCols] = {39, 41, 43, 45};
Keypad kpd = Keypad(makeKeymap(keymap), rowPins, colPins, numRows, numCols);
char key = kpd.getKey();


LiquidCrystal_I2C lcd(0x27, 16, 2);
int state = 0;
unsigned int previousWireLength = 0;
unsigned int previousWireQuantity = 0;
float cmPerStep = 0.017115;
unsigned int wireLength = 0;
unsigned int wireQuantity = 0;
const int stepsPerRevolution = 200;//
const int stepsToTake = (int)wireLength / cmPerStep;

static const uint8_t DRIVE_MODE = 4; // drive mode (number of microsteps taken, eg 1/8th stepping = 8)
static const uint16_t REVOLUTION_FULL_STEPS = 200; // number of full steps in one revolution of the test motor (see your motor's specs/datasheet)
static const uint32_t REVOLUTION_PULSES = REVOLUTION_FULL_STEPS * DRIVE_MODE; // number of microsteps in one revolution of the test motor
kissStepperNoAccel mot(PIN_DIR, PIN_STEP, PIN_ENABLE);

Servo snippers;
unsigned long currentMillis = 0;
unsigned long previousServoMillis = 0;

void setup() {

  pinMode(Go, INPUT_PULLUP);
  pinMode(Back, INPUT_PULLUP);
  pinMode(PIN_MS1, OUTPUT);
  pinMode(PIN_MS2, OUTPUT);
  pinMode(PIN_MS3, OUTPUT);

  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  kpd.begin(9600);
  Wire.begin();

  mot.begin();
  mot.setMaxSpeed(REVOLUTION_PULSES);
  mot.enable();

  lcd.setCursor(0, 0);
  lcd.print("CUTMEISTER!");
  snippers.attach(servo);
  snippers.write(openAngle);
  delay(1000);
}

void loop()
{

  if (!digitalRead(Go)) {
    if (state == 5) {
      state = 0;
    }
    else {
      state += 1;
    }
    delay(200);
    lcd.clear();
  }
  if (!digitalRead(Back) && state > 0 && state < 4) {
    state -= 1;
    delay(200);
    lcd.clear();
  }

  switch (state) {
    case 0:
      homeScreen();
      break;
    case 1:
      chooseWireLength();
      break;
    case 2:
      chooseWireQuantity();
      break;
    case 3:
      confirm();
      break;
    case 4:
      currentlyCutting();
      break;
    case 5:
      finishedCutting();
      break;
  }

}

void homeScreen() {
  lcd.setCursor(0, 0);
  lcd.print("CUTMEISTER!");
  lcd.setCursor (7, 1);
  lcd.print("PRESS GO>");
  delay(100);
}

void chooseWireLength() {

  //int input[4] = {0};
  //for (unsigned cursor = 0; cursor < 4; ++cursor) {// ignored for now. playing with other methods//
  //    input[cursor] = kpd.getKey();
  //}
  wireLength = 5;
  //clear LCD if required
  if (previousWireLength != wireLength) {
    lcd.clear();
    previousWireLength = wireLength;
  }


  {
    //Display information on LCD
    lcd.setCursor(0, 0);
    lcd.print("LENGTH:" + (String)wireLength + "cm");
    displayNavigation();
  }
}

void chooseWireQuantity() {

  wireQuantity = 50;

  //clear LCD if required
  if (previousWireQuantity != wireQuantity) {
    lcd.clear();
    previousWireQuantity = wireQuantity;
  }
  //Display information on LCD
  lcd.setCursor(0, 0);
  lcd.print("QUANTITY:" + (String)wireQuantity);
  Serial.println("QUANTITY:" + (String)wireQuantity);
  displayNavigation();
}
void confirm() {
  lcd.setCursor(0, 0);
  lcd.print((String)wireLength + "cm x " + (String)wireQuantity + "pcs");
  Serial.println((String)wireLength + "cm x " + (String)wireQuantity + "pcs");
  lcd.setCursor(0, 1);
  lcd.print("<BACK");
  lcd.setCursor(10, 1);
  lcd.print("START>");
  delay(100);
}
void currentlyCutting() {
  lcd.setCursor(0, 0);
  lcd.print((String)0 + "/" + (String)wireQuantity);
  lcd.setCursor(0, 1);
  lcd.print("???s");
  Serial.println("WireLength =" + (String)wireLength);
  Serial.println("TotalSteps =" + (String)stepsToTake);

  unsigned long timeForOneCycle = millis();

  for (int i = 0; i < wireQuantity; i++) {
    feed();
    cut();
    lcd.setCursor(0, 0);
    lcd.print((String)(i + 1) + "/" + (String)wireQuantity);
    lcd.setCursor(0, 1);
    unsigned long timeRemaining = ((millis() - timeForOneCycle) * (wireQuantity - (i + 1))) / 1000;
    lcd.print((String)timeRemaining + "s    ");
  }
  state = 5;
}

void finishedCutting() {

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("CUTTING COMPLETE");
  lcd.setCursor(10, 1);
  lcd.print("RESET>");
  delay(100);
}

//int changeValue() { //not fully functional


void displayNavigation() {
  lcd.setCursor(0, 1);
  lcd.print("<BACK");
  lcd.setCursor(10, 1);
  lcd.print("NEXT>");
  delay(100);
}

void feed() {
  long curPos = mot.getPos();
  int stepsToTake = (int)wireLength / cmPerStep;
  long newTarget = curPos + stepsToTake;

  Serial.print("Entering feed(): stepsToTake = ");
  mot.enable();
  Serial.println(stepsToTake);
  mot.prepareMove(newTarget);
  while (mot.move() != STATE_STOPPED);
  Serial.println("Exiting feed()");
  
}

void cut() {
  Serial.println("Entering Cut()");
  snippers.write(closedAngle);
  delay(600);
  snippers.write(openAngle);
  Serial.println("Exiting Cut()");
  delay(600);
}

Thank you so much for your help and advice, I appreciate the time you've taken assisting me with my project. Now to tackle the keypad :slight_smile:

Glad it worked out for you!