Creating a public locker

i’m doing a public locker, and this is my following plan for it:
the menu will ask you A for a new locker, and B to open an existing locker. i will be using a keypad, an eight
channel relay for each locker, and a 0x27 20x4 lcd screen.

next, After A or b it will ask you for a locker number

then, it processes if you put one or not. if it didn’t detect one, it will restart you. if it did, it
will say which locker you chose, and ask you to make a password, and it will open it once you put a 4 digit password.

i’ve been doing it well so far, until i realized my ClearData(); function doesn’t do what i intend it to do. can somebody find a way to clear the Data so I can reuse it? thanks! the code is below:

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>//declares the library
#include <Keypad.h>

#define Password_Length 2//declares the amount of letters in the password-1


int i;
int Arelay = A0;
int otherArelay = A1;

char Data[Password_Length] = {0};
char Master[Password_Length]; 
byte data_count = 0, master_count = 0;
bool Pass_is_good;
char customKey;

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] = 
  {7, 6, 5, 4};
byte colPins[COLS] = 
  {3, 2, 1, 0};

Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

LiquidCrystal_I2C lcd(0x27, 20, 4);  

void setup()
  {
  lcd.init(); 
  lcd.backlight();
  pinMode(Arelay, OUTPUT);
  pinMode(Arelay, LOW);
  pinMode(otherArelay, OUTPUT);
  pinMode(otherArelay, LOW);
   for(i = 8; i < 14; i++)
  {
    pinMode(i, OUTPUT);
    pinMode(i, LOW);
  }
}

void loop()
  {
  lcd.home();
  lcd.print("Welcome!");
  lcd.setCursor(0,2);
  lcd.print("A for new locker");
  lcd.setCursor(0,3);
  lcd.print("B to open locker");

  customKey = customKeypad.getKey();//none of this has to do with the issue
  if (customKey)
    {
    Data[data_count] = customKey; 
    data_count++; 
    
    if(!strcmp(Data, "A")) // if somebody put in A in the Keypad, do this
    {
      ClearData();
      
      lcd.clear(); // clear the screen
      lcd.print("Choose Locker Number");// write on the upper left corner of the lcd screen "Choose Locker Number"
      delay(5000);// wait 5000 milliseconds
      lcd.clear();
           if(!strcmp(Data, "1"))
        {
          lcd.print("Choose Password for");
          lcd.setCursor(0,1);
          lcd.print("locker number:");
          lcd.setCursor(0,3);
          lcd.print(Data); 
          delay(7500);
        } 
        else if(!strcmp(Data, "2"))
        {
          lcd.print("Choose Password for");
          lcd.setCursor(0,1);
          lcd.print("locker number:");
          lcd.setCursor(0,3);
          lcd.print(Data); 
          delay(7500);
        }  
        else if(!strcmp(Data, "3"))
        {
          lcd.print("Choose Password for");
          lcd.setCursor(0,1);
          lcd.print("locker number:");
          lcd.setCursor(0,3);
          lcd.print(Data); 
          delay(7500);
        }    
        else if(!strcmp(Data, "4"))
        {
          lcd.print("Choose Password for");
          lcd.setCursor(0,1);
          lcd.print("locker number:");
          lcd.setCursor(0,3);
          lcd.print(Data); 
          delay(7500);
        } 
        else if(!strcmp(Data, "5"))
        {
          lcd.print("Choose Password for");
          lcd.setCursor(0,1);
          lcd.print("locker number:");
          lcd.setCursor(0,3);
          lcd.print(Data); 
          delay(7500);
        } 
        else if(!strcmp(Data, "6"))
        {
          lcd.print("Choose Password for");
          lcd.setCursor(0,1);
          lcd.print("locker number:");
          lcd.setCursor(0,3);
          lcd.print(Data); 
          delay(7500);
          lcd.clear(); //clears the screen again
      lcd.print("PROCESSING...");
      delay(7000);
      lcd.clear();
        } 
        else if(!strcmp(Data, "7"))
        {
          lcd.print("Choose Password for");
          lcd.setCursor(0,1);
          lcd.print("locker number:");
          lcd.setCursor(0,3);
          lcd.print(Data); 
          delay(7500);
          lcd.clear(); //clears the screen again
      lcd.print("PROCESSING...");
      delay(7000);
      lcd.clear();
        } 
        else if(!strcmp(Data, "8"))
        {
          lcd.print("Choose Password for");
          lcd.setCursor(0,1);
          lcd.print("locker number:");
          lcd.setCursor(0,3);
          lcd.print(Data); 
          delay(7500);
          lcd.clear(); //clears the screen again
      lcd.print("PROCESSING...");
      delay(7000);
      lcd.clear();
        } 
        else
        {
          lcd.clear();
          lcd.print("Invalid Response:");
          lcd.setCursor(0,1);
          lcd.print(Data);
          lcd.setCursor(0,2);
          lcd.print("Please try again");
          delay(2500);
          lcd.clear();
          ClearData();
        }
         
    }
    else if(!strcmp(Data, "B")) // if somebody put in B in the keypad, do this:
    {
      ClearData();
      lcd.clear();//clears the screen
      lcd.print("Choose Locker Number");// prints the following on the lcd screen
      delay(5000);// pauses the screen for 5000 milliseconds
      lcd.clear();//clears the screen
    }
    else// otherwise:
    {
      lcd.clear();//clear the screen
      lcd.print("Invalid Answer:");// write the following on the lcd
      lcd.setCursor(0,1);//set the cursor to the next line
      lcd.print(Data);// prints what the person put on the keypad
      lcd.setCursor(0,2);//sets the cursor to the next line
      lcd.print("please try again");// writes that on the lcd
      delay(2500);// pauses that screen 2500 milliseconds
      lcd.clear();//clears the screen
      ClearData();
    }
    

    ClearData();
    }
  }
     
  


void ClearData()//declares the function
  {
  while(data_count !=0)//basically the rest of this deletes the data of the code, has nothing to do with the issue
  {
    Data[data_count--] = 0; 
  }
  return;
}

davidgao1345:
until i realized my ClearData(); function doesn’t do what i intend it to do.

What does it actually do and what do you want it to do?

It seems to me if you just want to write a 0 in each byte it would be simpler like this

for (byte n = 0; n < Password_Length; n++) {
   Data[n] = 0;
}

You should probably also set the variable data_count to 0 at the same time.

…R

Didn't look in depth but, compare these two excerpts from your listing:

char hexaKeys[ROWS][COLS] = 
  {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
//
//
    if(!strcmp(Data, "A")) // if somebody put in A in the Keypad, do this

In the first you have single characters enclosed in single quotes, which is correct. In the second the single character is inside double quotes.

Run this little sketch and observe the results on the serial monitor.

void setup() {
 Serial.begin(115200);
char test='A';

if(strcmp(test,'b')) Serial.println("match");
else Serial.println("not matched");

if(strcmp(test,"b")) Serial.println("match");
else Serial.println("not matched");
}

void loop() {
  // put your main code here, to run repeatedly:
}

I intend ClearData(); to remove the data in Data after I selected A or B. it doesn't do that. is there a way to do that?

This won't work, because Data is not properly zero terminated.

    Data[data_count] = customKey;
    data_count++;
   
    if(!strcmp(Data, "A")) // if somebody put in A in the Keypad, do this

To properly terminate Data, do this:

    Data[data_count] = customKey;
    data_count++;
    Data[data_count]=0;

To clear Data (set it to a zero length C-string), do this:

   Data[0]=0;

However, it is much easier to compare characters, which is what you are really doing.

 if (customKey)
   {
    if(customKey == 'A')) // if somebody put in A in the Keypad, do this

Memset()

Oh. later after I get through my current issue, I plan to do it so as if you choose an already chosen locker, then it won't let you choose it. to vacate a locker, then I plan to do a code as to when you open a locker you already chosen, I will do code so it will vacate.

davidgao1345:
I intend ClearData(); to remove the data in Data after I selected A or B. it doesn't do that. is there a way to do that?

Your description of the problem is very unclear. In your Original Post you said "until i realized my ClearData(); function doesn't do what i intend it to do" but now it seems that the problem (as you see it) is not the code within the function - it is the code that calls the function (or fails to do so).

As others have mentioned you need to study the difference between a char array used just to hold one or more individual characters and a char array with a null terminator that is used to hold cstrings.

If you just want to test for a single character there is no need to use strcmp(). You can, for example, do this

if (Data[0] == 'A') {

Note the use of single quotes for single characters

...R

There’s a lot more with your code.

davidgao1345:

if(!strcmp(Data, "A")) // if somebody put in A in the Keypad, do this

{
      ClearData();
   
      lcd.clear(); // clear the screen
      lcd.print(“Choose Locker Number”);// write on the upper left corner of the lcd screen “Choose Locker Number”
      delay(5000);// wait 5000 milliseconds
      lcd.clear();
          if(!strcmp(Data, “1”))
        {
          lcd.print(“Choose Password for”);
          lcd.setCursor(0,1);
          lcd.print(“locker number:”);
          lcd.setCursor(0,3);
          lcd.print(Data);
          delay(7500);
        }

So say someone presses the “A” (now I’m going to ignore the strcmp() issue as described above; just imagine that part is corrected).
Then you tell the user to choose the locker number.
You clear the contents of data.
You wait for 5 seconds.
You check data for containing a 1 (or other single digit - there’s a lot of repetition in your code, that should be addressed as well). Now this will never happen, as you had the Arduino sit on its bits for 5 seconds, not doing anything, including not reading presses on the keypad.
The request for a password is likewise wrong: you ask the Arduino to do nothing, instead of starting to look for keypresses as presumably the user is expected to enter their chosen password here.

Another thing: do remember to store all the user entered passwords in EEPROM. Try to explain to your users: “sorry, there’s been a short power outage, your passwords have been lost and you can’t open your locker any more”. Of course you also have to implement some kind of master password as your users are guaranteed to forget their password, and may have typed it wrong even if you ask them to enter it a second time to confirm.

yes i’ve fixed many issues in my code, but about the part about choosing the locker and having lots of repetition, how do you fix that? i don’t know how to do that. Also, this is an update on my code:

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

#define Locker_Length 2
#define Password_length 5

int used;
int i;
int Arelay = A0;
int otherArelay = A1;

bool Pass_is_good;

const byte ROWS = 4;
const byte COLS = 4;
byte data_count = 0, master_count = 0;
byte data_count2 = 0, master_count2 = 0;
byte rowPins[ROWS] = 
  {7, 6, 5, 4};
byte colPins[COLS] = 
  {3, 2, 1, 0};

char customKey;
char customKey2;
char Data[Locker_Length];
char Data2[Password_length];
char Master[Locker_Length];
char Master2[Password_length];
char hexaKeys[ROWS][COLS] = 
  {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

LiquidCrystal_I2C lcd(0x27, 20, 4);  

void setup()
  {
  lcd.init(); 
  lcd.backlight();
  pinMode(Arelay, OUTPUT);
  pinMode(Arelay, LOW);
  pinMode(otherArelay, OUTPUT);
  pinMode(otherArelay, LOW);
   for(i = 8; i < 14; i++)
  {
    pinMode(i, OUTPUT);
    pinMode(i, LOW);
  }
}

void loop()
  {
  lcd.home();
  lcd.print("Welcome!");
  lcd.setCursor(0,2);
  lcd.print("Enter locker number:");
  customKey = customKeypad.getKey();//none of this has to do with the issue
  if (customKey)
    {
    Data[data_count] = customKey; 
    data_count++; 
 
    if (Data[0] == '1')
    {
      
      lcd.clear();
      lcd.print("Choose Password:");
      delay(6000);
      lcd.clear();
      customKey2 = customKeypad.getKey();
  if (customKey2)
        { 
          Data2[data_count2] = customKey2;
          data_count2++;  
     
          }
        lcd.print("PROCESSING...");
        delay(7500);
        lcd.clear();
        lcd.print("Password Set!");
        lcd.setCursor(0,1);
        lcd.print("Your Password:");
        lcd.setCursor(16,1);
        lcd.print(Data2);
        delay(3000);
        lcd.clear();
        }
      }
    }
 
  


void ClearData()
  {
  while(data_count !=0) {
    Data[data_count--] = 0; 
  }
  return;
}

also, I removed the A for new locker or B to open existing locker because either way it says choose locker number.

davidgao1345:
but about the part about choosing the locker and having lots of repetition, how do you fix that? i don't know how to do that.

Arrays. First, get the locker number into an integer numeric type. Then you can use that as the index into an array of locker info. Note that arrays can only store like items - ints, chars, and so on. If you want different data types associated with each locker you can to step up to structures.

You could also use a state machine to step through the entry/assignment process. These are constructed with switch/case statements.

If you go this route don't try to do it all at once. Make a small sketch which illustrates the workings of arrays and then incorporate that into the project. Continue with switch/case, etc.

The highlighted terms are explained at the reference page (excepting structures) and there's a cornucopia of sources on the web explaining these things and there are tutorials on this site.

It's not 100% accurate but, the several instances of

      if (!strcmp(Data, "1"))
      {
        lcd.print("Choose Password for");
        lcd.setCursor(0, 1);
        lcd.print("locker number:");
        lcd.setCursor(0, 3);
        lcd.print(Data);
        delay(7500);
      }

could be reduced to one set of code like this but using array references array[element] instead of "1" and Data.

thank you, but is there a way for it to handle much bigger values? for example, in my code, I also implement 4 digit codes, which would take forever to write with switch/case codes or arrays.

The 4-digit code, that’s a PIN, right? Two options for that.

  1. store it as a string, as characters: e.g. “3456”, then use strcmp() to compare with the stored values. Requires four bytes to store. Then you use the keypad input directly. Easily extend to longer pin codes, if needed. Takes more bytes accordingly.
  2. convert the keypad input into an int value using atoi(), and compare that with a normal boolean == operator to the stored value. Requires two bytes to store. An unsigned int can store values of up to 65535, so maximum 4-digit pin codes. For longer codes, use a 32-bit unsigned long, that can store pin codes of up to 9 digits. Takes 4 bytes. In that case, use atol() instead.

I’d go for the second option. Easier, more efficient storage.

Thanks. can you tell me how to do that? i've never learned atol or atoi before and can't seem to find a good tutorial of that. Thanks!

Google search term: c++ atoi
Or: c++ atoi tutorial

wvmarle:

  1. store it as a string, as characters: e.g. "3456", then use strcmp() to compare with the stored values. Requires four bytes to store.

OOPS

That should be 5 bytes because there must be space for the terminating null ('\0')

...R

We’re talking C++ so as usual: it depends :slight_smile:

When storing with fixed length in EEPROM - it’s a pin so fixed length is in this case a sensible requirement - you don’t need the null. For storing in EEPROM I’d always use fixed length records, easier to look up. Even for storing in RAM four bytes is enough, it’s just that string functions don’t get the concept, so that’d require five bytes indeed :slight_smile:

wvmarle:
Even for storing in RAM four bytes is enough, it's just that string functions don't get the concept, so that'd require five bytes indeed :slight_smile:

It was your own Reply #13 that proposed using strcmp() so it seems to me that it is just confusing matters to tell the OP that 4 bytes is enough in some cases. Much easier for a newbie if just one solution is proposed and I doubt if the extra space for the 5th byte will be an obstacle.

...R

True, true.