Arduino Zero with Anet LCD2004 display shows gibberish

OK, there are thousands of people with this problem, none of the answers seem to help me.

I have an Anet LCD2004 display and this code works on my Uno, but not on my Zero. The UNO shows the responses expected in the code, the Zero shows gibberish. (any photo will do for example, "alien text" is how one person described it.)

I'm wired from the 10 pin ribbon to a protoboard shield, all the wires go to the correct places (gnd, 5v, A0, 12, 11, 5, 4, 3, 2, brightness is on a resistive divider.)

Like I say, it works on the UNO, shows garbage on the Zero. Is this down to voltages? Are there default pin settings I have to override?

Any suggestions?

//Sample using LiquidCrystal library
#include <LiquidCrystal.h>

// select the pins used on the LCD panel
int rs=12, en=11, d4=5, d5=4, d6=3, d7=2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

// define some values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

// Default settings
int thrs        = 2;  // 1, 2, 3 or 4 out of 0 to 5
int oct         = 2;  // 1, 2 or 3 out of 1 to 3
int vox         = 2;  // out of 1 to 8  
int bnk         = 5;  // out of 1 to 16 
int prog;
int chn         = 1;  // out of 1 to 16 

// read the buttons
int read_LCD_buttons()
{
 adc_key_in = analogRead(0);      // read the value from the sensor 
 // we add approx 50 to those values and check to see if we are close
 if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
 // For V1.1 us this threshold
 if (adc_key_in < 150)   return btnLEFT;  
 if (adc_key_in < 195)  return btnDOWN; 
 if (adc_key_in < 380)  return btnSELECT; 
 if (adc_key_in < 555)  return btnRIGHT;
 if (adc_key_in < 790)  return btnUP;

 return btnNONE;  // when all others fail, return this...
}

void setup()
{
 lcd.begin(20,4);              // start the library
 lcd.clear();
 lcd.setCursor(0,1);
 lcd.print("Message Here"); // print a simple message
 delay(5000);
}
 
void loop()
{
 lcd.setCursor(0,0);
 lcd.print("Data sent:");
 lcd.setCursor(9,1);            // move cursor to second line "1" and 9 spaces over
 lcd.print(random(40,76));      // display seconds elapsed since power-up
 delay(500);

 lcd.setCursor(0,2);
 lcd.print("Button Pressed:");
 lcd.setCursor(11,2);            // move to the begining of the second line
 lcd_key = read_LCD_buttons();  // read the buttons

 switch (lcd_key)               // depending on which button was pushed, we perform an action
 {
   case btnRIGHT:
     {
     if(bnk == 15){bnk=-1;}
     bnk = bnk+1;
     lcd.print("Btn1     ");
     break;
     }
   case btnLEFT:
     {
     if(oct == 3){oct=0;}
     oct = oct+1;
     lcd.print("Btn2     ");
     break;
     }
   case btnUP:
     {
     if(thrs == 4){thrs=0;}
     thrs = thrs+1;
     lcd.print("Btn3     ");
     break;
     }
   case btnDOWN:
     {
     if(chn == 16){chn=0;}
     if(chn == 9){chn=10;}
     chn = chn+1;
     lcd.print("Btn4     ");
     break;
     }
   case btnSELECT:
     {
     if(vox == 8){vox=0;}
     vox = vox+1;
     lcd.print("Btn2     ");
     break;
     }
     case btnNONE:
     {
     lcd.print("None     ");
     break;
     }
 }

 lcd.setCursor(0,3);
 lcd.print("Dflt:");
 lcd.setCursor(6,3);
 lcd.print("T");
 lcd.print(thrs);
 lcd.setCursor(9,3);
 lcd.print("O");
 lcd.print(oct);
 lcd.setCursor(12,3);
 lcd.print("P");
 prog = (8*bnk)+vox;
 if(prog > 128){
  prog = prog-128;
 }
 lcd.print(String(prog)+"  ");
 lcd.setCursor(17,3);
 lcd.print("C");
 lcd.print(String(chn)+" ");
}

What makes you think that the Zero and the Uno use the same pins?
Did you verify?

Essentially, the Zero and the Uno are the same form factor. The pin numbering is the same, the built-in LED is the same pin (13) and all other standard examples run happily on both. The pinouts for the display are determined by the lines...

int rs=12, en=11, d4=5, d5=4, d6=3, d7=2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

So Zero or Uno should be interchangeable. Is it to do with pin voltages perhaps? There are references to pin voltages being either 3.3 or 5.0 on the underside of the board but I can't find out how/if there is any control over this.

I don't have any experience with a Zero.

The LiquidCrystal library is fairly straightforward as I recall with no issues on the Uno.

Sorry I can't help further.

If it is 3.3V vs 5, I suppose you can use resistor voltage dividers.

crunchysteve:
OK, there are thousands of people with this problem, none of the answers seem to help me.
. . .

Actually I've been here for almost more than 10 years and this is the first mention of an Arduino Zero in the Display section of the forum that I recall.

The 'Zero runs at 3.3v, is your LCD module rated to work at 3.3v as well?

If not then typically the logic will work OK but the contrast voltage (pin 3) will be the problem. BUT... this should result in a blank display, not one with incorrect characters.

Unfortunately resistor voltage dividers won't help in this case since they don't work in reverse. You can't apply 3.3v to the 'output' and expect to see 5v at the 'input'.

In any case I suggest you do your troubleshooting with a simple program with setup() similar to yours and loop() empty. By similar I mean that you never need the initial clear() or setCursor() and in this case you won't need the delay() either.

Don

The zero is not 5v tolerant. Be careful with the ADC signal to the zero when using 5v. (you can't wire it directly)

--- bill

floresta:
Unfortunately resistor voltage dividers won't help in this case since they don't work in reverse. You can't apply 3.3v to the 'output' and expect to see 5v at the 'input'.

I was thinking resistor voltage dividers on inputs to the 3.3V device only.
The 3.3V device's output should be fine going to the 5V device's input.
Similar to interfacing ESP8266 or HC-05 to Arduino.

.

ieee488:
I was thinking resistor voltage dividers on inputs to the 3.3V device only.
The 3.3V device's output should be fine going to the 5V device's input.
Similar to interfacing ESP8266 or HC-05 to Arduino.

.

There aren't any 'inputs to the 3.3V device' in this case, at least as far as the LCD hookup is concerned.

When using a parallel connection (as opposed to I2C) pin 5 is typically connected to GND. This means that the 5v LCD is always in Read mode and will never send data back to the 3.3v processor.

Don

floresta:
There aren't any 'inputs to the 3.3V device' in this case, at least as far as the LCD hookup is concerned.

The device appears to have an LCD with buttons and the buttons are wired up with a resistor ladder to an ADC output pin in the same cable / 10 pin header/connector.
I'm guessing that the ADC signal is being fed to an analong which is used by analogRead(0) in the code.

--- bill

I was addressing the issue in the topic heading which is why I included the phrase 'at least as far as the LCD hookup is concerned.'

I guess you are concerned that an improper input elsewhere is affecting the processor and thus the display. That sounds feasible to me.

Don

bperrybap:
The zero is not 5v tolerant. Be careful with the ADC signal to the zero when using 5v. (you can't wire it directly)

--- bill

The resistor values on the button array, work out to 3v and lower, when powered by 5v, so that's not really a problem. The gibberish changes when I press a button, but it's still gibberish, so I guess the button reads are working. So the buttons aren't my issue here, the gibberish on the screen is and that's all outputs from the Zero.

floresta:
The 'Zero runs at 3.3v, is your LCD module rated to work at 3.3v as well?

It's one I "borrowed" from a 3D printer kit I bought as spares for my running 3D printer, so TTL (5v) rather than CMOS (3.3v) however, I would have thought the trigger points should have been just enough, apparently not, anyway...

floresta:
If not then typically the logic will work OK but the contrast voltage (pin 3) will be the problem. BUT... this should result in a blank display, not one with incorrect characters.

Contrast is fine, the garbage on screen is visible, but...

floresta:
Unfortunately resistor voltage dividers won't help in this case since they don't work in reverse. You can't apply 3.3v to the 'output' and expect to see 5v at the 'input'.

I did find some info elsewhere that suggesting using schotky buffers, powered from 5v, between the outputs of the Zero and the inputs to the display, so I've ordered some 74HC14 schotky hex inverters and will build up a breadboard rig and see if it all works happily.

I'll keep everybody posted when they arrive.

Thanks all for the contributions.

crunchysteve:
It's one I "borrowed" from a 3D printer kit I bought as spares for my running 3D printer, so TTL (5v) rather than CMOS (3.3v) however, I would have thought the trigger points should have been just enough, apparently not, anyway...

Typically 3v logic highs are high enough to work with 5v inputs;
I have used 5v logic LCDs with 3v processors with no external logic on pic32, the Teensy 3x SAM processors, and ESP8266 processors.
However, I have occasionally seen issues of slew rate on the E signal causing issues.
E typically requires a minimum of 450ns between transitions. The issue you can have when attempting to use 3v on 5v inputs is that you can potentially need a bit of extra time to allow the 3v signals to rise high enough and be stable for the 5v inputs.

It is also possible that there could also be an issue with the delayMicroseconds() code in the SAMD core.
Perhaps it isn't quite long enough or has a timing issue with delayMicroseconds(1) which is used to control the E signal.
I assume you are using the bundled LiquidCrystal library and not fm's newLiquidCrystal library.
(fm's library does things very differently related to timing and can have timing issues on fast processors.)

I looked at the SAMD core code for delayMicroseconds() and and I didn't see anything obvious even when using 1us, but I don't have a zero board to verify the actual timing with a logic analyzer.

You can do a quick test to see if it is timing issue vs a logic level issue.
Just add this to the top of LiquidCrystal.cpp after all the #include statements.

#define delayMicroseconds(_us) delayMicroseconds(_us * 10)

That macro will make all delays using delayMicroseconds() 10X longer.
If there is timing issue this should be enough increase to make it work.
If it works, then you may then want to determine if it is a timing issue on the E signal or if there is an issue with delayMicroseconds()
You can do that by removing the macro and bumping the delayMicroseconds(1) in pulseEnable(void) up to say 2,3, or more to test it.

--- bill

In doing some LCD testing for another thread, I stumbled upon an issue where the bundled LiquidCrystal library was not working with the ESP8266 and my LCD keypad shield.
It does work with my hd44780 library hd44780_pinIO class.
I had forgotten but I added some specific s/w timing tweaks for the ESP8266 which is related to 3v interfacing to 5v logic.
(It is a s/w workaround for the slew rate issue I mentioned previously)

The hd44780 library also has some other tweaks related to power up reset, and command timing that are different from the bundled LiquidCrystal library might also be necessary to work with the zero.

If possible, could you try your zero with the hd44780 library to see if it works.
I'd like to know if it works, or if it needs the timing tweak I used for the ESP8266 or perhaps other s/w work arounds.

The hd44780 library is available in the library manager so it is easy to install, and includes lots of documentation that can be accessed through the "Documentation" sketch.
You can read more about it on the hd44780 github page: GitHub - duinoWitchery/hd44780: Extensible hd44780 LCD library
I'd recommend first running the included hd44780_pinIO i/o class examples.
If you use the pins 8,9,4,5,6,7 the examples will work out the box.

The changes to use it vs LiquidCrystal are minimal.
Change the headers and the name of the constructor used for the lcd object.

--- bill