Serial not working after change <LiquidCrystal.h> to <LiquidCrystal_I2C.h>

I'm using a IR sensor with a LCD screen on an Arduino Uno when I test the code the serial was working without any issues but once I changed <LiquidCrystal.h> to <LiquidCrystal_I2C.h> I'm not getting any response from the serial monitor.

#include <LiquidCrystal_I2C.h>
#include <TimerOne.h>
#include <Wire.h>
//#include <LiquidCrystal.h>



LiquidCrystal_I2C lcd(0x27, 20, 4);  // set the LCD address to 0x27 for a 16 chars and 2 line display

//const int rs = 8, en = 3, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
//LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

const int IRSensorPin = 2; 
const int ledPin = 13;

int inputState;
int lastInputState = LOW;
long lastDebounceTime = 0;
long debounceDelay = 5;

long time;
long endTime;
long startTime;
int RPM = 0;
double trip = 0;
double kkbanspd = 0.00223;
float lnTime = 0;

void setup(void) {
  pinMode(IRSensorPin, INPUT);
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);

  lcd.init();                      // initialize the lcd 
  lcd.backlight();
  
  lcd.begin(16, 2);
  lcd.clear();
  lcd.print("Eduardo");
  lcd.setCursor(0, 1);
  lcd.print("Alvarez");
  delay(3000);
  lcd.clear();
  lcd.print("Test");
  lcd.setCursor(0, 1);
  lcd.print("Bike Speedometer");
  delay(2000);
  lcd.clear();
  endTime = 0;
  Timer1.initialize(1000000);  // Set the timer to 60 rpm, 1,000,000 microseconds (1 second)
  Timer1.attachInterrupt(timerIsr);  // Attach the service routine here

}

// ---------------------------------------------------------------
void loop(void) {
  time = millis();
  int currentSwitchState = digitalRead(IRSensorPin);

  if (currentSwitchState != lastInputState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (currentSwitchState != inputState) {
      inputState = currentSwitchState;
      if (inputState == LOW) {
        digitalWrite(ledPin, LOW);
        calculateRPM(); // Real RPM from sensor
      }
      else {
        digitalWrite(ledPin, HIGH);
      }
    }
  }
  lastInputState = currentSwitchState;


}

// ---------------------------------------------------------------
void calculateRPM() {
  startTime = lastDebounceTime;
  lnTime = startTime - endTime;
  RPM = 60000 / (startTime - endTime);
  endTime = startTime;
  trip++;
}

void timerIsr()
{
  // Print RPM every second
  // RPM based on timer
  Serial.println("---------------");
  time = millis() / 1000;
  Serial.print(time);
  Serial.print(" RPM: ");
  Serial.println(RPM);
  Serial.print(trip);
  

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Spd:");
  lcd.setCursor(5, 0);
  lcd.print((RPM * kkbanspd) * 60);
  lcd.setCursor(12, 0);
  lcd.print("Km/h");

  lcd.setCursor(0, 1);
  lcd.print("Trp:");
  lcd.setCursor(5, 1);
  lcd.print(trip * kkbanspd);
  lcd.setCursor(12, 1);
  lcd.print("Km");
  delay(500);
  RPM = 0;
}

While executing an ISR all interrupts are disabled. Serial and delay use interrupts so do not work well in an ISR.

Your ISR has way too much code. An ISR should be very short. Set a flag in the ISR and in loop(), if the flag is set, execute your code and clear the flag. That way interrupts are disabled for the minimum time.

Variables that are changed in an ISR should be declared volatile.

Multi byte variables can be corrupted if an interrupt occurs while the variable is being read. To prevent that, disable interrupts, copy the variable and enable interrupts. Then work with the copy.

1 Like

A quick glance shows it is doing what it should. You define a direction connection to the LCD in your code then you use an I2C adapter library, that works about as well as trying to paint the ocean pink. The "_I2C" on the end of the library indicates this. Go back to the old library it should work or change your code and use a LCD with an I2C interface. Post your annotated schematic so we can check it out I may have misinterpreted something.

You are trying to send data over I2C inside an interrupt.

As long as code from an interrupt-routine is executed interrupts are disabled
I2C requires to use interrupts too.
So from inside an interrupt-routine it is impossible to send data over I2C.

You calculate the rpm inside void loop and you are using the timer-interrupt for printing once per second.

You should do the printing with non-blocking timing

Some more comments / questions:
How many rows and columms does your real connected I2C-LCD have?

20 x 4 or 16 x 2

//LiquidCrystal_I2C lcd(0x27, 20, 4);  // set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

As you have specified the row and columms of the I2C-LCD inside the constructor this is this line of code

LiquidCrystal_I2C lcd(0x27, 16, 2);  // set the LCD address to 0x27 for a 16 chars and 2 line display

you don't need the parameters lines and columms in the init-function

lcd.begin is not nescessary

  lcd.begin(16, 2);
// variables that deal with function millis() 
// MUST be of type unsigned long
unsigned long time;  
unsigned long endTime;
unsigned long startTime;

unsigned long myTimer;

This code-version compiles but I don't have your hardware laying around for a real test.

For an I2C-LCD you must have connected the I2C-LCD to exactly that IO-pins that are named SDA and SCL.

#include <LiquidCrystal_I2C.h>
//#include <TimerOne.h>
#include <Wire.h>


//LiquidCrystal_I2C lcd(0x27, 20, 4);  // set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);  // set the LCD address to 0x27 for a 16 chars and 2 line display
//const int rs = 8, en = 3, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
//LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

const int IRSensorPin = 2;
const int ledPin = 13;

int inputState;
int lastInputState = LOW;
long lastDebounceTime = 0;
long debounceDelay = 5;

// variables that deal with function millis() 
// MUST be of type unsigned long
unsigned long time;  
unsigned long endTime;
unsigned long startTime;

unsigned long myTimer;

int RPM = 0;
double trip = 0;
double kkbanspd = 0.00223;
float lnTime = 0;



void setup() {
  pinMode(IRSensorPin, INPUT);
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);

  lcd.init();  // initialize the lcd
  lcd.backlight();

  // lcd.begin(16, 2); NOT NEEDED
  lcd.clear();
  lcd.print("Eduardo");
  lcd.setCursor(0, 1);
  lcd.print("Alvarez");
  delay(3000);
  lcd.clear();
  lcd.print("Test");
  lcd.setCursor(0, 1);
  lcd.print("Bike Speedometer");
  delay(2000);
  lcd.clear();
  endTime = 0;
  //Timer1.initialize(1000000);  // Set the timer to 60 rpm, 1,000,000 microseconds (1 second)
  //Timer1.attachInterrupt(timerIsr);  // Attach the service routine here

}

// ---------------------------------------------------------------
void loop() {
  readIR_pulses();

  // check if 1000 milliseconds of time has passed by 
  // since last time this did happen
  if ( TimePeriodIsOver(myTimer,1000) ) {
    // when REALLY 1000 milliseconds of time HAVE passed by
    timerIsr(); // print to serial monitor and LCD
  }  
}


void readIR_pulses() {
  time = millis();
  int currentSwitchState = digitalRead(IRSensorPin);

  if (currentSwitchState != lastInputState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (currentSwitchState != inputState) {
      inputState = currentSwitchState;
      if (inputState == LOW) {
        digitalWrite(ledPin, LOW);
        calculateRPM(); // Real RPM from sensor
      }
      else {
        digitalWrite(ledPin, HIGH);
      }
    }
  }
  lastInputState = currentSwitchState;  
}


// ---------------------------------------------------------------
void calculateRPM() {
  startTime = lastDebounceTime;
  lnTime = startTime - endTime;
  RPM = 60000 / (startTime - endTime);
  endTime = startTime;
  trip++;
}


void timerIsr() {
  // Print RPM every second
  // RPM based on timer
  Serial.println("---------------");
  time = millis() / 1000;
  Serial.print(time);
  Serial.print(" RPM: ");
  Serial.println(RPM);
  Serial.print(trip);


  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Spd:");
  lcd.setCursor(5, 0);
  lcd.print((RPM * kkbanspd) * 60);
  lcd.setCursor(12, 0);
  lcd.print("Km/h");

  lcd.setCursor(0, 1);
  lcd.print("Trp:");
  lcd.setCursor(5, 1);
  lcd.print(trip * kkbanspd);
  lcd.setCursor(12, 1);
  lcd.print("Km");
  delay(500);
  RPM = 0;
}

// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

best regards Stefan

1 Like

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