A problem with multiple conditions

Hi everyone,

I’m struggling to write a program that will be used for drilling holes in a metal rail. The number of holes needs to be entered first, then the distance from the beginning of the rail to the each hole, and in the end the length of the rail is entered.
The values are entered using a regular 4x4 keypad, and the distance is measured by an encoder that is connected to the motor shaft.
There are two 16x2 LCD displays with I2C, one is for displaying encoder value, and the other for displaying entered value.
One part of the code needs to run only when the previous is completed: a part used to enter the distance to each hole should only run when the number of holes is entered.
The program should display a message on one display that I need to enter a number of holes, after I enter that, it should display that I need to enter the distance, and so on.
When I start the program written below the lcd that tells me what to enter (for example “Enter the number of holes” or “Enter the distance to the hole 1”) somehow tries to display all messages at once. So there seems to be a problem with these conditions. Does anyone have any idea?

#include<Wire.h>
#include<LiquidCrystal_I2C.h>
#include<Encoder.h>
#include<Keypad.h>


//definisanje tastera za tastaturu
LiquidCrystal_I2C lcd1(0x27, 16, 2);
LiquidCrystal_I2C lcd2(0x23, 16, 2);
const byte ROWS=4;
const byte COLS=4;
char hexaKeys [ROWS][COLS]={
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'} 
};
byte rowPins[ROWS] = {4, 5, 6, 7};
byte colPins[COLS] = {8, 9, 10, 11};
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); 
Encoder myEnc(2,3);





int pin1 = 14; //starts the motor that moves the rail
int relay1 = 15; //starts the relay that is used or drilling the rail
int relay2 = 16; //starts the relay or cutting the rail to the length entered 
long debounce = 400;
unsigned long newTime;
unsigned long oldTime;
unsigned long newTime2;
unsigned long oldTime2;
int dt = 2000;
bool testRange = true;
bool testRange2 = true;
String niz1=""; //or entering the number of holes
String niz2=""; //for entering the length of the whole rail
String niz3=""; //for entering the length to the each rail
int zad_vr[20];  //entered value, its an array because the rail needs to have several holes
int encvr;  //encoder value
int prevvr; //previous value
int duzina; //length of the rail
int br_rupa; //number of holes on a rail
int i,j,k;
int reading; //used to read from a button, has a pullup
int error=2;  //
int previous=HIGH;
int newPosition=0;
long time = 0;


void setup() {
  lcd1.init();  //za enkoder
  lcd2.init();  //sa tastature
  lcd1.backlight();
  lcd2.backlight();
  pinMode(13,INPUT_PULLUP);
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(pin1,OUTPUT);//uses analog pin as digital out
}



void loop() {
  char keyin=customKeypad.getKey();
  lcd1.setCursor(0,0);
  lcd2.setCursor(0,0);
  newTime = millis();
  newTime2 = millis();


  //This part is for entering a number of holes that need to be drilled
  lcd2.print("Uneti broj rupa");
  if(keyin){
    if(isDigit(keyin)){
      niz1+=(char)keyin;
      lcd2.clear();
      delay(10);
      lcd2.print(niz1);
      br_rupa=niz1.toInt();
    }
      else if (keyin=='#'){
      niz1.remove(0);
      br_rupa=niz1.toInt();
      lcd2.clear();
      delay(10);
      lcd2.print(br_rupa);
    }
  }


  // When the number of holes is entered, this part of code is used
  // to enter the distance to each hole
  if(br_rupa!=0){
    for(j=0;j<br_rupa;j++){
      lcd2.print("Uneti rastojanje: ");
      lcd2.print(j+1);
      if(keyin){
        if(isDigit(keyin)){
         niz3+=(char)keyin;
       lcd2.clear();
       delay(10);
        lcd2.print(niz3);
        zad_vr[j]=niz3.toInt();
        niz3="";
        }
        else if (keyin=='#'){
        niz3.remove(0);
        zad_vr[j]=niz3.toInt();
        lcd2.clear();
        delay(10);
        lcd2.print(zad_vr[j]);
      }
      }
    }}


    //This part of code is used to enter the length of the metal rail, after the other two
    //values have been entered
  if(zad_vr!=NULL){
  lcd2.print("Uneti duzinu:");
  if(keyin){
    if(isDigit(keyin)){   //proverava da li je uneta vrednost neka cifra
      niz2 += (char)keyin;    //od unetih cifara kreira niz
      lcd2.clear();
      delay(10);
      lcd2.print(niz2);
      duzina=niz2.toInt();     //konvertuje dobijeni niz u broj (int)
      }
      else if (keyin=='#'){
      niz2.remove(0);
      duzina=niz2.toInt();
      lcd2.clear();
      delay(10);
      lcd2.print(duzina);
    }
      else if (keyin=='*'){
      myEnc.write(0);
      //lcd1.clear();
      //delay(10);
      ///*lcd1.print(encvr);
    }
  }}

  
  //after entering all those values, pin13 is used with a pull up to start the machine
  //pin14 starts the motor that moves the rail
  reading = digitalRead(13);
  if (reading == LOW && previous == HIGH && millis() - time > debounce) {
    time = millis();  
    digitalWrite(pin1,HIGH);
   }

  
  //this part compares the value from the encoder and the entered value
  previous = reading;
  encvr=(myEnc.read())*446/400;
  if(encvr!=prevvr){
    lcd1.clear();
    delay(10);
    prevvr=encvr;
  }
  lcd1.print(encvr);
    for(k=0;k<br_rupa;k++){
    if (encvr >= zad_vr[k] && testRange) {
      digitalWrite(pin1, LOW);
      digitalWrite(relay1,HIGH);
      oldTime = newTime;
      testRange = false;
     } else if (newTime - oldTime >= dt) {
        oldTime = newTime;
        digitalWrite(relay1, LOW);
        digitalWrite(pin1,HIGH);
      }
       if(encvr<(zad_vr[k]-error) || encvr>=(zad_vr[k]+error)){
       testRange = true;
       }
    }
  if (encvr >= duzina && testRange2){
    digitalWrite(relay2,HIGH);
    oldTime2 = newTime2;
    testRange2=false;
  }else if (newTime2 - oldTime2 >= dt) {
    oldTime2 = newTime2;
    digitalWrite(relay2, LOW);
  }
  if(encvr<(duzina-error)){
    testRange2 = true;
  }
}

I see a couple of things:

  1. getKey() does not wait for a key to be pressed and returns NO_KEY if a key was not pressed. You can use waitForKey() if you want to wait for a key to be pressed, otherwise "Uneti broj rupa" will be displayed for every time loop() is called.

  2. The following test will always be true:

if(zad_vr!=NULL){

because zad_vr is the address of the array and the array cannot be located at address 0! Hence, you are entering that code block every time loop() is called.

Since you need to have a specific sequence you could use a state machine architecture for this code.

The code to get user input in a way that is convenient for the user and that can deal with the usual user errors is often (always?) longer and more complicated than the rest of the code that actually makes a project work.

Have a look at the simple user input example in Planning and Implementing a Program. Note how the request and response are dealt with separately. And be aware that the example is very simple and a lot more would be needed for a practical system that could deal with errors.

Note also that by breaking the code into several short single-purpose functions it will be much easier to develop, test, debug and maintain.

...R

Guys thank you so much for your answers. I apologize for not being able to respond sooner. You have helped me a lot!

Hi, it’s me again :smiley:

I have solved the previous problem by using switch and case, so that the one part o the program can’t run if the other hasn’t been completed. Now I have encountered another problem. Case 2 is used to enter the number of big holes that need to be drilled in a rail, after that it goes to Case 3 where it should, for each hole, ask the user to enter the distance, that means that this part will be in a loop, with a condition that the counter is less or equal to the number of holes. When this is done it should go to Case 4. But the problem appears with Case 3. It seems that the loop (while loop) is creating some troubles. I’ve tried the program without the while loop ( so basically it only asks once to enter the distance) and it works. I think that maybe the part where I need to print the message “enter the value” and then print the keypad values creates some trouble with the while loop, maybe it slows it down.

I forgot to add, when I upload the program, and I start entering the values with a keypad, as soon as it goes to Case 3, the display prints a bunch of ones (the display fills up with 1s)

Could it be that this is too much for Arduino? Should i try to program it like a regular microcontroller, using arduino IDE but not using arduino commands? What do you think? Please ask if you need some clarification about the code. And thank you in advance.

#include<Wire.h>
#include<LiquidCrystal_I2C.h>
#include<Encoder.h>
#include<Keypad.h>


//definisanje tastera za tastaturu
LiquidCrystal_I2C lcd1(0x27, 16, 2);
LiquidCrystal_I2C lcd2(0x23, 16, 2);
const byte ROWS=4;
const byte COLS=4;
char hexaKeys [ROWS][COLS]={
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'} 
};
byte rowPins[ROWS] = {4, 5, 6, 7};
byte colPins[COLS] = {8, 9, 10, 11};
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS); 
Encoder myEnc(2,3);

unsigned long currentTime;
unsigned long previousTime2=0;
unsigned long previousTime3=0;
unsigned long previousTime4=0;
unsigned long previousTime5=0;
unsigned long oldTime=0;
int loopTime=5;
String niz1="";
String niz2="";
String niz3="";
String niz4="";
String niz5="";
int duzina;
int br_Vrupa, br_Mrupa;
int state=1;
int debounce=400;
int i=1;
int m=1;
int j,k,n;
int zad_vrV [30], zad_vrM [30];
int start_taster, previous_state; //button that signals the start
int encoder_reading;
int prev_vr;
int zbir;
int error=5;  
bool testRange1=true;
bool testRange2=true;
bool testRange3=true;
int machine_start=14; //relay that moves the rail
int relay1=15;  //relay for big holes
int relay2=16;  //relay for small holes
int relay3=17;  //relay for cutting the rail
int dt=2000;

void setup(){
  lcd1.init();  //printing values from the encoder
  lcd2.init();  //printing values from the keypad
  lcd1.backlight();
  lcd2.backlight();
  pinMode(12,INPUT_PULLUP);
  pinMode(machine_start,OUTPUT);
  pinMode(relay1, OUTPUT);
  pinMode(relay2, OUTPUT);
  pinMode(relay3, OUTPUT);
}


void loop(){
  char keyin=customKeypad.getKey();
  lcd1.setCursor(0,0);
  lcd2.setCursor(0,0);
  currentTime=millis();
  switch (state) {
    case 1:
    lcd2.print("Uneti duzinu:");
    if(keyin){
      if(isDigit(keyin)){   //proverava da li je uneta vrednost neka cifra
      niz1 += (char)keyin;    //od unetih cifara kreira niz
      lcd2.clear();
      lcd2.setCursor(0,1);
      delay(10);
      lcd2.print(niz1);
      duzina=niz1.toInt();     //konvertuje dobijeni niz u broj (int)
      }
      else if (keyin=='#'){
      niz1.remove(0);
      duzina=niz1.toInt();
      lcd2.clear();
      delay(10);
      lcd2.print(duzina);
      }
    
      if(keyin=='A'){
        state=2;
        lcd2.clear();
        delay(10);
    }
  }
    break;

    case 2:
    
    lcd2.print("Uneti br V");  //enter the number of big holes
    if(keyin){
      if(isDigit(keyin)){
        niz2+=(char)keyin;
        lcd2.clear();
        delay(10);
        lcd2.setCursor(0,1);
        lcd2.print(niz2);
        br_Vrupa=niz2.toInt();
        }
      else if (keyin=='#'){
        niz2.remove(0);
        br_Vrupa=niz2.toInt();
        lcd2.clear();
        delay(10);
        lcd2.print(br_Vrupa);
    }
    
    if(keyin=='A'){
        state=3;
        lcd2.clear();
        delay(10);
      }
    }
    break;

    case 3: //this case is used to enter the length to the each big hole
    
    while(i<=br_Vrupa);{
      j=i-1;
      if(keyin){
        if(isDigit(keyin)){
         niz3+=(char)keyin;
         lcd2.print("enter length ");
         lcd2.print(i);
         delay(10);
         lcd2.print(niz3);
         zad_vrV[j]=niz3.toInt();
        }
        else if (keyin=='#'){
          niz3.remove(0);
          zad_vrV[j]=niz3.toInt();
          lcd2.clear();
          delay(10);
          lcd2.print(zad_vrV[j]);
        }
      }
       if(keyin=='A'){
         i++;
         }
      if(i==br_Vrupa){  //when counter i reaches the br_Vrupa value, switch to the next case
        state=4;
      }
    }
    
      break;

      case 4:
      lcd2.clear();
      delay(10);
      lcd2.print("Uneti br M"); //enter the number of small holes
      if(keyin){
        if(isDigit(keyin)){
        niz4+=(char)keyin;
        lcd2.clear();
        lcd2.setCursor(0,1);
        delay(10);
        lcd2.print(niz4);
        br_Mrupa=niz4.toInt();
        }
        else if (keyin=='#'){
        niz4.remove(0);
        br_Mrupa=niz4.toInt();
        lcd2.clear();
        delay(10);
        lcd2.print(br_Mrupa);
        }
       if(keyin=='A'){
        state=5;
        lcd2.clear();
        delay(10);
        }
       }
      break;

      case 5: //this case is used to enter the length to the each small hole
      lcd2.clear();
      delay(10);
      while(m <= br_Mrupa){
        lcd2.print("enter length ");
        lcd2.print(m);
        n=m-1;
        if(keyin){
          if(isDigit(keyin)){
          niz5+=(char)keyin;
          lcd2.clear();
          lcd2.setCursor(0,1);
          delay(10);
          lcd2.print(niz5);
          zad_vrM[n]=niz5.toInt();
        }
        else if (keyin=='#'){
          niz5.remove(0);
          zad_vrM[n]=niz5.toInt();
          lcd2.clear();
          delay(10);
          lcd2.print(zad_vrM[n]);
        }
      
       if(keyin=='A'){
         m++;
        lcd2.clear();
        ;
         }
        }
      break;
  }
  
}
  start_taster=digitalRead(12);
  if(start_taster==LOW && previous_state==HIGH && currentTime-previousTime2 > debounce){
    previousTime2=currentTime;
    digitalWrite(machine_start,HIGH);
  }
  previous_state=start_taster;
  encoder_reading=(myEnc.read())*446/400;
  if(encoder_reading!=prev_vr){
    lcd1.clear();
    delay(10);
    prev_vr=encoder_reading;
  }
  lcd1.print(encoder_reading);
  
  zbir=br_Vrupa+br_Mrupa;
  for(k=0;k<zbir;k++){
    if(encoder_reading>=zad_vrV[k] && encoder_reading<zad_vrM[k]-tolerance && encoder_reading>zad_vrM[k]+tolerance && testRange1){
      digitalWrite(machine_start,LOW);
      digitalWrite(relay1,HIGH);
      previousTime3=currentTime;
      testRange1=false;
      if(currentTime-previousTime3 >= dt){
        previousTime3=currentTime;
        digitalWrite(relay1,LOW);
        digitalWrite(machine_start,HIGH);
      }
      if(encoder_reading<(zad_vrV[k]-error) || encoder_reading>=(zad_vrV[k]+error)){
        testRange1=true;
      }
    }
    else if(encoder_reading>=zad_vrM[k] && encoder_reading<zad_vrV[k]-tolerance && encoder_reading>zad_vrV[k]+tolerance && testRange2){
      digitalWrite(machine_start,LOW);
      digitalWrite(relay2,HIGH);
      previousTime4=currentTime;
      testRange2=false;
      if(currentTime-previousTime4 >= dt){
        previousTime4=currentTime;
        digitalWrite(relay2,LOW);
        digitalWrite(machine_start,HIGH);
      }
      if(encoder_reading<(zad_vrM[k]-error) || encoder_reading>=(zad_vrM[k]+error)){
        testRange2=true;
      }
    }
  }
  if(encoder_reading>=duzina && testRange3){
    digitalWrite(relay3,HIGH);
    previousTime5=currentTime;
    testRange3=false;
    if(currentTime-previousTime5>=dt){
      previousTime5=currentTime;
      digitalWrite(relay3,LOW);
    }
    if(encoder_reading<(duzina-error)){
      testRange3=true;
    }
  }
}

exzibit29:
Could it be that this is too much for Arduino?

No. That is certainly not the case.

IMHO your program is too big and too complex for all the code to be in the loop() function. You should break the program up into several short single-purpose functions. Ideally the code in any function should be short enough so you can see it all on the screen without needing to scroll.

Have a look at how the code is organized in Planning and Implementing a Program. Note especially how little code there is in the loop() function.

...R