LCD update function call freezes board

Using a pro micro to monitor a number of buttons, then act when it detects a button has been pressed. (yup…it’s a CNC pendant, if you can’t already tell by the function names)

I’m using the Buttons library, which can watch for single/double/triple/single+hold/double+hold/triple+hold clicks, and essentially have upwards of 6 actions per button. I have integrated a 1602 LCD with i2c backpack…and that’s where my problems hit. In a button action, I want to change the text on the LCD, and I do that by calling an lcd print function and sending the text as 2 string variables.

I have tested where it breaks, and it’s always with the call to the lcdUpdate function. It also hangs the board, requiring a reset to let go of the com port. Even sending a simple “hello”,“world” string pair to the function causes the issue, yet basically the same function call to ‘lcdHome()’ works fine. So I don’t think it’s what I’m sending, maybe way I’m sending, how it’s being received, or what it’s trying to print (my lack of brainpan space on what lcd.print actually does). Any help would be hugely appreciated.

Upload output:
Sketch uses 10888 bytes (37%) of program storage space. Maximum is 28672 bytes.
Global variables use 827 bytes (32%) of dynamic memory, leaving 1733 bytes for local variables. Maximum is 2560 bytes.

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

// Set some intitial values
const int buttons = 11;
String axSel = "X";

// Set the button to pin number assignments
const int btnDummy = 15;
const int btnX = 7;
const int btnY = 8;
const int btnZ = 6;
const int btnProbe = 9;
const int btnMove = 10;
const int btnConn = 16;
const int btnX1 = 18;
const int btnX2 = 19;
const int btnY1 = 20;
const int btnY2 = 21;

// Create button array
ClickButton button[buttons] =
{
  ClickButton (btnDummy, LOW, CLICKBTN_PULLUP),
  ClickButton (btnX, LOW, CLICKBTN_PULLUP),
  ClickButton (btnY, LOW, CLICKBTN_PULLUP),
  ClickButton (btnZ, LOW, CLICKBTN_PULLUP),
  ClickButton (btnProbe, LOW, CLICKBTN_PULLUP),
  ClickButton (btnMove, LOW, CLICKBTN_PULLUP),
  ClickButton (btnConn, LOW, CLICKBTN_PULLUP),
  ClickButton (btnX1, LOW, CLICKBTN_PULLUP),
  ClickButton (btnX2, LOW, CLICKBTN_PULLUP),
  ClickButton (btnY1, LOW, CLICKBTN_PULLUP),
  ClickButton (btnY2, LOW, CLICKBTN_PULLUP)
};

void setup()
{
  lcd.init();
  lcdHome();
  // Setup button timers (ms)
  for (int i = 1; i <= buttons; i++)
  {
    button[i].debounceTime   = 20;   // Debounce timer in ms
    button[i].multiclickTime = 250;  // Time limit for multi clicks
    button[i].longClickTime  = 1000; // Time until long clicks register
  }
}

void loop() {

  button[1].Update();
  if (button[1].clicks != 0) {
    switch (button[1].clicks) {
      case 1:
        axSel = "X";
        set_AXIS_XY();
        Serial.println("X1");
        lcdUpdate("Ax:" + axSel + "  Step:", "Jog on");
        break;
      case 2:
        Serial.println("X2");
        chg_STEP_XY();
        lcdUpdate("Ax:" + axSel + "  Step:", "Jog on");
        break;
      case -1:
        Serial.println("X1H");
        set_ZERO_X();
        lcdUpdate("Ax:" + axSel + "  Step:", axSel + " Axis Zeroed");
        break;
    }
  }
}



void lcdUpdate(String L1, String L2) {
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print(L1);
  lcd.setCursor(1, 1);
  lcd.print(L2);
}

void lcdHome() {
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print("Initial Line 1");
  lcd.setCursor(0, 1);
  lcd.print("Initial Line 2");
}

I had an issue once where the LCD would freeze up things (was on an EPS32) and adding 4.7kΩ pull-up resistor on SCL and SDA solved it.

Put lcd.init() in setup, only need to do that once.

Eliminate the use of String and use null-terminated char arrays instead.

What Arduino board are you using?

The hd44780 library comes with a diagnostic tool, I2CexpDiag, that can be run to test your LCD device.

--- bill

bperrybap: What Arduino board are you using? -

Arduino Pro Micro 5v

The hd44780 library comes with a diagnostic tool, I2CexpDiag, that can be run to test your LCD device.

  • The LCD appears fine, tested with other examples from i2c_LiquidCrystal library, i2c scanner detects address correctly, and the initial text I set on boot displays correctly. It's only when I call the lcdUpdate function and specifically lcd.print(). I can get it to go dark with lcd.backlight(), and lcd.write a char. Print seems to be an issue.

Thanks Bill.

david_2018: Put lcd.init() in setup, only need to do that once.

  • Done

Eliminate the use of String and use null-terminated char arrays instead.

  • For fear of 'expert say, monkey do', can you expand on why this course of action?..so I fully understand why. Thanks.

srhnz: - For fear of 'expert say, monkey do', can you expand on why this course of action?..so I fully understand why. Thanks.

That suggestion was more for long-term stability, the use of String can lead to memory fragmentation in the small memory of most arduino boards.

How is your wiring? Loose jumper wires can cause problems when communicating with an LCD.

The LCD appears fine

I'd still give it a try with the pull-ups...

srhnz: - The LCD appears fine, tested with other examples from i2c_LiquidCrystal library, i2c scanner detects address correctly, and the initial text I set on boot displays correctly. It's only when I call the lcdUpdate function and specifically lcd.print(). I can get it to go dark with lcd.backlight(), and lcd.write a char. Print seems to be an issue.

The diagnostic sketch included with the hd44780 library does actual testing of the device. It tests the i2c signals for external pullups, it tests the internal RAM of the LCD and it does some stress testing of moving data to the LCD. The sketches you ran did not do that.

Also, the backlight should come on when you call backlight() and go off when you call noBacklight().

Post some photos of your setup so we can take a look at your wiring and soldering.

--- bill

david_2018: How is your wiring? Loose jumper wires can cause problems when communicating with an LCD.

Gotcha with the Strings thing. Been reading the arguments for/against the use of strings. The wiring is soldered directly to the board and the LCD backpack. It's not a comms issue as I can load any one of the demo sketches and it's fine, the 'welcome' message ('lcdHome()' function) works fine, same as the 'Hello World" LCD demo. However, as of writing this comment, I've found that the Clickbutton library seems to be the cuplrit. Replacing the library parts with a simple:

void loop(){
  if(digitalRead(btnX)== LOW){
    lcdUpdate();
  }
}

...it works perfectly! Who would have thought! So suggestions on how I should approach this, given my rudimentary code cutting skills. Try and fix it myself, or go back to the library author. Thought I was going crazy for a while..like playing solitaire with a card missing!

The library can be found -> https://github.com/marcobrianza/ClickButton Was put onto it by a CNC forum article..and not mine.

Actually now that you say so

 for (int i = 1; i <= buttons; i++)
  {
    button[i].debounceTime   = 20;   // Debounce timer in ms
    button[i].multiclickTime = 250;  // Time limit for multi clicks
    button[i].longClickTime  = 1000; // Time until long clicks register
  }

indexes start at 0… you mess up with memory

The code I started with was a shameless copy'n'paste from the CNC article, and I noted that the author said he created the dummy button because he wanted to start counting at 1. So you're saying that omitting index 0 will mess up the memory?

BTW: Changed the code, looped from 0, no change...LCD still not happy

srhnz: So you're saying that omitting index 0 will mess up the memory?

No.

What he is saying is that you declared a button[] array with 11 members. They go from 0 to 10 You index into the button[] array 1 to 11 so you are attempting to access the 12th member of button[] which does not exist.

i.e. button[11] does not exist when you have an array with 11 members.

--- bill

Devil in the detail huh? Out of bounds error which I never picked up on. Tested and issue resolved..thanks for your help team. Much appreciated.

Unfortunately I did not try compiling the code in your post, which would have made the problem obvious:

/home/jdbarney/.arduino15/packages/arduino/hardware/avr/1.8.3/cores/arduino/main.cpp: In function 'main':
/home/jdbarney/Arduino/forumtest/forumtest.ino:45:30: warning: iteration 10 invokes undefined behavior [-Waggressive-loop-optimizations]
     button[i].debounceTime   = 20;   // Debounce timer in ms
                              ^
/home/jdbarney/Arduino/forumtest/forumtest.ino:43:21: note: within this loop
   for (int i = 1; i <= buttons; i++)
                     ^

david_2018 - What compiler are you using? Clearly the Arduino IDE wasn’t bothering to tell me the same thing…would have saved a lot of typing and hair loss :smiley:

You have to turn on all the warnings. They are not all on by default. File->Preferences->Compiler Warnings → All

I feel so foolish. LOL

I must admit I did not try to compile nor read the code in details, just suspected I2C as I have had issues there and kinda took for granted the code was fine...

blh64: You have to turn on all the warnings. They are not all on by default. File->Preferences->Compiler Warnings -> All

Actually I tried compiling with Compiler Warnings set to Default and it still showed up in version 1.8.13 of the Arduino IDE. Earlier versions of the compiler may not have shown the error, I know I've run into other warnings that only show up in recent versions.

I must admit I did not try to compile nor read the code in details, just suspected I2C as I have had issues there and kinda took for granted the code was fine...

Same problem here, was too fixated on the problem being with the LCD.