Is it possible that millis() is not incremented in this case?

I apologize if the post is not the subject of this forum (because of the board used).

Hi!
I do a little work in this free time and I started to run this sketch (it is not my work 100%) with an NTP clock and LCD 16x2 display based on an ESP32 board (this is the entire circuit).
I made small changes, many lines are commented, but my problem is the piece of program located between //****** //****** of the local function "printLocalTime()". This function is called every 1s in the loop. This "piece of program" should introduce an effect to the displayed text, and it has "String txtMsg = day_string;" as input , but it also includes two "delay(50)" that produce another delay when the data is displayed on the LCD.
So, I tried to replace the "delay" functions with millis, but now I can only see the last letter of the day, for example "y" from "Monday", nothing else happens. I anticipate that "currentMillis" from the local function is not updated, or quick reset, I think (I declared a global variable, and incremented in the loop.). However, what would you think? Maybe I can do something.
I have had fights with this type of situation before, and I can't say that I won. :sweat_smile:

#include <WiFi.h>
#include "time.h"
#include "sntp.h"
#include <LiquidCrystal_I2C.h>

//0x3F or 0x27
LiquidCrystal_I2C lcd(0x27, 16, 2);   //LCD Object

const char* ssid       = "$$$$$";
const char* password   = "$$$$$$";

const char* ntpServer1 = "ro.pool.ntp.org";
const char* ntpServer2 = "time.nist.gov";
const long  gmtOffset_sec = 7200;
const int   daylightOffset_sec = 3600;

const unsigned long eventInterval = 3000;
unsigned long previousTime = 0;

char *daynames[7] = {
  "Duminica",
  "    Luni",
  "   Marti",
  "Miercuri",
  "     Joi",
  "  Vineri",
  " Sambata"
}; // put them in this order as tm struct 0 = Sunday
//*****************************************************************************
int startPoint;
int endPoint;
int i, j;
// speed of the text movement
int speed = 50;
// text to display
//String txtMsg = "Hello my Arduino!";
**unsigned long previousMillis = 0; **
**unsigned long previousMillis1 = 0;**
**unsigned long previousMillis2 = 0;**
**unsigned long currentMillis = 0;**
//*****************************************************************************
void printLocalTime()
{
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("No time available (yet)");
    return;
  }
  //lcd.print(&timeinfo, "%A%H:%M:%S"); // e.g Tuesday 10:30:05
  //lcd.print(&timeinfo, "%d %B %Y");    //e.g November 22 2022
  //Display Time
  lcd.setCursor(0, 0);
  lcd.print(&timeinfo, "%H:%M:%S"); // 10:30:05
  // Display Date
  char *day_string = daynames[timeinfo.tm_wday];
  char buff[64] = "";
  snprintf(buff, 64, "Day: %s", day_string);
  Serial.println(buff);
  //lcd.setCursor(0, 1);
  //lcd.print(day_string);
  //*********************************************************************
  String txtMsg = day_string;
  startPoint = 0;   //set starting point
  endPoint = 8;    //set ending point
  lcd.setCursor(0, 1);
  //lcd.clear();
  lcd.print("        ");
  //for each letter of the string starting from the last one.
  //unsigned long currentMillis = millis();
  for (i = txtMsg.length() - 1; i >= 0; i--)
  {
    //unsigned long currentMillis = millis();
    startPoint = 0;
    //for each position on the LCD display

    **if (currentMillis - previousMillis >= 50) {**
**      previousMillis = currentMillis;**

      for (j = 0; j < endPoint; j++)
      {
        **if (currentMillis - previousMillis1 >= 50) {**
**          previousMillis1 = currentMillis;**

          lcd.setCursor(startPoint, 1);
          lcd.print(txtMsg[i]);
        }
        //**delay(speed);** // need to be replaced
        if (startPoint != endPoint - 1) {
          lcd.setCursor(startPoint, 1);
          lcd.print(' ');
        }
        startPoint++;
      }
      endPoint--;
    }
    //**delay(speed);** // need to be replaced
  }
  //********************************************************************

} // printLocalTime

// Callback function (get's called when time adjusts via NTP)
void timeavailable(struct timeval *t)
{
  Serial.println("Got time adjustment from NTP!");
  printLocalTime();
}

void setup()
{
  Serial.begin(115200);
  // Setup LCD with backlight and initialize
  lcd.init();
  lcd.backlight();
  // set notification call-back function
  sntp_set_time_sync_notification_cb( timeavailable );
  /**
     NTP server address could be aquired via DHCP,

     NOTE: This call should be made BEFORE esp32 aquires IP address via DHCP,
     otherwise SNTP option 42 would be rejected by default.
     NOTE: configTime() function call if made AFTER DHCP-client run
     will OVERRIDE aquired NTP server address
  */
  sntp_servermode_dhcp(1);    // (optional)
  /**
     This will set configured ntp servers and constant TimeZone/daylightOffset
     should be OK if your time zone does not need to adjust daylightOffset twice a year,
     in such a case time adjustment won't be handled automagicaly.
  */
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);
  //connect to WiFi
  Serial.printf("Connecting to %s ", ssid);
  lcd.clear();
  lcd.print("Connecting to ");
  lcd.setCursor(0, 1);
  lcd.print(ssid);
  delay(1000);

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" CONNECTED");
  lcd.clear();
  lcd.print("CONNECTED");
  delay(2000);

} // setup

void loop()
{
  **currentMillis = millis();**
**  //delay(1000);**
**  if (currentMillis - previousMillis2 >= 1000) {**
**    previousMillis2 = currentMillis;**
**    printLocalTime();     // it will take some time to sync time :)**
**  }**
}

Before you say: Are you that silly ?
The answer is: Yes.

We like to see the sketch that has the problem. You show a sketch that does not even compile with all the ** :wink:

1 Like

The function millis() is working.

When we say to use millis() instead of delay(), then we mean that the whole sketch has to be rewritten for millis().
A millis-timer runs on its own on the loop(). Therefor, your millis-timer in the loop() to run every second is okay.
Running a millis-timer inside a for-loop is not okay. Running a millis-timer inside a for-loop that is inside a for-loop is also not okay. That will never work.
You have to either just print the time and day without effect, or rewrite the sketch.

Using millis() is like looking at the clock to check if something needs to be done.
Suppose that you want to do something every 50ms, then you need a millis-timer of 50ms that runs on its own in the loop().

A liquidcrystal display is slow. Changing something 3 or 4 times per second is fast enough. Updating something every 250ms is therefor reasonable and 50ms is not useful.

1 Like

Whats that sntp.h file you are including? On a ESP32 you would just need time.h. The rest comes with the esp core. See NTP for the ESP32 including day light saving without 3rd party library

I tried to bold a certain piece of code.

Part of the program, but it works quite well. If you need, I can post the link to the source.

This seems to be where you overwrite row 1 (bottom row) data with blanks as startPoint increases to txtMsg.length() which holds day_string array (the day name).

Showing text without effect works fine, but I liked a video I saw on youtube and tried to add that to this program.
Rewriting the program is beyond me. I will leave the program like this.
Maybe enter in API weather next to the NTP clock.

Try your code with this line "commented out" (see the two slants)... and if it does not do what you want, go back to the original line.

Another approach is needed, I don't think it works because of the for loop.
Thank anyway.

Okay. What was the result when you tried it?

Only the last letter is displayed, but not even in the right position.
The effect is like running, I attach a link: Arduino LCD FlyIn text effect - YouTube
For example, I have 8 fields, _ _ _ _ _ _ _ _ . Now it displays like this: a _ _ _ _ _ _ _ , where "a" is the last letter of the word.
Overall, it is not a good synchronization, plus the data display methods are done every 1s in the loop. I'll try something else soon.

The video links to the code. The code uses a delay.
Here is that code in Wokwi simulation: Flying LCD characters - Wokwi ESP32, STM32, Arduino Simulator

1 Like

Yes yes, the author uses delay(), but it disturbs my program, the part with the NTP clock, in that case the time is displayed from 3s to 3s, basically the whole program moves more slowly. That's why I started this topic with millis() as a replacement method for delay().

I hope you don't mind, but you should have given more information.
You gave a link in post #12, only then we saw what you wanted.
You didn't gave a link to the Instructables. You never gave working code with a minimal sketch, I had to find that myself via the link of the video.

I was not joking when I wrote my post #2.

Here is a millis version with flying characters. I don't know if the code is good or bad, I didn't get to the point to actually think about it :face_with_raised_eyebrow:

// Sketch from: https://www.instructables.com/Arduino-LCD-Display-FlyIn-Text-Effect/
// Diagram from: https://wokwi.com/projects/294342288335700490
// Code adjusted for other pins to the display.
// Sketch adjusted for I2C display and millis() by Koepel.
//
// I start the position outside the screen.
// That was easier, because then it is never the same as
// the final position. Or maybe it is just bad coding.
// 

#include <Wire.h>
#include <hd44780.h>
#include <hd44780ioClass/hd44780_I2Cexp.h>

hd44780_I2Cexp lcd;

const int LCD_COLS = 16;
const int LCD_ROWS = 2;

unsigned long previousMillisFlying;
const unsigned long intervalFlying = 80;
bool enableFlying;

unsigned long previousMillisActivate;
const unsigned long intervalActivate = 15000;

//               0123456789012345
char text[16] = "Come Fly With Me";
int index;         // index to a characer in the text
int count;         // count for scrolling/flying

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

  int result = lcd.begin(LCD_COLS, LCD_ROWS);
  if (result) 
  {
    Serial.print("LCD initialization failed: ");
    Serial.println(result);
    hd44780::fatalError(result);
  }

  index = strlen(text) - 1;      // start at last character
  count = -1;                    // start outside screen on the left
  enableFlying = true;
}

void loop() 
{
  unsigned long currentMillis = millis();

  if(currentMillis - previousMillisActivate >= intervalActivate)
  {
    previousMillisActivate = currentMillis;

    // Start a new fly sequence
    lcd.setCursor(0,0);
    //         0123456789012345
    lcd.print("                ");
    index = strlen(text) - 1;
    count = -1;
    enableFlying = true;
  }

  if(enableFlying)
  {
    if(currentMillis - previousMillisFlying >= intervalFlying)
    {
      previousMillisFlying = currentMillis;

      // remove previous character
      if(count >= 0)
      {
        lcd.setCursor(count,0);
        lcd.print(' ');
      }

      // show the character in the next position
      count++;
      lcd.setCursor(count,0);
      lcd.print(text[index]);

      // check if the character is in its final position
      if(count == index)
      {
        index--;                // select character on the left
        count = -1;             // start outside screen

        // Ready with all the text ?
        // Then stop this timer.
        // The variables are set properly when started again.
        if(index < 0)
        {
          enableFlying = false;      // stop this timer.
        }
      }
    }
  }
}

Try it in Wokwi:

Maybe I can do something, I'll see.
I adapted the sketch for LCD 16x2 with I2C module, I only tested this sketch, it works, but I don't see that I could change the position of the message. If the message consists of 5 letters, then it is displayed up to position 5.
And I see that I already have an error when I add this sketch to the main program with NTP, because of "char text[16] = day_string; ".
Thanks anyway.

You are doing it again. You tell about a problem in the code, but you don't show the code.

You should not just add my demonstration to your main program. First, try to create the effect that you want. Then put it in a function, and then you can call that function from your main sketch.

From my point of view, I am the one who adapted the code from the Youtube video for a LCD 16x2 with I2C module. You can see in the simulation that a I2C LCD display is used.

Not just because of the for-loop but vecause you don't understand that every failed time check, every time that end - start < wait the function has to return to void loop() to run the next function and eventually void loop() runs this one and checks the time again... maybe a million times before the wait it up, that cycle, everything going round and round is how timing and sensing and action occurs.

Inside of void loop() if you want to go down a list/array, instead of making a for-next loop you make a static index and use that; example

// at the top of the function
  static byte j = 0;

// lower down
//      for (j = 0; j < endPoint; j++)
if ( j < endPoint )
{
  if ( millis() - startWait >= 50) {
     startWait += 50; // corrects tolerance-growth
     lcd.setCursor(startPoint, 1);
     lcd.print(txtMsg[j]);
     j++; / j is now +1
  }

  return;  // back to void loop()

}  // end of if j < endPoint

// rest of that function

It only prints but it is a way to dispense with for-next and other inside loops that can block smooth code execution.

Your sketch works quite well.
I meant that I adapted your sketch to the I2C library that is used in the program. It's not a big deal, and I tested everything on Arduino.
Then I tried to add it to the program, and I ran into the first problem, because I don't understand how I can set the position of the message because the function "lcd.setCursor(count,0);" contains the variable "count" and what other meaning. I tried to write in the whole program "count = 8;" , I thought that maybe it would position the text on position 8 on the LCD, but it doesn't do that, I see an "o" (from text "Arduino") appearing on the screen here and there.
Then I had "char text[16] = day_string; " which generates an error. But that remains after I solve the positioning of the text.
I'm not a programming genius, passing variables from one function to another, and calling them elsewhere puts me in trouble. That's why I didn't think of a function that would create that effect.

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

unsigned long previousMillisFlying;
const unsigned long intervalFlying = 80;
bool enableFlying;

unsigned long previousMillisActivate;
const unsigned long intervalActivate = 15000;

//               0123456789012345
char text[16] = "Arduino";
int index;         // index to a characer in the text
int count;         // count for scrolling/flying

void setup() 
{
  // Serial.begin(115200);
  lcd.init();
  lcd.clear();
  lcd.backlight();
  
  index = strlen(text) - 1;      // start at last character
  count = 8;                    // start outside screen on the left
  enableFlying = true;
}

void loop() 
{
  unsigned long currentMillis = millis();

  if(currentMillis - previousMillisActivate >= intervalActivate)
  {
    previousMillisActivate = currentMillis;

    // Start a new fly sequence
    lcd.setCursor(0,0);
    //         0123456789012345
    lcd.print("                ");
    index = strlen(text) - 1;
    count = 8;
    enableFlying = true;
  }

  if(enableFlying)
  {
    if(currentMillis - previousMillisFlying >= intervalFlying)
    {
      previousMillisFlying = currentMillis;

      // remove previous character
      if(count >= 0)
      {
        lcd.setCursor(count,0);
        lcd.print(' ');
      }

      // show the character in the next position
      count++;
      lcd.setCursor(count,0);
      lcd.print(text[index]);

      // check if the character is in its final position
      if(count == index)
      {
        index--;                // select character on the left
        count = 8;             // start outside screen

        // Ready with all the text ?
        // Then stop this timer.
        // The variables are set properly when started again.
        if(index < 0)
        {
          enableFlying = false;      // stop this timer.
        }
      }
    }
  }
} // loop

Can you show what you want ?

For example:

0123456789012345
----------------
  A  B  C
1 A  B  C
 1A  B  C
  A  B  C
  A1 B  C
  A 1B  C
  A  B1 C
  A  B 1C
  A  B  C
  A  B  C1
  A  B  C 1
2 A  B  C 1
 2A  B  C 1
  A  B  C 1
  A2 B  C 1
  A 2B  C 1
  A  B  C 1
  A  B2 C 1
  A  B 2C 1
  A  B  C 1
  A  B  C21
  A  B  C 1
  A  B  C 12
3 A  B  C 12
 3A  B  C 12
  A  B  C 12
  A3 B  C 12
  A 3B  C 12
  A  B  C 12
  A  B3 C 12
  A  B 3C 12
  A  B  C 12
  A  B  C312
  A  B  C 12
  A  B  C 12
  A  B  C 123