Log FuelFlow -Reading 4 frequency inputs (digital on/off) and logging to SD card

Well, I don't have the libraries installed, nor an SD shield, nor an Uno, so I'll have to leave the debugging up to you. You may need to back-step to a previous sketch then gradually revise it to what you need.

Hi,

Yes, I think so too. But thank you I really appreciate your help, you've helped me out sooo much so far. You can send me your address and I will send you an Adafruit SD shield :slight_smile:

Thanks, however you should keep it as its already compatible with your board.

I'm not sure how far I'll take things with my sketch (never have the time) but my intention is to one day create a Pulse Measurement library with functions for frequency, pulse width, pulse duty, flow, speed, power, energy, odometer, volume, phase, peak demand, averaged and instantaneous values, de-bouncing, stability, PWM output modes, PWM measurement modes, etc.

Sounds like a useful library. I could use al lot of that. I wanted to buy you a new SD card and send to you.

I have reverted to an earlier to an older skecth with success! At least so far. Check out the result and the sketch!

]:slight_smile: ]:slight_smile: ]:slight_smile: ]:slight_smile: ]:slight_smile: ]:slight_smile: ]:slight_smile: ]:slight_smile:

fuel_flow_10_inputs_SD_logging.ino (7.75 KB)

DATALOG.xls (9 KB)

Hi,

Information to anyone wanting to use this sketch:

The attached sketch logs fuel flow from 4 flow meters by a digital 5V impulse, measures the timeperiod and calculates the frequency and flowrate based on the set pulseconstant for the meter. It saves the flow data to an Adafruit data logger SD card shield with RTC timestamp. If serial monitor is active, the result is displayed there as well. The sketch is built for monitoring flow and burnrate on 2 engines simultaneously, using 4 flow meters. 2 for each engine (supply and return). SD card data is stored in a .CSV file and is easily imported into excel and flow data is displayed under the relevant heading.
Each time the sketch is restared, a new heading is written to distingish tests form each other. Data logging only takes place is an input is sensed, meaning that if there are no impulses on the inputs(flowflow), it stops logging.

The logged values for each set of flow meters, 10 values total:

New log started!
Date : Time
Supply(instantaneous) -- TotalSupply -- Return(instantaneous) -- TotalReturn -- Burnrate (supply - return) x 2
0.000000 0.000 0.000000 0.0000 0.000000

To set the flowrates to match your meters: Set the pulse constant (K-factor) to what is specific for your meters.

For some reason, the pulse generator is not working.

Fuel_Flow_Logging_to_SD_Card_4_channels.ino (8.26 KB)

The opening braces at lines 74 and 110 are spurious but harmless.

On line 131 it would be better to use the defined constants HIGH and LOW rather than literals 1 and 0.

On line 132 and throughout the remainder of this function I suggest calling micros() once (and saving the result to a local temporary variable) rather than calling it multiple times. As well as saving a tiny amount of processing time, it also avoids inaccuracies if the value changes between these calls.

All time values should be held as unsigned long, not (signed) long.

The code on line 215 seems to be in the wrong place (it is outside the if statement that tests whether the interval has passed). In general, I suggest putting the 'previous time' update as close as you can to the start of the block that executes when the interval has elapsed, so it is always visually obvious that you are updating it in the correct place. Also, I suggest that you update startTime by incrementing it by the interval rather than setting it to 'now'. This avoids timing slip that would otherwise occur if there is any latency between the interval elapsing and your code running that detects that it has elapsed. There will be a similar issue at line 230.

Since pgenPreviousMicros is only ever used inside pgen(), it doesn't need to be a global and could be defined as a static local inside pgen(). By reducing the scope of the variable you no longer need to think about other code when looking at how it may be used, and no longer need to think about this variable when understanding what the other code is doing.

The variable pgenInterval should be defined as unsigned long. Since you're using a value larger than an int in its initialisation expression, I suggest you use 1000000UL to make sure the compiler evaluates this expression using the correct type.

Values which you don't intend to change at runtime should be declared as const.

The issues above might explain why the pulse generation isn't working. If not, I suggest you post your revised code and we can see what else might be causing it.

Hi PeterH,

Thanks a lot for the feedback. I will have a look at it and see if I can figure out what you're talking about :slight_smile:
It's always good to optimize the code and I would be very surprised if this code didn't need tuning.
Tomorrow, that will be.

Hi Peter H,

I've changed all time variable datatypes to unsigned long and removed the extra curly braces from lines 74 and 110.

Line 131 is changed to HIGH and LOW.

I've also placed line 215 inside the if statement again. However I'm not sure what you mean about line 230. See new skecth.

pgenPreviousMicros has been moved to function as local variable. 1000000UL is used instead of 1000000 in pgenInterval.

I'm not sure what the line 114 dataString statement does..? Is that necessary?

About the micros() in line 132, I'm not really sure how to correct it, I've tried writing a micros() = presentTime; statement and defining presentTime as an unsigned long, but it didn't work for me, so I've left it alone so far.

EDIT: added updated sketch

Fuel_Flow_Logging_to_SD_Card_4_channels.ino (8.26 KB)

zenseidk:
EDIT: added updated sketch

Are you sure you attached the right version? I don't see the changes you described above.

Hi,

I may have attached the wrong one, try this instead.

Fuel_Flow_Logging_to_SD_Card_4_channels.ino (8.73 KB)

The pgenPreviousMicros variable in pgen() needs to be declared as a static variable, so that it keeps its value between calls. (Otherwise it will be allocated automatically on the stack and the value will be discarded when the function returns.) This change is important because pgen() won't work correctly without it.

static unsigned long pgenPreviousMicros = 0;

Given that your logging is only done once per second, using micros() to time it seems overkill. I would use millis() unless you need the extra resolution of micros(), although what you have ought to work.

The update to previousMicros is in the right code block now. Personally I'd prefer to put it at the top of the block (next to the 'if' statement that is doing your elapsed time check) so that it is more obvious that it is updated correctly, but it won't affect the behaviour.

To eliminate the redundant calls to micros() you would save the value of micros() in a variable somewhere near the start of the function.

unsigned long nowMicros = micros();

Then you would use the currentMicros variable throughout the rest of the function where you had calls to millis(). I wouldn't expect this change to make much difference to the functionality but it just eliminates the possibility of inaccurate timing if the value of millis() changes while this function is running.

I haven't spotted the dataString statement that you asked about.

Hi PeterH,

I removed the datastring bit from the last sketch, that's properbly why you couldn't see it.

In this new version I changed the pgenPreviousMicros to static unsigned long as you suggested, but to no effect, apparently. pgen is still not working.

I also added a nowMicros instead of micros() to the function, please see if I did it correctly. About millis, if it doesn't change anything I think I'll leave it like it is.

I've also added a total burn (supply burn - return burn) to each set/engine.

I'm working on getting values displayed on a 128x64 GLCD, that seems to be a not so easy task with some difficult programming. I may just get one of those new fancy color TFT displays instead. Maybe they are easier to program...

Thanks for your support.

Fuel_Flow_Logging_to_SD_Card_4_channels_2.ino (9.08 KB)

zenseidk:
In this new version I changed the pgenPreviousMicros to static unsigned long as you suggested, but to no effect, apparently. pgen is still not working.

I don't see that change in the version you attached.

The basic use of nowMicros in pulseMeasure() looks OK, but I think there is a problem due to the fact you're using it in a FOR loop. It would probably work by mistake. I would expect it to detect (nowMicros - previousMicros > 1000000) is true for the first element in the loop, write the entry to the log and then update previousMicros so that the condition is no longer true for the subsequent loop iterations. The SD output shouldn't be inside the FOR loop, should it?

unsigned long nowMicros = micros();
for (int i = 0; i < qty; i++) 
{
    digital read, change detection, pulse length calculation, flow rate calculation, update cumulative total
}
if (nowMicros - previousMicros > 1000000UL) 
{
    previousMicros += 1000000UL;
    write stats to SD
}

Hi Peter,
The pgen function is not important for me as I don't use it.

I'm focusing on printing values to GLCD:

S1 : 0.000 [L/h]
R1 : 0.000 [L/h]
BR1: 0.000 [L/h]
TB1: 0.000 [L]
S2 : 0.000 [L/h]
R2 : 0.000 [L/h]
BR2: 0.000 [L/h]
TB2: 0.000 [L]

I've succeeded in getting the values displayed on the 128x64 GLCD, however there are some inconsistencies between the TotalBurn values logged on the serial monitor and the values displayed on tha GLCD. But then again, the refresh rates are different, that might have something to do with it. it's only 0.06 L and is seems to be pretty constant, so it propably no big deal. Further testing is required!

Hi,

I hope someone can help me understand what is going on here...

I just returned from the ship where I was measuring fuel consumption and for which I built a datalogger with 4 inputs. On the bench the logger works fine with a signal generator. The calculations are good, fairly constant and everybody is happy. But when I attached the 4 flow meters, the numbers that were logged were fluctuating wildly, even though I could read the output on the handheld display to be very stabil at 1060 L/h for the supply line and 900~500 L/h on the return line. The handheld display is fed data via RS485, so the frequency output is a separate output for use with some external data logging hard-/software.
I have attached a .csv file where you can see the logged values.

I have also attached a diagram of how I connected the the wiring insode the datalogger box and a diagram of how the system connects together.

The code I used is also attached. There is a very strange issue with it, as in line 89 in the setup function, the serial printing of the headers: When I change the header text, the SD card doesn't work. If I remove text or add text, the SD stops working! This makes absolutely no sense to me, as changing the header text should have no effect on the SD card part of the setup function?!?!?

Is there a rational explanation for this or is it something in the Serial.print function that is not right?

DATALOG_4GB.CSV (1.44 MB)

Data logger fuelflow setup.pdf (16.1 KB)

Fuel_Flow_Logging_to_SD_Card_4_channels_UNO_WORKING.ino (8.85 KB)

And a question for dlloyd:

How do you use an Arduino Due for this when the flow meters work with 4.5-24 V and the max input on the Due is 3.3 V?

I purchased a Due which I'm trying to get working, but I have this voltage problem, you see...

Hello zenseidk,
I'm going to be out of touch for a few weeks, but the Due is a good choice for your project with its much higher speed, memory and the ability for each pin to have its own interrupt.

Yes the Due is a 3.3V board which applies to all pins - be careful as it is more sensitive than 5V boards and cannot tolerate 5V signals. It would be good to post what power supply voltage the fuel sensors have and what the output signals look like ... a specification sheet would be helpful.

You will need to translate all input voltage levels to 3.3V and all 3.3V outputs to back original levels. There are many methods of doing this, each having its pros and cons. Luckily, you don't seem to have numerous signals to work with. Please search the forums for voltage level translation ... there's a good pdf somewhere but I don't have a link to it.

On the bench the logger works fine with a signal generator. The calculations are good, fairly constant and everybody is happy. But when I attached the 4 flow meters, the numbers that were logged were fluctuating wildly, even though I could read the output on the handheld display to be very stabil at 1060 L/h for the supply line and 900~500 L/h on the return line. The handheld display is fed data via RS485, so the frequency output is a separate output for use with some external data logging hard-/software.

I would be good to know frequency range and what the signals look like from the flow meters. Note that may need around 10K pull-up resistors as MPU's internal pull-ups are quite weak. If the outputs of the flow meters are open-collector, then using pull-ups to 3.3V will improve the signal and also do the voltage level translation.

I see in your sketch that you could comment out the pulse generator in the main loop as it is not being used ... this will gain some extra performance.

Hi dlloyd,

The power supply is 12 V, but the output signal is an open collector type circuit, so I just connected it to 5V from the UNO's power. I don't know how low it will go though, 3.3 V seems pretty low, but it may work. Even if this will work, I still need to get the data logger shield working, which I'm currently working on. I found a thread on the Due forum, but I'm having any luck on it so far...

Another thing that is missing from the code is setting flow rates to zero, if an input loses the signal for some reason. As it is now, the value stays whatever the last measured value was. This means that is an input signal is lost, it will continually log the last measured value. Any ideas on how to do this? I'm currently experimenting with an if statement:
if ((inputsState == LOW) && inputsPrevious == LOW)){
_ flowRate == 0}_
So far I'm not seeing any success, though...

That wiring diagram seems to show the Arduino 5V line being connected to the yellow lines - is that correct?