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

    Help with Arduino timing problem

    SXRguyinMA

      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

      *http://www.superhawkforum.com

      *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

        thisReading++) {

        readings[thisReading] = 0;

        }

      }

       

      void loop() {

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

        readings[readIndex] = analogRead(sensorPin);// read from the sensor

        total = total + readings[readIndex];        // add the reading to the total

        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
          DAB

          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
              SXRguyinMA

              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
                  DAB

                  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
                      SXRguyinMA

                      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
                          bmsdoug

                          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
                            DAB

                            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
                      bmsdoug

                      /*

                      *LED Fuel Gauge for Honda SuperHawk 16L Tank

                      *http://www.superhawkforum.com

                      *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

                        thisReading++) {

                        readings[thisReading] = 0;

                        }

                      }

                       

                      void loop() {

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

                        readings[readIndex] = analogRead(sensorPin);// read from the sensor

                        total = total + readings[readIndex];        // add the reading to the total

                        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

                          averagingCount = numReadings;                           

                        }

                        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
                        SXRguyinMA

                        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

                        *http://www.superhawkforum.com

                        *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

                          thisReading++) {

                          readings[thisReading] = 0;

                          }

                          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

                          readings[readIndex] = analogRead(sensorPin);    // read from the sensor

                          total = total + readings[readIndex];            // add the reading to the total

                          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

                            averagingCount = numReadings;                          

                          }

                          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
                          bmsdoug

                          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
                            SXRguyinMA

                            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

                            *http://www.superhawkforum.com

                            *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

                              thisReading++) {

                              readings[thisReading] = 0;

                              }

                              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

                              readings[readIndex] = analogRead(sensorPin);            // read from the sensor

                              total = total + readings[readIndex];                    // add the reading to the total

                              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

                                averagingCount = numReadings;                          

                              }

                              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

                              }

                              Serial.println(analogRead(sensorPin));

                              delay(100);

                            }

                             

                            And a video:

                              • Re: Help with Arduino timing problem
                                DAB

                                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