Using millis to delay LCD off and On

Hello,

I am trying to built a message screen using LCD 20X04 and MQTT. The idea is to get messages to appear on the screen but to have the screen back light off until the message appears but after 15 seconds to turn off the LCD until next message. I managed to do this partialy. I can get the backlight on when an MQTT message is recieved and I can get the screen to turn off every 15 seconds.
But the two prcoesses are not linked meaning the screen can turn on when a messgae is recieved but it will turn off even after a second if the 15 seconds counter is done.
I thought that all I need to do is to reset the counter of the 15 seconds each time a MQTT message is recieved but it doesn't work (I wanted to put it where I worte "***********"). i guess i am doing something wrong.

Here is my code. if someone can help I will appreciate it very much:

I am only a beginner so if someone has an idea and can make it simple enough for me it would be great.

const char* ssid = "XXX"; // SSID of local network
const char* password = "XXX"; // Password on network
const char* mqttServer = "XXX";
const int mqttPort = XXX;
const char* mqttUser = "XXX";
const char* mqttPassword = "XXX";
unsigned long previousMillis = 0;
const long interval = 15000;

LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
WiFiClient espClient;
PubSubClient client(espClient);

void setup() {

lcd.begin(20,4);
int cursorPosition=0;
lcd.backlight();
lcd.print("Connecting ....");

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
delay(500);
lcd.setCursor(cursorPosition,1);
lcd.print(".");
cursorPosition++;
}

lcd.clear();
lcd.setCursor(0,0);
lcd.print("Connected!");
delay(1000);

lcd.clear();
lcd.setCursor(5, 0);

client.setServer(mqttServer, mqttPort);
client.setCallback(callback);

while (!client.connected()) {
Serial.println("Connecting to MQTT...");
lcd.clear();
lcd.setCursor(5, 0);
lcd.print("Connecting to MQTT...");

if (client.connect("ESP8266Client", mqttUser, mqttPassword )) {

Serial.println("connected");
lcd.clear();
lcd.setCursor(5, 0);
lcd.print("Connected to MQTT...");

} else {

Serial.print("failed with state ");
Serial.print(client.state());
lcd.clear();
lcd.setCursor(5, 0);
lcd.print("failed with state ");
delay(2000);

}
}

client.publish("esp/test", "Hello from ESP8266");
client.subscribe("esp/test");

}

void callback(char* topic, byte* payload, unsigned int length) {

Serial.print("Message arrived in topic: ");
lcd.clear();
lcd.setCursor(5, 0);
lcd.print("Message arrived:");

for (int i = 0; i < length; i++) {
Serial.print((char)payload*);*
_ lcd.print((char)payload*);_
_
lcd.backlight();_
_ *************************************************_
_
}
_

}
void loop() {
* client.loop();*
* unsigned long currentMillis = millis();*
* if (currentMillis - previousMillis >= interval) {*

* previousMillis = currentMillis;*
* lcd.noBacklight();*
* }*
}

Hi there. Your code looks as if you know what you're doing so asking you to read a couples of things first shouldn't be much of a problem for you.

First read this to see how to place your code in tags. It gets rid of the fancy formatting of the editor (which can actually change the characters) and makes it easier for those who would help to copy and paste into their own editor.

Secondly, a visit here will give you some more insight as to how the timing can work for you. Come back to us when you're done.

You appear to be using an asynchronous callback function, which means you will be dealing with multiple thread contexts as well as atomicity.
Once you have multiple contexts lots of potential concurrency issues can crop op since one thread can interrupt/preempt another.

One very important thing is whether the callback is run in the processor ISR context. If so I2C may hang.
On the 8266 I don't think any of the Arduino code runs at ISR level so that may not be an issue.
But I'm not sure how the 8266 does its scheduling, for things like callbacks.

I do have concerns about how I2C works in this type of callback environment since the callback function appears to interrupt the loop() code and both the callback function and the loop() code are both using the I2C library, specifically the lcd library.

The other issue is using a variable that is shared across threads that can be preempted.
If you are doing that (which you are) then you must tell the compiler about it using the volatile keyword.
i.e. if you are going to modify a variable in your callback context and you want the loop() code to see it,
you must declare it volatile otherwise the compiler will not know that it could be be modified by other code outside
the specific code in the executing function.

You then have the issue of atomicity. Just because the compiler can be told to force the code to always be looking at memory to examine the variable does not mean that what the code sees is a proper value.
For example, the variable may take multiple memory accesses to fetch or update and in between those fetches/writes the other thread decides to look at or modify it. This can result in a thread only modifying part of the actual variable or only seeing part of true value and creating or seeing a corrupted value in the variable.
There are techniques that can be used to ensure atomicity but they vary on the specific types of threads and/or ISR functions being used.

I'm not sure how to force atomicity on the esp8622.
Assuming that the i2c usage between the callback function and the loop() does not create issues,
you could work around the atomicity issue in a portable way by having the callback set a flag (declared as a volatile single byte variable) and the foreground loop looks for the flag and if it sees it, clears it and starts its timer.

But all this depends a lot on how the ic2 library works and if the lcd library can really be called from both a callback() and loop()
There could be serious issues like what if callback() is called right as loop() had just called the lcd code to turn off the backlight?
The lcd library as well as the i2c code would be interrupted an re-entered which is very likely to create issues.

--- bill

I would have thought that putting previousMillis = millis(); within the callback should do what you want.

void callback(char* topic, byte* payload, unsigned int length) {
 
  Serial.print("Message arrived in topic: ");
  lcd.clear();
  lcd.setCursor(5, 0);
  lcd.print("Message arrived:");
  previousMillis = millis();
  ...
  ...

But you said that doesn't work?