Weltweiter kostenloser Versand ab 120 € Bestellwert – Zahlung mit PayPal und Stripe – Hergestellt in Finnland

Ruuvi Firmware – Teil 7: Batteriemessung mit naivem Ansatz

Einleitungsbild zur Ruuvi Firmware-Serie Teil 7

In diesem Teil des Tutorials fügen wir eine einfache Batteriemessung mit dem internen Analog-Digital-Wandler (ADC) des nRF52832 hinzu. Den fertigen Code dieses Blog-Beitrags kannst du bei Ruuvi GitHub im ruuviblog-Branch unter dem Tag 3.7.0-alpha herunterladen.

Bitte folge Teil 1 der Serie für Details zum Klonen des Repositories und Kompilieren des Codes. Die finale Hex-Datei dieses Tutorials kann vom Ruuvi Jenkins heruntergeladen werden.

ruuvi.firmware.c Architektur 3.7.0

Batteriemessung – der einfache Weg

Der einfachste Weg, die verbleibende Batteriekapazität zu schätzen, besteht darin, die Spannung zu messen, mit der unser Tag betrieben wird. Dieser Ansatz hat jedoch einige Nachteile:

  • Die Temperatur wird nicht berücksichtigt
  • Der Innenwiderstand der Batterie wird nicht berücksichtigt
  • Die nichtlineare Kurve der Batteriekapazität und der verbleibenden Ladung wird nicht berücksichtigt
  • Eine punktuelle Spannungsmessung sagt uns unter Umständen nichts über den Spannungseinbruch aus, der bei leistungsintensiven Vorgängen wie einer BLE-Übertragung auftritt.

Wir werden den nRF52 ADC verwenden, um die Batterie vorerst trotz dieser Einschränkungen zu messen.

ADC-Schnittstelle

Obwohl das zuvor definierte Sensor-Interface für den Anwendungsfall, eine einzelne Spannung aus dem internen ADC des nRF52 auszulesen, etwas überdimensioniert sein mag, wird es bereits zum Auslesen der Temperatur des nRF52 verwendet. Lass es uns weiter nutzen, um unnötige Sonderfälle in unserer Datenverarbeitung zu vermeiden. Wir können den Handle eines Sensors verwenden, um einen Kanal des ADC darzustellen, genau wie wir ihn verwenden, um einen GPIO-Pin am SPI-Bus darzustellen.

ruuvi_interface_adc.h
ruuvi_interface_adc.h

Um so kompatibel wie möglich mit den anderen Sensoren zu bleiben, fügen wir zwei reservierte Floats zu unserer ADC-Datenstruktur hinzu.

Unser Sensor-Interface unterstützt das Erstellen von Sensor-Instanzen nicht richtig, daher werden wir keine Multichannel-Unterstützung für den ADC haben. Da wir zum jetzigen Zeitpunkt nicht mehr als die Batteriespannung benötigen, werden wir das Interface nicht erweitern, um mehrere ADC-Instanzen unterzubringen. Falls der Bedarf entsteht, werden wir uns vielleicht ansehen, das gesamte Interface mit C++ umzusetzen.

ADC-Treiber

Auflösung

Der ADC unterstützt Auflösungen von 8, 10, 12 und 14 Bit. Wir werden sie alle unterstützen und 10 Bit als Standardauflösung wählen, da dies auch im Beispiel von Nordic Semiconductor verwendet wird.

Skalierung

Die absolute Skala unseres ADC hängt von der Referenzspannung und dem Verstärkungsfaktor des ADC ab. Als Referenz können wir interne 600 mV oder die Versorgungsspannung (VDD) / 4 verwenden. Da unser Gerät batteriebetrieben ist, entscheiden wir uns für die bekannte interne Referenz. Der Verstärkungsfaktor kann zwischen 1/6 und 4/1 liegen, jedoch darf die analoge Eingangsspannung VDD nicht überschreiten, was uns eine Obergrenze von 3,6 V für die Skala gibt. Die Untergrenze wäre die Referenz / maximale Verstärkung, also 150 mV. Da wir jedoch nur den Single-Ended-Modus unterstützen werden und die maximale Auflösung von 14 Bit eine Millivolt-Genauigkeit bietet, die für unseren Anwendungsfall ausreicht, legen wir die Skala auf interne Referenz / minimale Verstärkung, also 3600 mV, fest. In der Funktion scale_get geben wir 3 zurück, um dem Prinzip treu zu bleiben, dass Parameter „mindestens“ erreicht werden.

DSP

Der ADC unterstützt Oversampling bis zu 256-fach. Da unser Konfigurationsdatenformat nur Werte bis 200 unterstützt, begrenzen wir das maximale Oversampling auf 128, was eine Stufe unter dem vom Hardware unterstützten Maximum liegt.

Modus

Theoretisch könnten wir kontinuierliches Sampling mit nRF52-Timern und Peripheral-Peripheral Interconnects (PPI) unterstützen, aber lass uns die Dinge auch hier einfach halten: Wir unterstützen nur Einzelmessungen und führen bei jedem data_get-Aufruf eine neue Messung durch, um den kontinuierlichen Modus zu emulieren.

Hinzufügen des ADC zum Programm

Der ADC-Task ist fast identisch mit dem Environmental-Task. Wir initialisieren und konfigurieren den ADC und geben die Messwerte bei Tastendruck aus.

Unser Main- und Button-Task erfordern kleine Anpassungen: Die ADC-Initialisierung muss in Main hinzugefügt und das ADC-Logging im Button-Task aufgerufen werden. Probieren wir es aus!

Ausgabe der Batteriespannung
Ausgabe der Batteriespannung

Mein billiges Multimeter zeigt 2,79 V an, daher scheint der ADC vernünftige Ergebnisse zu liefern. Es ist Zeit, das Stromprofil zu überprüfen.

RuuviTag+ im Leerlauf
RuuviTag+ im Leerlauf
RuuviTag+ bei der Messung
RuuviTag+ bei der Messung

Kurioserweise liegt unser Stromverbrauch im Leerlauf jetzt bei 5,0 μA, gegenüber 5,4 μA in unserem letzten Beitrag. Das könnte ein Temperatureffekt sein oder an der Wiederholgenauigkeit der Messungen mit dem Nordic Power Profiler Kit liegen. In jedem Fall können wir mit dem Stromverbrauch zufrieden sein.

Testen

Wie bereits angedeutet, steht der Aufbau einer besseren Testumgebung auf unserer Agenda. Wir sollten testen, ob die von uns geschriebenen Treiber wie erwartet funktionieren und Sonderfälle gnadenvoll behandeln. Die Tests geben uns auch ein klar definiertes Verhalten für die Versionierung: Jeder veröffentlichte Test, der einmal bestanden wurde, muss auch in Zukunft bestanden werden, es sei denn, wir erhöhen die Major-Versionsnummer der Ruuvi Firmware.

Wir werden einen neuen Ordner für die Tests erstellen, da wir sie von den Releases trennen wollen, die die Endnutzer ausführen. Hier ist das Sensor-Interface wieder sehr praktisch: Wir können genau denselben Test-Satz für jede Implementierung ausführen und prüfen, ob das Verhalten überall gleich ist.

Fangen wir mit dem Initialisierungs- und Deinitialisierungsverhalten jedes Sensors an. Die Initialisierung sollte bei gültiger Konfiguration keinen Fehler zurückgeben, es sei denn, der Sensor wurde bereits initialisiert. Die Deinitialisierung sollte immer erfolgreich sein. Funktionspointer des Sensors sollten nach der Initialisierung konfiguriert und nach der Deinitialisierung auf NULL gesetzt werden. Wenn NULL an eine der Funktionen übergeben wird, sollten sie einen Fehler zurückgeben.

Wir definieren außerdem eine Hilfsfunktion, um zu prüfen, wie viele Tests ausgeführt wurden und wie viele bestanden haben, sowie eine einzelne Funktion, um alle Tests auszuführen.

test_sensor.h – Initialisierung, Status der Tests und Ausführung.
test_sensor.h – Initialisierung, Status der Tests und Ausführung.

Wir verwenden hier die SESBuild-Konfigurationen und definieren RUUVI_RUN_TESTS in der Debug-Konfiguration. Dann fügen wir die Tests in die main.c ein.

main.c – Tests ausführen.
main.c – Tests ausführen.

Mal sehen, wie es mit dem Code läuft, den wir für das ADC-Sampling und das Power-Profiling des Tags verwendet haben!

Debug-Log der Tests. Ziemlich genau das, was man erwarten würde.
Debug-Log der Tests. Ziemlich genau das, was man erwarten würde.

Unsere Tests zahlen sich bereits aus, da sie zeigen, dass unsere Initialisierung nicht wie erwartet funktioniert. Nach einigem Suchen finden wir heraus:

  • Der ADC deinitialisiert nicht ordnungsgemäß
  • Beschleunigungssensor, BME280 und nRF52-Temperatursensoren erlauben eine Re-Initialisierung ohne vorherige Deinitialisierung
  • Der nRF52-Temperatursensor gibt keinen Fehler zurück, wenn NULL als Initialisierungs-Pointer übergeben wird
  • Der Funktionspointer samplerate_get wird bei der Initialisierung des nRF52-Umweltsensors nicht richtig gesetzt, da der Getter den Setter überschreibt.

Nachdem diese Probleme behoben sind, lassen wir das Programm erneut laufen:

Tests werden nun bestanden
Tests werden nun bestanden

Fazit

Wir haben nun die Unterstützung für die Messung der Gerätespannung über den internen ADC des nRF52 hinzugefügt. Unser Testen ist mit grundlegenden Unit-Tests für die Implementierungen der Sensor-Interface-Funktionen einen weiteren Schritt vorangekommen.

Wir werden die hier erstellten Tests weiter ausbauen, um andere Funktionen des Interface abzudecken, und schließlich im nächsten Teil der „Firmware Friday“-Serie Zeitstempel für die Sensordaten in unsere Treiber einbauen.

Bleib dran und folge @ojousima und @ruuvicom auf Twitter für #FirmwareFriday-Beiträge!