14 Replies Latest reply on Oct 3, 2015 4:12 PM by DAB

# Help with Arduino timing problem

Hey guys. I'm working on making a fuel gauge for my motorcycle. I have the basic concept working and I've added some averaging so that the gauge isn't going nuts while you're going down the road. Here's the current code:

/*

*LED Fuel Gauge for Honda SuperHawk 16L Tank

*Code by Will Lyon 9/23/2015. Contact: will.lyon12584@gmail.com

*5V to fuel sensor Grn/Blk

*Fuel sensor Gry/Blk to Analog 0 with 10k resistor to GND

*220 ohm resistor to each led and GND

*/

const int sensorPin = A0;       // the pin that the potentiometer is attached to

const int ledCount = 10;        // the number of LEDs in the bar graph

const int numReadings = 30;     // use this value to determine the size of the readings array

int ledPins[] = {

2, 3, 4, 5, 6, 7,8,9,10,11 }; // an array of pin numbers to which LEDs are attached

int readings[numReadings];      // the readings from teh fuel level gauge

int readIndex = 0;              // the index of the current reading

int total = 0;                  // the running total

int average = 0;                // the average

void setup() {

for (int thisLed = 0; thisLed < ledCount; thisLed++) {  // loop over the pin array and set them all to output:

pinMode(ledPins[thisLed], OUTPUT);

}

for (int thisReading = 0; thisReading < numReadings; // initialize all readings to zero

}

}

void loop() {

total = total - readings[readIndex];        // subtract the last reading

readIndex = readIndex + 1;                  // advance to the next position in the array

if (readIndex >= numReadings) {             // if we're at the end of the array

readIndex = 0;                            // wrap around to the beginning

}

average = total / numReadings;              // calculate the average

delay(1000);                                // delay in between readings for stability

average = map(average, 580, 913, 0, ledCount); // map the result to a range from 0 to the number of LEDs:

// loop over the LED array:

for (int thisLed = 0; thisLed < ledCount; thisLed++) {

// if the array element's index is less than ledLevel,

// turn the pin for this element on:

if (thisLed == average-1) {

digitalWrite(ledPins[thisLed], HIGH);

}

// turn off all pins higher than the ledLevel:

else {

digitalWrite(ledPins[thisLed], LOW);

}

}

delay(100);

}

Now the problem I have is that when you first power it up (or turn the bike on) it takes about 5-10 seconds to gather enough readings to get the average high enough to get the gauge working. I tried implementing code that would read whether or not the neural light is on and only run the averaging if it was off and display real-time readings when it is on. The problem with this though is that when it switches from real-time to the average it starts at zero and works its way back up.

I'd like it to ideally use real readings for about 10 seconds after power up while at the same time accumulating readings for the average. Then after 10 seconds it would switch to using the average but it would have already accumulated enough readings to get the gauge to stay steady. Most cars do this - when in Park the gauge reads all over the place (if the car is moving and fuel is sloshing in the tank) but once you put it into gear the gauge implements some kind of smoothing or averaging, but it doesn't start from zero and then work its way up.

A member over on the Arduino forums suggested this:

Use millis() and a 'flag'.

When the flag = 0, use the initial value you get from wherever.

When millis() is > your startTime - add startTime = millis(); at the end of setup() - then make the flag = 1 and start using real numbers.

The only problem is I've got no idea how to implement that in my code. I've searched for examples using millis() but all I can find is basic timer and/or stopwatch code and I don't know how to modify/adapt that to work with my situation. Thanks in advance!

• ###### Re: Help with Arduino timing problem

A trick I used long ago was to do a pre-read value into the average and then use a running filter so that you always had a rough initial value that would correct over time.

With most real time systems, you want them to be stable from the beginning without needing complex logic to make your decisions.

It all depends upon the response time you want for the gages.

DAB

• ###### Re: Help with Arduino timing problem

Well I want the response time slow (like a car) so that it's not bouncing all over the place. Having an initial value raises the problem of a quick "key-on" to see the fuel level without having to sit there and wait. I wonder how setting an arbitrary initial value would affect the on-going averaging. How owuld I implement that to give it a shot?

If I could have it use real-time readings for the first 10 seconds after power-on while at the same time accumulating readings to build up the average table then after 10 seconds switch to only the average table it'd be perfect.

Here's a video of how it works currently: https://goo.gl/photos/Mx5LCCY2TSy4D3YC8

• ###### Re: Help with Arduino timing problem

If you use a running filter, it will quickly converge on the ambient reading.

If you need it more responsive, then you should increase your sampling and update rate.

Look at your flow rate.  If it is slow, then your current setting is OK.

If it is fast, then you need a faster response time to prevent overflow conditions.

DAB

• ###### Re: Help with Arduino timing problem

I think you're missing the idea. I want it to run this slow normally. But when it first powers on I want it to be instant. I can make that happen. The problem is when it switches from instant to slow the gauge goes back to empty as it needs to accumulate readings for the average. I want it to read real-time for about the first 10 seconds while it's also accumulating readings for the average, then after 10 seconds use the average for the display.

• ###### Re: Help with Arduino timing problem

It shouldn't be to hard to run the averaging whilst displaying the real values until you have filled the buffer, but it would be more elegant to average the readings that you have until you have filled the buffer.

if you keep the const int numReadings as a maximum value but add a variable that keeps track of how many readings you have taken (starts at 0 and increments by 1 until capping at a maximum value (as set in numReadings)) then you can use the incremental value as the divisor for calculating the average.

• ###### Re: Help with Arduino timing problem

I understand.

A running filter will work at any update rate.

You just set the initial value as average and then calculate the difference of the next reading from the average.

After than you just establish how large a distance you need before you increment or decrement the average value.

It is a simple technique to quickly establish a reliable value over time, regardless of the update cycle.

DAB

• ###### Re: Help with Arduino timing problem

Ah OK, now I understand, I apologize

• ###### Re: Help with Arduino timing problem

/*

*LED Fuel Gauge for Honda SuperHawk 16L Tank

*Code by Will Lyon 9/23/2015. Contact: will.lyon12584@gmail.com

*5V to fuel sensor Grn/Blk

*Fuel sensor Gry/Blk to Analog 0 with 10k resistor to GND

*220 ohm resistor to each led and GND

*/

const int sensorPin = A0;       // the pin that the potentiometer is attached to

const int ledCount = 10;        // the number of LEDs in the bar graph

const int numReadings = 30;     // use this value to determine the size of the readings array

int ledPins[] = {

2, 3, 4, 5, 6, 7,8,9,10,11 }; // an array of pin numbers to which LEDs are attached

int readings[numReadings];      // the readings from the fuel level gauge

int readIndex = 0;              // the index of the current reading

int total = 0;                  // the running total

int average = 0;                // the average

int averagingCount = 0;                     // checks the number of values in the Index

void setup() {

for (int thisLed = 0; thisLed < ledCount; thisLed++) {  // loop over the pin array and set them all to output:

pinMode(ledPins[thisLed], OUTPUT);

}

for (int thisReading = 0; thisReading < numReadings; // initialize all readings to zero

}

}

void loop() {

total = total - readings[readIndex];        // subtract the last reading

readIndex = readIndex + 1;                  // advance to the next position in the array

if (readIndex >= numReadings) {             // if we're at the end of the array

readIndex = 0;                            // wrap around to the beginning

}

averagingCount = averagingCount + 1;                  // increments the count for averaging

if (averagingCount >= numReadings) {             // caps the averaging count to the array

}

average = total / averagingCount;              // calculate the average

delay(1000);                                // delay in between readings for stability

average = map(average, 580, 913, 0, ledCount); // map the result to a range from 0 to the number of LEDs:

// loop over the LED array:

for (int thisLed = 0; thisLed < ledCount; thisLed++) {

// if the array element's index is less than ledLevel,

// turn the pin for this element on:

if (thisLed == average-1) {

digitalWrite(ledPins[thisLed], HIGH);

}

// turn off all pins higher than the ledLevel:

else {

digitalWrite(ledPins[thisLed], LOW);

}

}

delay(100);

}

I hope this will work, I think it should.

• ###### Re: Help with Arduino timing problem

PERFECT! Thank you! Now I just need to design a PCB and 3D print and enclosure then hook it all up so I can fine tune it but that's EXACTLY what I wanted. Thanks again!

• ###### Re: Help with Arduino timing problem

Hey Doug - could you explain what you did for me so I undestand it? Thanks!

• ###### Re: Help with Arduino timing problem

Ok sohere's the final code. I added some more delay to further slow the display and also added a little LED sweep to setup() for effect only.

/*

*LED Fuel Gauge for Honda SuperHawk 16L Tank

*Code by Will Lyon 10/3/2015. Contact: will.lyon12584@gmail.com

*Help from user Doug Jefferies on the Element 14 Forums

*5V to fuel sensor Grn/Blk

*Fuel sensor Gry/Blk to Analog 0 with 10k resistor to GND

*220 ohm resistor to each led and GND

*/

const int sensorPin = A0;       // the pin that the potentiometer is attached to

const int ledCount = 10;        // the number of LEDs in the bar graph

const int numReadings = 35;     // use this value to determine the size of the readings array

int ledPins[] = {

2, 3, 4, 5, 6, 7,8,9,10,11 }; // an array of pin numbers to which LEDs are attached

int readings[numReadings];      // the readings from the fuel level gauge

int readIndex = 0;              // the index of the current reading

int total = 0;                  // the running total

int average = 0;                // the average

int averagingCount = 0;         // checks the number of values in the Index

int timer = 75;                 // timer for inital LED sweep

int pinCount = 10;              // number of LED pins

void setup() {

for (int thisLed = 0; thisLed < ledCount; thisLed++) {      // loop over the pin array and set them all to output

pinMode(ledPins[thisLed], OUTPUT);

}

for (int thisReading = 0; thisReading < numReadings;        // initialize all readings to zero

}

for (int thisPin = 0; thisPin < pinCount; thisPin++) {      // loop from the lowest pin to the highest

digitalWrite(ledPins[thisPin], HIGH);                     // turn the pin on

delay(timer);                                             // delay for time set bove

digitalWrite(ledPins[thisPin], LOW);                      // turn the pin off

}

for (int thisPin = pinCount - 1; thisPin >= 0; thisPin--) { // loop from the highest pin to the lowest

digitalWrite(ledPins[thisPin], HIGH);                     // turn the pin on

delay(timer);                                             // delay for time set above

digitalWrite(ledPins[thisPin], LOW);                      // turn the pin off

}

}

void loop() {

total = total - readings[readIndex];            // subtract the last reading

readIndex = readIndex + 1;                      // advance to the next position in the array

if (readIndex >= numReadings) {                 // if we're at the end of the array

readIndex = 0;                                // wrap around to the beginning

}

averagingCount = averagingCount + 1;            // increments the count for averaging

if (averagingCount >= numReadings) {            // caps the averaging count to the array

}

average = total / averagingCount;               // calculate the average

delay(3000);                                    // delay in between readings so the gauge doesn't fluctuate too fast

average = map(average, 580, 925, 0, ledCount);  // map the result to a range from 0 to the number of LEDs:

for (int thisLed = 0; thisLed < ledCount; thisLed++) {  // loop over the LED array

if (thisLed == average-1) {                   // if the array element's index is less than ledLevel

digitalWrite(ledPins[thisLed], HIGH);       // turn the pin for this element on:

}

else {

digitalWrite(ledPins[thisLed], LOW);        // turn off all pins higher than the ledLevel:

}

}

delay(100);

}

• ###### Re: Help with Arduino timing problem

Sure thing Will, If I understand DAB's post correctly then I added a running filter. If you look at the differences between your original code and my modification you will see:

(Starting at the end)

I changed the variable used to calculate average values to a new variable that I created.

The new variable starts at 0 value and increments by one each time through the loop.

The new variable is capped to the maximum buffer size constant that you defined at the beginning of the program.

This means that as you add values to the buffer they are included into the average.

• ###### Re: Help with Arduino timing problem

Ok so I added functionality that will use the factory low fuel LED as well and light it when either of the last two display bars are lit. I'll use the OEM LED but remove its PCB and wire it right to my PCB.

Here's the final code:

/*

*LED Fuel Gauge for Honda SuperHawk 16L Tank

*Code by Will Lyon 10/3/2015. Contact: will.lyon12584@gmail.com

*Help from user Doug Jefferies on the Element 14 Forums

*5V to fuel sensor Grn/Blk

*Fuel sensor Gry/Blk to Analog 0 with 10k resistor to GND

*220 ohm resistor to each led and GND

*/

const int sensorPin = A0;       // the pin that the potentiometer is attached to

const int ledCount = 10;        // the number of LEDs in the bar graph

const int numReadings = 35;     // use this value to determine the size of the readings array

const int fuelPin = 12;         // pin for factory low fuel LED

int ledPins[] = {

2, 3, 4, 5, 6, 7,8,9,10,11 }; // an array of pin numbers to which LEDs are attached

int readings[numReadings];      // the readings from the fuel level gauge

int readIndex = 0;              // the index of the current reading

int total = 0;                  // the running total

int average = 0;                // the average

int averagingCount = 0;         // checks the number of values in the Index

int timer = 75;                 // timer for inital LED sweep

int pinCount = 10;              // number of LED pins

void setup() {

Serial.begin(9600);

pinMode(fuelPin, OUTPUT);

digitalWrite(fuelPin, LOW);

for (int thisLed = 0; thisLed < ledCount; thisLed++) {      // loop over the pin array and set them all to output

pinMode(ledPins[thisLed], OUTPUT);

}

for (int thisReading = 0; thisReading < numReadings;        // initialize all readings to zero

}

for (int thisPin = 0; thisPin < pinCount; thisPin++) {      // loop from the lowest pin to the highest

digitalWrite(ledPins[thisPin], HIGH);                     // turn the pin on

delay(timer);                                             // delay for time set bove

digitalWrite(ledPins[thisPin], LOW);                      // turn the pin off

}

for (int thisPin = pinCount - 1; thisPin >= 0; thisPin--) { // loop from the highest pin to the lowest

digitalWrite(ledPins[thisPin], HIGH);                     // turn the pin on

delay(timer);                                             // delay for time set above

digitalWrite(ledPins[thisPin], LOW);                      // turn the pin off

}

}

void loop() {

total = total - readings[readIndex];                    // subtract the last reading

readIndex = readIndex + 1;                              // advance to the next position in the array

if (readIndex >= numReadings) {                         // if we're at the end of the array

readIndex = 0;                                        // wrap around to the beginning

}

averagingCount = averagingCount + 1;                    // increments the count for averaging

if (averagingCount >= numReadings) {                    // caps the averaging count to the array

}

average = total / averagingCount;                       // calculate the average

delay(3000);                                            // delay in between readings so the gauge doesn't fluctuate too fast

average = map(average, 585, 940, 0, ledCount);          // map the result to a range from 0 to the number of LEDs:

for (int thisLed = 0; thisLed < ledCount; thisLed++) {  // loop over the LED array

if (thisLed == average-1) {                           // if the array element's index is less than ledLevel

digitalWrite(ledPins[thisLed], HIGH);               // turn the pin for this element on:

}

else {

digitalWrite(ledPins[thisLed], LOW);                // turn off all pins higher than the ledLevel:

}

}

if (average <= 2) {                                     // if either of the last two LED's on the display are lit

digitalWrite(fuelPin, HIGH);                          // turn on the low fuel LED

}

else {

digitalWrite(fuelPin, LOW);                           // turn the low fuel LED off

}

delay(100);

}

And a video:

• ###### Re: Help with Arduino timing problem

Looks good.

For point of reference, averaging four samples reduces your noise level by 75%.

If you go to eight samples, you only pick up another 12%, so using four sample averages is pretty standard for simple filters.

You can go to more exotic filters, but unless you really need some subtle effects, the simple filter will serve you well.

DAB