Überarbeitung Auslesung Akkuspannung

Ich hatte mich bisher immer drauf verlassen, dass die Spannung gegen die Referenzspannung von 3,3 V gemacht wird. Bei meinen LFP-Develboards war mir daher (zugegeben) selbst nicht klar, wie das Ganze im reinen Akkubetrieb funktionieren kann, wenn gar keine 3,3 V vorhanden sind. Ein bisschen Recherche brachte dann hervor, dass als Referenz die interne Spannung von 1,1 V verwendet wird.
Bisher hatte ich den Spannungsteiler bei den Develboards mit 300k / 300k dimensioniert und Wemos mit seinem Lolin D32 pro mit 100k / 100k. Bedeutet also, dass bei meinen LFP-Develboards bis zu 1,8 V gemessen werden und bei den Wemos-Boards bis zu 2,1 V (jeweils die Hälfte der max. Ladespannung).

Insgesamt hat das recht ok funktioniert, jedoch musste das Offset teilweise ziemlich nachjustiert werden. Wie ich gelesen habe ist es am besten, wenn man mit der Spannung bei etwa 1,1 V rauskommt, da dort auch die Referenzspannung liegt. Ich habe daher bei der neuen Complete den Spannungsteiler auf 300k / 100k gesetzt, was zu einer max. Spannung von 0,9 V führt.

Benutzt man analogRead() ohne weitere Parametrierung, so ist per Default eine interne Dämpfung von 11 dB aktiv.

Mit der aktuellen ESPuino-Implementierung hat mein neuer Spannungsteiler dazu geführt, dass ich das Offset auf satte 0,45 V korrigieren musste. Insofern ist das vielleicht doch der falsche Weg, wie wir das aktuell machen.

Ich habe daher den aktuellen Code eingekürzt und die Dämpfung zur Messung mit meiner Complete testweise auf 0 dB gesetzt. Das Offset konnte ich nun auf 0,1 V zurücksetzen, um einen sehr exakten Wert zu kriegen. Setze ich jedoch absichtlich 11dB, so kommt ein komplett falscher Wert raus. Der Code sieht wie folgt aus:

float Battery_GetVoltage(void) {
	analogSetPinAttenuation(VOLTAGE_READ_PIN, ADC_0db);
	// analogSetAttenuation(ADC_11db);
	float averagedAnalogValue = 0;
	uint8_t i;
	for (i = 0; i <= 19; i++) {
		averagedAnalogValue += (float) analogRead(VOLTAGE_READ_PIN);
	}
	averagedAnalogValue /= 20000.0f;
	return averagedAnalogValue + offsetVoltage;
}

Zur Referenz: Der bisherige Code sieht so aus:


float Battery_GetVoltage(void) {
	float factor = 1 / ((float) rdiv2 / (rdiv2 + rdiv1));
	float averagedAnalogValue = 0;
	uint8_t i;
	for (i = 0; i <= 19; i++) {
		averagedAnalogValue += (float) analogRead(VOLTAGE_READ_PIN);
	}
	averagedAnalogValue /= 20.0f;
	return (averagedAnalogValue / maxAnalogValue) * referenceVoltage * factor + offsetVoltage;
}

Als Dämpfungsstufen gibt es:

  • ADC_0db,
  • ADC_2_5db,
  • ADC_6db,
  • ADC_11db,
  • ADC_ATTENDB_MAX

Anmerkungen:

  • Ich denke es würde generell Sinn machen, das Ganze mal zu überarbeiten. Der Programmieraufwand ist ziemlich überschaubar, jedoch ist das Testen halt aufwändig.
  • Der Code lässt sich etwas verschlanken: Also die Berechnung wird einfacher und die Größe des Spannungsteilers kann man rausnehmen. Was man weiterhin brauchen wird (nehme ich an), ist der Offset.
  • Bei der Complete wird’s für LFP und LiPo getrennte Boards geben. Insofern werde ich dafür Sorge tragen, dass der Spannungsteiler bei der LiPo-Variante etwas größer ist (vielleicht 400k zu 100k).
  • Bei der mini4L wird man weiterhin bei 11dB bleiben müssen, da wir hier nicht zwischen LFP und LiPo unterscheiden und halt mit max. 2,1 V gerechnet werden muss.
  • Mich verwundert es ehrlich gesagt, dass mein Code direkt eine Spannung und nicht den RAW-Wert liefert, den man umrechnen muss.
  • Noch mehr verwundert mich: Wenn ich analogReadMilliVolts() in meinem angepassten Code verwende, dann messe 2,9 V, wenn die Spannung tatsächlich 3,5 V ist und 2,7 V, wenn die Spannung 3,2 V ist?!?

Habt ihr dazu Anmerkungen?
Hat vielleicht jmd. dazu schon Tests gemacht?
Mag mich jmd. unterstützen? :slight_smile:

Infos:
[1] ESP32 ADC – Read Analog Input in Arduino IDE – DeepBlue
[2] ADC - - — Arduino ESP32 latest documentation

analogReadMilliVolts() does not handle changes in resolution made by analogReadResolution() · Issue #8999 · espressif/arduino-esp32 · GitHub maybe?

AnalogRead scheint die eingestellten Ref und die mögliche Spanne zu berücksichtigen und das passend umzurechnen

„Normal“ wäre in der Messtechnik ein Abgleich mit Offset + Gain, aber wo bekommt man dann die Referenz her für den oberen Messpunkt? daher ist immerhin der größte Einfluss weg (der Offset)

EDIT:

Das glaube ich ist ein Zufall bzw. vlt macht AnalogRead da doch Quatsch, woher soll der Eingang wissen welches Verhältnis an Widerständen ich da angeschlossen habe…

Ich wette wenn du denn Teiler anders macht passt der Wert nicht mehr, kannst ja mal einen Poti anschließen :wink:

Hmm, ein durchaus valider Punkt :slight_smile:
Ggf. habe ich heute Abend nochmal Zeit, mir das nochmal anzuschauen.

Ein weiterer Punkt ist die Verwendung „guter“ Widerstände, dann wäre ein Teil vom Gain Fehler auch weg:

kostet aber bißchen was :wink:

Habe gestern nochmal ein bisschen experimentiert. Aus dem Ergebnis der Funktion, die die Spannung direkt auslesen soll, werde ich nicht schlau. Lese ich im Leerlauf (kein Akku angeschlossen) dagegen den RAW-Wert aus, dann kommt da etwa 3550 (grob) raus.

3550 / 4095 = 0,8669
0,8669 / 0,95 = 0,912
0,912 * 4 = 3,65 V (mit Multimeter messe ich 3,61 V)

4095: RAW-Endwert
0,95: Endwert Spannung in V bei 0 dB
4: Faktor wegen Spannungsteiler

Ob das der korrekte Weg ist weiß ich nicht, aber zumindest erscheint der Wert ok. Muss das mal bisschen weitertesten und auch mal im Akkumodus.

Da der ESP32 ja eher ein Schätzeisen ist, macht’s aus meiner Sicht wenig Sinn, da groß in diese Richtung zu gehen. Die verbauten Widerstände haben einen Toleranz von 1 %. Wobei 0,1 % als 100k auch nur 2 Cent kosten (allerdings dann mit Feedergebühr weil Extended Part).

1 „Gefällt mir“

Also mit dieser Formel, die ich oben genannt habe, kommt es ganz gut hin. Auch wenn der Akku entladen wird.
Aber wenn ich die Dämpfung ändere, dann kommt komischer Kram raus.

Szenario:

  • Es ist kein Akku angeschlossen und ich messe mit meinem Multimeter eine Spannung von 3,61 V
  • Der Spannungsteiler ist R1=300 k und R2=100 k. Über R2 wird gemessen.

Hier die Rohwerte (jeweils 20 Messungen, jedoch mehrfach Messung ausgelöst):
0 dB: 3450 bis 3550
2,5 dB: 2620 bis 2700
6 dB: 1820 bis 1860
11 dB: 930 bis 980

Vielleicht kann’s mir ja wer erklären.

Wenn ich Formel anwende und +/- den Mittelwert aus den Ranges der Rohwerte nehme, dann komme ich auf:
0 dB: 0,81 V
2,5 dB: 0,81 V
6 dB: 0, 786 V
11 dB: 0,568 V

=> Das müsste man mit 4 multiplizieren wegen des Teilers, aber da kommt Käse raus.
Also ich versteh’s nicht.

Links:

OK, also der Grund, weswegen das mit dem direkten Auslesen der Spannung nicht funktioniert hat war, dass ich die Dämpfung lokal in der Messfunktion und nicht in setup() eingetragen hatte. Mache ich das, so kommen die Werte gut hin.

Sieht jetzt so aus:

float Battery_GetVoltage(void) {
	float resistorDividerFactor = 1 / ((float) rdiv2 / (rdiv2 + rdiv1));
	float averagedAnalogValue = 0;
	uint8_t i;
	for (i = 0; i <= 19; i++) {
		averagedAnalogValue += (float) analogReadMilliVolts(VOLTAGE_READ_PIN);
	}
	averagedAnalogValue /= 20000.0f; // Calc avg. value of loop and convert mV to V
	return averagedAnalogValue * resistorDividerFactor + offsetVoltage;
}

Zusätzlich habe ich in Battery_Init() noch eingetragen:

analogSetPinAttenuation(VOLTAGE_READ_PIN, ADC_0db);

Das werde ich mal bisschen testen. Offset habe ich aktuell auf -0,04V stehen.

Davon ab macht es Sinn, die Stufen für die Spannungseinstellungen im Webinterface von 0,1 V auf 0,05 V zu reduzieren. Dann könnte man nämlich „Alle LEDs leuchten bei dieser Spannung (in Volt)“ auf 3,25 V setzen. Denke das wäre besser als 3,3 V - siehe Messreihe. Das erzeugt nämlich sonst „Hä, wieso entlädt sich der Akku so schnell? Den habe ich doch die ganze Nacht geladen!“.

2 „Gefällt mir“

Also aus meiner Sicht funktioniert das gut, hab’s auch auf der mini4L getestet.

Habe versehentlich den RFID-Reader in diesem Commit geändert; das habe ich wieder rückgängig gemacht.

Ich habe das auf der Complete mit LFP und LiPo und auch auf der mini4L mit LFP getestet. Also aus meiner Sicht funktioniert das recht gut. Da muss man eigentlich gar keinen Offset mehr setzen, deswegen habe ich ihn bei allen Boards != Complete auch auf 0 V gesetzt.