Please fix my hourmeter

Trying to make an hour meter which starts counting time when there is an input signal higher than 10 and stops if the signal is lower than 10 (measured by fuel flow frequency). If the frequency rises it should resume counting. The problem is that it start counting when the arduino starts working. I know that it is a common question and there are lots of answers regarded to that problem but still I was unable to make it work with my project. Please explain how to fix it. Here are the main parts of the code:

unsigned long time, finished;
unsigned long startTime;
unsigned long elapsed;


void setup () {


  startTime = millis();


}


void DisplayResult()
{



  if (flow_frequency >= 10)


  {
    finished = millis(); // сохраняет время остановки, чтобы вычислить прошедшее время.
    // объявляем переменные



    elapsed = finished - startTime;
    // сохраняет время остановки, чтобы вычислить прошедшее время.
    // объявляем переменные
    float h, m, s, ms;
    unsigned long over;



    h = int(elapsed / 3600000);
    over = elapsed % 3600000;
    m = int(over / 60000);
    over = over % 60000;
    s = int(over / 1000);


    tft.setCursor(3, 100); // нужно
    tft.setTextSize(2);
    tft.print("Hour meter");

    tft.setCursor(0, 160);

    tft.print(h, 0);
    tft.print("h ");
    tft.print(m, 0);
    tft.print("m ");
    tft.print(s, 0);
    tft.print("s ");
  }

}

You didn't post the complete sketch.

#include <Adafruit_GFX.h>    
#include <Arduino_ST7789.h> 
#include <SPI.h>


#define TFT_DC    8
#define TFT_RST   9 //RES
#define TFT_CS    10 // only for displays with CS pin NIL
#define TFT_MOSI  11   // for hardware SPI data pin (all of available pins) SDA
#define TFT_SCLK  13   // for hardware SPI sclk pin (all of available pins)SCK
Arduino_ST7789 tft = Arduino_ST7789(TFT_DC, TFT_RST); //for display without CS pin


volatile int flow_frequency; // Measures flow sensor pulses
unsigned int l_hour; // Calculated litres/hour
unsigned char flowsensor = 2; // Sensor Input
unsigned long currentTime;
unsigned long cloopTime;
void flow () // Interrupt function
{
  flow_frequency++;
}


unsigned long time, finished;




unsigned long startTime;
unsigned long elapsed;





void setup () {


  tft.init(80, 160);  

  uint16_t time = millis();
  tft.fillScreen(BLACK);
  tft.setTextColor(WHITE, BLACK);



  pinMode(flowsensor, INPUT);
  digitalWrite(flowsensor, HIGH); 

  attachInterrupt(0, flow, RISING); 
  sei(); // Enable interrupts
  currentTime = millis();
  cloopTime = currentTime;
  startTime = millis();




}

void loop()
{
  CheckFlow();

  DisplayResult();



}




void CheckFlow()
{

  tft.setCursor(5, 30);

  tft.setTextSize(2);
  tft.println("Fuel Meter");


  currentTime = millis();
  // Every second, calculate and print litres/hour
  if (currentTime >= (cloopTime + 1000))
  {
    cloopTime = currentTime; // Updates cloopTime
    // Pulse frequency (Hz) = 7.5Q, Q is flow rate in L/min.
    l_hour = (flow_frequency * 60 / 5880); // (Pulse frequency x 60 min) / 7.5Q = flowrate in L/hour
    flow_frequency = 0; // Reset Counter


    tft.setCursor(5, 60);

    tft.setTextSize(2);

    tft.print(l_hour, DEC); // Print litres/hour
    tft.println(" L/m");



  }

}



void DisplayResult()
{



  if (flow_frequency >= 10)


  {
    finished = millis();



    elapsed = finished - startTime;
  
    float h, m, s, ms;
    unsigned long over;



    h = int(elapsed / 3600000);
    over = elapsed % 3600000;
    m = int(over / 60000);
    over = over % 60000;
    s = int(over / 1000);


    tft.setCursor(3, 100); // нужно
    tft.setTextSize(2);
    tft.print("Hour meter");

    tft.setCursor(0, 160);

    tft.print(h, 0);
    tft.print("h ");
    tft.print(m, 0);
    tft.print("m ");
    tft.print(s, 0);
    tft.print("s ");
  }

}

What do you understand by “input signal higher than 10”? Your ISR is incrementing a counter whenever the pin state rises but it does not read the state of the pin.

You should be more careful when accessing “flow_frequency” outside the ISR, an interrupt may actually change it whilst you are getting or setting it which may cause weird behaviour. You may also want to use an unsigned value since you probably do not want negative values.

//How to access volatile multi-byte variables:

volatile unsigned int value = 0;

void ISR()
{
  value++;
}

void loop()
{
  cli(); //Clear interrupts
  unsigned int copy_of_value = value; //Copy value
  value = 0; //Reset value
  sei(); //Start interrupts
  
  //From here you can safely use "copy_of_value"
}

Thanks, never heard about this issue, added the code you provided, but still nothing has changed

#include <Adafruit_GFX.h>   
#include <Arduino_ST7789.h>
#include <SPI.h>


#define TFT_DC    8
#define TFT_RST   9 //RES
#define TFT_CS    10 // only for displays with CS pin NIL
#define TFT_MOSI  11   // for hardware SPI data pin (all of available pins) SDA
#define TFT_SCLK  13   // for hardware SPI sclk pin (all of available pins)SCK
Arduino_ST7789 tft = Arduino_ST7789(TFT_DC, TFT_RST); //for display without CS pin


volatile unsigned int flow_frequency = 0; // Measures flow sensor pulses
unsigned int l_hour; // Calculated litres/hour
unsigned char flowsensor = 2; // Sensor Input
unsigned long currentTime;
unsigned long cloopTime;
void flow () // Interrupt function
{
  flow_frequency++;
}


unsigned long time, finished;




unsigned long startTime;
unsigned long elapsed;





void setup () {


  tft.init(80, 160); 

  uint16_t time = millis();
  tft.fillScreen(BLACK);
  tft.setTextColor(WHITE, BLACK);



  pinMode(flowsensor, INPUT);
  digitalWrite(flowsensor, HIGH);

  attachInterrupt(0, flow, RISING);
  sei(); // Enable interrupts
  currentTime = millis();
  cloopTime = currentTime;
  startTime = millis();




}

void loop()
{
  CheckFlow();

  DisplayResult();



}




void CheckFlow()
{

  tft.setCursor(5, 30);

  tft.setTextSize(2);
  tft.println("Fuel Meter");


  currentTime = millis();
  // Every second, calculate and print litres/hour
  if (currentTime >= (cloopTime + 1000))
  {
    cloopTime = currentTime; // Updates cloopTime
    // Pulse frequency (Hz) = 7.5Q, Q is flow rate in L/min.
    l_hour = (flow_frequency * 60 / 5880); // (Pulse frequency x 60 min) / 7.5Q = flowrate in L/hour
    flow_frequency = 0; // Reset Counter


    tft.setCursor(5, 60);

    tft.setTextSize(2);

    tft.print(l_hour, DEC); // Print litres/hour
    tft.println(" L/m");



  }

}



void DisplayResult()
{

cli(); //Clear interrupts
  int copy_flow_frequency = flow_frequency; //Copy value
  flow_frequency = 0; //Reset value
  sei(); //Start interrupts

  if (copy_flow_frequency >= 10)


  {
    finished = millis();



    elapsed = finished - startTime;
 
    float h, m, s, ms;
    unsigned long over;



    h = int(elapsed / 3600000);
    over = elapsed % 3600000;
    m = int(over / 60000);
    over = over % 60000;
    s = int(over / 1000);


    tft.setCursor(3, 100); // нужно
    tft.setTextSize(2);
    tft.print("Hour meter");

    tft.setCursor(0, 160);

    tft.print(h, 0);
    tft.print("h ");
    tft.print(m, 0);
    tft.print("m ");
    tft.print(s, 0);
    tft.print("s ");
  }

}

Not sure I understand the problem. Are you seeing the display showing increasing hours but there is no flow?

Instead of blindly copy-pasting something, you might want to think about what you are actually doing, you did not fix anything with your modification. Consider using the Gigs and collabs board if you want someone to make your code.

after adding the line flow_frequency = 0; //Reset value as Danois90 suggested the flow counter stop working so I’ve deleted it

void DisplayResult()
{
cli(); //Clear interrupts
int copy_flow_frequency = flow_frequency; //Copy value
// flow_frequency = 0; //Reset value -
sei(); //Start interrupts

2 wildbil:l the stopwatch should detect the fuel flow and start counting time from zero. Instead of it it starts cointing time not from zero but from arduino startup. For ex: arduino is powered on for 9 minutes, then I blow air in the flow sensor for 16 seconds. Stopwatch shows zero, but then detects air flow and shows 9 minutes and starts counting seconds. But the real flow was only during 16 seconds

Danois90: Instead of blindly copy-pasting something, you might want to think about what you are actually doing, you did not fix anything with your modification. Consider using the Gigs and collabs board if you want someone to make your code.

yes, I know that it might be easy for me to ask somebody to write the code, but I really want to make the device by myself and I think that it's almost done, I'm just missing something obvious

This line is your issue:

    elapsed = finished - startTime;

startTime is only set in setup and is effectively the time that the Arduino was powered up, so as you observe, once the hour meter comes on, it assumes that the time that there was flow is based on that.

I think that you need a different method to get elapsed.

I would try doing it in CheckFlow: just before you zero out flow_frequency, if it is >= ten, add 1000 to elapsed. Then you'll get a running total instead of the bogus number you have now.

If I understood you correctly it should be like this, but still the same problem exists

Moved the startTime to checkFlow and added +1000 to elapsed

void CheckFlow()
{

  tft.setCursor(5, 30);

  tft.setTextSize(2);
  tft.println("Fuel Meter");


  currentTime = millis();
  // Every second, calculate and print litres/hour
  if (currentTime >= (cloopTime + 1000))
  {
    cloopTime = currentTime; // Updates cloopTime
    // Pulse frequency (Hz) = 7.5Q, Q is flow rate in L/min.
    l_hour = (flow_frequency * 60 / 5880); // (Pulse frequency x 60 min) / 7.5Q = flowrate in L/hour

   startTime = millis();

    flow_frequency = 0; // Reset Counter


    tft.setCursor(5, 60);

    tft.setTextSize(2);

    tft.print(l_hour, DEC); // Print litres/hour
    tft.println(" L/m");



  }

}



void DisplayResult()
{


  if (flow_frequency >= 10)


  {


 elapsed = startTime + 1000;

I'm suggesting that you get rid of startTime and just accumulate run time in elapsed.

Sorry, can't understand you. Could you please explain it to me?

In checkFlow what should I add? How to specify that elapsed means running time? If you mean to write elapsed = millis() + 1000; then it's not working either

telect: Sorry, can't understand you. Could you please explain it to me?

In checkFlow what should I add? How to specify that elapsed means running time? If you mean to write elapsed = millis() + 1000; then it's not working either

Nope,

elapsed+=1000;

The problem still exists :confused:

void CheckFlow()
{

  tft.setCursor(5, 30);

  tft.setTextSize(2);
  tft.println("Fuel Meter");


  currentTime = millis();
  // Every second, calculate and print litres/hour
  if (currentTime >= (cloopTime + 1000))
  {
    cloopTime = currentTime; // Updates cloopTime
    // Pulse frequency (Hz) = 7.5Q, Q is flow rate in L/min.
    l_hour = (flow_frequency * 60 / 5880); // (Pulse frequency x 60 min) / 7.5Q = flowrate in L/hour

  elapsed+=1000;

    flow_frequency = 0; // Reset Counter


    tft.setCursor(5, 60);

    tft.setTextSize(2);

    tft.print(l_hour, DEC); // Print litres/hour
    tft.println(" L/m");



  }

}



void DisplayResult()
{



  if (flow_frequency >= 10)
  

  {


  
    float h, m, s, ms;
    unsigned long over;



    h = int(elapsed / 3600000);
    over = elapsed % 3600000;
    m = int(over / 60000);
    over = over % 60000;
    s = int(over / 1000);

I would try doing it in CheckFlow: just before you zero out flow_frequency, if it is >= ten, add 1000 to elapsed. Then you'll get a running total instead of the bogus number you have now.

You missed this bit :)

If I add this line before zeroing the flow_frequency the flow meter stops working, so I've added the line after flow_frequency = 0;. But know the seconds runs very fast because of elapsed += 1000; And thank you very much for helping me.

void CheckFlow()
{

  tft.setCursor(5, 30);

  tft.setTextSize(2);
  tft.println("Fuel Meter");


  currentTime = millis();  // Every second, calculate and print litres/hour
  if (currentTime >= (cloopTime + 1000))
{
    cloopTime = currentTime; // Updates cloopTime
    // Pulse frequency (Hz) = 7.5Q, Q is flow rate in L/min.
    l_hour = (flow_frequency * 60 / 5880); // (Pulse frequency x 60 min) / 7.5Q = flowrate in L/hour

    flow_frequency = 0; // Reset Counter

    tft.setCursor(5, 60);

    tft.setTextSize(2);

    tft.print(l_hour, DEC); // Print litres/hour
    tft.println(" L/m");
 }

    if (flow_frequency >= 10)
 {
    elapsed += 1000;   


  }

}



void DisplayResult()
{

  float h, m, s, ms;
  unsigned long over;



  h = int(elapsed / 3600000);
  over = elapsed % 3600000;
  m = int(over / 60000);
  over = over % 60000;
  s = int(over / 1000);

telect: yes, I know that it might be easy for me to ask somebody to write the code, but I really want to make the device by myself

So, why aren't you making it yourself? Seems to me like you just keep asking how to do it and then copy the answers into your code.. Good luck with that!

This is a very tricky program. You want to calculate flow rate from the pulses but you also want to use the flow rate to determine if the hour meter is running or not. I think part of the problem is resetting the flow meter pulse count every second. That sometimes causes the hour meter to be off when it should be on. What I did was keep the pulse count continuous, never setting it back to zero. Then you can subtract the count at the current second from the count at the previous second to get the flow rate (pulses per second). That same flow rate is used to determine if the hour meter should be running or not.

Another problem is keeping the amount of time the hour meter has been on, rather than the amount of time the Arduino has been on. I did that by updating a global Total only when the meter went from ON to OFF. This is like the “DetectStateChange” example where you only care if a signal has just turned on (start a timer) or just turned off (add the timer to the total). WHILE the device is ‘running’ a separate counter is used to calculate what the global total would be if the device were to shut off right then.

Here is my attempt. I didn’t hace the “Arduino_ST7789” library so I used the “Adafruit_ST7789” library from Library Manager.

#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
#include <SPI.h>


#define TFT_DC    8
#define TFT_RST   9 //RES
#define TFT_CS    10 // only for displays with CS pin NIL
#define TFT_MOSI  11   // for hardware SPI data pin (all of available pins) SDA
#define TFT_SCLK  13   // for hardware SPI sclk pin (all of available pins)SCK
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);

unsigned long AccumulatedRunTime = 0;  // Updated only when flow stops (milliseconds)
unsigned long CurrentRunTime;  // Updated continuously while flow is running  (milliseconds)


const byte FlowMeter_ISRPin = 2;
volatile unsigned long TotalFlowMeterPulses = 0;

// Each pulse of the flow meter represents 0.125 liters (1/8th of a liter)
void FlowMeterISR()
{
  TotalFlowMeterPulses++;
}

// How often, in milliseconds, do we update the display?
const unsigned DisplayInterval = 1000;  // Every second

const uint16_t  Display_Color_Black        = 0x0000;
const uint16_t  Display_Color_White        = 0xFFFF;

void setup ()
{
  tft.init(80, 160);
  tft.fillScreen(Display_Color_Black);
  tft.setTextColor(Display_Color_White, Display_Color_Black);

  pinMode(FlowMeter_ISRPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(FlowMeter_ISRPin), FlowMeterISR, RISING);
}

void loop()
{
  unsigned long currentMillis = millis();


  static unsigned long previousTotalFlowMeterPulses = 0;

  // Because 'TotalFlowMeterPulses' is 'volatile' we need a local copy to work on.
  unsigned long currentTotalFlowMeterPulses;
  noInterrupts();
  currentTotalFlowMeterPulses = TotalFlowMeterPulses;
  interrupts();

  static unsigned long lastDisplayTime = 0;
  if (currentMillis - lastDisplayTime >= DisplayInterval)
  {
    lastDisplayTime = currentMillis;

    unsigned long flowPulsesThisInterval = currentTotalFlowMeterPulses - previousTotalFlowMeterPulses;
    previousTotalFlowMeterPulses = currentTotalFlowMeterPulses;  // Set up for next interval

    boolean isRunning = flowPulsesThisInterval > 10;

    static boolean wasRunning = false;
    static unsigned long sessionStartTime = 0;

    if (isRunning != wasRunning)
    {
      wasRunning = isRunning;

      if (isRunning) // Just started
      {
        sessionStartTime = currentMillis;
      }
      else // Just stopped
      {
        AccumulatedRunTime += currentMillis - sessionStartTime;
      }
    }

    if (isRunning)
      CurrentRunTime = AccumulatedRunTime + (currentMillis - sessionStartTime);
    else
      CurrentRunTime = AccumulatedRunTime;


    unsigned litersPerMinute = (flowPulsesThisInterval * 60) / 8;  // 8 pulses per liter, 60 intervals per minute

    tft.setCursor(5, 30);

    tft.setTextSize(2);
    tft.println("Fuel Meter");

    tft.setCursor(5, 60);

    tft.setTextSize(2);

    tft.print(litersPerMinute, DEC); // Print litres/minute
    tft.println(" lpm");

    unsigned d, h, m, s, ms;
    unsigned long time = CurrentRunTime;

    ms = time % 1000;
    time /= 1000;  // Convert to seconds
    s = time % 60;
    time = time / 60; // Convert to minutes
    m = time % 60;
    time = time / 60; // Convert to hours
    h = time % 60;
    time = time / 24; // Convert to days
    d = time % 60;

    tft.setCursor(3, 100); // нужно
    tft.setTextSize(2);
    tft.print("Hour meter");

    tft.setCursor(0, 160);

    tft.print(d);
    tft.print("d ");
    tft.print(h);
    tft.print("h ");
    tft.print(m);
    tft.print("m ");
    tft.print(s);
    tft.print("s ");
  }

}

OMG, that is something completely different! Thank you! It works perfectly, I just need some time to understand your sketch. Again, thank you very much!