No text on LCD 16x2 I2C

Hello,
This is my first project with the Arduino nano. The circuit uses the Arduino nano, MPU6050, LCD 16x2 I2C. It is supposed to display the angle in degrees on the LCD also light up appropriate LEDs at certain angles. The push button switch is used to turn on the buzzer to have different tones at different angles. Everything as wired works as it should except for there is no text displayed on the LCD. I have watched many videos on possible causes. I have also tried a simple sketch to display "hello World" on the LCD, and it works fine, text shows up good. Any help would be apreceiated.
Here is the schematic:


And the sketch:






/*
 * Arduino based digital spirit level Version 1:1 by Bob Syers - 2020.
 * Based on original code by DroneBot workshop (dronebotworkshop.com) and Paul McWhorter (toptechboy.com)
 * With addition improvements!
 * Please feel free to use this code, but if you do, give me a mention, thanks!
 */


// Include libraries for I2C, liquid crystal display, math and EEPROM.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <math.h>
#include <EEPROM.h>

//Define LCD address and configuration. If 0x27 doesn't work, 0x3F might.  If using a 20 x 4 display, change 16 to 20 and 2 to 4!
LiquidCrystal_I2C lcd(0x3F, 16, 2);

#define switchPin 2     //The Calibration/Buzzer pin!
#define LED_2 3         //LED showing -2 degrees
#define LED_1 4         //LED showing -1 degrees
#define LED_0 5         //LED showing level
#define LED_P1 6        //LED showing +1 degree
#define LED_P2 7        //LED showing +2 degrees
#define BUZZER 8        //Buzzer
#define HORIZONTAL 0    // Constant for horizontal
#define VERTICAL 1      //Constant for vertical


//////////////////////////////// 
//Variables Defenition Section//
////////////////////////////////

float gyro_x, gyro_y, gyro_z;                 //Raw gyro values from MPU6050
float gyro_x_cal, gyro_y_cal, gyro_z_cal = 0; //Stores the offsets for the gyro
float acc_x, acc_y, acc_z;                    //Raw acceleration values from MPU6050
float acc_x_cal, acc_y_cal, acc_z_cal = 0;    //Stores the offsets for the accelerometer
float thetaM;                                 //theta and thetaM relate to the roll angle
float theta;
float phiM;                                   //phi and phiM relate to the pitch angle
float phi; 
int LEDCount = 0;                             //When calibrating, allows easy iteration of LED's in setup loop
long loop_timer;                              //Timer for main loop to ensure it doesn't run too fast or too slow
int temp;                                     //Temperature gained from the MPU6050
int displaycount = 0;                         //Each time main loop executed, displaycount increments.  Only on every 100 counts does the display update
float dt;                                     //Time constant - difference between old and new time (for angle calculations)
unsigned long millisOld;                      //Variable to store old time
boolean orientation;                          //Either HORIZONTAL or VERTICAL dependant on orientation of MPU6050 module
int eeprom_address = 0;                       //To keep track of current eeprom address
int horizonalCalibration = 0;                 //0 if not calibrated, 255 if calibrated
int verticalCalibration = 0;                  //0 if not calibrated, 255 if calibrated
boolean horizonalCalibrationNotice = 0;       //On first iteration of loop allows display of 'Not calibrated' notice if not calibrated!
boolean verticalCalibrationNotice = 0;        //On first iteration of loop allows display of 'Not calibrated' notice if not calibrated!
boolean playBuzzer = 0;

//////////////////////
//Main setup routine//
/////////////////////

void setup() 
{


  //Assigning all the pins as required.
  pinMode(switchPin, INPUT_PULLUP);
  pinMode(LED_2, OUTPUT);
  pinMode(LED_1, OUTPUT);
  pinMode(LED_0, OUTPUT);
  pinMode(LED_P1, OUTPUT);
  pinMode(LED_P2, OUTPUT);
  pinMode(BUZZER, OUTPUT);
  
  // Start Serial Monitor                                                 
  Serial.begin(115200);

  //Start I2C
  Wire.begin();
  
  //Start LCD
  lcd.begin(16,2);

  //Just so they know, we will tell them!
  lcd.print("Setting up"); 
    
   //Setup the registers of the MPU-6050                                                    
  setup_mpu_6050_registers(); 

  delay(500);   //Without this, would never see the 'Setting up' notice!

  // Init Timer 
  loop_timer = micros();

  //If the switchPin is low (Pressed), the calibration mode starts.  This allows 5 seconds to settle, then calibrates current orientation to 0.
  if(digitalRead(switchPin)==0)
  {
    delay(500);
    lcd.clear();
    lcd.print("Calibrating in ");
    for(int i = 5; i > 0; i--)
    {
      lcd.setCursor(15,0);
      lcd.print(i);
      LEDCount = i + 2;
      digitalWrite(LEDCount, HIGH);
      delay(1000);
      digitalWrite(LEDCount, LOW);
    }
    lcd.setCursor(11,0);
    lcd.print("-----");
    delay(500);
    
    read_mpu_6050_data();         //To determine orientation, we first need data from the MPU6050
    
    if(acc_x < acc_z)             //If Horizontal, acc_x will be less than acc_z, if vertical, it will be reversed (due to the effect of gravity
    {
      lcd.setCursor(0,1);
      lcd.print("Horizontal!");
      orientation = HORIZONTAL;
      //Read the raw acc and gyro data from the MPU-6050 1000 times                                          
      for (int cal_int = 0; cal_int < 1000 ; cal_int ++)
      {                  
        read_mpu_6050_data(); 
        //Add the gyro x offset to the gyro_x_cal variable                                            
        gyro_x_cal += gyro_x;
        //Add the gyro y offset to the gyro_y_cal variable                                              
        gyro_y_cal += gyro_y; 
        //Add the gyro z offset to the gyro_z_cal variable                                             
        gyro_z_cal += gyro_z; 
        //Add the acc x offset to the acc_x_cal variable                                            
        acc_x_cal += acc_x;
        //Add the acc y offset to the acc_y_cal variable                                            
        acc_y_cal += acc_y;                                                          
      }
    
      // Divide all results by 1000 to get average offset
      gyro_x_cal /= 1000.0;                                                 
      gyro_y_cal /= 1000.0;                                                 
      gyro_z_cal /= 1000.0;
      acc_x_cal /= 1000.0;
      acc_y_cal /= 1000.0;

      horizonalCalibration = 255;
      eeprom_address = 0;
      EEPROM.put(eeprom_address, horizonalCalibration);
      eeprom_address += sizeof(int);
      EEPROM.put(eeprom_address, gyro_x_cal);
      eeprom_address += sizeof(float);
      EEPROM.put(eeprom_address, gyro_y_cal);
      eeprom_address += sizeof(float);
      EEPROM.put(eeprom_address, gyro_z_cal);
      eeprom_address += sizeof(float);
      EEPROM.put(eeprom_address, acc_x_cal);
      eeprom_address += sizeof(float);
      EEPROM.put(eeprom_address, acc_y_cal);
      eeprom_address += sizeof(float);
      //Note we are not storing an offset for acc_z, due to gravity!

      delay(500);
    }
    else
    {
      lcd.setCursor(0,1);
      lcd.print("Vertical!");
      orientation = VERTICAL;
      //Read the raw acc and gyro data from the MPU-6050 1000 times                                          
      for (int cal_int = 0; cal_int < 1000 ; cal_int ++)
      {                  
        read_mpu_6050_data(); 
        //Add the gyro x offset to the gyro_x_cal variable                                            
        gyro_x_cal += gyro_x;
        //Add the gyro y offset to the gyro_y_cal variable                                              
        gyro_y_cal += gyro_y; 
        //Add the gyro z offset to the gyro_z_cal variable                                             
        gyro_z_cal += gyro_z; 
        //Add the acc x offset to the acc_x_cal variable                                            
        acc_y_cal += acc_y;
        //Add the acc y offset to the acc_y_cal variable                                            
        acc_z_cal += acc_z;                                                         
      }
    
      // Divide all results by 1000 to get average offset
      gyro_x_cal /= 1000.0;                                                 
      gyro_y_cal /= 1000.0;                                                 
      gyro_z_cal /= 1000.0;
      acc_y_cal /= 1000.0;
      acc_z_cal /= 1000.0;

      verticalCalibration = 255;
      eeprom_address = 24;
      EEPROM.put(eeprom_address, verticalCalibration);
      eeprom_address += sizeof(int);
      EEPROM.put(eeprom_address, gyro_x_cal);
      eeprom_address += sizeof(float);
      EEPROM.put(eeprom_address, gyro_y_cal);
      eeprom_address += sizeof(float);
      EEPROM.put(eeprom_address, gyro_z_cal);
      eeprom_address += sizeof(float);
      EEPROM.put(eeprom_address, acc_y_cal);
      eeprom_address += sizeof(float);
      EEPROM.put(eeprom_address, acc_z_cal);
      //Note we are not storing an offset for acc_x, due to gravity!
      
      delay(500);
    }
  }
  //We are just about to go into main loop, so we setup display ready to show data
  setLcdBaseline();                                                
}

/////////////////////
//Main program loop//
/////////////////////


void loop()
{

  if(digitalRead(switchPin) == 0)     //Check to see if switch is activated (low).  If so, toggles beeper on/off
  {
    playBuzzer = !playBuzzer;
    delay(200);                       //Delay added to give enough time for user to respond
  }
  // Get data from MPU-6050
  read_mpu_6050_data();
  //acc_x and acc_z data used to determine correct orientation of module
  if(acc_x < acc_z)
  {
    orientation = HORIZONTAL;
  }
  else
  {
    orientation = VERTICAL;
  }

  if(orientation == HORIZONTAL)   //If horizontal, eeprom calibration data is loaded (if not present, will display 'Not calibrated')
  {
    if(horizonalCalibrationNotice == 0)   //This section is only run once through then ignored
    {
      eeprom_address = 0;                 //Set EEPROM address to 0 (start of horizontal calibration offsets
      if(EEPROM.get(eeprom_address, horizonalCalibration) == 255)   //Data only accessed if it actually exists!
      {
         eeprom_address += sizeof(int);
         EEPROM.get(eeprom_address, gyro_x_cal);
         eeprom_address += sizeof(float);
         EEPROM.get(eeprom_address, gyro_y_cal);
         eeprom_address += sizeof(float);
         EEPROM.get(eeprom_address, gyro_z_cal);
         eeprom_address += sizeof(float);
         EEPROM.get(eeprom_address, acc_x_cal);
         eeprom_address += sizeof(float);
         EEPROM.get(eeprom_address, acc_y_cal);
         eeprom_address += sizeof(float);
      }
      else   //If no calibration data, it tells you so!
      {
        lcd.clear();
        lcd.print("Not calibrated!");
        delay(1000);
        setLcdBaseline();
      }
      horizonalCalibrationNotice = 1;
    }
    
    //Subtract the offset values from the raw gyro values
    gyro_x -= gyro_x_cal;                                                
    gyro_y -= gyro_y_cal;                                                
    gyro_z -= gyro_z_cal; 
    acc_x -= acc_x_cal;
    acc_y -= acc_y_cal;
  
    /*
     * The next few lines process the raw data to change it into angles that can be output to the LCD and LED's.
     * The value of 4096, which the acceleration data is divided by is taken from the MPU6050 datasheet and is based on sample rate.
     * The value of 9.8 is gravity
     * The atan2 function is from the math module and is used to calculate the angles from the given data
     */
    thetaM =-atan2((acc_x/4096.0)/9.8 , (acc_z/4096.0)/9.8)/2/3.141592656 * 360;  //Raw data
    phiM =-atan2((acc_y/4096.0)/9.8 , (acc_z/4096.0)/9.8)/2/3.141592656 * 360;  //Raw data
  
    dt=(millis()-millisOld)/1000.;
    millisOld=millis();
    /*
     * This section uses the gyro data to make the system more responsive
     * the value of 65.5, which the gyro data is divided by is taken from the MPU6050 datasheet and is based on the sample rate
     */
    theta=(theta+(gyro_y/65.5)*dt)*.96 + thetaM*.04;  //Low pass filter
    phi=(phi+(gyro_x/65.5)*dt)*.96 + phiM*.04;  //Low pass filter
  
    
  }
  else      //If vertical, eeprom calibration data is loaded (if not present, will display 'Not calibrated')
  {
    if(verticalCalibrationNotice == 0)    //This section is only run once through then ignored
    {
      eeprom_address = 24;                //Set EEPROM address to 24 (start of vertical calibration offsets
      if(EEPROM.get(eeprom_address, verticalCalibration) == 255)      //Data only accessed if it actually exists!
      {
         eeprom_address += sizeof(int);
         EEPROM.get(eeprom_address, gyro_x_cal);
         eeprom_address += sizeof(float);
         EEPROM.get(eeprom_address, gyro_y_cal);
         eeprom_address += sizeof(float);
         EEPROM.get(eeprom_address, gyro_z_cal);
         eeprom_address += sizeof(float);
         EEPROM.get(eeprom_address, acc_y_cal);
         eeprom_address += sizeof(float);
         EEPROM.get(eeprom_address, acc_z_cal);
         eeprom_address += sizeof(float);
      }
      else    //If no calibration data, it tells you so!
      {
        lcd.clear();
        lcd.print("Not calibrated!");
        delay(1000);
        setLcdBaseline();
      }
      verticalCalibrationNotice = 1;
    }
    
      //Subtract the offset values from the raw gyro values
    gyro_x -= gyro_x_cal;                                                
    gyro_y -= gyro_y_cal;                                                
    gyro_z -= gyro_z_cal; 
    acc_y -= acc_y_cal;
    acc_z -= acc_z_cal;


    /*
     * The next few lines process the raw data to change it into angles that can be output to the LCD and LED's.
     * The value of 4096, which the acceleration data is divided by is taken from the MPU6050 datasheet and is based on sample rate.
     * The value of 9.8 is gravity
     * The atan2 function is from the math module and is used to calculate the angles from the given data
     */
     
    thetaM =-atan2((acc_z/4096.0)/9.8 , (acc_x/4096.0)/9.8)/2/3.141592656 * 360;  //Raw data
    phiM =-atan2((acc_y/4096.0)/9.8 , (acc_x/4096.0)/9.8)/2/3.141592656 * 360;  //Raw data
  
    dt=(millis()-millisOld)/1000.;
    millisOld=millis();
    /*
     * This section uses the gyro data to make the system more responsive
     * the value of 65.5, which the gyro data is divided by is taken from the MPU6050 datasheet and is based on the sample rate
     */
    theta=(theta+(gyro_y/65.5)*dt)*.96 + thetaM*.04;  //Low pass filter
    phi=(phi+(gyro_z/65.5)*dt)*.96 + phiM*.04;  //Low pass filter
  }

  /*
   * Regardless of orientation, this section updates the LCD, LED's and buzzer
   */
  // Increment the display counter
  displaycount = displaycount +1;
  
  if (displaycount > 100) 
  {
    // Check Angle for Level LEDs
    if (phi < -2.01) 
    {
      // Turn on Level LED
      digitalWrite(LED_2, HIGH);
      digitalWrite(LED_1, LOW);
      digitalWrite(LED_0, LOW);
      digitalWrite(LED_P1, LOW);
      digitalWrite(LED_P2, LOW);
      if(playBuzzer)
      {
        tone(8,200,200);
      }
      
    } 
    else if ((phi > -2.00) && (phi < -1.01)) 
    {
      // Turn on Level LED
      digitalWrite(LED_2, LOW);
      digitalWrite(LED_1, HIGH);
      digitalWrite(LED_0, LOW);
      digitalWrite(LED_P1, LOW);
      digitalWrite(LED_P2, LOW);
      if(playBuzzer)
      {
        tone(8,1000,400);
      }
      
    } 
    else if ((phi < 1.0) && (phi > -1.0)) 
    {
      // Turn on Level LED
      digitalWrite(LED_2, LOW);
      digitalWrite(LED_1, LOW);
      digitalWrite(LED_0, HIGH);
      digitalWrite(LED_P1, LOW);
      digitalWrite(LED_P2, LOW);
      if(playBuzzer)
      {
        tone(8,2000,600);
      }
      
    } 
    else if ((phi > 1.01) && (phi < 2.00)) 
    {
      // Turn on Level LED
      digitalWrite(LED_2, LOW);
      digitalWrite(LED_1, LOW);
      digitalWrite(LED_0, LOW);
      digitalWrite(LED_P1, HIGH);
      digitalWrite(LED_P2, LOW);
      if(playBuzzer)
      {
        tone(8,1000,400);
      }
      
    } 
    else if (phi > 2.01) 
    {
      // Turn on Level LED
      digitalWrite(LED_2, LOW);
      digitalWrite(LED_1, LOW);
      digitalWrite(LED_0, LOW);
      digitalWrite(LED_P1, LOW);
      digitalWrite(LED_P2, HIGH);
      if(playBuzzer)
      {
        tone(8,200,200);
      }
    }

  //Before printing the angle to the display, it makes any negative numbers positive, so it always shows 0 degrees +.
  //If you want to show negative angles, omit this section
  if(phi<0)
  {
    phi = phi - (2*phi);
  }
  if(theta<0)
  {
    theta = theta - (2*theta);
  }

  //End of section

  lcd.setCursor(9,0);
  lcd.print(phi,1);
  lcd.print("   ");
  lcd.setCursor(9,1);
  lcd.print(theta,1);
  lcd.print("   ");

  Serial.print("Pitch = ");
  Serial.print(phi);
  Serial.print(", Roll = ");
  Serial.println(theta);
    
    
  displaycount = 0;
  
  }
  

 while(micros() - loop_timer < 4000);  //4000
 //Reset the loop timer                                
 loop_timer = micros();
 //Back to the top of the loop to start again!
}

//////////////////////
//Subroutine section//
//////////////////////

// This routine just clears the lcd and prints Angle and Roll ready to show values.
void setLcdBaseline()
{
  lcd.clear();
  lcd.print("Angle = ");
  lcd.setCursor(0,1);
  lcd.print("Roll = ");
}


//Required for setting up the MPU6050 for first use.
void setup_mpu_6050_registers(){

  //Activate the MPU-6050
  //Serial.println("Setting up MPU6050 - Wake up");
  //Start communicating with the MPU-6050
  Wire.beginTransmission(0b1101000); //This is the I2C address of the MPU (b1101000/b1101001 for AC0 low/high datasheet sec. 9.2)
  Wire.write(0x6B); //Accessing the register 6B - Power Management (Sec. 4.28)
  Wire.write(0b00000000); //Setting SLEEP register to 0. (Required; see Note on p. 9)
  Wire.endTransmission();  
                                              
  //Configure the accelerometer (+/-8g)
  //Serial.println("Setting up MPU6050 - Wake up - Done");
  //Serial.println("Setting up MPU6050 - setup acc");
  //Start communicating with the MPU-6050
  Wire.beginTransmission(0x68); 
  //Send the requested starting register                                       
  Wire.write(0x1C);   
  //Set the requested starting register                                                 
  Wire.write(0x10); 
  //End the transmission                                                   
  Wire.endTransmission(); 
    //Serial.println("Setting up MPU6050 - setup acc - done");                                          
  //Configure the gyro (500dps full scale)
  //Serial.println("Setting up MPU6050 - setup gyro");
  //Start communicating with the MPU-6050
  Wire.beginTransmission(0x68);
  //Send the requested starting register                                        
  Wire.write(0x1B);
  //Set the requested starting register                                                    
  Wire.write(0x08); 
  //End the transmission                                                  
  Wire.endTransmission(); 
 //Serial.println("Setting up MPU6050 - setup gyro - done");                                             
}


//This routine is required to get the gyro and acceleration values from the MPU6050
void read_mpu_6050_data(){ 

  //Read the raw gyro and accelerometer data
  //Serial.println("Reading data - 1");
  //Start communicating with the MPU-6050                                          
  Wire.beginTransmission(0x68);  
  //Serial.println("Reading data - 2");
  //Send the requested starting register                                      
  Wire.write(0x3B);
  //Serial.println("Reading data - 3");
  //End the transmission                                                    
  Wire.endTransmission(); 
  //Serial.println("Reading data - 4");
  //Request 14 bytes from the MPU-6050                                  
  Wire.requestFrom(0x68,14); 
  //Serial.println("Reading data - 5");   
  //Wait until all the bytes are received                                       
  while(Wire.available() < 14);
  //Serial.println("Reading data - 6");
  //Following statements left shift 8 bits, then bitwise OR.  
  //Turns two 8-bit values into one 16-bit value                                       
  acc_x = Wire.read()<<8|Wire.read();                                  
  acc_y = Wire.read()<<8|Wire.read();                                  
  acc_z = Wire.read()<<8|Wire.read();                                  
  temp = Wire.read()<<8|Wire.read();                                   
  gyro_x = Wire.read()<<8|Wire.read();                                 
  gyro_y = Wire.read()<<8|Wire.read();                                 
  gyro_z = Wire.read()<<8|Wire.read();                 
}

What is different between the "Hello World" that works and your sketch? Same I2C address?

Your schematic looks wrong. You show VCC going to the LCD and the MPU6050 and Vin on the Nano. What is the voltage of VCC? Normally, Vin would be 7-9 V and the Nano would regulate it down to 5V. If it is already 5V then it should go directly in 5V, not Vin

1 Like

Are the first lines concerning the LCD the same as in the test sketch? I wonder.
Does the message "Setting up" show up?
Does the LCD show anything with the MPU disconnected?
Does the test sketch work with the MPU connected?

1 Like

The VCC tag in the schematic is there just to add a hole in my pcb to apply +5v. There is +5V going to nano VIN and VCC on 6050. I will move +5v to 5V and see what that does. Thank you for your help!

VIN on the Nano needs to be at least 7V because it goes through a 5V voltage regulator on the board

1 Like

I tried to compare the two different sketches, this is my test sketch for the LCD, it worked fine.


No text has ever shown in the display, except for the test sketch. I have not tried to disconnect any components yet, I will try. Thank you for your help! Mark

As of right now I only have +5vdc supply, I need to buy one, Thank you!

Compare the I2C address in this code with that of your main sketch

Which one works ?

1 Like

The not working sketch uses another hex address then the working sketch...

1 Like

I remember in the main sketch 0x27 did not work, no backlight or text. I then changed it to 0x3F and the backlight came on but no text. I will have to reload the test sketch, I don't remember if I had to change it, I have tried so many variations and different libraries my head is spinning. Thank you!

I reloaded the test sketch, it will only work with 0x27 address, I tried 0x3F and only the backlight came on, no text.

The main sketch is now working! I changed the I2C address to 0x27 and moved +5v on the nano to 5V. I can barely read the display because of not enough voltage, but its working, Thank you!

Run the I2C scanner sketch that is installed with the Wire library to determine the address of the display once and for all

1 Like

Thank you!

Thank you to everyone for taking time out of your day to help me! very encouraged to try new things!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.