Ein Pulsmessgerät

Hintegrund

Burkhard hatte mich Ende Mai 2020 angesprochen, weil er auf meine Webseite zur Pulsüberwachung mittels Ohrclip gestoßen ist. Er hatte sich aus Brocken eines ziemlich teuren Ergometers ein neues Fitnessgerät gebaut, das soweit funktioniert, aber keine Pulsüberwachung hat. Seine Idee war, einen eigenständigen Beat Counter mit meiner Pulsdetektor-Schaltung zu verknüpfen. Dieser Beat Counter (etwas das ich bis dahin noch nicht gekannt hatte) ist ein kleines Gerät mit einer Taste und einem 4-stelligen Display -- man drückt die Taste im Takt der Musik und das Display zeigt einem an, wie viele BPM (Beats Per Minute) das sind. Allerdings fehlten ihm die notwendigen Kenntnisse, um meine Schaltung entsprechend anzupassen. Das Ganze klang also nach einem klassischen Hardware-Hack zu dem ich nicht nein sagen konnte. Mein Vorschlag war, den Taster des Beat Counters mit einem Reed-Relais zu überbrücken und dieses direkt vom OpAmp-Ausgang treiben zu lassen.

Die erste Version der Schaltung hatte nicht funktioniert und die Ferndiagnose war zu schwierig, weshalb Burkard die Brocken dann Ende Oktober entnervt in die Post geworfen hat, damit ich mir die Sache aus der Nähe ansehen konnte. Es war letztendlich ein kleiner Fehler im Aufbau, der leicht zu beheben gewesen wäre. Aber bei näherer Betrachtung des Beat Counters wurde mir bewusst, wie rudimentär die Lösung geworden wäre und ich habe Burkhard vorgeschlagen, mit ihm gemeinsam eine "richtige" Schaltung zu entwerfen. So entstand dann Ende Januar 2021 dieses Projekt. Ja, es war eine bewusste Entscheidung so gemächlich vorzugehen, bevor jemand fragt. ;-)

Die Anforderungen

Zuerst haben wir uns über die Anforderungen unterhalten und kamen zu diesen Must-Haves:

Die letzte Anforderung war durchaus ernst gemeint: das Gerät soll einfach tun; Einschalten, los geht's. Kein langwieriges Konfigurieren, Button-Rumgedrücke oder sonstwas. Ich war da anfangs etwas skeptisch ob der vielen verschenkten Optionen (wenn eh schon ein Mikrocontroller drin steckt, dann könnte man doch noch dieses und jenes machen...), aber konnte mich dem Wunsch nach kompromissloser Simplizität dann doch irgendwann gut anschließen.

Dem geneigten Bastler wird gleich auffallen, dass die ersten beiden Punkte natürliche Feinde sind: eine 7-Segment-Anzeige frisst Strom, Batterien haben eine begrenzte Ladung. Primärzellen waren daher ziemlich schnell vom Tisch. Bei den Überlegungen bezüglich Akku und Ladetechnik kamen wir dann überein auf eine USB-Powerbank zu setzen. Damit ist die Ladetechnik gekapselt und die stabilisierten 5 Volt sind genau das, mit dem sich eine kleine AVR-Schaltung am wohlsten fühlt.

Ich konnte Burkhard dann noch zu ein paar weiteren Features "überreden", damit der Mikrocontroller wenigstens ein bisschen was zu tun bekommt:

Anfangs war noch die Idee im Gespräch, die Glättung der Messergebnisse ein- und ausschaltbar zu machen, aber dieses Feature hatten wir dann zur Status-LED umgemodelt. Die Notwendigkeit für diese ist mir nämlich bei ersten "Produkttests" aufgefallen -- ohne ging es einfach nicht. Das Problem mit den Ohrclips ist, dass sie ziemlich gut sitzen müssen, damit sie sinnvolle Ergebnisse liefern. Ohne direktes Feedback gelingt das allerdings schlecht: wenn man nur einen extrem geglätteten Anzeigewert hat, dann ist schwer abzuschätzen, ob alle Schläge erkannt wurden, oder ob da zwischendrin etwas "stolpert". Eine LED, die exakt im Takt des (erkannten) Herzschlags blinkt, ist jedoch sehr leicht mit der Positionierung des Ohrclips in Verbindung zu bringen.

Die Hardware

Burkhard ist ein sehr begabter Bastler was die mechanische Bearbeitung von Materie angeht. Wir hatten uns grob über die Dimensionen unterhalten und wie das Ganze aussehen soll (er hatte zu der Zeit schon ein konkretes Gehäuse im Blick). Danach habe ich mich an den Entwurf einer Leiterplatte gemacht und wir haben uns iterativ angenährert. Mitte März haben wir dann die Leiterplatte beim Platinenbelichter in Auftrag gegeben und Burkhard hielt sie bereits zwei Wochen später in den Händen. Mitte Mai war das Gerät dann hardwaremäßig aufgebaut und auf dem Weg zu mir, damit ich dem Mikrocontroller das Leben einhauchen konnte.

Mein eigentlicher Plan bei der Erstellung des Layouts für das Display-Modul war, ein 10-poliges Flachkabel direkt von der Oberseite her einzulöten und dann am Rand um die Leiterplatte herum zu führen, zwischen Leiterplatte und Gehäuse durch. Dazu hatte ich im Layout ein Stück kupferfreie Fläche zum Rausfräsen vorgesehen. Ich hatte aber wohl vergessen diese Idee auch mitzuteilen, weshalb Burkhard davon ausgegangen ist, dass auf der Platinenunterseite ein Pin-Header angebracht werden sollte (was ich so nie probiert hätte, auf einer Leiterplatte ohne Stopplack ist das widerlich zu löten).

Bei der ersten Inbetriebnahme hatte das nicht weiter gestört, da ich ohnehin einen Haufen individueller Steckbrett-Kabel verwendet habe um das Display-Modul mit dem dafür vorgesehenen Pin-Header der Hauptplatine zu verbinden. Allerdings sah ich in dem Moment schon meine Idee des Flachkabels dahin schwinden. Zum Glück hat sich aber später gezeigt, dass mein Symmetrie-Fanatismus etwas Gutes hatte: von den 10 Leitungen sind drei besonders, weil sie durch Transistoren getrieben sind um die gemeinsamen Kathoden der 7-Segment-Module auf GND ziehen zu können. Diese hatte ich mittig angeordnet, sodass ich nun die Oberseite/Unterseite-Vertauschung durch eine 180°-Drehung des Steckers ausgleichen konnte; zu kompliziert für Worte, hier eine Skizze:

Die mit X markierten Pins gehören zu den "starken" Leitungen, zuerst aus der Sicht von oben (wie im Layout-Programm). Jetzt wenden wir die Leiterplatte -- geschehen dadurch, dass der Pin-Header auf die Unterseite gelötet wurde. Jetzt sind die "starken" Leitungen links, man müsste also beim Flachkabel die Adern paarweise drehen (was vermutlich ginge, aber sehr fummelig wäre). Doch durch das Drehen des Steckers um 180° wird wieder links gegen rechts getauscht, auf Kosten einer Vertauschung von oben und unten -- jetzt sind die Pins 1 und 2 am oberen Rand der Leiterplatte. Das macht aber nichts, weil die 7 Leitungen für die Segmente alle gleichberechtigt sind und letztendlich nur das Bitmuster in der Ansteuerung geändert werden muss. Dem aufmerksamen Leser wird jetzt vielleicht aufgefallen sein, dass die 7-Segment-Module 1 und 3 (also Einerstelle und Hunderterstelle) ebenfalls in der Ansteuerung vertauscht werden müssen. Aber auch das ist in der Software eine Kleinigkeit.

Im fertigen Aufbau sieht man einen weiteren 5x2-Pin-Header. Dieser dient zunächst als Anschluss für den ISP-Adapter zum Programmieren des Mikrocontrollers. Diese Funktion gibt auch die eigentliche Pinbelegung vor. Darüber hinaus ergeben sich aus der Tatsache, dass die Pins im Normalbetrieb als GPIO genutzt werden können, weitere Anwendungsmöglichkeiten:

Signal Pin-Nummer Signal Alternative Funktion
MOSI 1 2 VCC Status-LED (ehemals Messwertglättung)
--- 3 4 GND
RESET 5 6 GND
SCK 7 8 GND J1 für Schwellwert-Auswahl
MISO 9 10 GND J2 für Schwellwert-Auswahl

Die Eingänge der AVR-Controller verfügen über interne, optional aktivierbare Pullup-Widerstände. Bei J1 und J2 können diese gut genutzt werden, weil der jeweils angrenzende Pin auf dem Pin-Header mit GND verbunden ist. Bei dem obersten Pärchen ist der "feste" Pin jedoch auf VCC gelegt, weshalb ich zunächst einen externen Pulldown-Widerstand vorgesehen habe. Als dann später die Funktion zur Status-LED umgewidmet wurde, ist der Widerstand obsolet geworden, jedoch in der Schaltung verblieben, da er auch nicht weiter stört.
Die Reset-Leitung könnte man übrigens auch als GPIO nutzen, allerdings muss dies separat konfiguriert machen und verhindert danach die weitere Programmierung des Mikrocontrollers via ISP-Adapter. Wenn man dann nicht über einen "High Voltage Programmer" (der mit 12 Volt arbeitet) verfügt, oder einen Bootloader hinterlassen hat, ist man angeschmiert.

Die Software

Auf der Software-Seite habe ich mal was Neues probiert. Ich habe ein GPIO-Modul geschrieben, das "GPIO Items" bereitstellt. Diese werden über eine ID referenziert (GpioItemId) und können Werte liefern oder annehmen, wobei der Witz darin besteht, dass diese Werte mehrere Bits umfassen können. Das heißt der Aufrufer muss sich keine Gedanken darüber machen, wie viele Pins dazugehören und wo sie physikalisch liegen -- stattdessen kann er z.B. über die ID GPIO_ITEM_SEGMENTS einen Zahlenwert zwischen 0 und 127 schreiben, der ein Bitmuster für die Ansteuerung eines 7-Segment-Moduls darstellt. Danach kann über GPIO_ITEM_DRIVER_1 der Treiber für Display-Modul 1 aktiviert werden. In UML sieht das ungefähr so aus:

Von der Idee her ist das so ähnlich wie bei meinem avr-classes-Projekt, aber mit den Sprachmitteln von C umgesetzt. Leider ist damit einiges konzeptionell etwas hässlicher geraten, insbesondere dass häufiger IDs übergeben werden müssen, wo in einer objektorientierten Sprache das aufgerufene Ding wüsste, welche Instanz angesprochen ist. Auch die Teilung der Zuständigkeiten ist bei diesem Ansatz nicht so stark ausgeprägt bzw. nur mit Disziplin vorhanden und durch die Funktionsnamen sichtbar. Ein Vorteil ist, dass sich sehr viel über Tabellen ausdrücken lässt, und sich diese via PROGMEM im ROM ablegen lassen. Somit ist der RAM-Bedarf dieser Lösung sensationell niedrig, was bei einem so schmalen Controller wie dem ATtiny2313A extrem hilfreich ist (insgesamt 128 Byte RAM, für Stack und Variablen).

Die restliche Architektur ist eher traditionell gestaltet:

Ich konnte bei dieser Software fast komplett auf statische Variablen verzichten, lediglich die Message-Queue liegt als solche vor, weil sie aus verschiedenen Kontexten genutzt wird. Die einzelnen Komponenten legen ihren Zustand in einer Instanz-Struktur ab, die vom Aufrufer angelegt und bereitgestellt wird. Allerdings hat dieser keinen Einblick, die Datenstrukturen werden nur über von den Modulen bereitgestellte Funktionen manipuliert (Blackbox-Prinzip). Gerne hätte ich die Struktur-Definitionen komplett verborgen, aber da der Aufrufer den Speicher bereitstellen muss, müssen die Strukturen auch inhaltlich bekannt sein.

Die Symmetrie im Komponenten-Diagramm ist zwar hübsch, aber tatsächlich auch mit etwas Zufall zustande gekommen -- die Abhängigkeiten zu "config" ergeben sich z.B. daraus, dass ich hier auch symbolische Namen für Größen wie CFG_UPDATES_PER_MINUTE abgelegt habe. Was aber systematische Gründe hat und damit für das Design typisch ist:

Die Software belegt in Release-Konfiguration 1930 Byte Flash-Speicher und 59 Byte RAM (zuzüglich Stack). Damit ist einerseits der ATtiny2313A recht gut befüllt und ich andererseits wieder einmal erstaunt, wie kompakt 8-Bit-Programme sein können.

Downloads

Version Datum Änderungen Datei Beschreibung
1.0 2021-09-26 Erstes Release pulsmesser-1.0.zip Schaltplan im Eagle-Format (Version 9.2.2), Firmware (Atmel Studio 7/AVR-GCC)

Zurück zur Hauptseite