Probador de capacidad de batería simple en Arduino

Recientemente, comencé a notar que mi teléfono inteligente comenzó a descargarse más rápido. La búsqueda del software "devorador" no trajo energía de la fruta, así que comencé a preguntarme si era hora de reemplazar la batería. Pero no había certeza absoluta de que no había razón para la batería. Por lo tanto, antes de pedir una batería nueva, decidí intentar medir la capacidad real de la antigua. Para hacer esto, se decidió ensamblar un medidor de capacidad de batería simple, especialmente porque esta idea se ha desarrollado durante mucho tiempo: hay tantas baterías y acumuladores que nos rodean en la vida cotidiana, y sería bueno poder probarlos de vez en cuando.



La idea misma que subyace al funcionamiento del dispositivo es extremadamente simple: hay una batería cargada y una carga en forma de resistencia, solo necesita medir la corriente, el voltaje y el tiempo durante la descarga de la batería, y de acuerdo con los datos obtenidos, calcular su capacidad. En principio, puede hacerlo con un voltímetro y un amperímetro, pero sentarse en los dispositivos durante varias horas es un placer dudoso, por lo que es mucho más fácil y preciso hacerlo utilizando el registrador de datos. Usé la plataforma Arduino Uno como tal registrador.

1. Esquema

No hay problemas para medir el voltaje y el tiempo en Arduino: hay un ADC, pero necesita una derivación para medir la corriente. Se me ocurrió la idea de usar la resistencia de carga como una derivación. Es decir, conociendo el voltaje en él y habiendo medido previamente la resistencia, siempre podemos calcular la corriente. Por lo tanto, la versión más simple del circuito consistirá solo en una carga y una batería, con una conexión a la entrada analógica de Arduino. Pero sería bueno proporcionar un desprendimiento de carga cuando se alcanza el umbral de voltaje en la batería (para Li-Ion, generalmente es de 2.5-3V). Por lo tanto, proporcioné en el circuito un relé controlado por el pin digital 7 a través de un transistor. La versión final del circuito en la figura a continuación.



Puse todos los elementos del circuito en una pieza de la placa de pruebas, que está instalada directamente en Uno. Como carga, utilicé una espiral de alambre de nicromo de 0,5 mm de espesor con una resistencia de aproximadamente 3 ohmios. Esto proporciona un valor calculado de la corriente de descarga de 0.9-1.2A.



2. Medición de corriente

Como se mencionó anteriormente, la corriente se calcula en función del voltaje en la bobina y su resistencia. Pero vale la pena considerar que la espiral se calienta y que la resistencia del nicromo depende en gran medida de la temperatura. Para compensar el error, simplemente tomé la característica de voltios-amperios de la espiral usando una fuente de alimentación de laboratorio y dejándola calentar antes de cada medición. Luego derivó la ecuación de la línea de tendencia en Excel (el gráfico a continuación), que proporciona una dependencia bastante precisa de i (u) teniendo en cuenta el calentamiento. Se puede ver que la línea no es recta.



3. Medición de voltaje

Dado que la precisión de este probador depende directamente de la precisión de la medición de voltaje, decidí prestar especial atención a esto. Otros artículos han mencionado repetidamente el método que le permite medir el voltaje con mayor precisión con los controladores Atmega. Repetiré solo brevemente: la esencia es determinar el voltaje de referencia interno por medio del controlador mismo. Usé los materiales en este artículo.

4. El programa

Código no es nada complicado:

Texto del programa
#define A_PIN 1
#define NUM_READS 100
#define pinRelay 7

const float typVbg = 1.095; // 1.0 -- 1.2
float Voff = 2.5; //  
float I;
float cap = 0;
float V;
float Vcc;
float Wh = 0;
unsigned long prevMillis;
unsigned long testStart;

void setup() {
  Serial.begin(9600);
  pinMode(pinRelay, OUTPUT);
  Serial.println("Press any key to start the test...");
  while (Serial.available() == 0) {
  }
  Serial.println("Test is launched...");
  Serial.print("s");
  Serial.print(" ");
  Serial.print("V");
  Serial.print(" ");
  Serial.print("mA");
  Serial.print(" ");
  Serial.print("mAh");
  Serial.print(" ");
  Serial.print("Wh");
  Serial.print(" ");
  Serial.println("Vcc");
  digitalWrite(pinRelay, HIGH);
  testStart = millis();
  prevMillis = millis();
}

void loop() {
  Vcc = readVcc(); //  
  V = (readAnalog(A_PIN) * Vcc) / 1023.000; //  
  if (V > 0.01) I = -13.1 * V * V + 344.3 * V + 23.2; //    
  else I=0;
  cap += (I * (millis() - prevMillis) / 3600000); //    
  Wh += I * V * (millis() - prevMillis) / 3600000000; //    
  prevMillis = millis();
  sendData(); //     
  if (V < Voff) { //     
    digitalWrite(pinRelay, LOW);
    Serial.println("Test is done");
    while (2 > 1) {
    }
  }
}

void sendData() {
  Serial.print((millis() - testStart) / 1000);
  Serial.print(" ");
  Serial.print(V, 3);
  Serial.print(" ");
  Serial.print(I, 1);
  Serial.print(" ");
  Serial.print(cap, 0);
  Serial.print(" ");
  Serial.print(Wh, 2);
  Serial.print(" ");
  Serial.println(Vcc, 3);
}


float readAnalog(int pin) {
  // read multiple values and sort them to take the mode
  int sortedValues[NUM_READS];
  for (int i = 0; i < NUM_READS; i++) {
    delay(25);
    int value = analogRead(pin);
    int j;
    if (value < sortedValues[0] || i == 0) {
      j = 0; //insert at first position
    }
    else {
      for (j = 1; j < i; j++) {
        if (sortedValues[j - 1] <= value && sortedValues[j] >= value) {
          // j is insert position
          break;
        }
      }
    }
    for (int k = i; k > j; k--) {
      // move all values higher than current reading up one position
      sortedValues[k] = sortedValues[k - 1];
    }
    sortedValues[j] = value; //insert current reading
  }
  //return scaled mode of 10 values
  float returnval = 0;
  for (int i = NUM_READS / 2 - 5; i < (NUM_READS / 2 + 5); i++) {
    returnval += sortedValues[i];
  }
  return returnval / 10;
}


float readVcc() {
  // read multiple values and sort them to take the mode
  float sortedValues[NUM_READS];
  for (int i = 0; i < NUM_READS; i++) {
    float tmp = 0.0;
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
    ADCSRA |= _BV(ADSC); // Start conversion
    delay(25);
    while (bit_is_set(ADCSRA, ADSC)); // measuring
    uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
    uint8_t high = ADCH; // unlocks both
    tmp = (high << 8) | low;
    float value = (typVbg * 1023.0) / tmp;
    int j;
    if (value < sortedValues[0] || i == 0) {
      j = 0; //insert at first position
    }
    else {
      for (j = 1; j < i; j++) {
        if (sortedValues[j - 1] <= value && sortedValues[j] >= value) {
          // j is insert position
          break;
        }
      }
    }
    for (int k = i; k > j; k--) {
      // move all values higher than current reading up one position
      sortedValues[k] = sortedValues[k - 1];
    }
    sortedValues[j] = value; //insert current reading
  }
  //return scaled mode of 10 values
  float returnval = 0;
  for (int i = NUM_READS / 2 - 5; i < (NUM_READS / 2 + 5); i++) {
    returnval += sortedValues[i];
  }
  return returnval / 10;
}




Cada 5 segundos, los datos sobre el tiempo, el voltaje de la batería, la corriente de descarga, la capacidad de corriente en mAh y Wh, así como el voltaje de alimentación se transmiten al puerto serie. La corriente se calcula mediante la función obtenida en el párrafo 2. Cuando se alcanza el voltaje umbral Voff, la prueba finaliza.
En mi opinión, el único punto interesante en el código que destacaría es el uso de un filtro digital. El hecho es que al leer el voltaje, los valores inevitablemente "bailan" hacia arriba y hacia abajo. Al principio intenté reducir este efecto simplemente tomando 100 mediciones en 5 segundos y tomando el promedio. Pero el resultado aún no me satisfizo. Durante la búsqueda, me encontré con talfiltro de software Funciona de manera similar, pero en lugar de promediar, ordena los 100 valores de medición en orden ascendente, selecciona el 10 central y calcula el promedio de ellos. El resultado me impresionó: las fluctuaciones de medición se detuvieron por completo. Decidí usarlo para medir el voltaje de referencia interno (función readVcc en el código).

5. Resultados Los

datos del monitor del puerto serie se importan a Excel en unos pocos clics y se ven de la siguiente manera:



A continuación, es fácil construir un gráfico de descarga de batería:



En el caso de mi Nexus 5, la capacidad de batería BL-T9 reclamada es de 2300 mAh. Medido por mí: 2040 mAh con una descarga de hasta 2.5 V. En realidad, el controlador apenas permite que la batería tenga una tensión tan baja, probablemente un valor umbral de 3V. La capacidad en este caso es 1960 mAh. Un año y medio de servicio telefónico condujo a una reducción de aproximadamente el 15% de la capacidad. Con la compra de una batería nueva, se decidió posponer.
Con la ayuda de este probador, varias otras baterías de iones de litio ya se han descargado. Los resultados parecen muy realistas. La capacidad medida de las baterías nuevas coincide con la declarada con una desviación de menos del 2%.
Este probador también es adecuado para baterías de dedo de hidruro metálico. La corriente de descarga en este caso será de aproximadamente 400 mA.

Source: https://habr.com/ru/post/es389105/


All Articles