Ziele
Das Hauptziel der Software lag darin, eine Abstraktionsschicht zu schaffen, welche es ermöglicht eine darauf geschriebene Anwendung mit jedem Prozessor zu verwenden.
Zusätzlich war es nötig Software Module zur einfacheren Verwendung der Hardware Komponenten, wie z.B. ADCs, zu implementieren.
Entwicklungsumgebung
Um möglichst viele Mikroprozessor-Architekturen und -Hersteller möglichst einfach handhaben zu können, setzten wir auf PlatformIO. Es handelt sich um ein Werkzeug, welches die Tool-Chains von verschiedenen Herstellern zusammenfasst. Im Hintergrund werden also je nach Hersteller und Mikroprozessor plattformübergreifende oder herstellereigene SDKs genutzt.
PlatformIO kann eigenständig als Kommandozeilenprogramm genutzt, aber auch in Entwicklungsumgebungen integriert werden. In unserem Fall wurde die VSCode Erweiterung verwendet. So müssen sich Entwickler nicht immer an neue Umgebungen anpassen.
Module

SPI Wrapper
SPI (Serial Peripheral Interface) ist ein synchrones, serielles Kommunikationsprotokoll, das häufig zur Verbindung von Mikrocontrollern mit Peripheriegeräten wie Sensoren, Displays oder Speicherchips verwendet wird. Es basiert auf einer Master-Slave-Architektur und nutzt vier Hauptleitungen: MOSI (Master Out, Slave In), MISO (Master In, Slave Out), SCLK (Serial Clock), und CS (Chip Select).
Der SPI Wrapper abstrahiert die Prozessorspezifischen SPI implementierungen. Für jeden Chip, welcher über SPI angesteuert wird, kann ein neues Objekt der SPI Wrapper Klasse instanziiert werden. Diese bietet Single- als auch Multibyte Read- und Write Operationen an. Sämtliche Kommunikation, als auch setzen der Chip Select Leitung, übernimmt dabei der SPI Wrapper.
I2C Wrapper
I2C (Inter-Integrated Circuit) ist ein synchrones, serielles Kommunikationsprotokoll, das die Kommunikation zwischen mehreren Geräten über nur zwei Leitungen ermöglicht: SDA (Datenleitung) und SCL (Taktsignal). Es basiert auf einem Master-Slave-Prinzip, unterstützt mehrere Geräteadressen und ermöglicht bidirektionale Datenübertragung mit Kollisionsvermeidung.
Ähnlich wie der SPI Wrapper abstrahiert auch der I2C Wrapper die prozessorspezifischen Implementierungen. Auch hier kann für jeden Chip ein Objekt instanziiert werden, welches ebenso Single- und Multibyte Read- und Write Operationen anbietet.
ADCs (ADS1261, MAX1415)
Für die Beiden ADCs, der Stationären und der Mobilen Messplatinen, wurde jeweils ein eigenes Modul implementiert. Abgesehen vom Konstruktor ähneln sich die Schnittstellen sehr start. Diese sind sehr primitiv aufgebaut, da hier nur eine Funktion zum Reset / Re-initialisierung des ADCs und eine Funktion zum Auslesen eines Wertes angeboten wird.
Die Implementierung der Beiden ADCs unterscheidet sich jedoch sehr stark von einander. Beide benutzen ein Kommandoprinzip, welches über SPI kommuniziert. Jedoch ist der Aufbau der Kommandos, der Register und der benötigten Abläufe grundlegend verschieden.
MUX (HEF4067BT)
Der Multiplexer dient zum Umschalten zwischen den 10 Kanälen der Stationären Messplatine. Die Kanalauswahl erfolgt durch 4 GPIO Pins. Eine Kombination aus High- und Low-Pegel über die 4 Leitungen ergibt den ausgewählten Kanal.
Dies wird vom Modul abgenommen. Nach außen wird eine Schnittstelle angeboten um den aktuellen Kanal auszulesen und auf den nächsten Kanal zu wechseln.
LED Debugging
Um das Debugging zu erleichtern und um eine Statusanzeige für den Fehlerfall zu schaffen wurde das Modul “LED Debugging” entworfen. Hierbei wird die LED, welche sich auf dem Prozessor Board befindet, um bei einem Fehlerfall Blinkcodes anzuzeigen.
Durch Zählen wie oft die LED blinkt kann ermittelt werden um welchen Fehler es sich handelt. Blinkt die LED zum Beispiel vier mal kurz, gefolgt von einer längeren Pause, handelt es sich um den Fehler, dass das Dateisystem der SD Karte nicht richtig gemounted werden konnte. Ein Teil dieser Fehlercodes wird bereits vom Modul vorgegeben. Es können aber auch anwendungspezifische Fehlercodes definiert werden.
SD Card / Filesystem
Um Messwerte zu speichern und zu einem späteren Zeitpunkt wieder auszulesen wurde ein Modul zu einfachen Ansteuerung der SD Karte benötigt.
Hier wurde auf bestehenden Code gesetzt. FatFS von Elm Chan ist ein generisches FAT Filesystem Modul für embedded devices. Hier werden Funktionen wie Mount, Open, Read und Write angeboten, welche sehr ähnlich zum POSIX Standard aufgebaut sind.
Dadurch, dass FatFS nur eine Abstraktionschicht ist, welche wiederum auf einer Implementierung aufbaut welche Low Level Funktionen bereitstellt, wurde eine Implementierung dieser Funktionen benötigt. Glücklicherweise wurde dieses Modul bereits in einer LVA verwendet. Dieser Code konnte dann relativ leicht für unser System adaptiert werden.
Display
Um den Implementierungsaufwand zu minimieren wurde für die Ansteurung des Displays die Draw Library von Adafruit verwendet. Darauf wurde ein weiteres Modul aufgebaut um das Anzeigen von Graphen möglichst einfach zu gestallten. Dieses Modul bietet nach außen eine einzige Funktion zum Hinzufügen von Messwerten zum Graph. Intern zeichnet das Modul das Koordinatensystem, skaliert dieses je nach Spektrum der Messwerte und setzt wenn benötigt einen Titel.
Digitales Potentiometer (XDCP)
Ein digitales Potentiometer ermöglicht es ein Widerstands-Array in gleich großen Stufen zu- und wegzuschalten und somit den Gesamtwiderstand zu beeinflussen.
Funktionsweise
Der gewählte Typ (XDCP) stellt hierzu 100 Stufen (0 bis 99) bereit. Es werden also pro Stufe 1/99 des größtmöglichen Widerstands zu/weggeschaltet. Das Auswählen des gewünschten Widerstandswerts erfolgt über eine “3-Draht serielle Schnittstelle”, welche sich aus folgenden Signalen zusammensetzt: ChipSelect(inverted), Up/Down und Increment(inverted). Das digitale Potentiometer wird sehr ähnlich wie übliche Schrittmotortreiber angesteuert: ChipSelect auf LOW, mittels Up/Down die Richtung bestimmen und dann löst eine fallende Flanke des Increment Signals einen “Schritt” in die gewünschte Richtung aus.
Da der Widerstandswert beim ersten Start-Up nicht bekannt ist, wird vom Modul dieser entweder auf den Maximal- oder Minimalwert (99 Schritte in gewünschte Richtung) mittels Initialisierungssequenz gesetzt.
Nicht umgesetzte Funktionen
Die digitalen Potentiometer der XDCP Reihe besitzen einen internen Speicher. Um die aktuelle Position in diesen Speicher zu schreiben muss Increment HIGH sein und eine steigende Flanke an ChipSelect löst dann den Schreibvorgang aus. Beim Power-Up wird immer die, in diesem Speicher zuletzt abgelegte, Position geladen.
ESP-Now Wrapper
ESP-NOW ist ein drahtloses Kommunikationsprotokoll, welches von Espressif für ESP32- und ESP8266-Mikrocontroller entwickelt wurde. Es ermöglicht schnelle, direkte Kommunikation ohne auf Router angewiesen zu sein.
Kommunikationspartner werden über die MAC-Adresse identifiziert. Sowohl Unicast (Kommunikation mit einem bestimmten Partner) als auch Broadcast ist möglich.
Espressif definiert im OSI-Modell nur Schichten 1 (Physikalische Schicht) und 2 (Sicherungsschicht). Alles weitere wurde von uns in in einem eigenen Wrapper-Modul definiert. Unterschieden wird zwischen Server und Clients. Im Wrapper wird jedoch lediglich die Grundstrukur der Datenpakete wird definiert. Wie die Daten selbst aussehen und wie sie verarbeitet werden, wird von Außen bestimmt. So bleibt diese Wrapper-Einheit flexibel und kann bei vielen verschiedenen Anwendungen eingesetzt werden.
Kommunikation
In einer Instanz des Moduls können hierzu für 255 Nachrichten IDs Callback-Funktionen definiert werden. Somit sind 255 verschiedene Nachrichtenpakete möglich.
Eine Nachricht besteht aus der Message-ID (1Byte) und den Daten (bis zu 250 Bytes). Die Struktur der Daten und die Callbacks werden ausserhalb des Moduls definiert. Callbacks müssen im Modul registriert werden. Wenn eine Nachricht empfangen wird, wird erst nur die Message-ID bestimmt und die entsprechende Callback Funktion aufgerufen.
Pairing
Um eine Unicast-Kommunikation zu ermöglichen müssen vorher die MAC-Adressen der Teilnehmer und der Channel jeweils in eine Liste an Kommunikationspartner eingetragen werden.
Hierfür ist die Callback-Funktion der Message-ID 0 ist vorbestimmt.
Der Client sendet eine Broadcast-Message aus, welche nach folgendem Schema aufgebaut ist: Message-ID 0 (1Byte), Channel (1Byte), Identifier (16Byte). Über den Identifier wird zusätzlich sichergestellt, dass die Teilnehmer zur selben Applikation gehören.
Der Server empfängt die Nachricht und trägt den Client in die Liste der ESP-Now Peers ein (falls er sich im Pairing Modus befindet). Danach wird eine Pairing-Nachricht an den Client gesendet, welcher dann den Server auch in seine Peer-Liste aufnimmt
Der Pairing Modus am Server und das Absenden der Pairing Nachrichten des Clients werden von Außen (z.B. auf Knopfdruck) ausgelöst.
Battery Voltage
Bei der, von uns gewählten SoM-Familie ist auf dem M.2 Stecker ein Pin für das Messen der Batterie-Spannung vorgesehen. Ziel des Battery-Voltage Moduls ist es, diese Spannung zu Messen und in einen möglichst brauchen SoC (State of Charge) Wert, also restliche Batteriekapazität (in %) zu wandeln.
Funktionsweise
Im Modul sind bekannte Punkte einer typischen Entladekurve eines LiPo-Akkus definiert. Diese Punkte sind nicht gleichmäßig über den erwarteten Spannungsbereich verteilt. Stattdessen konzentrieren sie sich vor allem auf die nicht-linearen Bereiche (Anfang und Schluss).
Zuerst wird der ADC-Wert in einen Spannungswert umgerechnet, dann mittels der bekannten Entladekurve abgeglichen. Um zum SoC-Wert zu gelangen, wird zwischen den zwei nächsten bekannten Punkten linear interpoliert.
Einschränkungen
- Die Zusammenhänge von Spannung und SoC für eine Entladekurve sind sehr abhängig vom Verhältnis des bezogenen Stroms zur Akkugesamtkapazität: Umso mehr Strom (aktuell) vom Akku bezogen wird, umso stärker fällt, auch unabhängig vom SoC die Akku-Spannung. Es wurde in Folge ein gewisser Strombedarf geschätzt und eine entsprechende Entladekurve gewählt.
Der Strombedarf muss zwingend bekannt und möglichst gleichbleibend sein um brauchbare Werte zu erhalten. Das macht diese Methode vor allem für wechselnde Lasten unbrauchbar. - Der Aufbau unserer (und auch der gekauften) Hardware kann unter ~3.3V Versorgungsspannung keine stabile Referenzspannung mehr bereitstellen. Es sinken mit fallender Versorgungsspannung in diesem Fall Referenzspannung (des ADCs) und Batteriespannung gleichermaßen.
Das bewirkt, dass sich die gemessenen Werte im Bereich von 3.3V bis zum Brown-Out (im Testaufbau ca. 2.0V) nicht unterscheiden und somit gänzlich unbrauchbar sind.
Um dieses Problem zu umgehen, wurde die definierte Entladekurve angepasst und 3.3V entsprechen nun 0% SoC anstatt ca. 10%.
Inertial Measurement Unit (BNO085)
Die BNO085 IMU kann entweder über I2C, SPI oder UART angesprochen werden. In unserem Fall verwenden wir SPI.
Das Nachrichtenprotokoll der BNO085 nennt sich SH2 und baut auf “Reports” auf. Der Ablauf sieht immer folgendermaßen aus:
- BNO085 signalisiert, dass Daten bereit stehen (über Interrupt Pin)
- Microcontroller holt den/die entsprechenden Report(s) ab
- Report(s) werden vom Microcontroller interpretiert
Damit die IMU Reports erstellt, muss vorher eine entsprechender Konfiguration mit Wiederholzeit (ms) erstellt werden und an die IMUgesendet werden.
Es gibt hier eine Vielzahl an möglichen Reports:
- ACCELEROMETER
- GYROSCOPE
- MAGNETIC_FIELD
- LINEAR_ACCELERATION
- ROTATION_VECTOR
- GRAVITY
- UNCALIBRATED_GYRO
- GAME_ROTATION_VECTOR
- GEOMAGNETIC_ROTATION_VECTOR
- GYRO_INTEGRATED_ROTATION_VECTOR
- TAP_DETECTOR
- STEP_COUNTER
- STABILITY_CLASSIFIER
- RAW_ACCELEROMETER
- RAW_GYROSCOPE
- RAW_MAGNETOMETER
- PERSONAL_ACTIVITY_CLASSIFIER
- AR_VR_STABILIZED_ROTATION_VECTOR
- AR_VR_STABILIZED_GAME_ROTATION_VECTOR
Schwierigkeiten
Weder verschiedene Standard-Implementierungen eines passenden Treibers, noch eine eigene Implementierung konnten von der IMU brauchbare Daten empfangen. Ein Abgleich des Datenblatts und einigen Hardware-Implementierungen von Break-Out Boards mit unserer Implementierung lieferte nur die Erkenntnis, dass wir auf den internen Taktgeber setzen und an den Break-Out Boards typischerweise externe Taktgeber verbaut sind. Ein offensichtlicher Fehler ist nicht erkennbar.