Einfacher Batteriekapazitätstester auf Arduino

Vor kurzem bemerkte ich, dass sich mein Smartphone schneller entlud. Die Suche nach der Software „Verschlinger“ brachte keine Früchteergie, und ich begann mich zu fragen, ob es Zeit war, die Batterie auszutauschen. Es gab jedoch keine absolute Gewissheit, dass es keinen Grund für die Batterie gab. Bevor ich eine neue Batterie bestellte, beschloss ich daher, die tatsächliche Kapazität der alten Batterie zu messen. Zu diesem Zweck wurde beschlossen, ein einfaches Batteriekapazitätsmessgerät zusammenzubauen, zumal diese Idee schon lange entwickelt wurde - es gibt so viele Batterien und Akkus, die uns im Alltag umgeben, und es wäre schön, sie von Zeit zu Zeit testen zu können.



Die Idee, die dem Betrieb des Geräts zugrunde liegt, ist äußerst einfach: Es gibt eine geladene Batterie und eine Last in Form eines Widerstands. Sie müssen nur den Strom, die Spannung und die Zeit während der Entladung der Batterie messen und anhand der erhaltenen Daten deren Kapazität berechnen. Im Prinzip können Sie mit einem Voltmeter und einem Amperemeter arbeiten, aber mehrere Stunden an den Geräten zu sitzen ist ein zweifelhaftes Vergnügen, daher ist es viel einfacher und genauer, dies mit dem Datenlogger zu tun. Ich habe die Arduino Uno-Plattform als solchen Registrar verwendet.

1. Schema

Es gibt keine Probleme beim Messen von Spannung und Zeit in Arduino - es gibt einen ADC, aber Sie benötigen einen Shunt, um den Strom zu messen. Ich hatte die Idee, den Lastwiderstand selbst als Shunt zu verwenden. Das heißt, wenn wir die Spannung kennen und zuvor den Widerstand gemessen haben, können wir immer den Strom berechnen. Daher besteht die einfachste Version der Schaltung nur aus einer Last und einer Batterie mit einer Verbindung zum Analogeingang von Arduino. Es wäre jedoch schön, einen Lastabwurf bereitzustellen, wenn die Schwellenspannung an der Batterie erreicht ist (für Li-Ion sind dies normalerweise 2,5 bis 3 V). Daher habe ich in der Schaltung ein Relais bereitgestellt, das vom digitalen Pin 7 über einen Transistor gesteuert wird. Die endgültige Version der Schaltung in der folgenden Abbildung.



Ich habe alle Elemente der Schaltung auf ein Stück des Steckbretts gelegt, das direkt auf Uno installiert ist. Als Last habe ich eine 0,5 mm dicke Nichromdrahtspirale mit einem Widerstand von ca. 3 Ohm verwendet. Dies ergibt einen berechneten Wert des Entladestroms von 0,9-1,2A.



2. Strommessung

Wie oben erwähnt, wird der Strom basierend auf der Spannung an der Spule und ihrem Widerstand berechnet. Es ist jedoch zu bedenken, dass sich die Spirale erwärmt und der Widerstand von Nichrom stark von der Temperatur abhängt. Um den Fehler zu kompensieren, habe ich einfach die Volt-Ampere-Charakteristik der Spirale mit einem Labornetzteil gemessen und vor jeder Messung aufwärmen lassen. Dann leitete er die Gleichung der Trendlinie in Excel ab (Grafik unten), die eine ziemlich genaue Abhängigkeit von i (u) unter Berücksichtigung der Erwärmung ergibt. Es ist zu sehen, dass die Linie nicht gerade ist.



3. Spannungsmessung

Da die Genauigkeit dieses Testers direkt von der Genauigkeit der Spannungsmessung abhängt, habe ich beschlossen, dies besonders zu beachten. In anderen Artikeln wurde wiederholt die Methode erwähnt, mit der Sie die Spannung mit Atmega-Controllern am genauesten messen können. Ich werde nur kurz wiederholen - das Wesentliche ist, die interne Referenzspannung mittels der Steuerung selbst zu bestimmen. Ich habe die Materialien in diesem Artikel verwendet.

4. Das

Code- Programm ist nicht kompliziert:

Programmtext
#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;
}




Alle 5 Sekunden werden Zeitdaten, Batteriespannung, Entladestrom, Stromkapazität in mAh und Wh sowie die Versorgungsspannung an die serielle Schnittstelle übertragen. Der Strom wird durch die in Absatz 2 erhaltene Funktion berechnet. Wenn die Schwellenspannung Voff erreicht ist, wird der Test beendet.
Der einzige meiner Meinung nach interessante Punkt in dem Code, den ich herausgreifen würde, ist die Verwendung eines digitalen Filters. Tatsache ist, dass beim Ablesen der Spannung die Werte unweigerlich auf und ab „tanzen“. Zuerst habe ich versucht, diesen Effekt zu reduzieren, indem ich einfach 100 Messungen in 5 Sekunden durchgeführt und den Durchschnitt ermittelt habe. Aber das Ergebnis hat mich immer noch nicht befriedigt. Während der Suche bin ich auf solche gestoßenSoftwarefilter. Es funktioniert auf ähnliche Weise, aber anstatt zu mitteln, sortiert es alle 100 Messwerte in aufsteigender Reihenfolge, wählt die zentrale 10 aus und berechnet den Durchschnitt davon. Das Ergebnis hat mich beeindruckt - die Messschwankungen haben komplett aufgehört. Ich habe mich entschieden, damit die interne Referenzspannung zu messen (readVcc-Funktion im Code).

5. Ergebnisse Die

Daten vom Monitor der seriellen Schnittstelle werden mit wenigen Klicks in Excel importiert und sehen folgendermaßen aus:



Als Nächstes können Sie ganz einfach ein Diagramm zur Batterieentladung erstellen:



Bei meinem Nexus 5 beträgt die beanspruchte BL-T9-Akkukapazität 2300 mAh. Gemessen von mir - 2040 mAh mit einer Entladung von bis zu 2,5 V. In der Realität lässt der Controller die Batterie kaum auf eine so niedrige Spannung, höchstwahrscheinlich einen Schwellenwert von 3 V, einwirken. Die Kapazität beträgt in diesem Fall 1960 mAh. Eineinhalb Jahre Telefondienst führten zu einem Kapazitätsabbau von ca. 15%. Mit dem Kauf einer neuen Batterie wurde beschlossen, diese zu verschieben.
Mit Hilfe dieses Testers wurden bereits mehrere andere Li-Ion-Akkus entladen. Die Ergebnisse sehen sehr realistisch aus. Die gemessene Kapazität neuer Batterien stimmt mit der angegebenen mit einer Abweichung von weniger als 2% überein.
Dieser Tester ist auch für Metallhydrid-Fingerbatterien geeignet. Der Entladestrom beträgt in diesem Fall ca. 400 mA.

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


All Articles