Corrupt i2c LCD display

Hi all,

Hopefully you guys can help, im doing a project at work and im using the i2c Liquid display (16 x 2)on a photon, 90% of the time it works fine but occasionally (once every day or 2) the display goes corrupt, a power recycle brings it back but eventually it goes again, ive looked around for others having this issue and found 1 post in 2015 with a timing issue that i have tweaked, seemed to help a bit, i have 3 of these units and it happens on all of them so im assuming its the code (all use same code).

Code below

//**************************************************************************
//DO NOT ADJUST ANY CODE WITHOUT CONSULTING MARK 
//**************************************************************************
//********************************************************************************
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

String version = "Deskv5.2LED"; //SET VERSION OF SOFTWARE TO BE EXTRACTED BY VARIABLE EACH CHANGE

//Versions

// Deskv2 - original firmware
// Deskv3 - added ESAB with number - e1
// Deskv4OLED - Added OLED support
// Deskv5.2LED - Added Shot blast

//********************************************************************************
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

// Add LED library
#include "LiquidCrystal_I2C_Spark.h"

//SET VARIABLES
char myIpString[24];
char macString[18];

//SET RESET TIMER TO RESET IF LOST CLOUD
int connectedtimer=0;
int move = 0;

//SET LOCATION TO SUBSCRIBE TO N.B THIS MUST MATCH THE MACHINE LOCATION (CAPS SENSITIVE)

String location = "MTLA"; //CHANGE THIS PER LOCATION OF THE MACHINE

// SET STRING VALUES FOR MACHINE MESSAGES
String l1 = "";
String l2 = "";
String l4 = "";
String l5 = "";
String l6 = "";
String l7 = "";
String l8 = "";
String e1 = "";
String t = "";
String sb = "";

int scrolldelay = 3000;

//SET STRINGS FOR DISPLAY TEXT
String firstline = "MTL M/C handling";
String secondline = "No calls";
String callline = "M/C's calling:-";

//SET TIMER STATUS TRIGGER
bool timerstat = false;
bool flag = false;

//SE TIMERS FOR DIMMING BACKLIGHT AND SCROLL EFFECTS
Timer timer0(20000, BackLight);
Timer timer1(3000, Scroll);

//ASSIGN LCDDISPLAY TO lcd
LiquidCrystal_I2C *lcd;

void setup() {
//LOADUP DISPLAY WITH TEXT AND SET DIM TIMER RUNNING
  lcd = new LiquidCrystal_I2C(0x27, 16, 2);
  lcd->init();
  lcd->backlight();
  lcd->clear();
  lcd->print(firstline);
  lcd->setCursor(0,1);
  lcd->print(secondline);
  timer0.start();
  
  //SETUP TEXT FUNCTION FOR TESTING PURPOSES  
  Particle.function("Text", text);
    
  
  //SET VERSION TO VARIABLE
  Spark.variable("Version", version);
  
  
  //USE EXTERNAL ANTENNA
  WiFi.selectAntenna(ANT_EXTERNAL);
  
  //RETREIVE CURRENT IP ADDRESS AND PUBLISH TO A VARIABLE
  IPAddress myIp = WiFi.localIP();
  sprintf(myIpString, "%d.%d.%d.%d", myIp[0], myIp[1], myIp[2], myIp[3]);
  Spark.variable("ipAddress", myIpString, STRING);
  
  
  //RETREIVE CURRENT MAC ADDRESS AND PUBLISH TO A VARIABLE
  byte mac[6];
  WiFi.macAddress(mac);
  sniprintf(macString, 18, "%02x:%02x:%02x:%02x:%02x:%02x",
      mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  Spark.variable("MACAddress", macString, STRING);
  
  
//SUBSCRIBE TO 'location' STRING TOPIC AND ONLY SUBSCRIBE TO MY DEVICES
  Particle.subscribe(location, myHandler, MY_DEVICES);
 
}

// CHECK FOR CLOUD CONNECTION, RESET AFTER 5 MIN IF NO CONNECTION
void checkcloud() {
     if (Particle.connected()==true){
        connectedtimer=0;
    }       
       else{
        connectedtimer++;
        }
    
    if (connectedtimer>300000){  //300000 = 5mins no cloud then reset
        connectedtimer=0;
        
       // complete reset
        System.reset();// reboot photon
        
    }

}

//TEXT TEST FUNCTION
int text(String command){
    lcd->clear();
    lcd->backlight();
    lcd->print(firstline);
    lcd->setCursor(0,1);
    lcd->print(command);
          Particle.publish("Message", command, PRIVATE);
    if (command==""){
          Particle.publish("LED", "dim timer started", PRIVATE);
          timerstat=true;
    }
  return 1;
}
//BACKLIGHT TIMER VOID 
void BackLight() { 
       lcd->noBacklight();
       timer0.stop();
       timerstat=false;
        }

void loop() {
   
 delay(5000);
if (l1==""){
    if (l2==""){
        if (l4==""){
            if (l5==""){
                if (l6==""){
                    if (l7==""){
                        if (l8==""){
                            if (e1==""){
                                if (t==""){
                                    if (sb==""){
                                lcd->clear();
                                 lcd->print(firstline);
                                 lcd->setCursor(0,1);
                                 lcd->print(secondline);
                                if (timerstat == true){
                                lcd->backlight();
                                  timer0.start();
                                  timerstat = false;
                                 timer1.stop();
                                }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    }
}
}
//ALLOW MESSAGE TO BE SCROLLED 
void Scroll(){
    if (l1!=""){
  lcd->clear();
  lcd->print(callline);
    lcd->setCursor(0,1);
        l1="Laser 1";
        lcd->print(l1);
    delay(scrolldelay);
    }
    if (l2!=""){
  lcd->clear();
  lcd->print(callline);
    lcd->setCursor(0,1);
        l2="Laser 2";
        lcd->print(l2);
    delay(scrolldelay);
    }
    if (l4!=""){
  lcd->clear();
  lcd->print(callline);
    lcd->setCursor(0,1);
        l4="Laser 4";
        lcd->print(l4);
    delay(scrolldelay);
    }
    if (l5!=""){
  lcd->clear();
  lcd->print(callline);
    lcd->setCursor(0,1);
        l5="Laser 5";
        lcd->print(l5);
    delay(scrolldelay);
    }
    if (l6!=""){
  lcd->clear();
  lcd->print(callline);
    lcd->setCursor(0,1);
        l6="Laser 6";
        lcd->print(l6);
    delay(scrolldelay);
    }
    if (l7!=""){
  lcd->clear();
  lcd->print(callline);
    lcd->setCursor(0,1);
        l7="Laser 7";
        lcd->print(l7);
    delay(scrolldelay);
    }
    if (l8!=""){
  lcd->clear();
  lcd->print(callline);
    lcd->setCursor(0,1);
        l8="Laser 8";
        lcd->print(l8);
    delay(scrolldelay);
    }
    if (e1!=""){
  lcd->clear();
  lcd->print(callline);
    lcd->setCursor(0,1);
        e1="Esab 1";
        lcd->print(e1);
    delay(scrolldelay);
    }
    if (t!=""){
  lcd->clear();
  lcd->print(callline);
    lcd->setCursor(0,1);
        t="Test message";
        lcd->print(t);
    delay(scrolldelay);
    }
    if (sb!=""){
  lcd->clear();
  lcd->print(callline);
    lcd->setCursor(0,1);
        sb="Shot Blast";
        lcd->print(sb);
    delay(scrolldelay);
    }
    
}


//HANDLE EVENT AND TOGGLE LED DEPENDING ON EVENT RECEIVED
void myHandler(const char *event, const char *data){


  if (strcmp(event,location)==0) {
      
    if (strcmp(data,"l1off")==0) {
    l1 = "";

  }
  else if (strcmp(data,"l1on")==0) {
    l1 = "1 ";
//IF RECEIVE TEXT INSTRUCT LED TO TURN ON AND SET TIMER TRIGGER TO TRUE ON EVERY HANDLE
    lcd->backlight();
    timerstat=true;
    timer1.start();
  }
  else {
  }}
  if (strcmp(event,location)==0) {
      
    if (strcmp(data,"l2off")==0) {
    l2 = "";

  }
  else if (strcmp(data,"l2on")==0) {
    
    l2 = "2 ";
    lcd->backlight();
    timerstat=true;
    timer1.start();
  }
  else {
  }}
  if (strcmp(event,location)==0) {
      
    if (strcmp(data,"l3off")==0) {
    


  }
  else if (strcmp(data,"l3on")==0) {
   
 
  }
  else {
  }}
  if (strcmp(event,location)==0) {
      
    if (strcmp(data,"l4off")==0) {
    l4 = "";

  }
  else if (strcmp(data,"l4on")==0) {
    l4 = "4 ";

    lcd->backlight();
    timerstat=true;
    timer1.start();
  }
  else {
  }}
  if (strcmp(event,location)==0) {
      
    if (strcmp(data,"l5off")==0) {
    l5 = "";

  }
   else if (strcmp(data,"l5on")==0) {
    
    l5 = "5 ";

    lcd->backlight();
    timerstat=true;
    timer1.start();
  }
  else {
  }}
  if (strcmp(event,location)==0) {
      
    if (strcmp(data,"l6off")==0) {
    l6 = "";

  }
  else if (strcmp(data,"l6on")==0) {
    
    l6 = "6 ";

    lcd->backlight();
    timerstat=true;
    timer1.start();
  }
  else {
  }}
  if (strcmp(event,location)==0) {
      
    if (strcmp(data,"l7off")==0) {
    l7 = "";

  }
  else if (strcmp(data,"l7on")==0) {
    
    l7 = "7 ";

    lcd->backlight();
    timerstat=true;
    timer1.start();
  }
  else {
  }}
  if (strcmp(event,location)==0) {
      
    if (strcmp(data,"l8off")==0) {
    l8 = "";

  }
  else if (strcmp(data,"l8on")==0) {
    
    l8 = "8 ";

    lcd->backlight();
    timerstat=true;
    timer1.start();
  }
  else {
  }}
  
  if (strcmp(event,location)==0) {
      
    if (strcmp(data,"e1off")==0) {
    e1 = "";

  }
  else if (strcmp(data,"e1on")==0) {
    
    e1 = "E ";
    lcd->backlight();
    timerstat=true;
    timer1.start();
  }
  else {
  }}
  
  if (strcmp(event,location)==0) {
      
    if (strcmp(data,"toff")==0) {
    t = "";
 
  }
  else if (strcmp(data,"ton")==0) {
      
    t = "TEST";
    
    lcd->backlight();
    timerstat=true;
    timer1.start();
  }
  else {
  }}
  
   
  if (strcmp(event,location)==0) {
      
    if (strcmp(data,"sboff")==0) {
    sb = "";
 
  }
  else if (strcmp(data,"sbon")==0) {
      
    sb = "Shot Blast";
    
    lcd->backlight();
    timerstat=true;
    timer1.start();
  }
  else {
  }}
}

I believe the problem is that the software timer that calls Scroll() runs in a separate thread. Thus it can interleave the code that runs in loop() that also updates the display, causing a mix of both commands to confuse the display.

Instead of using a timer directly, either set a flag variable in the timer and update only from loop, or adjust the loop code to use System.millis() instead of using a timer to handle scrolling.

1 Like

To add onto what @rickkas7 said, there is a lot of code here that can be shortened. For example, your loop() could be:

void loop() {
  delay(5000);

  if (l1 == "" && l2 == "" && l4 == "" && l5 == "" && l6 == "" && l7 == "" &&
      l8 == "" && e1 == "" && t == "" && sb == "") {
    lcd->clear();
    lcd->print(firstline);
    lcd->setCursor(0, 1);
    lcd->print(secondline);
    if (timerstat == true) {
      lcd->backlight();
      timer0.start();
      timerstat = false;
      timer1.stop();
    }
  }
}

Also your Scroll() could possibly be shortented to the following. (It compiles for me, but Iā€™m not entirely sure if it would work):

void Scroll() {
  String* strings[10] = {&l1, &l2, &l4, &l5, &l6, &l7, &l8, &e1, &t, &sb};
  const char* messages[10] = {"Laser 1",      "Laser 2",   "Laser 4", "Laser 5",
                              "Laser 6",      "Laser 7",   "Laser 8", "Esab 1",
                              "Test message", "Shot Blast"};

  for (int i = 0; i < 10; ++i) {
    String* str = strings[i];
    const char* msg = messages[i];

    if (*str != "") {
      lcd->clear();
      lcd->print(callline);
      lcd->setCursor(0, 1);
      *str = msg;
      lcd->print(*str);
      delay(scrolldelay);
    }
  }
}

Your myHandler() could be shortened in a similar fashion.

And as always ( :wink: ) if you intend to have your application running for a long time, stay away from using String but rather use char arrays instead.

1 Like

Thanks Guys, im not the most professional at this but i get by, at the moment this seems to be working fine but ill update after a few days usage.

OK after a few days testing on 3 units looks like this is sorted, thanks again for your help guys