I2C LCD display getting corrupted [SOLVED]

I have been using these displays with arduino for a long time, but I cannot figure out what is happening here.

The display is (after several minutes, getting corrupted a seen in the following videos

As expected:

Corrupted display:

Has anyone experienced this? I am porting a project from Arduino to Spark… this display is the las of it…

Without looking at the code it’s going to be a bit hard to diagnose. Can you post the LCD part of it?

I have same LCD and i also experience text corruption issues. Since they happen even with minimal “hello world” code and occurance time is directly related to delay() value (smaller delay = faster corruption). Imo it’s related to library or spark i2c protocol.
https://gist.github.com/anonymous/dc7ecc5e6574105b4fbd - link to library used.

In my code i run lcd->init(); after every 4 minutes. If there has been txt corruption it fixes it. I hope that info helps out finding and fixing that problem.

I2C depends strongly on timing. So normaly, during a I2C communication every interrupt should be disabled / suppressed (including Serial Communication)

Also, some display (types) have specific timings for accessing the different lines and pages (means timing between different writing activities). Normally this is specified in the data sheet of the controller.

Please keep in mind: the Spark-delay function is not a true delay, cause they can also end up in a cloud communication or housekeeping activities (if the delay value is high enough).

Even the most simple ‘hello world’ program can be causing this - so I’d not blame the library first.

One possible cause might be the excessive use of String objects, fragmenting the heap.

If you use String try to switch to char[] instead.

I am not using Serial communication. Should I have to disable interrupts for every lcd->print?

"Excessive" ... Is there some kind of limit, you think?

That will work, but it destroys the aesthetic of what I'm working on which is essentially two fixed rows and two changing...

I've had this working for months with Arduino and Ethernet...

#include "Adafruit_DHT.h"
#include "LiquidCrystal_I2C.h"
//
//#define DEBUG_ON
#define DISPLAY_UPDATE_TIME 3000UL
#define NUMBER_OF_MESSAGE_TYPES 19
#define NUMBER_OF_ACTIONS 5
#define DIMMER_PIN A0
#define DHTTYPE  DHT22       // Sensor type DHT11/21/22/AM2301/AM2302
#define DHTPIN   3
#define RELAY_PIN D7
#define VERA_IP {192, 168, 1, 59}
#define VERA_PORT 3480
//
#ifdef  DEBUG_ON
#define DEBUG_PRINT(x)  Serial.print(x)
#define DEBUG_PRINTLN(x)  Serial.println(x)
#define SERIAL_START(x)  Serial.begin(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#define SERIAL_START(x)
#endif
//
class httpCommand {
  String argument;
public:
  void extractValues(String);
  String mssgCommand (void) {
    return argument.substring(argument.indexOf("command#") + 8, argument.indexOf("#text="));
  }
  String mssgText (void) {
    return argument.substring(argument.indexOf("#text=") + 6, argument.indexOf("#value0="));
  }
  int mssgValue0 (void) {
    return (argument.substring(argument.indexOf("#value0=") + 8, argument.indexOf("#value1="))).toInt();
  }
  int mssgValue1 (void) {
    return (argument.substring(argument.indexOf("#value1=") + 8, argument.indexOf("?"))).toInt();
  }
};
//
void httpCommand::extractValues (String stringPassed){
  argument = stringPassed;
}
//
typedef enum {
  LCD_TIME,LCD_EMAIL, LCD_ALARM_STATUS, LCD_GARAGE, LCD_GUEST_GARAGE, LCD_IP_ADDRESS, \
  LCD_TEMPERATURE, LCD_AC_SETPOINT, LCD_OUTSIDE_TEMP, LCD_WEATHER, LCD_FORECAST, \
  LCD_HI_LOW, LCD_WIND, LCD_MESSAGE, LCD_NO_DATA}
lcdState;
//
lcdState state = LCD_TIME;  // display starts on Time
//
DHT dht(DHTPIN, DHTTYPE);
//
unsigned long messageTimerStart;
unsigned long messageTimerDuration;
//
// display variables...
//
bool lightOn;
bool relayState;
bool garageOpen = false;
bool guestGarageOpen = false;
String customMessage;
String weatherCondition = "  Not Yet Reported";
String weatherForecast = "  Not Yet Reported";
String windDirection = "ZZZ";
int outdoorTemp = -99;
int outdoorHumid = -99;
int todayHigh = -99;
int todayLow = -99;
int airconSetpoint = -99;
int alarmArmed = -1;
int windSpeed = -99;
int emailCount = -99;

uint8_t iconClock[8] = {
  0x0,0xe,0x15,0x17,0x11,0xe,0x0}; //
//
int pushButton[NUMBER_OF_ACTIONS] = {
  A3,A4,A5,A6,A7};
byte lastButtonState[NUMBER_OF_ACTIONS] = {
  HIGH, HIGH, HIGH, HIGH, HIGH};
//

// Utility variables
IPAddress server(192,168,1,59);
TCPClient client;
char reply[512];
float temperature;
float humidity;
//
bool oldData = false;
bool messageFlag = false;
int messageSignal = 0;
byte brightLevel = 255;
byte oldBrightLevel;
unsigned long lastDataTransmitTime;
unsigned long updateVeraTime;
int resetTime = 2;
int lastHour; // Auto Reset timer
int buttonPressed = -1;
unsigned long buttonTimer;
bool lcdUpdated = false;
unsigned long lastPress;
//
//String incomingMessage;
//
String messageType[NUMBER_OF_MESSAGE_TYPES] = {
  "ledStatus", "alarmState", "garageState", "guestGarageState", "weatherCondition", \
  "outsideTemp", "outsideHumid", "airconSetpoint", "weatherForecast", "messageData", \
  "todayHigh", "todayLow", "windSpeed", "windDirection", \
  "relayState", "brightLevel", "emailCount", "resetSpark", "resetHour" };
//
const String dayOfWeek[] = {
  "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
const String verboseMonth[] = {
  "January", "February", "March", "April", "May", \
  "June", "July", "August", "September", "October" "November", "December"};
//
//
LiquidCrystal_I2C   *lcd;
//
//
//char myIpString[24];
//
void setup()
{
  SERIAL_START(115200);
  pinMode(DIMMER_PIN, OUTPUT);
  //analogWrite(DIMMER_PIN, brightLevel);
  DEBUG_PRINTLN("Starting...");
  lcd = new LiquidCrystal_I2C(0x27, 20, 4);
  lcd->init();
  lcd->backlight();
  lcd->clear();
  //lcd->createChar(0, iconClock);
  pinMode(RELAY_PIN, OUTPUT);
  dht.begin();
  for (byte i = 0; i < NUMBER_OF_ACTIONS; i++)
  {
    pinMode(pushButton[i], INPUT_PULLUP);
  }
  Spark.function("httpRequest", httpRequest);
  Spark.variable("temperature", &temperature, INT);
  Spark.variable("humidity", &humidity, INT);
  delay(6000);
  DEBUG_PRINTLN("Requesting Vera States...");
  startupSyncVera();
}
//
void loop()
{
  // Adjust for DST
  Time.zone(IsDST(Time.day(), Time.month(), Time.weekday())? -4 : -5);
  // check DHT
  humidity = dht.getHumidity();
  temperature = dht.getTempFarenheit();
  // check for button press
  for (byte i = 0; i < NUMBER_OF_ACTIONS; i++)
  {
    byte buttonState = digitalRead(pushButton[i]);
    if(buttonState == LOW && buttonState != lastButtonState[i] && millis() - lastPress > 250)
    {
      lastPress = millis();
      DEBUG_PRINTLN();
      DEBUG_PRINT("ButtonSwitch ");
      DEBUG_PRINT(i);
      DEBUG_PRINTLN(" Pressed!!!");
      DEBUG_PRINTLN();
      buttonPressed = i;
      DEBUG_PRINTLN(buttonPressed);
      buttonTimer = millis();
      DEBUG_PRINTLN(buttonTimer);
      fireAction(i);
    }
    lastButtonState[i] = buttonState;
  }
  //
  if (buttonPressed >= 0)
  {
    lcdButtonDisplay(buttonPressed);
  }
  else
  {
    lcdUpdate();
  }
  // this function will expire customMessage
  messageTimer();
  // Send Temperature and Humidity to Vera every 10mins
  if (millis() - updateVeraTime > 60000UL)
  {
    //sendConditions();
    updateVeraTime = millis();
  }
  if (millis() - lastDataTransmitTime > 360000UL)
  {
    oldData = true;
  }
  int currentHour = Time.hour();
  if (lastHour == resetTime && lastHour != currentHour)
  {
    System.reset();
  }
  lastHour = currentHour;
}
//
int httpRequest(String mssgArgs)
{
  //lcd->setCursor(19,0);
  //lcd->write(0); // indicate icoming message with clock icon
  DEBUG_PRINTLN("command recieved...");
  oldData = false;
  lastDataTransmitTime = millis();
  httpCommand command;
  command.extractValues(mssgArgs);
  DEBUG_PRINTLN(command.mssgCommand());
  boolean badMessage = true;
  for (int i = 0; i < NUMBER_OF_MESSAGE_TYPES; i++)
  {
    if (command.mssgCommand().equals(messageType[i]))
    {
      DEBUG_PRINTLN("Valid Message Recieved...");
      DEBUG_PRINTLN("Message type:");
      DEBUG_PRINTLN(command.mssgCommand());
      DEBUG_PRINT("messgText = ");
      DEBUG_PRINTLN(command.mssgText());
      DEBUG_PRINT("messgValue0 = ");
      DEBUG_PRINTLN(command.mssgValue0());
      DEBUG_PRINT("messgValue1 = ");
      DEBUG_PRINTLN(command.mssgValue1());
      updateVariables(i, command.mssgText(), command.mssgValue0(), command.mssgValue1());
      badMessage = false;
    }
  }
  if (badMessage)
  {
    DEBUG_PRINTLN("non-conforming message attempt...");
    return -1;
  }
  else return 1;
}
//
void updateVariables(int myMessg, String myText, int val0, int val1)
{
  switch (myMessg)
  {
  case 0: // "?ledStatus="
    //
    lightOn = val0;
    //if (lightOn) lcd->backlight();
    //else lcd->noBacklight();
    //digitalWrite(D7,val0);
    DEBUG_PRINT("LCD Backlight: ");
    DEBUG_PRINTLN(lightOn ? "ON" : "OFF");
    break;
  case 1: // "?alarmState="
    //
    alarmArmed = val0;
    DEBUG_PRINT("Alarm ");
    DEBUG_PRINTLN(alarmArmed ? "ON" : "OFF");
    break;
  case 2: // "?garageState="
    //
    garageOpen = val0;
    DEBUG_PRINT("Garage ");
    DEBUG_PRINTLN(garageOpen ? "Open" : "Closed");
    break;
  case 3: // "?guestGarageState="
    //
    guestGarageOpen = val0;
    DEBUG_PRINT("Guest Garage ");
    DEBUG_PRINTLN(guestGarageOpen ? "Open" : "Closed");
    break;
  case 4: // "?weatherCondition="  // Parse Weather
    //
    weatherCondition = myText;
    DEBUG_PRINT("Weather: ");
    DEBUG_PRINTLN(weatherCondition);
    break;
  case 5: // "?outsideTemp="  // Parse Outside Temperature
    //
    outdoorTemp = val0;
    DEBUG_PRINT("Outdoor Temp: ");
    DEBUG_PRINTLN(outdoorTemp);
    break;
  case 6: // "?outsideHumid=" // Parse Outside Humidity
    //
    outdoorHumid = val0;
    DEBUG_PRINT("Outdoor Humid: ");
    DEBUG_PRINTLN(outdoorHumid);
    break;
  case 7: // "?airconSetpoint="  // Parse A/C Setpoint
    //
    airconSetpoint = val0;
    DEBUG_PRINT("Climate Control: ");
    DEBUG_PRINTLN(airconSetpoint);
    break;
  case 8: // "?weatherForecast="  // Parse the weather forecast
    //
    weatherForecast = myText;
    DEBUG_PRINT("length of message:");
    DEBUG_PRINTLN(myText.length());//
    DEBUG_PRINT("Forecast: ");
    DEBUG_PRINTLN(weatherForecast);
    break;
  case 9: //"?messageData="  // Parse display message
    //
    customMessage = myText;
    messageTimerDuration = val0;
    messageSignal = val1;
    //centerText(customMessage);
    messageTimerStart = millis();
    messageFlag = true;
    break;
  case 10: // "?todayHigh="  // Parse Outside High Temperature
    //
    todayHigh = val0;
    DEBUG_PRINT("Today's high: ");
    DEBUG_PRINTLN(todayHigh);
    break;
  case 11: // "?todayLow="  // Parse Outside Low Temperature
    //
    todayLow = val0;
    DEBUG_PRINT("Today's low: ");
    DEBUG_PRINTLN(todayLow);
    break;
  case 12: // "?windSpeed="  // Parse Outside Low Temperature
    //
    windSpeed = val0;
    DEBUG_PRINT("Windspeed: ");
    DEBUG_PRINTLN(windSpeed);
    break;
  case 13: // "?windDirection"  // Parse Outside Low Temperature
    //
    myText.substring(0,4);
    windDirection = myText;
    DEBUG_PRINT("Wind Direction: ");
    DEBUG_PRINTLN("windDirection");
    break;
    //
  case 14:  //  "?relayState=" // Get the state of the relay
    //
    relayState = val0;
    digitalWrite(RELAY_PIN, relayState);
    DEBUG_PRINT("Relay is: ");
    DEBUG_PRINTLN(relayState? "1" : "0");
    break;
    //
  case 15:  //  "?brightLevel=" // Get the brightness of the LED
    //
    brightLevel = val0;
    brightLevel = map(brightLevel, 0, 100, 0, 255);
    DEBUG_PRINT(F("Briteness transmitted from Vera:"));
    DEBUG_PRINTLN(brightLevel);
    if (brightLevel != oldBrightLevel)
    {
      analogWrite(DIMMER_PIN, brightLevel);
    }
    oldBrightLevel = brightLevel;
    break;
    //
  case 16: // "?emailCount"  // Parse email count
    //
    emailCount = val0;
    DEBUG_PRINT("Email Count: ");
    DEBUG_PRINTLN(emailCount);
    break;
  case 17: // "?resetSpark"  // Order Spark to reset
    //
    DEBUG_PRINTLN("reset attempt");
    if (val0)
    {
      DEBUG_PRINT("Resetting Spark Core.....");
      delay(3000);
      System.reset();
      while(true)
      {
        //nothing to do here...
      }
    }
    break;
  case 18: // "?resetHour"  // Order Spark to reset
    //
    DEBUG_PRINTLN("New Reset Time Received...");
    resetTime = val0 % 24;
    DEBUG_PRINT("New Reset Time = ");
    DEBUG_PRINT(resetTime);
    DEBUG_PRINTLN(":00");
    break;
  default:
    {
      //Nothing to do here....
    }
  }
}
void lcdUpdate()
{
  static unsigned long lastDisplayChangeTime = millis();
  String buffer = "";
  if (millis() - lastDisplayChangeTime >= DISPLAY_UPDATE_TIME)
  {
    DEBUG_PRINTLN(Time.timeStr());
    DEBUG_PRINTLN(state);
    switch (state)
    {
    case LCD_TIME:
      lcdPaintBackground();
      lcd->setCursor((20 - dayOfWeek[Time.weekday() - 1].length()) / 2, 1);
      lcd->print(dayOfWeek[Time.weekday() - 1]);
      DEBUG_PRINTLN(dayOfWeek[Time.weekday() - 1]);
      buffer += verboseMonth[Time.month()-1];
      buffer += ", ";
      buffer += Time.day();
      if(Time.day() % 10 == 1 && Time.day() != 11 )
      {
        buffer += "st";
      }
      else if (Time.day() % 10 == 2 && Time.day() != 12)
      {
        buffer += "nd";
      }
      else if (Time.day() % 10 == 3 && Time.day() != 13)
      {
        buffer += "rd";
      }
      else
      {
        buffer += "th";
      }
      lcd->setCursor((20-buffer.length())/2, 2);
      lcd->print(buffer);
      DEBUG_PRINTLN(buffer);
      if (emailCount > 0)
        state = LCD_EMAIL;
      else
        state = LCD_ALARM_STATUS;
      break;
      //
    case LCD_EMAIL:
      lcdPaintBackground();
      lcd->setCursor(0,1);
      lcd->print("    Email Count");
      lcd->setCursor(0,2);
      lcd->print("         ");
      lcd->print(emailCount);
      DEBUG_PRINT("eMails: ");
      DEBUG_PRINTLN(emailCount);
      state = LCD_ALARM_STATUS;
      break;
      //
    case LCD_ALARM_STATUS:
      lcdPaintBackground();
      lcd->setCursor(0,1);
      lcd->print("    Alarm Status");
      DEBUG_PRINT("Alarm: ");
      lcd->setCursor(0,2);
      if (alarmArmed < 0)
      {
        lcd->print("  Not Yet Reported");
        DEBUG_PRINTLN("Not Yet Reported");
      }
      else
      {
        lcd->print(alarmArmed? "        Armed" : "     Not  Armed");
        DEBUG_PRINTLN(alarmArmed ? "ARMED" : "NOT ARMED");
      }
      state = LCD_GARAGE;
      break;
      //
    case LCD_GARAGE:
      lcdPaintBackground();
      lcd->setCursor(0,1);
      lcd->print("    Main  Garage");
      DEBUG_PRINT("Garage: ");
      lcd->setCursor(0,2);
      if (garageOpen == 0)
      {
        lcd->print("       Closed");
        DEBUG_PRINTLN("Closed");
      }
      else if (garageOpen == 1)
      {
        lcd->print("        Open");
        DEBUG_PRINTLN("OPEN");
      }
      else
      {
        lcd->print("  Not Yet Reported");
        DEBUG_PRINTLN("Not Yet Reported");
      }
      state = LCD_GUEST_GARAGE;
      break;
      //
    case LCD_GUEST_GARAGE:
      lcdPaintBackground();
      lcd->setCursor(0,1);
      lcd->print("    Guest Garage");
      DEBUG_PRINT("Guest Garage: ");
      lcd->setCursor(0,2);
      if (guestGarageOpen == 0)
      {
        lcd->print("       Closed");
        DEBUG_PRINTLN("Closed");
      }
      else if (guestGarageOpen == 1)
      {
        lcd->print("        Open");
        DEBUG_PRINTLN("Open");
      }
      else
      {
        lcd->print("  Not Yet Reported");
        DEBUG_PRINTLN("Not Yet Reported");
      }
      state = LCD_TEMPERATURE;
      break;
      //
    case LCD_TEMPERATURE:
      {
        lcdPaintBackground();
        lcd->setCursor(0,1);
        lcd->print("  Temperture: ");
        lcd->print(floor(temperature + 0.5), 0);
        DEBUG_PRINT("Inside Temp:");
        DEBUG_PRINTLN(floor(temperature + 0.5));
        lcd->print((char)223);
        lcd->print("F");
        lcd->setCursor(0,2);
        lcd->print("    Humidity: ");
        lcd->print(floor(humidity + 0.5), 0);
        DEBUG_PRINT("Humidity:");
        DEBUG_PRINTLN(floor(humidity + 0.5));
        lcd->print('%');
      }
      state = LCD_AC_SETPOINT;
      break;
    case LCD_AC_SETPOINT:
      lcdPaintBackground();
      lcd->setCursor(0,1);
      lcd->print("    A/C Setpoint");
      lcd->setCursor(0,2);
      lcd->print("        ");
      lcd->print(airconSetpoint);
      lcd->print((char)223);
      lcd->print("F");
      DEBUG_PRINT("A/C setpoint:");
      DEBUG_PRINTLN(airconSetpoint);
      state = LCD_OUTSIDE_TEMP;
      break;
      //
    case LCD_OUTSIDE_TEMP:
      lcdPaintBackground();
      lcd->setCursor(0,1);
      lcd->print(" Outdoor Temp: ");
      lcd->print(outdoorTemp);
      lcd->print((char)223);
      lcd->print("F");
      DEBUG_PRINT("Ext. Temp:");
      DEBUG_PRINTLN(outdoorTemp);
      lcd->setCursor(0,2);
      lcd->print("     Humidity: ");
      DEBUG_PRINT("Humidity:");
      lcd->print (outdoorHumid);
      lcd->print('%');
      DEBUG_PRINTLN(outdoorHumid);
      state = LCD_WEATHER;
      break;
      //
    case LCD_WEATHER:
      lcdPaintBackground();
      lcd->setCursor(0,1);
      lcd->setCursor(0,1);
      lcd->print("  Today's  Weather");
      lcd->setCursor((20-weatherCondition.length())/2, 2);
      lcd->print(weatherCondition);
      DEBUG_PRINT("Weather: ");
      DEBUG_PRINTLN(weatherCondition);
      state = LCD_HI_LOW;
      break;
      //
    case LCD_HI_LOW:
      lcdPaintBackground();
      lcd->setCursor(0,1);
      lcd->print("Today's High: ");
      lcd->print(todayHigh);
      lcd->print((char)223);
      lcd->print("F");
      lcd->setCursor(0,2);
      lcd->print("         Low: ");
      lcd->print (todayLow);
      lcd->print((char)223);
      lcd->print("F");
      DEBUG_PRINT("Today's High: ");
      DEBUG_PRINTLN(todayHigh);
      DEBUG_PRINT("Today's Low: ");
      DEBUG_PRINTLN(todayLow);
      if (windSpeed > 2)
      {
        state = LCD_WIND;
      }
      else
      {
        state = LCD_FORECAST;
      }
      break;
      //
    case LCD_WIND:
      lcdPaintBackground();
      lcd->setCursor(0,1);
      lcd->print("   Winds  Gusting");
      lcd->print(" ");
      lcd->setCursor(1,2);
      if (windSpeed < 10) lcd->print(" ");
      lcd->print(windSpeed);
      lcd->print("mph from the ");
      lcd->print(windDirection);
      DEBUG_PRINT("Winds are ");
      DEBUG_PRINT(windSpeed);
      DEBUG_PRINT(" mph from the ");
      DEBUG_PRINTLN(windDirection);
      state = LCD_FORECAST;
      break;
    case LCD_FORECAST:
      lcdPaintBackground();
      lcd->setCursor(0,1);
      lcd->print("      Tomorrow");
      lcd->setCursor((20-weatherForecast.length())/2, 2);
      lcd->print(weatherForecast);
      DEBUG_PRINT("Forecast: ");
      DEBUG_PRINTLN(weatherForecast);
      if (messageFlag)
        state = LCD_MESSAGE;
      else
        state = LCD_IP_ADDRESS;
      break;
      //
    case LCD_MESSAGE:
      lcdPaintBackground();
      lcd->setCursor(0,1);
      if (messageSignal == 2 && brightLevel > 0)
      {
        for (int i = 0; i < 20; i++)
        {
          digitalWrite(DIMMER_PIN, HIGH);
          delay(50);
          digitalWrite(DIMMER_PIN,LOW);
          delay(50);
        }
        analogWrite(DIMMER_PIN,brightLevel);
      }
      else if (messageSignal == 1 && brightLevel >= 20)
      {
        beepTone();
      }
      lcd->setCursor(0,1);
      lcd->print("  *** Message ***");
      lcd->setCursor((20-customMessage.length())/2, 2);
      lcd->print(customMessage);
      DEBUG_PRINT("Message: ");
      DEBUG_PRINTLN(customMessage);
      state = LCD_IP_ADDRESS;
      break;
      //
    case LCD_IP_ADDRESS:
      lcdPaintBackground();
      lcd->setCursor(0,1);
      lcd->print("     IP Address");
      DEBUG_PRINT("IP Address: ");
      lcd->setCursor(0,2);
      lcd->print("    ");
      /*
      byte mac[6];
       WiFi.macAddress(mac);*/
      {
        IPAddress myIp = WiFi.localIP();
        for (int i = 0; i < 4; i++)
        {
          lcd->print(myIp[i]);
          DEBUG_PRINT(myIp[i]);
          if (i < 3)
          {
            lcd->print(".");
            DEBUG_PRINT(".");
          }
        }
      }
      DEBUG_PRINTLN("");
      lcd->print(" ");
      if (oldData) state = LCD_NO_DATA;
      else state = LCD_TIME;
      break;
    case LCD_NO_DATA:
      lcdPaintBackground();
      lcd->setCursor(0,1);
      lcd->print(" **** WARNING **** ");
      lcd->setCursor(0,2);
      lcd->print("   VERY OLD DATA   ");
      for (int i = 0; i < 20; i++)
      {
        digitalWrite(DIMMER_PIN, HIGH);
        delay(50);
        digitalWrite(DIMMER_PIN,LOW);
        delay(50);
      }
      analogWrite(DIMMER_PIN,brightLevel);
      state = LCD_TIME;
      break;
    }
    lastDisplayChangeTime = millis();
  }
}
//
void lcdPaintBackground()
{
  //lcd->clear();
  // Clear Display rows 1 and 2
  for (int i = 1; i < 3; i++)
  {
    lcd->setCursor(0,i);
    for (int j = 0; j < 20; j++)
    {
      lcd->print(" ");
    }
  }
  // Print Time and date at top
  lcd->setCursor(0,0);
  if (Time.hourFormat12() < 10) lcd->print(" ");
  lcd->print(Time.hourFormat12());
  lcd->print(Time.minute() < 10 ? ":0" : ":");
  lcd->print(Time.minute());
  lcd->print(Time.isAM() ? "am" : "pm");
  lcd->setCursor(9,0);
  if (Time.month() < 10) lcd->print(" ");
  lcd->print(Time.month());
  lcd->print(Time.day() < 10 ? "/0" : "/");
  lcd->print(Time.day());
  lcd->print("/");
  lcd->print(Time.year());
  //lcd->print(" ");
  // print spaces over old text
  for (int i = 0; i < 20; i++)
  {
    lcd->setCursor(i,1);
    lcd->print(" ");
    lcd->setCursor(i,2);
    lcd->print(" ");
  }
  // Print EmailCount and Alarm Status at bottom
  lcd->setCursor(0,3);
  lcd->print(emailCount < 0? 0 : emailCount);
  if (emailCount == 1)
  {
    lcd->print(" eMail   ");
  }
  else
  {
    lcd->print(" eMails   ");
  }
  lcd->setCursor(11,3);
  if (alarmArmed != 1)
  {
    lcd->print("Not Armed");
  }
  else
  {
    lcd->print("    Armed");
  }
}
//
bool IsDST(int dayOfMonth, int month, int dayOfWeek)
{
  if (month < 3 || month > 11)
  {
    return false;
  }
  if (month > 3 && month < 11)
  {
    return true;
  }
  int previousSunday = dayOfMonth - dayOfWeek;
  //In march, we are DST if our previous sunday was on or after the 8th.
  if (month == 3)
  {
    return previousSunday >= 8;
  }
  //In november we must be before the first sunday to be dst.
  //That means the previous sunday must be before the 1st.
  return previousSunday <= 0;
}
//
void messageTimer()
{
  if (millis() - messageTimerStart >= messageTimerDuration * 60000UL)
  {
    messageFlag = false;
    //signal = 0;
  }
}
//
void beepTone()
{
  delay(1);
}
//
void fireAction(int buttonID)
{
  DEBUG_PRINTLN("Connecting to web server: ");
  if (client.connect(server, 3480))
  {
    switch (buttonID)
    {
    case 0:  // adjust backlight brightness down
      {/*
        int brite = brightLevel;
       brite -= 25;
       if (brite < 0) brite = 0;
       myVera.print(F("GET /data_request?"
       "id=action&output_format=xml"
       "&DeviceNum=111"
       "&serviceId=urn:upnp-org:"
       "serviceId:Dimmer1"
       "&action=SetTarget"
       "&newTargetValue="));
       myVera.println(brite);*/
        //
        break;
      }
    case 1:  // adjust backlight brightness up
      {
        DEBUG_PRINTLN("Adjusting Brightness Up...");
        int brite = brightLevel += 50;
        if (brite > 255) brite = 255;
        ;
        brite = map(brite, 0, 255, 0, 100);
        char buffer[4];
        sprintf(buffer, "%3d", brite);
        DEBUG_PRINTLN(buffer);
        out("GET /data_request?id=lu_action&output_format=html&DeviceNum=118&serviceId=urn:upnp-org:serviceId:Dimming1&action=SetLoadLevelTarget&newLoadlevelTarget=");
        out(buffer);
        out(" HTTP/1.1\r\n");
        /*
        int brite = brightLevel;
         brite += 25;
         if (brite > 255) brite = 255;
         myVera.print(F("GET /data_request?id=action&output_format=xml&DeviceNum=111&serviceId=urn:upnp-org:serviceId:Dimmer1&action=SetTarget&newTargetValue="));
         myVera.println(brite);*/
        //
        break;
      }
    case 2:  // Toggle Relay
      //
      DEBUG_PRINTLN("Toggling Relay...");
      digitalWrite(RELAY_PIN,!relayState);
      out("GET /data_request?id=action&output_format=xml&DeviceNum=116&serviceId=urn:upnp-org:serviceId:SwitchPower1&action=SetTarget&newTargetValue=");
      out(relayState? "0" : "1");
      out(" HTTP/1.1\r\n");
      break;
      //
    case 3:  // run lights out scene  //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      /*
      myVera.print(F("GET /data_request?"
       "id=action&output_format=xml"
       "&DeviceNum=112"
       "&serviceId=urn:upnp-org:"
       "serviceId:SwitchPower1"
       "&action=SetTarget"
       "&newTargetValue="));
       myVera.print(relayState ? F("0") : F("1"));
       */
      break;
      //
    case 4:  // Lower Air Conditioning //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      /*
      myVera.print(F("GET /data_request?"
       "id=action&output_format=xml"
       "&DeviceNum=112"
       "&serviceId=urn:upnp-org:"
       "serviceId:SwitchPower1"
       "&action=SetTarget"
       "&newTargetValue="));
       if (airconSetpoint > 72) myVera.print(airconSetpoint - 1);
       else myVera.print(airconSetpoint - 1);
       */
      break;
      //
    default:
      {
        //nothing to do here...
      }
    }
    out("Host: 192.168.1.59\r\n");
    out("User-Agent: Spark/1.0\r\n");
    out("Content-Type: text/html\r\n");
    out("Connection: close\r\n\r\n");
    DEBUG_PRINTLN("Closing Connection...");
    in(reply, 500);
  }
}
//
void startupSyncVera() // RUNS A VERA SCENE ON STARTUP... Push the dimmer value from this scene
{
  DEBUG_PRINTLN("Connecting to web server: ");
  if (client.connect(server, 3480))
  {
    DEBUG_PRINTLN("Connected...");
    out("GET /data_request?id=action&serviceId=urn:micasaverde-com:serviceId:HomeAutomationGateway1&action=RunScene&SceneNum=40 HTTP/1.1\r\n");
    out("Host: 192.168.1.59\r\n");
    out("User-Agent: Spark/1.0\r\n");
    out("Content-Type: text/html\r\n");
    out("Connection: close\r\n\r\n");
    DEBUG_PRINTLN("Closing Connection...");
    in(reply, 3000);
  }
  /*
  else
   {
   DEBUG_PRINTLN("Connection failed");
   DEBUG_PRINTLN("Disconnecting.");
   client.stop();
   }*/
}

void out(const char *s)
{
  client.write( (const uint8_t*)s, strlen(s) );
  //Serial.write( (const uint8_t*)s, strlen(s) );
}

void in(char *ptr, uint8_t timeout)
{
  int pos = 0;
  unsigned long lastTime = millis();
  while( client.available()==0 && millis()-lastTime<timeout)
  {
  }  //do nothing
  lastTime = millis();
  unsigned long lastdata = millis();
  while ( client.available() || (millis()-lastdata < 500))
  {
    if (client.available())
    {
      char c = client.read();
      DEBUG_PRINTLN(c);
      lastdata = millis();
      ptr[pos] = c;
      pos++;
    }
    if (pos >= 512 - 1)
      break;
  }
  ptr[pos] = '\0'; //end the char array
  while (client.available()) client.read(); // makeshift client.flush()
  client.flush();  //for safety
  delay(400);
  client.stop();
}
//
void lcdButtonDisplay(int button)
{
  if (millis() - buttonTimer <= 5000UL)
  {
    if (!lcdUpdated)
    {
      //print to LCD
      DEBUG_PRINT("Button Number: ");
      DEBUG_PRINT(button);
      DEBUG_PRINTLN("pressed...");
      lcdUpdated = true;
    }
  }
  else
  {
    buttonPressed = -1;
    lcdUpdated = false;
  }
}

There is no "hard" limit as such. The thing is that, in the current implementation of String, a lot of possible string manipulations (possibly unexpected by the programmer/user) cause the allocation of a new chunk of heap space (min. 16byte) and freeing of others. And since garbage collection of freed space is not working perfectly (yet) this might/will either lead to heap exhaustion or other data corruptions.
This is why I prefere to limit the use of String to the absolute minimum - e.g. libraries demand String as parameter.

But for the sake of trapping the source of your display errors, you may want to check if the faulty strings only appear on the LCD or also in your debug output. If the latter than I'd suspect a problem in the string handling.

As your videos show the display corruption does seem to be limited to string boundaries and not within your strings.
If it was a timing or electrical reason the corruptions should appear all over the place.
To me it seems as if there was maybe one faulty string that placed the extra characters on the display and since you never rewrite these screen positions the glitch persists.
As a workaround, if you build up your four display lines as full strings with blanks, you will immediately wipe the corrupt characters away.


It would be nice to see a video that actually shows the transition from correct to faulty display :wink:

Happened (this time?) with what looks like the time print here:

void lcdPaintBackground()
{
  //lcd->init();
  // Clear Display rows 1 and 2
  for (int i = 1; i < 3; i++)
  {
    lcd->setCursor(0,i);
    for (int j = 0; j < 20; j++)
    {
      lcd->print(" ");
    }
  }//
  // Print Time and date at top
  lcd->setCursor(0,0);
  if (Time.hourFormat12() < 10) lcd->print(" ");
  lcd->print(Time.hourFormat12());
  lcd->print(Time.minute() < 10 ? ":0" : ":");
  lcd->print(Time.minute());
  lcd->print(Time.isAM() ? "am" : "pm");
  lcd->setCursor(9,0);
  if (Time.month() < 10) lcd->print(" ");
  lcd->print(Time.month());
  lcd->print(Time.day() < 10 ? "/0" : "/");
  lcd->print(Time.day());
  lcd->print("/");
  lcd->print(Time.year());
  //lcd->print(" ");
  // print spaces over old text
  for (int i = 0; i < 20; i++)
  {
    lcd->setCursor(i,1);
    lcd->print(" ");
    lcd->setCursor(i,2);
    lcd->print(" ");
  }
  // Print EmailCount and Alarm Status at bottom
  lcd->setCursor(0,3);
  lcd->print(emailCount < 0? 0 : emailCount);
  if (emailCount == 1)
  {
    lcd->print(" eMail   ");
  }
  else
  {
    lcd->print(" eMails   ");
  }
  lcd->setCursor(11,3);
  if (alarmArmed != 1)
  {
    lcd->print("Not Armed");
  }
  else
  {
    lcd->print("    Armed");
  }
}
//
bool IsDST(int dayOfMonth, int month, int dayOfWeek)
{
  if (month < 3 || month > 11)
  {
    return false;
  }
  if (month > 3 && month < 11)
  {
    return true;
  }
  int previousSunday = dayOfMonth - dayOfWeek;
  //In march, we are DST if our previous sunday was on or after the 8th.
  if (month == 3)
  {
    return previousSunday >= 8;
  }
  //In november we must be before the first sunday to be dst.
  //That means the previous sunday must be before the 1st.
  return previousSunday <= 0;
}

This is what made me think it is an I2C issue, it is not occurring with the DEBUG print, but...

it occurs to me that if a String was corrupted, it is possible that if there were characters, they may not be visible...?

I thought it may be electronic, tried to decouple the display... the only other component is the DHT sensor! lean and mean.

Sorry, can't see the video as it seems to be private.

Or the error manifested some time before (several lines up in your log) you looked at it and since the wrong chars just stay there and are not actually reprinted you might see lots of correct strings but miss the one culprit.

I fixed it... sorry.

Ahh, I see.

This indeed looks like a communication problem.

Do you think if I pulled up or pulled down adjacent pins which may be affecting communication it could help?

EDIT: I'm trying

pinMode(D2, INPUT_PULLDOWN);

to see...

I’d guess not, but since I’m not a I2C expert @peekay123 or @bko might have more pointers for you.

Just to tick off some common problems ( -; I see you’ve just edited something I wanted to address ;- ) .
Have you attached some external I2C pull-ups (e.g. 4k7) or does your display provide its own?
Avoid parallel pull-ups (including internal ones) since they reduce the over-all resistance resulting in making pulling signal low harder for the attached devices (higher current to drain).


Maybe you could also provide a sketch of your hardware setup (DHT??, LCD??, pull-up/down resistors, power source, …)

@ScruffR

adjacent pin theory just de-bunked…

Yes, I had two 4.7kOhm resistors pulling up the I2C pins at D0 and D1…

I had not played with that yet, so I’ll try removing them, there are resistors on the I2C backpack… so perhaps you are onto something there.

I’ll report back after we spend some time there.

If this doesn’t help, I’ll check the O-scope out out of the library and get a few traces… we can try to see what is really happening here.

Thanks for your ideas.

1 Like

Here is a photo. Please, no comments if the wire colours are not what you expect… I’m colour blind.

:slight_smile:

@BulldogLowell, what is the LCD power voltage?

1 Like

I have 2 of those LCD’s (both have same problem when connected to Spark Core). It works from 5V (powered from Core Vin pin). I can assure that those problems are not hardware related (wrong setup).

@BulldogLowell, @qwerty009, which backpack do these displays use? Are they the type with an mcp23008 chip on them?

Backpack here... using the PCF8574T chip

I am powering the LCD from one side of the breadboard power supply, 5V.

The Spark is on the other side of the same power supply, 5V too.

I have to insist, much like @qwerty009, this device is powered and connected consistent with it working properly as demonstrated in other applications... it may be electronic nonetheless.

I thought I might be getting a ripple and have tried caps of many different sizes on the LCD display. In no case did it prove to help me.

Removing the pullups... didn't help either.

So, I am down to some electronic cause or something in Spark's I2C. Given that others have had this problem, I think we are looking at I2C. That being said, I didn't find anyone with issues with other I2C devices... in the world of I2C, these LCD's don't seem like they are all that demanding.

I'm befuddled. I'll attach a oscilloscope tonight and we can get some better data.

I have to say, spending so much time on a basic I2C LCD does take away from the awesome. :frowning:

@BulldogLowell, I too suspect the I2C. Migrating any code from Arduino to Spark carries risk so don’t lose site of the awesome just yet! My hunch is that there is some issue with the library and the PCF85741. I’ll start digging. :smile:

1 Like