NeoPixel Delay Problem

Hi,

It is my first time using AdaFruit NeoPixels. My project aims to read Li-Ion battery voltage and show its charging state as in the laptop's or mobile phone's battery gauges. It will calculate the percentage and show it on the NeoPixel ring with colors and animations.

Let me explain it with an image:

I have divided the circle into 4 areas. These 4 areas represent the percentage of the battery as the first and red colored area shows it's between %0-%25 and the second area which orange colored shows it's between %25-%50 and so on and so forth. While the battery is charging, my setup reads the voltage, decides on which area should be active, and show an animation to indicate charging and state. The animation is just turning on the LEDs sequentially.

The problem is I have decided on the speed of the animation and if I just run the animation code, there is no problem with the animation speed but whenever I run the whole code which I put below, I cannot control the animation speed, it's working so slowly and independent from the delay I put there to control the speed.

Here is my code:

//CHARGE INDICATE 
//Ahmet Faruk Koc 

#include <Adafruit_NeoPixel.h>

unsigned long startTime;
int neoPixelPin = 6;
int numPixels = 24;
int startPixel = 0;
int delayTime = 10; // speed of the animation

int chargeState = 1;

const int sensorPin = A0;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(numPixels, neoPixelPin, NEO_GRB + NEO_KHZ800);

// from voltage divider equation
float voltageFactor = 1;

// set initial data point as 1
long measurementNumber = 1;

void setup() {
  Serial.begin(9600);

  strip.begin();

  strip.clear();
  startTime = millis();
  strip.setBrightness(255); 

}

void loop() {

  int count = 0;
  float periodVoltageSum = 0;

  while (count < 10000) {
    int rawVoltRead = analogRead(sensorPin);
    float voltage = (rawVoltRead / 1024.0) * 5.09 * voltageFactor;
    periodVoltageSum += voltage;
    count++;
  }
  float voltageAve = periodVoltageSum / count;

  // prints the output to Serial in a CSV format
  Serial.print(measurementNumber);
  Serial.print(',');
  Serial.println(voltageAve, 3);
  measurementNumber++;

  if (chargeState == 0) {
    if (voltageAve > 3.9) {
      bolge4();
      strip.clear();
    } else if (voltageAve > 3.4 && voltageAve < 3.9) {
      bolge3();
      strip.clear();
    } else if (voltageAve > 2.9 && voltageAve < 3.4) {
      bolge2();
      strip.clear();
    } else if (voltageAve < 2.9) {
      bolge1();
      strip.clear();
    }
  } else if (chargeState == 1) {

    if (startTime + delayTime < millis()) {

      if (voltageAve < 2.9) cbolge1();

      else if (voltageAve > 2.9 && voltageAve < 3.4) {
        strip.setPixelColor(0, 255, 0, 0);
        strip.setPixelColor(1, 255, 0, 0);
        strip.setPixelColor(2, 255, 0, 0);
        strip.setPixelColor(3, 255, 0, 0);
        strip.setPixelColor(4, 255, 0, 0);
        strip.setPixelColor(5, 255, 0, 0);
        cbolge2();
      } else if (voltageAve > 3.4 && voltageAve < 3.9) {
        uint32_t magenta = strip.ColorHSV(1331, 255, 255);
        strip.setPixelColor(0, 255, 0, 0);
        strip.setPixelColor(1, 255, 0, 0);
        strip.setPixelColor(2, 255, 0, 0);
        strip.setPixelColor(3, 255, 0, 0);
        strip.setPixelColor(4, 255, 0, 0);
        strip.setPixelColor(5, 255, 0, 0);
        strip.setPixelColor(6, magenta);
        strip.setPixelColor(7, magenta);
        strip.setPixelColor(8, magenta);
        strip.setPixelColor(9, magenta);
        strip.setPixelColor(10, magenta);
        strip.setPixelColor(11, magenta);
        cbolge3();
      } else if (voltageAve > 3.9) {
        uint32_t magenta = strip.ColorHSV(1331, 255, 255);
        uint32_t sari = strip.ColorHSV(5000, 255, 255);
        strip.setPixelColor(0, 255, 0, 0);
        strip.setPixelColor(1, 255, 0, 0);
        strip.setPixelColor(2, 255, 0, 0);
        strip.setPixelColor(3, 255, 0, 0);
        strip.setPixelColor(4, 255, 0, 0);
        strip.setPixelColor(5, 255, 0, 0);
        strip.setPixelColor(6, magenta);
        strip.setPixelColor(7, magenta);
        strip.setPixelColor(8, magenta);
        strip.setPixelColor(9, magenta);
        strip.setPixelColor(10, magenta);
        strip.setPixelColor(11, magenta);
        strip.setPixelColor(12, sari);
        strip.setPixelColor(13, sari);
        strip.setPixelColor(14, sari);
        strip.setPixelColor(15, sari);
        strip.setPixelColor(16, sari);
        strip.setPixelColor(17, sari);
        cbolge4();
      }
      startTime = millis();

    }
  }

}
void bolge1() {
  strip.setPixelColor(0, 255, 0, 0);
  strip.setPixelColor(1, 255, 0, 0);
  strip.setPixelColor(2, 255, 0, 0);
  strip.setPixelColor(3, 255, 0, 0);
  strip.setPixelColor(4, 255, 0, 0);
  strip.setPixelColor(5, 255, 0, 0);
  strip.show();
}

void bolge2() {
  uint32_t magenta = strip.ColorHSV(1331, 255, 255);
  strip.setPixelColor(6, magenta);
  strip.setPixelColor(7, magenta);
  strip.setPixelColor(8, magenta);
  strip.setPixelColor(9, magenta);
  strip.setPixelColor(10, magenta);
  strip.setPixelColor(11, magenta);
  strip.show();
}

void bolge3() {
  uint32_t sari = strip.ColorHSV(5000, 255, 255);
  strip.setPixelColor(12, sari);
  strip.setPixelColor(13, sari);
  strip.setPixelColor(14, sari);
  strip.setPixelColor(15, sari);
  strip.setPixelColor(16, sari);
  strip.setPixelColor(17, sari);
  strip.show();
}

void bolge4() {
  strip.setPixelColor(18, 0, 150, 0);
  strip.setPixelColor(19, 0, 150, 0);
  strip.setPixelColor(20, 0, 150, 0);
  strip.setPixelColor(21, 0, 150, 0);
  strip.setPixelColor(22, 0, 150, 0);
  strip.setPixelColor(23, 0, 150, 0);
  strip.show();
}

void cbolge1() {
  for (int i = 0; i < numPixels; i++) {
    if (i == startPixel) {
      for (int i = 0; i < 6; i++) {
        strip.setPixelColor(i, 255, 0, 0);
      }
    } else {
      strip.setPixelColor(i - 1, 0, 0, 0);
    }
  }

  strip.show();
  startPixel++;
  if (startPixel == 24)
    startPixel = 0;
}

void cbolge2() {
  uint32_t magenta = strip.ColorHSV(1331, 255, 255);
  for (int i = 1; i < numPixels; i++) {
    if (i == startPixel) {
      for (int i = 6; i < 12; i++) {
        strip.setPixelColor(i, magenta);
      }
    } else {
      strip.setPixelColor(i + 5, 0, 0, 0);
    }
  }

  strip.show();
  startPixel++;
  if (startPixel == 24)
    startPixel = 0;
}

void cbolge3() {
  uint32_t sari = strip.ColorHSV(5000, 255, 255);
  for (int i = 1; i < numPixels; i++) {
    if (i == startPixel) {
      for (int i = 12; i < 18; i++) {
        strip.setPixelColor(i, sari);
      }
    } else {
      strip.setPixelColor(i + 11, 0, 0, 0);
    }
  }

  strip.show();
  startPixel++;
  if (startPixel == 24)
    startPixel = 0;
}

void cbolge4() {
  for (int i = 1; i < numPixels; i++) {
    if (i == startPixel) {
      for (int i = 18; i < 24; i++) {
        strip.setPixelColor(i, 0, 150, 0);
      }
    } else {
      strip.setPixelColor(i + 17, 0, 0, 0);
    }
  }

  strip.show();
  startPixel++;
  if (startPixel == 24)
    startPixel = 0;

}

Voltage Measurement Part

As I asked further and want to hear your Ideas about precise voltage reading methods, I will share a hand-drawn schematic of my setup here.

Right now for the prototype, I am using Arduino Uno. The nominal voltage of my Li-Ion battery is 3.7 volts and 1500mAh.

Because I need to indicate the charging state with LEDs, I cannot use the standard reference voltage of the Arduino because I want to avoid a change in the reference voltage due to LEDs and the charger. So that's why I have used an external reference voltage circuit here. In the voltage reading part, I have not used a voltage sensor yet because I am still looking for a precise and good one.

  while (count < 10000) {
    int rawVoltRead = analogRead(sensorPin);
    float voltage = (rawVoltRead / 1024.0) * 5.09 * voltageFactor;
    periodVoltageSum += voltage;
    count++;
  }

Each time thru loop() you do this 10,000 times.

Why.

This takes time.

1 Like

Every loop starts with 10,000 readings from one pin. oops. late.

Thank you for your interest. It's because I'm trying to avoid voltage reading fluctuations from the Arduino. It takes an average of 10000 measurements for each data point. Is there any other way to prevent these fluctuations? I suspect I won't be able to solve this problem without changing this voltage reading part.

Why ?

Read the voltage once each time through loop(), average it over a number of readings then act on the average

1 Like

You have divided your LEDs into just 4 parts. If you would have such big fluctuations that the LEDs jump from 25% to 50% or even 75% and then back to 25% there would be something badly wrong with your voltage-measurement.

There is a library RunningMedian.h
which can smooth values.
But I highy doubt that this makes a difference because you divided into
just 4 areas 25%, 50%. 75%, 100%

best regards Stefan

Thank you guys for your interest. Having your ideas is such a pleasure for me. You made clear that I have to play with the voltage measure part. Dividing into 4 parts is something that I have to do. I wanted to read that voltage precisely, I will think about your ideas and fix that issue asap. Again, thanks a lot. If you have any recommendations about precise voltage measuring methods, please let me know.

long unshielded wires catch up more noise than shielded wires or short wires.
But I doubt that measuring a voltage between 3V and 12V will be disturbed by electromagnetic noise.

A general recommendation is to write more than less.

You have already done things right by posting your code as a code-section.
re-edit and add to you initial posting:

  • exact type of microcontroller that you are using
  • nominal voltage of your Li-On-battery
  • a handdrawn schematic. Do you read ? handdrawn! I'm very serious with this detail!
    no frowny frizzing picture!
    hand drawn which show just that details that you are really using leaving away all unnescessary information like not used IO-pins etc.

best regards Stefan

1 Like

I have shared the information that you wanted from me, Mr.Stefan. If there are any unclear points that you wonder about, please inform me. Thank you for your interest.

@sirmarcius, since you seem to only use discrete bands or buckets for the analog voltage you delvelop, you might benefit from using hysteresis.

See @6v6gt's tutorial here:


You might also be able to use "leaky integration". Every loop, read a value and adjust the tracking value:

  int rawVoltRead = analogRead(sensorPin);
  float voltage = (rawVoltRead / 1024.0) * 5.09 * voltageFactor;
  static float voltageAve = 0.9 * voltageAve + 0.1 * voltage

One reading per loop. Every time you measure, keep most of the old average and add a little bit of the new reading to the old average.

HTH

a7

1 Like

Thank a lot for your interest. Your ideas are inspiring to me and seem sensible. I will check and share my feedback.

Very nice schematic.
3.7V points to a Lion-polymer-battery
A single-cell Lion-Poylmer-Battery is charged up to 4.2V
A LiFePo-battery is charged up to 3,6V

So IMHO there is no need for an external reference-voltage. You could use the internal 5V
Again: if your device shall only show 0%, 25%, 50%, 75%, 100%
there is no need for high precision.

Your schematic has a 100 Ohm resistor in parallel to the battery
Is this the load you want to drive?
If not a 100 Ohm-resistor will draw your 1500 mA-battery empty in
3.7V / 100 Ohm = 37 mA
(1500 mAh * 0.8) / 37 mA = 32.4 hours

If the 100 Ohm-resistor is not the load I guess this is not what you want.

With Lion-batteries you don't need a load for measuring the capacity.
The remaining capacity is pretty linear to the voltage.

If you want a real precise measuring of the capacity you would have to use a mA-hours-measuring

The inner resistance of Lion-batteries is very low and as long as you don't draw 20-40A from it like in a electric RC-airplane the voltage is the same with a small current or no current. (the ADC-input will draw some microamperes)

best regards Stefan

1 Like

Thanks for your interest and valuable information. The urge for me to aim for precision comes from that this project has a possibility of application on an electric vehicle. Yes, I think you are right about the no need for high precision in this state but I may be required to provide a precise system in the future. So we can say I want my prototype to be ready for that.

But I will consider your idea and implement it in case of a possible presentation.

Yes, the 100 Ohm-resistor is a load there. The purpose of this Load is to observe different cases. I am running the system every day and observe if there is something unexpected happens. I was thinking of replacing that load with a bigger one because as you stated, it takes a lot of time for the battery to drain.

I will also consider measuring mA-hours. Thanks for your valuable idea.

On a vehicle even if it is only an electric wheelchair the best precision you can get is by measuring mAh or on a vehicle it will rather be Ah.

This is done by measuring a small voltage-drop across a high-precision low-ohm measuring resistor.

If you don't want to build something like that from scratch you could use a photovoltaic-charge/discharge-controller

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.