LCD print arrays of Strings with millis();

Hi guys,
I need to print some arrays of Strings using millis(); Somehow I cannot make my LCD to do that. Probably it's just one silly mistake, but I've spent quite some hours figuring this out. My whole program is 430 lines of code, so instead I am sharing the part I am interested in:

#include <Wire.h>
#include <LiquidCrystal.h> // includes the LiquidCrystal Library 

LiquidCrystal lcd(32, 30, 28, 26, 24, 22);
//LiquidCrystal lcd_name(rs, en, d4, d5, d6, d7);

void setup() {
  //My LCD was 16x2
  lcd.begin (16, 2);
  lcd.home(); // go home

}

void loop() {
  //bunch of other stuff here

  kalba();
}
void kalba() {
  //function telling users they should select the language
  //four languages should switch between with the delay of 2 sec.

  unsigned long currentLCDMillis = millis(); //check current time
  long  interval2 = 2000;
  int previousLCDMillis = 0; //set first old time to 0

  String languages[4] = {"Kalba?", "Language?", "Runa?", "Y3Ik?"};

  if (currentLCDMillis - previousLCDMillis > interval2) {
    for  (int i = 0; i < 4; i++) {
      lcd.setCursor (6, 0);
      lcd.print("       ");           //delete the old text
      lcd.setCursor (6, 0);
      lcd.print(languages[i]);       //This shows every language at the same time, flickering LCD. A pretty nice effect though
      previousLCDMillis = currentLCDMillis;
    }
  }
}

Cheers!

 int previousLCDMillis = 0; //set first old time to 0

Every time the function (kalba()) is called, previousLCDMillis is set to 0. Probably not what you want? Make previousLCDMillis static so that it keeps its value from call to call. And make it unsigned long because it holds a time. The interval2 variable should, also, be unsigned long. All values that deal with time (millis() or micros()) should be unsigned long.

  unsigned long currentLCDMillis = millis(); //check current time
  unsigned long  interval2 = 2000;
  static unsigned long previousLCDMillis = 0; //set first old time to 0

Thanks!
Somehow I haven't copied the code from my full program - thus wrong data types.

After the change LCD shows the last message only. It runs through the function very fast when the interval is passed. It should wait those 2000 millis. I probably made it more complicated than it should be.
I imagine loop should also restart itself once all 4 languages have been displayed.

Now I've made this, but only String no.2 of an array is being showed. Aghh.

void kalba() {
  //function telling users they should select the language
  //four languages should switch between with the delay of 2 sec.

  unsigned long currentLCDMillis = millis(); //check current time
  unsigned long  interval2 = 2000;
  static unsigned long previousLCDMillis = 0; //set first old time to 0


  String languages[4] = {"Kalba?", "Language?", "Runa?", "Y3Ik?"};
  int i = 0;
  if (currentLCDMillis - previousLCDMillis > interval2) {
    i++;
    lcd.setCursor (6, 0);
    lcd.print("       ");           //delete the old text
    lcd.setCursor (6, 0);
    lcd.print(languages[i]);
    previousLCDMillis = currentLCDMillis;
    if (i == 3) {
      i = 0;
    }
  }
}
  int i = 0;
  if (currentLCDMillis - previousLCDMillis > interval2) {
    i++;
    lcd.setCursor (6, 0);
    lcd.print("       ");           //delete the old text
    lcd.setCursor (6, 0);
    lcd.print(languages[i]);

The first time that languages is accessed, i=1. You don't want to increment i until AFTER it has been used the first time.

Since that function is called over and over, it seems likely that i should be static.

And, i is a crappy name for a static variable.

PaulS:

  int i = 0;

if (currentLCDMillis - previousLCDMillis > interval2) {
    i++;
    lcd.setCursor (6, 0);
    lcd.print("      ");          //delete the old text
    lcd.setCursor (6, 0);
    lcd.print(languages[i]);



The first time that languages is accessed, i=1. You don't want to increment i until AFTER it has been used the first time.

Since that function is called over and over, it seems likely that i should be static.

And, i is a crappy name for a static variable.

Thanks, it works better now. Only problem - it does not delete old text. Tried writing spaces on top and use lcd.clear();

I hate the name i also, but isn't it a default name for variables that being incremented?

void kalba() {
  //function telling users they should select the language
  //four languages should switch between with the delay of 2 sec.

  unsigned long currentLCDMillis = millis(); //check current time
  unsigned long  interval2 = 2000;
  static unsigned long previousLCDMillis = 0; //set first old time to 0


  String languages[4] = {"Kalba?", "Language?", "Runa?", "Y3Ik?"};
  int langCycle = 0; //cycle is 0, it is used and waits for millis to hit 2000
  if (currentLCDMillis - previousLCDMillis > interval2) {
    langCycle++;
    lcd.setCursor (6, 0);
    lcd.print("       ");         //delete the old text - DOES NOT WORK
    previousLCDMillis = currentLCDMillis;
  }
  lcd.setCursor (6, 0);
  lcd.print(languages[langCycle]);
  if (langCycle == 3) {
    langCycle = 0;
  }
}

I hate the name i also, but isn't it a default name for variables that being incremented?

There is no such thing as a default name. There are names that are commonly used for certain purposes. i is often used in for loops:

   for(int i=0; i<10; i++)
   {
   }

But, you can use any name you liks:

   for(int sdfjhnhfdhpahb=0; sdfjhnhfdhpahb<10; sdfjhnhfdhpahb++)
   {
   }

Post your current, complete, code. Writing spaces should overwrite text that is there.

Of course, complete code, where lcd.print(" "); won't delete old text :

#include <Wire.h>
#include <LiquidCrystal.h> // includes the LiquidCrystal Library 

LiquidCrystal lcd(32, 30, 28, 26, 24, 22);
//LiquidCrystal lcd_name(rs, en, d4, d5, d6, d7);

int previousLCDMillis = 0; //set first old time to 0

void setup() {
  //My LCD was 16x2
  lcd.begin (16, 2);
  lcd.home(); // go home

}

void loop() {
  //bunch of other stuff here

  kalba();
}
void kalba() {
  //function telling users they should select the language
  //four languages should switch between with the delay of 2 sec.

  unsigned long currentLCDMillis = millis(); //check current time
  unsigned long  interval2 = 2000;
  static unsigned long previousLCDMillis = 0; //set first old time to 0


  String languages[4] = {"Kalba?", "Language?", "Runa?", "Y3Ik?"};
  int langCycle = 0; //cycle is 0, it is used and waits for millis to hit 2000
  if (currentLCDMillis - previousLCDMillis > interval2) {
    langCycle++;
    lcd.setCursor (6, 0);
    lcd.print("         ");         //delete the old text - DOES NOT WORK
    previousLCDMillis = currentLCDMillis;
  }
  lcd.setCursor (6, 0);
  lcd.print(languages[langCycle]);
  if (langCycle == 3) {
    langCycle = 0;
  }
}

langCycle is better, but it still isn't static.

You are writing to the LCD every time that function is called, and only erasing text every two seconds. Immediately after you erase, you write new data, so it's hard to understand why you think that writing spaces isn't erasing data.

PaulS:
langCycle is better, but it still isn't static.

You are writing to the LCD every time that function is called, and only erasing text every two seconds. Immediately after you erase, you write new data, so it's hard to understand why you think that writing spaces isn't erasing data.

But I need langCycle to be dynamic, no? I should be able to switch between languages[4].

[edit] Also langCycle++; doesn't work it seems. Serial.print shows 0 all along.

But I need langCycle to be dynamic, no?

Static doesn't mean that you can't change the value. It means that the Arduino won't reset the value to 0 every time the function gets called.

 if (currentLCDMillis - previousLCDMillis > interval2)

Which of the two different variables named previousLCDMillis is this checking ?
The global one or the local one ?

UKHeliBob:

 if (currentLCDMillis - previousLCDMillis > interval2)

Which of the two different variables named previousLCDMillis is this checking ?
The global one or the local one ?

Local one. Serial.println(currentLCDMillis - previousLCDMillis); shows values 0-2000ish and reseting.

Thanks PaulS, now it works.

#include <Wire.h>
#include <LiquidCrystal.h> // includes the LiquidCrystal Library 

LiquidCrystal lcd(32, 30, 28, 26, 24, 22);
//LiquidCrystal lcd_name(rs, en, d4, d5, d6, d7);

int previousLCDMillis = 0; //set first old time to 0

void setup() {
  //My LCD was 16x2
  lcd.begin (16, 2);
  lcd.home(); // go home
  Serial.begin(9600);
}

void loop() {
  //bunch of other stuff here

  kalba();
}
void kalba() {
  //function telling users they should select the language
  //four languages should switch between with the delay of 2 sec.

  unsigned long currentLCDMillis = millis(); //check current time
  unsigned long  interval2 = 2000;
  static unsigned long previousLCDMillis = 0; //set first old time to 0

  String languages[4] = {"Kalba?", "Language?", "Runa?", "Y3Ik?"};
  static int langCycle = 0; //cycle is 0, it is used and waits for millis to hit 2000
  if (currentLCDMillis - previousLCDMillis > interval2) {
    langCycle++;
    lcd.setCursor (6, 0);
    lcd.print("         ");         //delete the old text - DOES NOT WORK
    previousLCDMillis = currentLCDMillis;
  }
  lcd.setCursor (6, 0);
  lcd.print(languages[langCycle]);

//  Serial.println(langCycle);
//  Serial.println(currentLCDMillis - previousLCDMillis);

  if (langCycle == 4) {
    langCycle = 0;
  }
}

Local one.

I suggest that you get rid of the global one as its only purpose is to confuse anyone reading the code.

You have to be very careful when you have variables with the same name in different scopes, particularly when one of them has global scope. For instance, look at this program

int aVariable = 0;

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  static unsigned long aVariable = millis();
  Serial.println(aVariable);
  delay(1000);
}

Before running it can you predict what it will print ?

Now look at this one

int aVariable = 0;

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  static unsigned long aVariable;
  aVariable = millis();
  Serial.println(aVariable);
  delay(1000);
}

Again, before running it can you predict what it will print ?

I don’t recall ever making that mistake in C. In the industrial computers I work with daily, the local scope takes precedent. Usually it gets interesting when the OIT display doesn’t match the variable in the subroutine. The OIT programs default to the global variables. Several hours after you notice the difference is when you realize you have a local and a global variable of the same name.

Let the OP stew on the issue for while and then post up what happens in C/C++.

UKHeliBob:
Again, before running it can you predict what it will print ?

I would guess first sketch will print millis() and the second - a bunch of zeros?
[after running it] WRONG, other way around.

Yes, now I have no global, seems now everything is working fine.
I will need some custom characters, since this LCD does not support cyrillic alphabet.

code without global:

#include <Wire.h>
#include <LiquidCrystal.h> // includes the LiquidCrystal Library 

LiquidCrystal lcd(32, 30, 28, 26, 24, 22);
//LiquidCrystal lcd_name(rs, en, d4, d5, d6, d7);

void setup() {
  //My LCD was 16x2
  lcd.begin (16, 2);
  lcd.home(); // go home
  Serial.begin(9600);
}

void loop() {
  //bunch of other stuff here

  kalba();
}
void kalba() {
  //function telling users they should select the language
  //four languages should switch between with the delay of 2 sec.

  unsigned long currentLCDMillis = millis(); //check current time
  unsigned long  interval2 = 2000;
  static unsigned long previousLCDMillis = 0; //set first old time to 0

  String languages[4] = {"Kalba?", "Language?", "Runa?", "Yazyk?"};
  static int langCycle = 0; //cycle is 0, it is used and waits for millis to hit 2000
  if (currentLCDMillis - previousLCDMillis > interval2) {
    langCycle++;
    lcd.setCursor (6, 0);
    lcd.print("         ");         //delete the old text - DOES NOT WORK
    previousLCDMillis = currentLCDMillis;
  }
  lcd.setCursor (6, 0);
  lcd.print(languages[langCycle]);

  if (langCycle == 4) {
    langCycle = 0;
  }
}

Ok, since nobody else has said it...

Don't use String™. It can cause hangs and crashes at random times, especially when your program runs for a long time, or when you add features to your program. You don't even use String methods on those constants. Simply change the languages array declaration to this:

      const char *languages[] = {"Kalba?", "Language?", "Runa?", "Yazyk?"};

The LCD print doesn't have to change. It can print char *, too:

      lcd.print(languages[langCycle]);

Arrays of characters are much more efficient than using the String class. They are also called "C strings" (note the lowercase "string").

If you have questions about how to do something with C strings, just ask. There are many C string functions to choose from. The complete list of functions is here, and some tutorials are here and here. Good alternative reference here.

-dev:
Ok, since nobody else has said it...

Don't use String™. It can cause hangs and crashes at random times, especially when your program runs for a long time, or when you add features to your program.

Thank you! I thought String/string should be avoided only in high complexity programs (not a silly one like mine:)). Changed it to const char *. Works fine! Should it be const char * languages[4]? (with a space before the name I guess?)

Thanks again, apparently it is quite easy to change these data types.

Should it be const char * languages[4]?

No, the compiler figures it out for you.

And instead of hard-coding a 4 later, you can declare an array maximum like this:

   const size_t MAX_LANG = sizeof(languages) / sizeof(languages[0]);

Then use that constant in your if test:

  if (langCycle == MAX_LANG) {

If you ever change the languages array, you don't have to change all the places you used a 4. The constant and the array dimensions will automatically be adjusted to match the number of language strings you put between the {} braces.

Yup, I still like to hardcode :/// In this case it was because I knew I will change the last language to cyrillic alphabet made out of custom characters.
Probably this last part could be smarter.

void kalba() {
  //function telling users they should select the language
  //four languages should switch between with the delay of 2 sec.

  unsigned long currentLCDMillis = millis(); //check current time
  unsigned long  interval2 = 2000;
  static unsigned long previousLCDMillis = 0; //set first old time to 0

  const char *languages[] = {"Kalba?", "Language?", "Valoda?", ""};
  static int langCycle = -1; //I want to increment it only AFTER it has been used the first time
  const size_t MAX_LANG = sizeof(languages) / sizeof(languages[0]);

  if (currentLCDMillis - previousLCDMillis > interval2) {
    langCycle++;
    lcd.setCursor (6, 0);
    lcd.print("         ");         //delete the old text
    previousLCDMillis = currentLCDMillis;
    if (langCycle == MAX_LANG - 1) {
      yazik();
    }
    if (langCycle == MAX_LANG) {
      langCycle = 0;
    }
  }
  lcd.setCursor (6, 0);
  lcd.print(languages[langCycle]);
}
//russian language made out of custom chars
void yazik() {
  lcd.setCursor (6, 0);
  lcd.write((byte)2);
  lcd.print("3");
  lcd.write((byte)3);
  lcd.print("IK?");
}

Using a constant name instead of a literal "2" would really help this:

    lcd.write((byte)2);

What is that? If you defined a constant like this:

    const byte CAPITAL_BE = 2; // Б

Then it would be obvious to the reader:

    lcd.write( CAPITAL_BE );

And you wouldn't have to remember the byte values.