Error in calculating fuel consumption.

Hello I have a trip computer which counts fuel consumption and speed. Problem is with that i do not get steady readouts.

All code is here: https://pastebin.com/SRE5GNTS

I had made a test. I connect another arduino and send a square signal to trip computer ( arduino mega) to simulate fuel injector signal. And this is what i get: |500x282 Why it somtimes shows 17.15 and sometimes 17.44 Everytime when is interrupt call it only counts injector open time and and then with every second counts instant fuel consumption.

I was trying with moving average but problem is stil visable.

Any one knows how to fix this?

Not many members will download code from those sites. You will get faster help if you post your code in accordance with the forum guidelines in the how to use this forum-please read stickies at the top of each forum topic.

you might have float "rounding error"... what type of arduino do you use?

For trip computer i use arduino mega. Can "rounding error" happen like this, that every few seconds change value for a while. I was reading about volatile variable, that while the variable is read the interrupts need to be disabled but i do not figured out how to implement this in code. By the way, it is not my code i only modyfied it for Arduino mega and diferent display, digital temp sensor.

It’s a Good Thingtm that you found this before your code grew too big.

If you have a variable that fits on more than one byte, which you update in an interrupt, you declare it as volatile to avoid issues due to the code optimizer volatile uint16_t count;but when you do math with this variable in the loop for example, the bytes can be changed whilst you do your computation. Imagine our variable was 0x00FF an an operation starts looking at the LSB (the 0xFF) and then an interrupt happens à increments the counter, which is now 0x0100. When you come back from the ISR the math continues looking at the MSB which is 0x01 now. So you have done an operation as if the counter was 0x01FF (511dec) instead of 0x00FF or 0x0100 (255dec or 256dec) => your math will see a value almost 2x as large as reality and if you print it you won’t see the issue…

To prevent this, either you block interrupts whilst doing your maths

// disable interrupts
noInterrupts(); // https://www.arduino.cc/reference/en/language/functions/interrupts/
// YOUR MATH GOES HERE
// re-enable interrupts
interrupts(); // https://www.arduino.cc/reference/en/language/functions/interrupts/interrupts/

but if the math takes a long time you might miss a few interrupts and millis is no longer maintained, you can’t use Serial etc… so an alternative is to duplicate the variables that might be changing in a protected section and do the math with the copy

uint16_t countCopy;
// disable interrupts
noInterrupts(); // https://www.arduino.cc/reference/en/language/functions/interrupts/
// duplicate the variable, nothing “bad” can happen to the variable as interrupt are off
countCopy = count;
// re-enable interrupts
interrupts(); // https://www.arduino.cc/reference/en/language/functions/interrupts/interrupts/
// YOUR MATH GOES HERE USING THE countCopy VARIABLE

I did what you wrote, hope that it is correct, but with this is even worse than before and i get lower value than before.

In variables declaration i add :

uint16_t unleadinj_Open_Duration2;

then in first row void loop i add:

noInterrupts(); 
         unleadinj_Open_Duration2 = unleadinj_Open_Duration;
          interrupts();

And there where is math do, unleadinj_Open_Duration; switch to unleadinj_Open_Duration2

I do not know that you saw my code but instant consuption is calculated every one second from unleadinj_Open_Duration which is total time when injector is open throught one second. For some reason some times it see that injector is open longer throught one second then before.

No I have not seen your code

There might be other issues such as what I was referring to in #1. A float is encoded on 4 bytes on your mega so does not have much precision. If your maths involve small or big divide/multiply you might loose precision along the way

Here is part of the code resposible for fuel consuption calculation:

void UnleadedTime() // it is called every time a change occurs at the gasoline injector signal and calculates gasoline injector opening time, during the 1sec interval
{
  if (digitalRead(19) == LOW)
      {
        unleadTime1 = micros();
      }
  if (digitalRead(19) == HIGH)
      {
        unleadTime2 = micros();
      }
  if (unleadTime2 > unleadTime1)
      {if ((unleadTime2 - unleadTime1) > 500 && (unleadTime2 - unleadTime1) < 12000) // some conditions to avoid false readings because of noise
                                                                                  
        { 
        unleadinj_Open_Duration = unleadinj_Open_Duration + (unleadTime2 - unleadTime1); //total useconds that the gasoline injector opens throughout 1sec                                                                                
        } 
      }  
   }

void unleadedConsumption()
{
  if (speed > 2 ) instant_unlead_consumption = (100*((unleadinj_Open_Duration * unleadedFlow)*3600))/speed; 
                                                                                                       
  else  instant_unlead_consumption = unleadinj_Open_Duration * unleadedFlow * 3600; // when the car stops calculates the instant consumption in l/h
  
  
  used_Unleaded = used_Unleaded + (unleadinj_Open_Duration * unleadedFlow);   
  used_Unleaded2 = used_Unleaded2 + (unleadinj_Open_Duration * unleadedFlow); 
  Unleaded_in_tank = Unleaded_in_tank - (unleadinj_Open_Duration * unleadedFlow); 
}

“Unleaded Time” is called every time when injector signal turn on interrupts. Then after one second when timer overflow it call “unleadedConsumption()” which counts instant consuptions.
I just wonder why this alwas happend every exactly 3 to 4 seconds.

DigitalRead is slow (a few microseconds) and you try to measure things in micro-seconds ? You should use an else in between your if Are your variables float ? (use 3600.0 or 100.0 to force compiler to use a float in calculation if you have some integers)

AdamK24: |500x282 Why it somtimes shows 17.15 and sometimes 17.44

The “only” code you posted has just 1 line of serial print, Serial.println(instant_unlead_consumption);

So are you saying “that” screenshot is based on “that” code?

J-M-L: DigitalRead is slow (a few microseconds) and you try to measure things in micro-seconds ? You should use an else in between your if Are your variables float ? (use 3600.0 or 100.0 to force compiler to use a float in calculation if you have some integers)

So what could be better from Digitalread? For injector open duration code use "volatile unsigned long", and for instant_unlead_consumption "volatile float"

Slumpert: The “only” code you posted has just 1 line of serial print, Serial.println(instant_unlead_consumption); So are you saying “that” screenshot is based on “that” code?

Yes it is based on that code, why you asking.

AdamK24:
So what could be better from Digitalread?

You could read the port directly. You would lose some flexibility and portability, but gain spare.