Problem using interrupt funcion an I2C serial LCD display

Hi there

Im new in the Arduino family but very entusiastic

Im running a sketch in order to simulate the fuel inyection and spark control of a system using the sensors (temperature, pressure, HEI module) etc from my old car.

The "polling" part from the analog read of the sensors runs smooth
I also made a "PANTALLA()" (display, im latin speaker) function to display in a 16x2 LCD my 4 variables (MAP, ECT, TPS, and RPM)

Problem is, now I got to the digital part of the ecuation.

I was reading documentation about using the interrupt pin and ISR function to measure RPM (from a PC fan) for example.

I got that running

However, if I enable the "PANTALLA()" function (remember is the function that runs the display) the RPM variable does not show on the serial monitor (as the code copied runs inside the if(milis() function)

I also tried to make the display take the global variable RPM (wich the ISR has part in modifing) and it always shows 0

If I put the "PANTALLA()" function INSIDE the code running and calculating the RPM taking the rpmcounts++ of the ISR() I got the value in the display but refreshing at the interrupts rate.

Any ideas what I may be missing here? It seems like the RPM variable is constantly reset to 0 or like the global variable only works inside the function if(millis()....) and once the function ends it´s reseted to 0 again

Here is a bit of my code

//RPM
volatile int rpmcount = 0;
int RPM = 0;
unsigned long lastmillis = 0;
//RPM

//CHK_Tiempo_Inyeccion = pulseIn(pinINJ, LOW); muestra microsegundos, para ver tiempo de inyeccion

/*** FUNCIONES ARDUINO ***/

void setup()
{

Serial.begin(9600);
lcd.begin (16,2); // Inicializar el display con 16 caraceres 2 lineas
attachInterrupt (digitalPinToInterrupt(2), rpm_fan, FALLING);
//Pines Analogicos
pinMode(A0,INPUT); //MAP
pinMode(A1,INPUT); //TPS
pinMode(A2,INPUT); //ECT
pinMode(A3,INPUT); //CO2
/pinMode(6,INPUT);
pinMode(7,INPUT);
pinMode(5,OUTPUT); //Posible Señal HEI
/

//Pines Digitales
pinMode(9,OUTPUT); //Salida Inyector PWN
pinMode(12,OUTPUT); //Salida Rele 1 ELECTROVENTILADOR
pinMode(13,OUTPUT); //Salida Rele 2
}//Cierra funcion Setup

void loop()
{

if (millis() - lastmillis == 1000){ /Uptade every one second, this will be equal to reading frecuency (Hz)./

detachInterrupt(digitalPinToInterrupt(2)); //Disable interrupt when calculating

RPM = rpmcount * 60; /* Convert frecuency to RPM, note: this works for one interruption per
full rotation. For two interrups per full rotation use rpmcount * 30.*/
S
erial.print("RPM =\t"); //print the word "RPM" and tab.
Serial.print(rpm); // print the rpm value.
Serial.print("\t Hz=\t"); //print the word "Hz".
Serial.println(rpmcount); /print revolutions per second or Hz. And print new line or enter./

rpmcount = 0; // Restart the RPM counter
lastmillis = millis(); // Uptade lasmillis
attachInterrupt(digitalPinToInterrupt(2), rpm_fan, FALLING); //enable interrupt

}// Cierra if millis()

int MAP=analogRead(A0); // Cable Azul 0.5V Cerrada 1V arranca a acelerar 4.5V Abertura Maxima
int TPS=analogRead(A1); // Cable verde
int ECT=analogRead(A2); // Cable Amarillo
int CO2=analogRead(A3); // Cable naranja

TEMP = NTC10K(ECT);
PRESION = map(MAP,245,1023,600,0);//600 1,2 0 4,8
ACCEL = map(TPS,124,1023,100,0);

VENT(); //Llama funcion para prender electro si temp supera 50
PANTALLA(); // Maneja display

}// Cierra funcion Loop

/*** FUNCIONES ***/

void PANTALLA(){ //Funcion que controla display y variables
lcd.print("MAP ECT TPS RPM");
lcd.setCursor ( 0, 1 );
lcd.print(String(PRESION));
lcd.setCursor ( 4, 1 );
lcd.print(String(TEMP));
lcd.setCursor ( 8, 1 );
lcd.print(String(ACCEL));
lcd.setCursor ( 12, 1 );
lcd.print(String(RPM));
delay(300);
lcd.clear();
}//Cierra PANTALLA

void rpm_fan(){ /* this code will be executed every time the interrupt 0 (pin2) gets low.*/
rpmcount++;
}// Cierra rpm_fan()

To use an analog pin, you can do analogRead() without setting it as pinMode( A0, INPUT) in the setup() function.
See the documentation: analogRead() - Arduino Reference.
It is the analogRead() without any initialization of that pin.

Testing for a specific time interval value is dangerous: if (millis() - lastmillis == 1000)
Don't use "==", but use ">=".
So it will be this: if (millis() - lastmillis >= 1000)
See the BlinkWithoutDelay example: https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay.

The String() makes a String object, but the lcd.print() want a normal array of characters.
Which LCD library do you use ? It should accept an integer variable without the need to convert it to text.

If you need text, then you can use the c_str: c_str() - Arduino Reference

Always show a complete sketch.
We don't know, for example, what 'TEMP' is, it could be a 'int' or a 'float' or a char array.
There is even a website for it: http://snippets-r-us.com/

Hi first of all thanks for your time and sharing

Sorry I realized after that the libraries and global variables where missing

For the LCD after reading online I came up with the hd44780.h wich was awesome since I realized (after knocking my head on the wall) that several chinese manufactures of the I2C chip like to change the pin disposition. And in the LCD library that is more common found, you need to declare each pin in the LCD into de I2C. This baby (hd44780) makes all that automatically!

As for the string to pass it to the LCD you make a good observation. Since I used that method with the LCD library that didn´t worked at first, I didn´t bother in aknolwedging if the one that I use now (hd44780) can handle it or not. (It was like "if it ain´t broke, don´t fix it" kind of thing)

But, the variables printed in the lcd are all INT in the global form. The only problem that I have is with the RPM type. Wich was my first major bump in this proyect. Since the only way that I have to realize the RPM variable is functioning as expected is in the serial.print lines of the code, and through the serial monitor. (As the function copied from another sketch works). I don`t know why the PANTALLA() (lcd function) cannot "retrieve" the RPM variable and display it, rather it only displays "0". But if you delete the SERIAL.PRINT code and put the "PANTALLA()" function INSIDE the IF (millis()) it works, at the interrupt´s interval (or each 1000 millis?, sorry Im thinking out keyboard). since the LCD keeps blinking slow enough to realize is not polling fast enough

I don´t know if the function to calculate how many interrupts per 1000 millis does not change the global RPM variable, or if the function "clears" it when it closes his process in the loop. And you can only see the RPM = value when you are INSIDE that block of code.

Or something else that I`m missing.

But I will need this variable (RPM) not only for display purposes but to calculate and make further code. So im here.

Also thanks for the insight regarding the analog pin and the >=. Appreciate the effort in optimizing the code!

/*** LIBRERIAS ***/
#include <math.h>
#include <Wire.h>
#include <hd44780.h> // main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header

hd44780_I2Cexp lcd; // declare lcd object: auto locate & config exapander chip

/*** VARIABLES GLOBALES ***/

int PRESION;
int TEMP;
int ACCEL;
float DENSIDAD;

int FAN = 12; // Pin
int INY = 6; // Pin

//RPM
volatile int rpmcount = 0;
int RPM = 0;
unsigned long lastmillis = 0;
//RPM

O no :o Be careful, or I start talking about my crystal ball. I use my crystal ball to see the pieces of code that you are hiding from us. Some Arduino users on this forum have the punchline: "the problem is in the part that you are not showing us".
Can you show a complete sketch. I mean a complete sketch, not a few pieces that we have to put together and come to the conclusion that a few pieces are missing (the VENT() function for example).

This one ? GitHub - duinoWitchery/hd44780: Extensible hd44780 LCD library.
That library uses class hd44780 : public Print
That means you can use everything that the Arduino print() can handle, just like the Serial.print(): Serial.print() - Arduino Reference
You can use a 'int' as parameter.

You might have to re-organize your code.
Using millis() to do something every second is perfect.
Let the loop() run at full speed, that means no delay().
Don't use I2C at full speed, slow that down with a millis() as well. For example 4 or 5 times per second is good for a LCD display.

When you show your sketch, please put it between code tags. They are < code > and < / code > and you can use the button in the upper-left corner above the text field. The "< / >" button.

Code tags are nice.
Crystal balls are not.

Sorry and thanks for the insights

I tweaked the code a bit following your indications (>= analog pin declarations, STRING(INT) etc)

It seems the problem was indeed the STRING convertion of the variable now Im getting readings.

Now something you last said got my attention.

I do indeed have a delay() inside the PANTALLA() function in order to wait 300ms to refresh, but this is stopping my 16MHZ CPU from doing something else for 300ms right?

Can you explain me if you are so kind how to tweak this function using millis as you last recall?

Thanks

INT TEMP = 100; //CHANGES VALUES 100 just to put something INT PRESION = 100; //CHANGES VALUES 100 just to put something INT RPM = 100; //CHANGES VALUES 100 just to put something

void setup{

Serial.begin(9600);

lcd.begin (16,2); // Inicializar el display con 16 caraceres 2 lineas

}// Closes SETUP

void loop(){

PANTALLA();

} //Closes LOOP

//FUNCTIONS

void PANTALLA(){ //Funcion que controla display y variables

lcd.print("MAP ECT TPS RPM");
lcd.setCursor ( 0, 1 );

lcd.print(PRESION);
lcd.setCursor ( 4, 1 );

lcd.print(TEMP);
lcd.setCursor ( 8, 1 );

lcd.print(ACCEL);
lcd.setCursor ( 12, 1 );

lcd.print(RPM); // no esta declarado como global*/
delay(3000);
lcd.clear();

}//Closes PANTALLA()

You didn't find the button for code tags ? They are with lowercase.

During a delay(), you can do nothing else. Only the interrupts are working.
To keep track of the rpm, you better keep the loop() running without using delay().

You can try my millis demo, and see what the the serial monitor shows. I made it two days ago, maybe it was my crystal ball that told me to make it for you :wink:

Remove the delay(300) or delay(3000) and use millis for a 4 times per second update of the display:

unsigned long previousMillisLCD;
const unsigned long intervalLCD = 250;  // interval in ms

...

void loop()
{
  if( millis() - previousMillisLCD >= intervalLCD)
  {
    previousMillisLCD = millis();

    lcd.print("MAP ECT TPS RPM");
    lcd.setCursor ( 0, 1 );

    lcd.print(PRESION);
    ...
  }
}

If you make a new sketch, please show it, so I can see for myself that you have removed the delay().

Thanks again

Sorry for the code brackets, I just wrote it as mentioned XD now I saw the button in the upper left corner.

I shall thinker with the specs you gave me this afternoon and see how it does. Again thanks for your time (PATIENCE) and effort.

Code works as expected, however I need to use frequently the lcd.clear(); function in order to "refresh" the segments used.

For example, the "PANTALLA()" function is looping and "polling" the variables into the display.

One variable, (TPS) for example always starts around 4 or 6 (unit)
However this variable is ruled by a potentiometer (Throttle positition sensor) So when I "accelerate", it goes from 6 to let`s say 50 (decimal, 2 digits). (Using the map function it translates the digital reading into 0% to 100% opening, 0V -> 5V)

However once I stop "accelerating" and the potentiometer goes to it`s resting estate (around 4 %or 5% value as mentioned) the display "freezes" that value in the highest form (if it was decimal = decimal, if it was centesimal = centesimal)

That`s why I was using in the end of the function the LCD.CLEAR()

But, if I put LCD.CLEAR(); now, the lcd gets cleared 99% of the time, rendering the data almost unseable to the naked eye.

I tried to put an else statement to clear the lcd but it does not change the status quo

Any ideas?

/// Variables
unsigned long previousMillisLCD;
const unsigned long intervalLCD = 300;  // interval in ms*

int MAP = analogRead(pin) //to exemplify it varies over time
int ECT = analogRead(pin) //to exemplify it varies over time
int TPS = analogRead(pin) //to exemplify it varies over time
int RPM = 1000; // lets say its static

void setup()
{

  Serial.begin(9600);
  lcd.begin (16,2); // Inicializar el display con 16 caraceres 2 lineas
  }//Cierra funcion Setup

void loop()
{

  PANTALLA(); // Maneja display
  
}// Cierra funcion 


/// FUNCTION DECLARATION

void PANTALLA(){ //Funcion que controla display y variables

if(millis() - previousMillisLCD >= intervalLCD)

{

lcd.print("MAP ECT TPS RPM");
lcd.setCursor ( 0, 1 );
lcd.print(MAP);
lcd.setCursor ( 4, 1 );
lcd.print(ECT);
lcd.setCursor ( 8, 1 );
lcd.print(TPS);
lcd.setCursor ( 12, 1 );
lcd.print(RPM);
previousMillisLCD = millis();

}else{

//lcd.clear();

} //Close ELSE

}//Close PANTALLA

BTW I know the lcd.clear function is comented, I just leave it there to show some context

Using lcd.clear() is not a good way to manage the display. It is slow and can lead to flicker.

You need to manage the cursor and write spaces to clear old data.

First, unchanging text should be written once in setup(). The display has an internal memory.

void setup()
{
  Serial.begin(9600);
  lcd.begin (16, 2); // Inicializar el display con 16 caraceres 2 lineas
  lcd.print("MAP ECT TPS RPM");
}

Then the display function can lool like this

void PANTALLA(){ //Funcion que controla display y variables

if(millis() - previousMillisLCD >= intervalLCD)

{

//lcd.print("MAP ECT TPS RPM");
lcd.setCursor(0,1);
lcd.print("                ");//16 blank spaces
lcd.setCursor ( 0, 1 );
lcd.print(MAP);
lcd.setCursor ( 4, 1 );
lcd.print(ECT);
lcd.setCursor ( 8, 1 );
lcd.print(TPS);
lcd.setCursor ( 12, 1 );
lcd.print(RPM);
previousMillisLCD = millis();
}

This approach can be optimized to only write and clear things which are changing or only clear one space if a three digit number goes to two.

Thanks! Both of you for the insight

The code and display now works beautiful

I shall progress with further parts of code.

If anyone is curious to work on, or see a homemade EFI system built in an arduino feel free to contact me. I´m running this idea to present in my college this project on the end of the year´s exposition.

The arduino is using the sensors and logic (reverse engineered mostly) of a Rochster Multec 700 fuel inyection. (GM Motors and Pontiac, from around 80´s to 90´s)

(Still not published the entire code because is only a skecth, still needs finetuning and more documentation)

Best regards