Batterietester nachbauen

Hallo,

bin ziemlich neu hier im Bereich Arduino und versuche nun folgenden 18650 Kapazität Tester nachzubauen. link.
Nun ist im sketch folgendes float Vcc = 4.410; //Measure it and change . Gibt es hier keine Möglichkeit diese Spannung irgendwie automatisch zu messen bzw. zu bekommen. Möchte nicht jedes mal mit einem Multimeter die Spannung messen und händisch korrigieren, da ja sonst die mAh und die Ampere nicht stimmen.

Hoffe ihr könnt mir helfen.

//edit

Diese Platine habe ich schon fertig gekauft. Dort habe ich jedoch das Problem, wenn die Stromaufnahme mit einem Amperemeter kontrolliert stimmt es mit dem angezeigten nicht überein(laut Arduino 995mAh laut Messgerät 340mAh). Daher würde mich die andere Platine intressieren.

MFG

Aus dem Text:

Now to perform all calculations it is not practically possible so here we will use an Arduino which will measure the current time and the voltage, process the information and in the end give us the capacity.

Für beide Projekte ist die Berechnung anders.

  Bat_Volt = 2* sample1 *Vcc/ 1024.0; 
  BAT_Voltage = sample * Vcc/ 1024.0; 

Da die Aufbauten auch anders sind, brauchst den richtigen Sketch für die richtige Platine.

Bitte beachte was unter

Updated on 01.04.2020

steht

Das 2. projekt habe ich fertig gekauft , den Sketch raufgespielt und läuft nur dort glaube ich, dass die mAh bzw. Die aktuellen Ampere nicht stimmen.

Bat_Volt = 2* sample1 *Vcc/ 1024.0; das ist mir klar nur die Vcc müsste jedoch jedes mal meu gemessen werden bzw. kontrolliert, da bei dem usb nicht immer 5v ankommen sondern zwischen 4,8 und 5,2

Schau mal hier.

Gruß Tommy

Welche Version hast Du nun?
DIY Li-ion Capacity Tester !
oder
DIY Arduino Battery Capacity Tester - V2.0
Wobei es hier auch unterschiedliche Platinen gibt:
Updated on 01.04.2020
Add a precision reference (LM385BLP-1.2) and connected it to A1.

Welchen Sketch verwendest Du?

Dazu ist die Spannungsreferenz ja gedacht, um die Versorgungsspannung automatisch zu messen.

Du kannst Den Strom einfach messen indem Du die Spannung über den Entladewiderstand mißt.
Der Strom ist dann I = U gemessen / 1 Ohm.

Grundsätzliches.
Die Nennkapazität eines Akkus wird durch Entladung/Ladung mit 0,2C gemessen. Die Vorgestellte Schatung entlädt mit 1C. In dieser Messung ist die gemessene Kapazität kleiner wegen Verluste am Innenwiderstand des Akkus.

Grüße Uwe

Das mit der externen Referenz wollte ich auch vorschlagen. Allerdings hatte ich einen TL431 im Sinn...

http://www.netzmafia.de/skripten/hardware/TL431/index.html

Jetzt habe ich die Version 2 mit dem schon inkludierten LM385BLP-1.2 leider vertrau ich den Angaben nicht deswegen möchte ich die einfache Version nachbauen.

Danke das werde ich mir heute Abend genauer anschauen.

Habe gerade gemessen. Laut Messgerät liegen am Widerstand 0,334V an d.h. 0,334mA. Das Display zeigt jedoch 998mA current discharge an.

In der einfachsten Schaltungsvariante hättest Du eine externe Referenzspannung von 2,5V auf die Du die Messungen beziehen könntest. Unabhängig davon ob der Microcontroller nun mit 5,2 oder 4,8 Volt betrieben wird.

Welchen Sketch hast Du raufgeladen?
grüße Uwe

Diesen

sketch

` //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// ARDUINO BATTERY CAPACITY TESTER
//Version-2.1
//by deba168,INDIA // The code is taken from Hesam Moshiri ( Battery capacity measurement using Arduino [Lithium-NiMH-NiCd] - Technology - PCBway )
//Dated : 02/02/2022
//
//Dated : 29/02/2020
//Added code to read a band gap reference connected to analog pin A1,
// thus allowing measurement of Vcc and avoiding the need to measure it using a multimeter
//Replaced 1 ohm 5% resistor with 1 ohm 1% power resistor
//Modified code to compute actual current draw based on desired values. Measurements
// using ammeter show computed values within 2 milliamps of measured
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

#include<JC_Button.h>
#include <Adafruit_SSD1306.h>

#define DEBUG 0 // Binary Treatment 0=Nothing, 1=Early, 2=Running Voltages, 4=Finish
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void timerInterrupt();
void Display_UP_DOWN();//Debugging support
void Print_DEBUG_4();//Debugging support

const float Low_BAT_level = 2.7;// Threshold for stopping test
//Desired Current steps with a 3R load (R7)
int Current [] = {0,50,100,200,300,400,500,600,700,800,900,1000};
int PWM [] = {0, 2, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50};
int Array_Size;// calculated during setup
const byte PWM_Pin = 10;
const byte Buzzer = 9;
const int BAT_Pin = A0;
const int Vref_Pin = A1;//Pin to which Band Gap Reference is attached
int Current_Value = 0; // Selected Current value for test
int PWM_Value = 0;// Value of PWM during test
int PWM_Index = 0;// Index into PWM array during test
unsigned long Capacity;
float Capacity_f;
int ADC_Value = 0;
float Vref_Voltage = 1.215; // LM385BLP-1.2 Band Gap Reference voltage
float Vcc = 5.32; // Voltage of Arduino 5V pin ( Measured during setup using Band Gap Reference )
float BAT_Voltage = 0;
float Resistance = 1.0; // Value of R3 Power Resistor
float sample = 0;
byte Hour = 0, Minute = 0, Second = 0;
bool calc = false, Done = false, Report_Info = true;
Button UP_Button(2, 25, false, true);
Button Down_Button(3, 25, false, true);

// string values for reporting intermediate information
const int VAL_MAX = 10;
char val_0[VAL_MAX]={""};
char val_2[VAL_MAX]={""};

void setup () {//Setup

Serial.begin(38400);
pinMode(PWM_Pin, OUTPUT);
pinMode(Buzzer, OUTPUT);
analogWrite(PWM_Pin, PWM_Value);
UP_Button.begin();
Down_Button.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(12,25);
display.print("Open Green Energy");
display.display();
delay(3000);
display.clearDisplay();
display.setTextSize(2);
display.setCursor(2,15);
display.print("Adj Curr:");
display.setCursor(2,40);
display.print("UP/Down:");
display.print("0");
display.setCursor(2,55);
display.setTextSize(1);

#if (DEBUG == 1 || DEBUG == 5)
Serial.println("\nStart of calculations");
#endif

Array_Size = sizeof(Current)/2;
//Read voltages of Band Gap Reference and use it to calculate actual Vcc
float fTemp=0.0;
for(int i=0;i< 100;i++)
{
fTemp=fTemp+analogRead(Vref_Pin); //read the Band Gap Reference voltage
delay (2);
}
fTemp=fTemp/100.0;
Vcc = Vref_Voltage * 1024.0 / fTemp;

// Convert desired current levels to PWM using actual Vcc
// Convert PWM values back to actual current levels
// While measuring actual current I discovered that the actual draw is
// (PWM + 1)/256 rather than PWM/255 as indicated in Arduino documentation
int iTemp;
for (int i=0;i<Array_Size;i++)
{
iTemp = int( Resistance * Current[i] * 256.0 / Vcc / 1000.0 - 1.0 + 0.5 ); // desired current to nearest PWM
iTemp = min(iTemp,255);
iTemp = max(iTemp,0);
Current[i] = int( (iTemp+1) * Vcc / 256.0 / Resistance * 1000.0); // actual current for PWM
PWM[i] = iTemp;//Save PWM in array
}

//Include Threshold and Vcc in startup display
dtostrf(Low_BAT_level, 5, 3, val_0);
dtostrf(Vcc, 5, 3, val_2);

display.print("Thr=");
display.print(val_0);
display.print("v, Vcc=");
display.print(val_2);

display.display();
display.setTextSize(2);

}//Setup

//************************* End of Setup function *******************************

void loop() {

if (Report_Info)
{//Report_Info
Serial.flush();
#if (DEBUG == 1 || DEBUG == 5)
// Serial.println("Measured Vcc Voltage: " + String(Vcc) + " volts");
Serial.print("Threshold: ");
Serial.print(Low_BAT_level,3);
Serial.println(" volts");
Serial.print("R3 Resistance: ");
Serial.print(Resistance,3);
Serial.println(" ohms");
Serial.print("Measured Vcc Voltage: ");
Serial.print(Vcc,4);
Serial.println(" volts");
sample = 0.0;

Serial.println("Index Actual(mA) PWM");
for (int i=0;i<Array_Size;i++)
{
// Serial.println( "["+String(i)+"]="+String(Current[i])+" mA PWM["+String(i)+"]="+String(PWM[i]) );
Serial.print("[");
Serial.print(i);
Serial.print("]");
Serial.print(" ");
Serial.print(Current[i],DEC);
Serial.print(" ");
Serial.print(PWM[i],DEC);
Serial.println(" ");
}
#endif
Report_Info = false;
}//Report_Info

UP_Button.read();
Down_Button.read();
if (UP_Button.wasReleased() && PWM_Index < (Array_Size-1) && calc == false)
{
PWM_Value = PWM[++PWM_Index];
analogWrite(PWM_Pin,PWM_Value);

Display_UP_DOWN();
}

if (Down_Button.wasReleased() && PWM_Index > 0 && calc == false)
{
PWM_Value = PWM[--PWM_Index];
analogWrite(PWM_Pin,PWM_Value);

Display_UP_DOWN();
}

if (UP_Button.pressedFor (1000) && calc == false)
{
digitalWrite(Buzzer, HIGH);
delay(100);
digitalWrite(Buzzer, LOW);
display.clearDisplay();
timerInterrupt();
}
}

//************************* End of Loop function *******************************

void timerInterrupt(){
calc = true;
while (Done == false) {//(Done == false)
Second ++;
if (Second == 60) {
Second = 0;
Minute ++;
}
if (Minute == 60) {
Minute = 0;
Hour ++;
}

//************ Measuring Battery Voltage ***********

for(int i=0;i< 100;i++)
{
sample=sample+analogRead(BAT_Pin); //read the Battery voltage
delay (2);
}
sample=sample/100;
BAT_Voltage = sample * Vcc / 1024.0;
//*********************************************

display.clearDisplay();
display.setTextSize(2);
display.setCursor(20,5);
// display.print(String(Hour) + ":" + String(Minute) + ":" + String(Second));
display.print(Hour);
display.print(":");
display.print(Minute);
display.print(":");
display.print(Second);

display.setTextSize(1);
display.setCursor(0,25);
display.print("Disch Curr: ");
// display.print(String(Current[PWM_Index])+"mA");
display.print(Current_Value);
display.print("mA");

display.setCursor(2,40);
// display.print("Bat Volt:" + String(BAT_Voltage)+"V" );
display.print("Bat Volt:");
display.print(BAT_Voltage,3);
display.print("V");

// When total seconds is greater than 32767 the statement below did not work until the byte values
// Hour, Minute and Second were cast to an unsigned long. Apparently the compiler cast the byte
// values to an "int" first which cannot represent 32768 correctly
Capacity = ((unsigned long)Hour * 3600) + ((unsigned long)Minute * 60) + (unsigned long)Second;
Capacity_f = ((float)Capacity * Current_Value) / 3600.0;

display.setCursor(2, 55);
// display.print("Capacity:" + String(Capacity) + "mAh");
display.print("Capacity:");
display.print(Capacity_f,1);
display.print("mAh");
display.display();

#if (DEBUG == 4 || DEBUG == 2)
Print_DEBUG_4();
#endif

if (BAT_Voltage < Low_BAT_level)
{//BAT_Voltage < Low_BAT_level

#if (DEBUG == 4 || DEBUG == 5)
Serial.println("\nCurrent_Value PWM_Value");
Serial.print(Current_Value);
Serial.print(" ");
Serial.println(PWM_Value);
Serial.println("\nHour Minute Second PWM_Index");
Serial.print(Hour);
Serial.print(" ");
Serial.print(Minute);
Serial.print(" ");
Serial.print(Second);
Serial.print(" ");
Serial.println(PWM_Index);
#endif

// When total seconds is greater than 32767 the statement below did not work until the byte values
// Hour, Minute and Second were cast to an unsigned long. Apparently the compiler cast the byte
// values to an "int" first which cannot represent 32768 correctly
Capacity = ((unsigned long)Hour * 3600) + ((unsigned long)Minute * 60) + (unsigned long)Second;

#if (DEBUG == 4 || DEBUG == 5)
Serial.println("Capacity HMS");
Serial.println(Capacity);
#endif

Capacity_f = ((float)Capacity * Current_Value) / 3600.0;

#if (DEBUG == 4 || DEBUG == 5)
Serial.println("Capacity HMS*PWM");
Serial.println(Capacity_f,1);
#endif

display.clearDisplay();
display.setTextSize(2);
display.setCursor(2,15);
display.print("Capacity:");
display.setCursor(2,40);
// display.print(String(Capacity) + "mAh");
display.print(Capacity_f,1);
display.print("mAh");
display.display();
Done = true;
PWM_Value = 0;
analogWrite(PWM_Pin, PWM_Value);
digitalWrite(Buzzer, HIGH);
delay(100);
digitalWrite(Buzzer, LOW);
delay(100);
digitalWrite(Buzzer, HIGH);
delay(100);
digitalWrite(Buzzer, LOW);
delay(100);
}//BAT_Voltage < Low_BAT_level
delay(1000);
}//(Done == false)

}// timerInterrupt

void Display_UP_DOWN()
{//Display_UP_DOWN()
Current_Value = Current[PWM_Index];

display.clearDisplay();
display.setCursor(2,25);
display.print("Curr:");
display.print(Current_Value);
display.print("mA ");
display.setCursor(2,40);
display.print("PWM=");
display.print(PWM_Value);
display.display();
}//Display_UP_DOWN()

void Print_DEBUG_4()
{//Print_DEBUG_4()
Serial.print(Hour);
Serial.print(":");
Serial.print(Minute);
Serial.print(":");
Serial.print(Second);
Serial.print(" ");
Serial.print(BAT_Voltage,3);
Serial.print("v ");
Serial.print(Capacity_f,1);
Serial.println("mAh");
}//Print_DEBUG_4()`

@daschmidt Was wird Nach dem Einschalten auf dem Display denn als Vcc ausgegeben?

dtostrf(Vcc, 5, 3, val_2);
...
display.print("v, Vcc=");
display.print(val_2);

@ alle
Kann mir mal jemand die Wertetabelle oder besser den Graf für die Funktion

for (int i=0;i<Array_Size;i++)
{
iTemp = int( Resistance * Current[i] * 256.0 / Vcc / 1000.0 - 1.0 + 0.5 ); // desired current to nearest PWM
iTemp = min(iTemp,255);
iTemp = max(iTemp,0);
Current[i] = int( (iTemp+1) * Vcc / 256.0 / Resistance * 1000.0); // actual current for PWM
PWM[i] = iTemp;//Save PWM in array
}

für die Werte
int Current [] = {0,50,100,200,300,400,500,600,700,800,900,1000};
und die Spannung VCC von 5,5V bis 2V in 1/10 Schritten ausrechnen lassen?
Wobei Resistance =1 ist

Danke Grüße Uwe

Aehm...
Kannst Du mir den Code erklären?
Das ist eine Vorgabe:
int Current [] = {0,50,100,200,300,400,500,600,700,800,900,1000};

Hier wird damit gerechnet:
iTemp = int( Resistance * Current[i] * 256.0 / Vcc / 1000.0 - 1.0 + 0.5 ); // desired current to nearest PWM

Und hier wird die Vorgabe Current[i] überschrieben
Current[i] = int( (iTemp+1) * Vcc / 256.0 / Resistance * 1000.0); // actual current for PWM

Warum macht man sowas?

Ich hab mal Deine Vorgabe gebaut:

const float Resistance = 1.0;
const byte currentWerte = 12;
unsigned int Current [currentWerte] = {0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
const float maxVCC = 5.5;
const float minVCC = 2.0;
const float tikVCC = 0.1;

int iTemp;
uint8_t PWM[currentWerte] = {0};

void setup()
{
  Serial.begin(115200);
  delay(500);
  Serial.println(F("Start..."));
  for (float Vcc = minVCC; Vcc < maxVCC; Vcc += tikVCC)
  {
    Serial.println();
    for (int i = 0; i < currentWerte; i++)
    {
      iTemp = int( Resistance * Current[i] * 256.0 / Vcc / 1000.0 - 1.0 + 0.5 ); // desired current to nearest PWM
      iTemp = min(iTemp, 255);
      iTemp = max(iTemp, 0);
      Current[i] = int( (iTemp + 1) * Vcc / 256.0 / Resistance * 1000.0); // actual current for PWM
      PWM[i] = iTemp;//Save PWM in array
      Serial.print(Vcc);Serial.print('\t'); 
      Serial.print(Current[i]); Serial.print('\t');
      Serial.println(PWM[i]);
    }
  }
}


void loop()
{
}

War es das, was Du wolltest?

Ich versuch's

int Current [] = {0,50,100,200,300,400,500,600,700,800,900,1000};

Sind die verschiedenen wählbaren Entladestromstärken. Auswählbar durch 2 Taster Up und DOWN.

Aus der Stromvorgabe wird in Funktion der Referenzspannung (Versorgungsspannung) die neue Stromvorgabe berechnet. Diese erfolgt durch Runden (+0,5) und in INT umwandeln (Kommastellen löschen) und zurückrechnen. (Das ist was ich nicht genau verstehe, die Rechenweise).


Der Operationsverstärker regelt das Durchsteuern des MOSFET in Funktion des Spannungsabfalls über den Strommeßwiderstand (Istwert des Stroms) (1 Ohm Widerstand in der Funktion die Variable Resistance) und der geglätteten PWM Spannung (Sollwert des Stroms). Diese Regelung ist nicht unabhängig von der Versorgungsspannung da die größe der Versorgungsspannung in die Sollgröße eingeht. Wenn überspitzt die Versorgungsspannung statt 5V nur 2,5V ist dann ist die Sollspannung bei 10% PWM nicht 0,5V sonden 0,25V. Also braucht es für eine kleienre Versorgungsspannung einen höheren PWM Wert (Sollwert). Die wird glaube ich in dieser Funktion nicht berücksichtigt, da kleinere Spannungen kleinere Entladestromstärken ergeben. Das ist ein Fehler.
Der effektiven Entladestrom wird nicht gemessen sondern aus der Rechnung angenommen. Da man einen Strommeßwiderstand hat, dereinpolig mit Masse verbunden ist könnte man an diesen leicht den Spannungsabfall messen. Das kann direkt erfolgen (einfach an en freies, analoges Pin anschließen) Ich würde aber mittels eines weiteren OPAMP eine Spannungsfolge bauen und so den Spannungsabfall abzukoppeln. Man könnte die Spannung auch um 5x Verstärken und man hätte bei 1000mA Entladestrom eine Meßspannung von 5V und somit den gesamten Meßbereich des A/D Wandlers ausgenutzt.

Andererseits wenn die Versorgungsspannung als Referenzspannung für die A/D Wandlung verwendet wird wird bei einer geringeren Referenzspannung ein höherer ADC Wert gelesen.

Dies wird in Zeile 203 gemacht

BAT_Voltage = sample * Vcc / 1024.0;

Die Versorgungsspannung wird in Zeile 85 korrigiert:

Vcc = Vref_Voltage * 1024.0 / fTemp;

Wobei Vref_Voltage die Spannung der Referenzspannungsquelle genommen wird.

Ich verstehe das Herumgerechne für den neuen Stromsollwert Current[i] nicht (die Formel, ob das Korrekt ist)
Ich bekomme für die Spannungen 5V; 4,5V; 4V; 3V; 2V die korrigierten Werte des 50mA Sollstromes :
58; 52; 46;46;46
Da ich keine Lust habe 50 Werte zu berechnen und ich irgendwie eine Welle vermute wollte ich jemanden bitten das mit Hilfe eines Programms automatisch auszurechnen.

Jein.
Ich dachte da eher an Matlab und grafische Darstellung.
Trotzdem Danke. Werde es morgen ausprobieren von einem Arduino berechnen zu lassen.
Grüße Uwe

Danke für eure bemühungen.
Da ich eben noch neu in dieser Materie bin(bin zwar Elektriker) versteh das aber eben mit der aktuellen Stromstärke in diesem Sketch nicht. Meine Vermutung war, dass das Messgerät mit der PWM Probleme hat. Deswegen wollte ich den einfachen Kapazitätmesser bauen, dann ist es auch leichter mit einem Messgerät zu kontrollieren.

Sollte es nicht, da die Batterie nicht getaktet entladen wird, sonder mit einem (Mehr oder weniger) konstanten Strom. Das kannst Du kontrollieren indem Du die Spannung über den Entladewiderstand mit dem Wechselstrombereich des Multimeters misst. So mißt Du nur den Wechselstromanteil (Rippel) des Stroms.
Das PWM Signal wird geglättet und als (ich weiß nicht wieviel) rippelfreie Gleichspannung als Sollwert dem Operationsverstärker gegeben. Diese regelt den MOSFETso daß der Strom in proportional dieser Spannung ist.

Warte ein bißchen bis wir hinter die Logik bzw Logikfehler des Sketches gekommen sind.

Grüße Uwe

1 Like

Hab mal excel bemüht und die Rechnung machen lassen. In Anhang das PDF davon.
Corr Strom.pdf (31.3 KB)
Auf Anfrage gebe ich gerne das Excel File.
Der Verlauf ist ein Sägezahn, der mit der Spannungsverlusten Korrekturen nichts zu tun hat. Da hat der Autor nur einen Bock geschossen.
Eine Differenz von 900 zu 300mA (Angezeigt zu gemessen) ist meiner Ansich für diesen Fehler zu groß, aber wer einen Fehler macht der macht auch noch mehr.

Zum TO.
Das ist nichts besonderes. Oft pubblizieren Menschen ihre Basteleien und die stimmen vorne und hinten nicht.

@To
Ich wiederhole meine Frage:

Bitte miß das mit einem Multimeter nach ob der Wert stimmt.

Grüße Uwe

1 Like

Aehm ja, das sagte der Arduino-Plotter gestern abend auch.

Dann passt zumindest mein Code - ich war nicht ganz so davon überzeugt, weil die Erwartungshaltung eigentlich anders war...

Danke für die Feststellung.

Weiter Nachgerechnet:

      iTemp = int( Resistance * Current[i] * 256.0 / Vcc / 1000.0 - 1.0 + 0.5 );      
      PWM[i] = iTemp;//Save PWM in array

Ergibt eine Spannung von 0 bis 1V das dem Strom entspricht (Meßwiderstand 1 Ohm) und eliminiert den Spannungsfehler durch von 5V differenzierende Versorgungsspannung.
Bei kleinen Strömen (50mA) ist der Fehler groß (bis zu 60% zu klein) bei großen Strömen ist der Fehler nur ca 3% zuwenig. Da könnte man durch eine bessere Ausnutzung des gesamten PWM Bereichs mit Hilfe eines 1:5 Spannungsteiler Genauigkeit gewinnen.
Corr Strom2.pdf (34.5 KB)

@TO
Abschließend muß ich sagen, daß ich keine Erklärung habe wieso Du 334mA gemessen hast aber 998 angezeigt bekommen hast.
Ist der Meßwiderstand richtig ( 1Ohm) ?
Mach ein paar scharfe Fotos vom Aufbau.

Grüße Uwe

Du weißt schon, daß nicht Du gemeint warst?
Grüße Uwe