Waiting for 'Blank' help

Hey! One of my projects involves creating a small layout of electronics that will control a RGB Led. I have wired everything up correctly. I am using a 3x4 Keypad, a 16x2 LCD screen with an adapter, and an RGB led. I am in the very beginning of this code and I have a slight problem. When attempting to print the numbers into strings and later into the input for the RGB led, I get a slight problem.

The problem is that I can't seem to write for each individual string. I don't really know how to explain this so please forgive me if I'm doing a bad job. I want to be able to press a button on my keypad, that button press will be translated into a number for a string ("string[Red, Blue, or Green][One, Two, or Three]"). I seem to be only able to write for the first string and I can't figure out how to fix this. A wait function or while function maybe? The wait function just delays it for a few seconds and I want to be able to wait for as much time as needed. As for the while function, I don't know how to use it.

My program is below, I am just working on the "red" right now. I apologize for my code if it seems a bit messy or rudimentary. I haven't yet put in the editing of the RGB led color.

/* This code allows you top input values via a 3x4 keypad which will display
on the lcd screen in number format and alter the RGB Led color. You can switch
which color you edit with the * button and finalize your current number with
the # button. The current values will be displayed at the bottom.

Pins:
Arduino to lcd interface
5V to VCC
Gnd to Gnd
A5 to SCL
A4 to SDA

Arduino to keypad
8 to 1
7 to 2
6 to 3
5 to 4
4 to 5
3 to 6
2 to 7

Arduino to RGB
~220ohm resistors between digital pins and color pins
9 to Red
10 to Blue
11 to Green
Gnd to Gnd
*/



String stringRedInput, stringRedOne,stringRedTwo, stringRedThree;           
String stringGreenInput, stringGreenOne, stringGreenTwo, stringGreenThree;  //Creates all the strings
String stringBlueInput, stringBlueOne, stringBlueTwo, stringBlueThree;      

int color = 1;  //Allows control over waht color you are writing
int currentString = 1; //Tells you what string number to write on

int redPin = 9;
int greenPin = 10;  //Sets up the individual color pins
int bluePin =11;


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

const byte ROWS = 4; //Four rows
const byte COLS = 3; //Three columns
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'}, //Creates the keypad
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {5, 4, 3, 2}; //Connect to the row pinouts of the keypad
byte colPins[COLS] = {8, 7, 6}; //Connect to the column pinouts of the keypad

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);


Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup(){
  lcd.begin(16,2);
  lcd.clear();
  
  //TEST
  Serial.begin(9600);
  //TEST

  stringRedInput = String();
  stringRedOne = String(); //Initializes red strings
  stringRedTwo = String();
  stringRedThree = String();
  
  stringBlueInput = String();
  stringBlueOne = String();  //Initializes blue strings
  stringBlueTwo = String();
  stringBlueThree = String();
  
  stringGreenInput = String();
  stringGreenOne = String();  //Initializes green strings
  stringGreenTwo = String();
  stringGreenThree = String();
}

void loop(){
  char key = keypad.getKey();
  lcd.setCursor(0,0);
  
  if(key == '*'){
  color+=1;
  }
  
  if(color == 4){
  color = 0;
  }
  
  if(color == 1){
    lcd.print("Red: ");
    if(key != '*'){
      lcd.setCursor(5,0);
      
      stringRedOne = String(key);
      lcd.print(stringRedOne);
      
      char keyTwo = keypad.getKey();
      stringRedTwo = String(keyTwo);
      lcd.print(stringRedTwo);
      
      char keyThree = keypad.getKey();
      stringRedThree = String(keyThree); 
      lcd.print(stringRedThree);
      
      stringRedInput = stringRedOne + stringRedTwo + stringRedThree;
      Serial.println(stringRedInput);
    }
  }
  
  if(color == 2){
  lcd.print("Green: ");
  }
  
  if(color == 3){
    lcd.print("Blue: ");
  }
  
  lcd.setCursor(0,1);  //Prints this line on the bottom of the LCD display
  lcd.print("R" + stringRedInput + " G" + stringGreenInput + " B" + stringBlueInput);
  
}

The problem is with the section below I think.

  if(color == 1){
    lcd.print("Red: ");
    if(key != '*'){
      lcd.setCursor(5,0);
      
      stringRedOne = String(key);
      lcd.print(stringRedOne);
      
      char keyTwo = keypad.getKey();
      stringRedTwo = String(keyTwo);
      lcd.print(stringRedTwo);
      
      char keyThree = keypad.getKey();
      stringRedThree = String(keyThree); 
      lcd.print(stringRedThree);
      
      stringRedInput = stringRedOne + stringRedTwo + stringRedThree;
      Serial.println(stringRedInput);
    }
}

[code]

The Serial.print function was just so I could see what was happening without the LCD. It just displayed a whole bunch of blank characters until I pressed a number, which displayed then was swept away by the blank characters.

Any help appreciated! Thanks!

I want to be able to press a button on my keypad, that button press will be translated into a number for a string

This doesn't make sense, unfortunately.

      stringRedOne = String(key);

Making a f**king String to wrap ONE character is a complete waste.

      char keyTwo = keypad.getKey();
      stringRedTwo = String(keyTwo);

This assumes that getKey() waits for a key press. It does NOT. It pisses away resources, too.

You need to store the key presses, when they happen, in a char array, NULL terminated. You need to ditch the String class altogether.

PaulS:
This doesn't make sense, unfortunately.

      stringRedOne = String(key);

Making a f**king String to wrap ONE character is a complete waste.

      char keyTwo = keypad.getKey();

stringRedTwo = String(keyTwo);



This assumes that getKey() waits for a key press. It does NOT. It pisses away resources, too.

You need to store the key presses, when they happen, in a char array, NULL terminated. You need to ditch the String class altogether.

That's fantastic that you replied and seemed to dislike how I wrote my code. I apologize, I do not know how to do this and I am learning. Unfortunately I do not understand how to or what you mean by creating a character array because as I said, I am new at this. Can you or someone else please elaborate how I can go about making this array?

Can you or someone else please elaborate how I can go about making this array?

char vals[10];
byte index = 0;

void loop()
{
   char key = keypad.getKey();
   if(key != NO_KEY)
   {
      if(index < 9)
        vals[index++] = key;
      vals[index] = '\0';
   }

   // Now, you have a string (NOT a String) of pressed keys.
   // You need some way of communicating "use this data"
   // You need to see if the "use this data" trigger has arrived
   // You need to use the data, and then set index back to 0
}

You have no way of knowing this, but using the String class enrages PaulS.

It is somewhat resource-hungry. If you look up C arrays you will see what he is talking about. An array is a sequence (vector if you like) of elements, in the example he gave, of single characters.

PaulS:

char vals[10];

byte index = 0;

void loop()
{
  char key = keypad.getKey();
  if(key != NO_KEY)
  {
      if(index < 9)
        vals[index++] = key;
      vals[index] = '\0';
  }

// Now, you have a string (NOT a String) of pressed keys.
  // You need some way of communicating "use this data"
  // You need to see if the "use this data" trigger has arrived
  // You need to use the data, and then set index back to 0
}

Ok... I am still unsure how I would implement this into my code..

... EDIT, misread post

Perhaps if you clarify this bit?

I want to be able to press a button on my keypad, that button press will be translated into a number for a string ("string[Red, Blue, or Green][One, Two, or Three]"). I seem to be only able to write for the first string and I can't figure out how to fix this.

Ok. So being uneducated in this my basic approach was to assign a variable (a string) for each digit of the three numbers I would use to control the RGB Led. For instance, if I wanted to make the color 'red' have a 'strength' of 225 I would push the button 2, then 2 again, then 5. This number would be easy for me to input using a serial monitor yet using a keypad as an input is brand new to me (just got it last week and haven't had all that much time with it). So what I figured is that for each button press I would need a variable, then I would organize, not add, these variables at the end to create my end number.

Say I wanted the number 147 now

I would press the number 1 which would make the string stringRedOne = '1'
Then I would press the number 4 which would make the string stringRedTwo = '4'
Finally I would press 7 which would make the string stringRedThree = '7'
Then I would combine the strings together into the stringRedInput string.
I would then take this and use the parseInt function(I don't know the terminology, sorry) to make the string a number.
That number I would then use to analogWrite my final input value for red.

That was my goal but I ran into a problem early on with how to get it to wait for each key press before running over the entire code and just rewriting my first value. The main problem now it seems, is that I am using the wrong library for this or that it could be much simpler..?

Please help. Thank you.

Sidenote: after the last 'B' on the bottom line, whenever I type in any number with the keypad it produces a second 'B' after the first. This doesn't seem to affect anything and is very inconsequential to the program and the way I want to run it. Nonetheless if you could also help with that it would be great!

What if you want the number 5? Do you type 005?

That was my goal but I ran into a problem early on with how to get it to wait for each key press before running over the entire code and just rewriting my first value.

Well, how would you handle it if I read out a number to you over the phone? You start writing, and you keep adding to the end, until some "end condition" is reached, eg. exactly 3 digits, or some "end marker".

A simple way is to take each digit, and multiply the previous number by 10 and add the new digit.

eg.

Initial result is zero.
Press 2: Multiply previous result by 10, and add the new one. Result is 2
Press 2: Multiply previous result by 10, and add the new one. Current result: 22
Press 5: Multiply previous result by 10, and add the new one. Current result: 225.

Okay, I will try rewriting the way the end number is created with the way you explained. I tried reading (skimming) over what you said with statemachines but I don't see how this applies/ I don't understand how it works. How do I get the program to wait IN the loop without it repeating until I have given it 3 numbers? Could I use a "wait until function" or something of the sort? E.g. waitUntil(Key received){}? Is there anything like that?

Don't wait "in the loop".

A state machine basically handles inputs and processes them in light of earlier states.

Something like (pseudo code):

function loop:

if (button pressed)
  {
  handle_button_press
  }

...

function handle_button_press:

  previous number = previous number x 10
  previous number = previous number + new button
  number of presses = number of presses + 1
  if (number of presses >= 3)
    {
    handle final number
    }

My example code on the linked page actually does something quite similar. Notice in the middle:

  receivedNumber *= 10;
  receivedNumber += digit - '0';

RedStar,

You can read and store keypresses into a buffer and then evaluate that or you can do what Nick showed which is evaluate each press when it is read and just store the value without buffering.

I can tell you from long experience that what Nick showed you takes less code and runs quicker.

But still you have to work out how the code knows what keypress goes with what color. Like as he asked, what do you do when the number will be less than 3 digits? That's not a world-ender but you need to decide which way it will work and BTW, leading zeroes is what a lot of industrial machines use instead of decimal points so don't be shy. Maybe for you the # key will signify entry complete.

Me, I would use 3 pots (for R G B) connected to 3 analog pins and divide the results by 4 to PWM to the led pins but that's just me. You may be experimenting with keypad and LCD rather than the led.

How do I get the program to wait IN the loop without it repeating until I have given it 3 numbers?

You don't.

You make a variable to serve as a flag when input is ready or not and put the code that uses the input inside of an if ( flag > 0 ) { use the input here }.

That way the keypad always runs. And the led always runs. And the next thing will also always run.

When you make things wait then you can only have things happen when the wait and the logic it is inside allows. Every new thing, every change means changing the logic. The more things inside of that logic, the more things to go wrong and need even more code for any change. What that leads to is the dreaded spaghetti code.

Here is an extremely simplified form of don't-wait code.

It takes in text from serial monitor and immediately prints back the hex values of the characters.
When it reads a newline character, it also prints a newline.

Serial monitor must be set to 115200 baud and next to where you set that, set monitor to add newline to every line typed in. Both are on the bottom right corner of serial monitor.

This example only shows different actions in separated pieces of code and how information and control is passed from one to the next.

Nothing waits for anything else. That means if I add more inputs and outputs that also do not block loop() then everything will run together.

If loop() is a fence around a field then the modules inside are my dogs and sheep. The dogs know what to do and I can whistle to them any time, and the sheep go where the dogs make them. Okay?

void setup( void )
{
  Serial.begin( 115200 );
  Serial.println( "\nserial connected..." );
}

void loop( void )
{
  static byte ser, flag;
  
  if ( Serial.available()) // zero is false, non-zero is true
  {
    ser = Serial.read();
    flag = 1;
  }
  
  if ( flag ) // zero is false, non-zero is true
  {
    flag = 0;
    if ( ser < 16 )
    {
      Serial.print( '0' );
    }
    Serial.print( ser, HEX );
    if ( ser == '\n' ) // if newline
    {
      Serial.println();
    }
    else
    {
      Serial.print( " " );
    }
  }
}