control systems for 3 linear actuators and 1 motor using a 4 by 4 key and LCD

Hi there,

I am very new to Arduino and am struggling to design the control system menu for my testing rig. I was hoping if somebody can please help me? I want to be able to use the letters A, B, C, and D to set the displacements and RPM of my linear actuators and Brushless motor speed. I am using a 4 by 4 keypad and a 20 by 4 LCD. For example, I would like to press the button A and then enter a 3 digit number. Also, I would like to use the * button to delete numbers and use the # to save numbers for that selected linear actuator or motor.

At the moment I am only interested in designing the menu part of it. Any advice would be appreciated.

What have you done so far? Does the LCD work? Can you print to it? Does the keypad work? Can you enter characters and print them to serial monitor?

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

// Variables to hold entered number on Keypad
int firstnumber=99; // used to tell how many numbers were entered on keypad
int secondnumber=99;
int thirdnumber=99;
//int Keypadpressedlcd = 0;

// Variables to hold Distance and CurrentPosition
int keyfullnumber=0; // used to store the final calculated distance value
String currentposition = “”; // Used for display on LCD

String displayvalue = “”;

const byte ROWS = 4; //four rows
const byte COLS = 4; //four colums
//define the smybols on the buttons of the keyboard

char keys[ROWS][COLS] = {
{‘1’,‘2’,‘3’,‘A’},
{‘4’,‘5’,‘6’,‘B’},
{‘7’,‘8’,‘9’,‘C’},
{’*’,‘0’,’#’,‘D’}
};

byte rowPins[COLS] = {2,3,4,5}; //connect to the row pinouts of the keypad
byte colPins[ROWS] = {6,7,8,9}; //connect to the colum pinouts of the keypad

//initalize an instance of class NewKeypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

LiquidCrystal_I2C lcd(0x3F,2,1,0,4,5,6,7,3,POSITIVE);
//0x3F is 20 by 4 LCD

void setup() {
// put your setup code here, to run once:
lcd.backlight();
lcd.begin(20,4);
lcd.backlight();
lcd.setCursor(0,0);
lcd.print(“Pin A”);
lcd.setCursor(17,0);
lcd.print(“mm”);
lcd.setCursor(0,1);
lcd.print(“Pin B”);
lcd.setCursor(17,1);
lcd.print(“mm”);
lcd.setCursor(0,2);
lcd.print(“Pin C”);
lcd.setCursor(17,2);
lcd.print(“mm”);
lcd.setCursor(0,3);
lcd.print(“MOTOR Speed”);
lcd.setCursor(17,3);
lcd.print(“RPM”);

}

void loop() {

char key = keypad.getKey(); // Get value of keypad button if pressed
if (key != NO_KEY){ // If keypad button pressed check which key it was
switch (key) {

case ‘A’:
lcd.setCursor(10,0);
break;

case ‘B’:
lcd.setCursor(10,1);
break;

case ‘C’:
lcd.setCursor(10,2);
break;

case ‘D’:
lcd.setCursor(13,3);
break;

case ‘1’:
checknumber(1);
break;

case ‘2’:
checknumber(2);
break;

case ‘3’:
checknumber(3);
break;

case ‘4’:
checknumber(4);
break;

case ‘5’:
checknumber(5);
break;

case ‘6’:
checknumber(6);
break;

case ‘7’:
checknumber(7);
break;

case ‘8’:
checknumber(8);
break;

case ‘9’:
checknumber(9);
break;

case ‘0’:
checknumber(0);
break;
}
}
}

void checknumber(int x){
if (firstnumber == 99) { // Check if this is the first number entered
firstnumber=x;
String displayvalue = String(firstnumber); // Transform int to a string for display
lcd.print(firstnumber);

} else {
if (secondnumber == 99) { // Check if it’s the second number entered
secondnumber=x;
String displayvalue = (String(firstnumber) + String(secondnumber));
lcd.print(secondnumber);

} else { // It must be the 3rd number entered
thirdnumber=x;
String displayvalue = (String(firstnumber) + String(secondnumber) + String(thirdnumber));
lcd.print(thirdnumber);
}
}
}

So you want me to figure out what the code does and what the problem(s) are so that I can help you? I don’t have time. Please describe what the code does and how that differs from your expectations.

Please read the “How to use the forum-Please read” stickies to see how to format and post code. See, especially, #7 and #11 here.

Hi groundFungus,

Thank you for your help, the LCD and Keypad are working. The code is posted above. The issue I have is that I want to be able to press buttons A, B, C or D and then enter a number. At the moment if I enter a number instead of a letter the number will print on the first line of the LCD screen. I think I need to make a "Getnumber" Void function in my switch case but I am not sure if that is the right way or how to make it work.

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

// Variables to hold entered number on Keypad
 int firstnumber=99;  // used to tell how many numbers were entered on keypad
 int secondnumber=99;
 int thirdnumber=99;
//int Keypadpressedlcd = 0;

// Variables to hold Distance and CurrentPosition
int keyfullnumber=0;  // used to store the final calculated distance value
String currentposition = "";  // Used for display on  LCD

 String displayvalue = "";


const byte ROWS = 4; //four rows
const byte COLS = 4; //four colums
//define the smybols on the buttons of the keyboard

char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};

byte rowPins[COLS] = {2,3,4,5}; //connect to the row pinouts of the keypad
byte colPins[ROWS] = {6,7,8,9}; //connect to the colum pinouts of the keypad

//initalize an instance of class NewKeypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

LiquidCrystal_I2C lcd(0x3F,2,1,0,4,5,6,7,3,POSITIVE);
//0x3F is 20 by 4 LCD

void setup() {
  // put your setup code here, to run once:
  lcd.backlight();
  lcd.begin(20,4);
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Pin A");
  lcd.setCursor(17,0);
  lcd.print("mm");
  lcd.setCursor(0,1);
  lcd.print("Pin B");
  lcd.setCursor(17,1);
  lcd.print("mm");
  lcd.setCursor(0,2);
  lcd.print("Pin C");
  lcd.setCursor(17,2);
  lcd.print("mm");
  lcd.setCursor(0,3);
  lcd.print("MOTOR Speed");
  lcd.setCursor(17,3);
  lcd.print("RPM");
  
}

void loop() {

  char key = keypad.getKey();  // Get value of keypad button if pressed
  if (key != NO_KEY){  // If keypad button pressed check which key it was
    switch (key) {

      case 'A':
        lcd.setCursor(10,0);
      break;

      case 'B':
        lcd.setCursor(10,1);
      break;

      case 'C':
        lcd.setCursor(10,2);
      break;

      case 'D':
        lcd.setCursor(13,3);
      break;
      
      case '1':
        checknumber(1);
      break;
        
      case '2':
        checknumber(2);
      break;

      case '3':
        checknumber(3);
      break;

      case '4':
        checknumber(4);
      break;

      case '5':
        checknumber(5);
      break;

      case '6':
        checknumber(6);
      break;

      case '7':
        checknumber(7);
      break;

      case '8':
        checknumber(8);
      break;

      case '9':
        checknumber(9);
      break;

      case '0':
        checknumber(0);
      break;
    }
  }
}



void checknumber(int x){
  if (firstnumber == 99) { // Check if this is the first number entered
    firstnumber=x;
   String displayvalue = String(firstnumber);  //  Transform int to a string for display
   lcd.print(firstnumber);
    
  } else {
    if (secondnumber == 99) {  // Check if it's the second number entered
      secondnumber=x;
      String displayvalue = (String(firstnumber) + String(secondnumber));
      lcd.print(secondnumber);
      
    } else {  // It must be the 3rd number entered
      thirdnumber=x;
      String displayvalue = (String(firstnumber) + String(secondnumber) + String(thirdnumber));
      lcd.print(thirdnumber);
    }
  }
}

For example, I would like to press the button A and then enter a 3 digit number.

You have this backwards. If you press A, then 1, then 2, the Arduino will keep waiting for you to press more keys.

If you press 1, then 2, then A, the Arduino can know that the value is 12, and is to be used for the A purpose.

Hi Paul,

How would I be able to make A, B, C or D the purpose? Should I create a Getnumber() void function? Would the code look something like this?

char key = keypad.getKey();  // Get value of keypad button if pressed
  if (key != NO_KEY){  // If keypad button pressed check which key it was
    switch (key) {

      case 'A':
        lcd.setCursor(10,0);
        Getnumber();
      break;

      case 'B':
        lcd.setCursor(10,1);
        Getnumber();
      break;

      case 'C':
        lcd.setCursor(10,2);
        Getnumber();
      break;

      case 'D':
        lcd.setCursor(13,3);
        Getnumber();
      break;

You want something like this:

   static int number = 0;

   char key = keypad.getKey();
   switch(key)
   {
       case '0'...'9':
          number *= 10;
          number += key - '0';
          break;

       case 'A':
          useForCaseA(number);
          break;

       case '*': // Oops key
          number = 0;
          break;
   }

Add cases for 'B', 'C', and 'D', too.

Hi Paul, thanks for your reply, the Void "useForCaseA()" function, how would I describe that? Would it look something like this?

void useForCaseA(int x){
  if (firstnumber == 99) { // Check if this is the first number entered
    firstnumber=x;
   String displayvalue = String(firstnumber);  //  Transform int to a string for display
   lcd.print(firstnumber);
    lcd.setCursor(10,0);
    
  } else {
    if (secondnumber == 99) {  // Check if it's the second number entered
      secondnumber=x;
      String displayvalue = (String(firstnumber) + String(secondnumber));
      lcd.print(secondnumber);
       lcd.setCursor(10,0);
      
    } else {  // It must be the 3rd number entered
      thirdnumber=x;
      String displayvalue = (String(firstnumber) + String(secondnumber) + String(thirdnumber));
      lcd.print(thirdnumber);
       lcd.setCursor(10,0);
    }
  }
}

Would it look something like this?

Good god, no.

There is NO reason to convert an int to a string, and then wrap that string in a String, so that lcd.print() has to unwrap the String.

lcd.print() is perfectly capable of printing an int.

The value passed to the function is 1 or 12 or 345 or 86544. There is no need to deal with the digits individually.

Hi Paul,

Thanks for your advice, I am a little stuck on how the useForCaseA() Void function should look like? At the moment I put the code like this but the error message that comes up is " 'number' was not declared in this scope"

void useForCaseA(){
 lcd.setCursor(10,0);
 lcd.print(number);
}
void useForCaseA(int num)
{
  lcd.setCursor(10,0);
  lcd.print(num);
}

I would really expect this function to do more with the input than just print it on the lcd.

Hi Paul,

Thank you for your help. Ideally, I want to use the letters on the 4 by 4 matrix keypad to select one of the linear actuator or motor, and then input a number into the system. I want to use the hash Key to save that number and cause the linear actuator to move to the entered distance. If I wanted to input a range of numbers, for example, a number from 1-999, how would I enter that? Would I need to use a "for" loop in the switch case statement part of the code? I just want the be able to enter a 3 digit number into my display.

Many thanks again for your help

I want to use the hash Key to save that number and cause the linear actuator to move to the entered distance.

Why can't the A, B, C, and D keys do that? You'd know when the C key was pressed that the end of the value for the C device had been reached, and that the value was for the C device.

Would I need to use a "for" loop in the switch case statement part of the code?

If you NEED to use Cxxx# to enter the number, yes. And, you use waitForKey(), not getKey().

But, if you could use xxxC, then no. On each pass through loop(), see if a key was pressed. If so, change the value in number, if the key was a number key (or the oops, that's not what I meant key). If the key is a letter key, use the number for the letter's purpose.

I just want the be able to enter a 3 digit number into my display.

Do you mean "exactly three digits, and I'll never make a mistake"? Or, do you mean "up to 3 digits, and I might press the wrong key sometimes"?

The issue I have with using the numbers and then the letter is when I type in a number xxxC and then another 3 digits and then a letter A (for example), it combines the numbers together. I thought using the letter digit and hash key would be an easier interface. If I wanted to go with the letter number and then hash key how would I do it?

At the moment this is the code below:

  static int number = 0;
  char key = keypad.waitForKey();
   switch(key)
   {
       case '0'...'9':
          number *= 10;
          number += key - '0';
          break;

       case 'A':
          useForCaseA(number);
          //lcd.setCursor(10,0);
          break;

       case 'B':
          useForCaseB(number);
          //lcd.setCursor(10,0);
          break;

       case 'C':
          useForCaseC(number);
          //lcd.setCursor(10,0);
          break;

       case 'D':
          useForCaseD(number);
          //lcd.setCursor(10,0);
          break;

          

       case '*': // Oops key
          number = 0;
          lcd.print(" ");
          break;
   }
}

void useForCaseA(int num){
 lcd.setCursor(10,0);
 lcd.print(num);
}

void useForCaseB(int num){
 lcd.setCursor(10,1);
 lcd.print(num);
}

void useForCaseC(int num){
 lcd.setCursor(10,2);
 lcd.print(num);
}

void useForCaseD(int num){
 lcd.setCursor(13,3);
 lcd.print(num);
}

Also, regarding the 3 digits what I meant was "up to 3 digits, and I might press the wrong key sometimes." Ideally, I wanted to setup a range for the linear actuator to move between, 1mm to about 600mm range.

The issue I have with using the numbers and then the letter is when I type in a number xxxC and then another 3 digits and then a letter A (for example), it combines the numbers together.

After you use the number for the C purpose, you should reset number to 0.

123A456C should result in the value 123 being available for the A purpose and the value 456 being available for the C purpose.

       case 'A':
          useForCaseA(number);
          //lcd.setCursor(10,0);
          number = 0; // Add this to the B, C, and D cases, too.
          break;

Hi Paul,

Thank you, this has been very helpful for my project. I just want to ask another thing regarding the project.

I was thinking of using the # key to work as a "check and start" button. So the # key would check that the interger entered for Pins A B C are within range of 1mm to about 600mm and D between 100-600 RPM. If it is not correct then an error message can prompt up in the second LCD screen (LCD2) saying "check inputted numbers." If it is correct then it will start moving the pins and then RPM.

oid loop() {

   static int number = 0;
  char key = keypad.waitForKey();
   switch(key)
   {
       case '0'...'9':
          number *= 10;
          number += key - '0';
          break;

       case 'A':
          useForCaseA(number);
         // lcd.setCursor(10,0);
          number = 0;
         // lcd.print("   ");
           
          //lcd.setCursor(10,0);
          break;

       case 'B':
          useForCaseB(number);
          number = 0;     
          //lcd.setCursor(10,0);
          break;

       case 'C':
          useForCaseC(number);
          number = 0;
          //lcd.setCursor(10,0);
          break;

       case 'D':
          useForCaseD(number);
          number = 0;
          //lcd.setCursor(0,3);
          //lcd.print("MOTOR Speed");
          //lcd.setCursor(17,3);
          //lcd.print("RPM");
          //lcd.setCursor(10,0);
          break;

       case '*': // Oops key
          number = 0;
          lcd.print("");
          break;

 case '#': // check and start.
          
          break;

   }
}

void useForCaseA(int numA){
 lcd.setCursor(10,0);
 lcd.print("       ");
 lcd.setCursor(10,0);
 lcd.print(numA);
}

void useForCaseB(int numB){
 lcd.setCursor(10,1);
 lcd.print("       ");
 lcd.setCursor(10,1);
 lcd.print(numB);
}

void useForCaseC(int numC){
 lcd.setCursor(10,2);
 lcd.print("       ");
 lcd.setCursor(10,2);
 lcd.print(numC);
}

void useForCaseD(int numD){
 lcd.setCursor(13,3);
 lcd.print("    ");
 lcd.setCursor(13,3);
 lcd.print(numD);
}

I tried to create a void CheckNumber and Void ErrorMessage function. The Void CheckNumber is creating to check the range when the Hash key is pressed. If not all the conditions are met then it will promote an error message in the second screen.

void loop() {

   static int number = 0;
  char key = keypad.waitForKey();
   switch(key)
   {
       case '0'...'9':
          number *= 10;
          number += key - '0';
          break;

       case 'A':
          useForCaseA(number);
          number = 0;
          break;

       case 'B':
          useForCaseB(number);
          number = 0;     
          break;

       case 'C':
          useForCaseC(number);
          number = 0;
          break;

       case 'D':
          useForCaseD(number);
          number = 0;
        
          break;

       case '*': // Oops key
          number = 0;
          lcd.print("");
          break;

       case '#':// Check and Start Key
       
       break;
       
}
   }

void useForCaseA(int numA){
 lcd.setCursor(10,0);
 lcd.print("       ");
 lcd.setCursor(10,0);
 lcd.print(numA);
}

void useForCaseB(int numB){
 lcd.setCursor(10,1);
 lcd.print("       ");
 lcd.setCursor(10,1);
 lcd.print(numB);
}

void useForCaseC(int numC){
 lcd.setCursor(10,2);
 lcd.print("       ");
 lcd.setCursor(10,2);
 lcd.print(numC);
}

void useForCaseD(int numD){
 lcd.setCursor(13,3);
 lcd.print("    ");
 lcd.setCursor(13,3);
 lcd.print(numD);
}

void CheckNumber(){
if((numA >1) && (numA<600)){};
if((numB >1) && (numB<600)){};
if((numC >1) && (numC<600)){};
if((numD >100) && (numD<600)){};
else{ErrorMessage()}
  
}

void ErrorMessage(){ //Error message
  //Display messages for (0x3E) 16 by 2 LCD
  lcd2.setCursor(0,0);
  lcd2.print("Error Please");
  lcd2.setCursor(0,1);
  lcd2.print("Check input values");
  delay(10000);
  lcd2.clear();
  
  lcd2.begin(16,2);
  lcd2.backlight();
  lcd2.setCursor(0, 0);
  lcd2.print("No of Cycles:");
}
oid loop() {

loop() is not an oid function.

case '#': // check and start.
         
          break;

You forgot to call useTheStoredDataIfItIsGoodOtherwiseDisplayAnError() function.