Raspberry Pi for an Ergometer

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Ein Projekt... mmhh, ja es ist eines geworden. Ich betreibe es schon länger. Und jetzt soll es auch endlich zum Ende kommen. Somit ist die Zeit gekommen, das Projekt in Worte zu fassen, Taten sind schon zu genüge erfolgt.

    Ein Wort richte ich an eine besondere Klientel. Kritik ist jederzeit herzlich willkommen, wenn sie nicht aus dem Munde eines Oberlehrers kommt, dessen Einfallsreichtum darin besteht, dass seine Ideen die besten sind. Darüber hinaus hat das Projekt aus der Sicht der Hardware einen Stand erreicht, der keine Änderungen mehr zulässt, wer das nicht bedenkt, beteiligt sich daran, Eulen nach Athen zu tragen.

    Eigentlich hatte ich der Elektrotechnik schon längst Adieu gesagt und beinahe wäre aus diesem Projekt auch nichts geworden, wenn ich nicht dem Teufel in letzte Sekunde von der Schippe gesprungen wäre. Und das ist auch gleichzeitig der Haken, an dem dieses Projekt aufgehängt ist, da ich um meine weitere Rehabilitation bemüht bin. Ein Ergometer, wie ich es unter medizinischer Betreuung kennengelernt habe, wäre zwar super, ist aber für den normalen Hausgebrauch unerschwinglich, zumindest nicht für Leute, die nur über ein normales Taschengeld verfügen. Bleiben noch die üblichen Händler oder dass was der Gebrauchtmarkt so hergibt.

    Was die Mechanik des erworbenen Ergometers betrifft hab' ich wohl ein Schnäppchen machen können, die Elektronik ist dagegen entsprechend dem Zeitgeist flickernd, blickend und glitzernd ausgelegt, Consumer-Elektronik der billigsten Machart. Die Bedienung ist alles andere als einfach. Mein Sohn wusste um meine Talente und war der Meinung, die Elektronik durch einen Raspberry Pi ersetzen zu können. Das war zwar im generell blauäugig, er hatte schließlich auch den RasPi und einen Bildschirm herumliegen, Teile, die er auf diese Art und Weise einer sinnvollen Verwendung zuzuführen gedachte. Ich habe mich darauf eingelassen, für eine Umkehr ist es mittlerweile zu spät.

    So habe ich ein Projekt gestartet und bin schnell zu dem Ergebnis gekommen, ohne zusätzliche Hardware, würde das Projekt nicht realisierbar sein. Der vorgesehene Bildschirm war in der Art ausgelegt, dass der RasPi auf der Rückseite des Bildschirms aufgesteckt wird, womit der Zugang zu freien GPIOs schon aus physischer Sicht unmöglich war.

    Den Bedarf der zusätzlichen Hardware hatte ich schnell skizziert. Und es stellte sich sehr bald heraus, dass die Zwangsehe, die zwischen Raspberry Pi und dem Bildschirm von Waveshare geschlossen wurde, nur dem Funktionieren des Touchpanels geschuldet war, das über ein SPI und einem IRQ mit dem Raspberry Pi kommunizierte. Die folgende Darstellung gibt einen Gesamteindruck der Hardware wieder, die für dieses Projekt erforderlich wurde.

    Das Elektroschema ist das CAD-Ergebnis des Paketes von DipTrace, aus dem dann später auch das PCB-Layout entstanden ist. Die verschiedenen Abschnitte werde ich noch ausführlich beschreiben. Das Elektroschema ist aber kein gewöhnliches, sondern dahinter verbirgt sich ein nicht unbeträchtlicher Anteil an Organisation, mit der das spätere PCB-Layout realisiert wird. Hinter jedem Schaltzeichen verbirgt sich der Footprint eines Bauteils, hinter jeder Verbindung steht eine Zuordnung zu Attributen eines Netzwerks.

    Für den Betrieb der Kombination aus Raspberry Pi und Bildschirm hätte ich zwei Netzgeräte vorsehen müssen, der kombinatorische Betrieb via nur einer Speisung über die Mini-USB des Raspberry PIs führte zu Fehlfunktionen.

    Ich habe daher eine zentrale Stromversorgung vorgesehen und das vom Hersteller beim Raspberry Pi realisierte Speiseprinzip abgekupfert, um so die für den Raspberry Pi vorgesehenen Schutzfunktionen nicht zu unterlaufen. Die Bauelemente D101 und F101 zählen dabei zu den wesentlichen Teilen. Über die Buchsenleiste X101 (2 x 20 Kontakte) wird nun die Zusatzplatine mit dem Raspberry Pi verbunden. Über die Pins (+5V) wird nun der Raspberry Pi "rückwärts" mit Energie versorgt. Die Spannung an den Pins (+3,3V) der Buchsenleiste X101 stammt dagegen jedoch selbst aus dem Raspberry Pi und wird für weitere, aber eingeschränkte Zwecke verwendet. Da die ganze Unternehmung sich eigentlich immer noch im Stadium des Prototyping befindet, habe ich z.B. mit ST102 für einen stabilen Messpunkt gesorgt, der auch mal ein Fliegenbein verträgt.

    Die Beschriftung der Buchsenleiste X101 habe ich nach der vorgegebenen Ordnung vorgenommen und je nach Bedarf durch weiter Begriffe ergänzt. Ich habe die Schnittstelle des Hardware-PWMs aus der Bibliothek pigpio verwendet, dies sind die beiden Pins GPIO12/PWM_fwd und GPIO13/PWM_rev. GPIO0 und GPIO1 sind bei mir "deprecated", ich habe sie in der ganzen Zeit der Erprobung nicht dazu bewegen zu können, sich meinem Willen unterzuordnen.

    Dadurch dass das Touchpanel eine SPI des Raspberrys schon in Beschlag genommen hat, war ich natürlich gezwungen, die Unterscheidung zu einer zweiten SPI herbeizuführen. Das Touchpanel ist über GPIO10/TP_SI, GPIO9/TP_SO, GPIO11/TP_SCLK und GPIO7/TP_CS verbunden, zusätzlich meldet sich das Touchpanel über GPIO25/TP_IRQ noch beim Raspberry Pi, wenn eine Bedienung über das Panel erfolgt ist.

    Soweit notwendig werden weitere GPIOs später noch detailliert beschrieben.

    Wie bereits an anderer Stelle erwähnt, habe ich eine zweite SPI aufgemacht, ohne dabei auf Systemfunktionen des Raspberrys zugreifen zu müssen. Diese Schnittstelle wird gebildet aus GPIO4/MISOx, GPIO14/SLCKx, GPIO15/MOSIx, GPIO17/CSx0 (chip select des ADC) und GPIO27/CSx1 (chip select des EEproms). Für das EEprom hätten ggf. 2 weitere GPIOs vorgesehen werden können, jedoch habe ich bei dem Hold-Eingang darauf verzichtet.

    Beim EEprom habe ich es schon angesprochen, dessen SPI dient auch zur Kommunikation mit dem AD-Wandler. Nur die CS-Leitung ist eine andere. Ganz wichtig, das habe ich ernsthaft mit Franky07 diskutiert, war die Versorgung des ADCs mit einer "sauberen" Spannung, daher die Anbindung an "Power (2)", auf die ich später noch zu sprechen komme. Es gibt außerdem noch den Jumper J141, dem eigentlich nur in der Phase des Routens eine Aufgabe zufällt. Durch diesen "Trick" soll verhindert werden, dass sich "digitaler Schmutz" auf dem "AGND"-Potential ansammeln kann, weil durch seine separate Führung niemals digitale Ausgleichsströme auf analogen Pfaden fließen können.

    Zwei Kanäle des MCP3204 nutze ich intern

    • zur Abfrage eines Potentiometers
    • zur Abfrage eines RC-Netzwerkes, das einer Impulsstufe nachgeschaltet ist.

    Um eine spätere Verwendbarkeit der zur Zeit nicht genutzten Kanäle nicht auszuschließen, sind diese auf 3-polige Stiftleisten gelegt, die leicht nachgerüstet werden können.

    Obwohl der Raspberry PI auch seine selbst erzeugten 3,3 Volt anbietet, habe ich mich dazu entschlossen, unabhängige Referenzen zu bilden:

    • das Potential "Ref 3.3"
    • die beiden Powerlinien "Power (2)" und "Power (3)"

    "Ref 3.3" dient ausschließlich dazu den Pin "VRef" des MCP3204-Bausteins zu versorgen. Der Sinn dieser Übung liegt darin, den Pin dieses Bausteins von anderen Einflüssen frei zu halten. Alles was sonst noch mit dem ADC zu tun hat, wird über "Power (2)" gespeist. Ein 1-facher OPV hätte es auch getan, doch die Gehäuseform des 2-fachen OPVs gleicher Grundtype ist bei einem Pitch von 1,27 mm deutlich besser zu handhaben als 0,76 mm, wenn man auf manuelles Löten angewiesen ist. Und so musste ich nicht lange suchen, um einen Verwendungszweck für "Power (3)" zu finden.

    Der Bildschirm mit Touchpanel wird nach gleichem Muster versorgt, wie auch der Raspberry Pi. Bei der Kombination aus D131 und F131 handelt es sich um eine TVS-Surpressordiode und eine selbst rückstellende Sicherung PPTC. Bei der 26-poligen Buchsenleiste sind effektiv nur die näher bezeichneten Pins belegt. Trifft die 26-polige Buchsenleiste auf die 40-polige Stiftleiste des Raspberry Pi, so wird klar, dass keine Chance besteht, auf einem einfachem Wege die freien GPIOs des Raspberry Pi zu nutzen. Das Touchpanel ist auf die +3,3 Volt aus dem Raspberry Pi kommend angewiesen, um das SPI (TP_SO) und den Interruptrequest (TP_IRQ) zu bedienen, die anderen Signale (TP_SI, TP_SCK, TP_CS) sind aus der Sicht des Raspberry Pi aktiv.

    Rein vorsorglich habe ich die meisten der noch frei verfügbaren GPIOs auf eine 2-reihige Pinleiste (X104) verschaltet. Jedem so verfügbarem GPIO liegt ein GND-Pin gegenüber. Sollte sich jemals ein Bedarf ergeben, so sollte es keine Schwierigkeiten bereiten, eine Anwendung auch wirklich ohne mechanische Schwierigkeiten durchzuführen.

    Ein bisschen Strom muss schließlich auch über ordentlichem Wege an das System gelangen können. Dazu habe ich eine 10-polige verriegelbare Steckvorrichtung vorgesehen. Jeder Kontakt ist gut für 5 A, das wirkt zwar etwas übertrieben. Aber angesichts der Tatsache, dass der Raspberry Pi schon gute 2,5 A für sich alleine beansprucht, wobei sich Waveshare dagegen über jegliche Verbrauchsangabe vornehm ausschweigt, die Erfahrung aber gezeigt hat, dass auch Waveshare gut versorgt sein möchte, so habe ich je 2 Kontakte für die Energiezuführung vorgesehen. Über 3 weitere Kontakte wird ein Potentiometer angeschlossen, 1 Kontakt übermittelt die Impulse zur Erfassung der Trittdrehzahl und last but not least braucht es noch 2 weitere Kontakte um das Motörchen der Seilwinde betreiben zu können. Mit dem Messpunkt ST101 biete ich einen stabilen Zugriff auf das Massepotential an.

    Ursprünglich hatte ich vorgesehen, die Impulse zur Erfassung der Trittdrehzahl direkt auszuwerten. Doch mein mangelhaftes Vertrauen in Python hat mich davon abgehalten. Spätere Versuche mit dem Raspberry Pi unter Python eine stabile Frequenz zu erzeugen haben mein Misstrauen bestätigt. So habe ich die Idee entwickelt, mit den Impulsen, die an Tretkurbel des Ergometers erzeugt werden, seinerseits Impulse mit konstanter Dauer zu erzeugen, die am Ausgang des TCL555 über den Widerstand R153 die beiden Kondensatoren C155 und C156 aufladen. Über das nachgeschaltete Filternetzwerk gelangt der Ladezustand dieser Kondensatoren an den Eingang des AD-Wandlers, der ohnehin zur Erfassung eines Potentiometers vorgesehen war. Die gesamte Schaltung hatte ich zuvor in Spice simuliert. Bei konstanter Trittdrehzahl von 60 Umdrehungen in der Minute, muss der Impulseingang ein Signal mit einer Frequenz von 24 Hz liefern. Zwecks Abgleich gibt es die 3-polige Stiftleiste X151 mit der ich die Signalquelle umschalten kann. Auf "In1" liegt dann ein Frequenzsignal, das vom Raspberry selbst erzeugt wird. Die Frequenzkonstanz dieses Signals war allerdings sehr ernüchternd. Aus dieser Erfahrung ist im letzten Augenblick der Anschlusspunkt X152 entstanden.

    Auf lange Sicht werde ich überlegen, die Anwendung von Python zu verwerfen. Sollte sich das bis jetzt in Python entworfene System nicht bewähren, wird ein Wechsel auf C/C++ die unausweichliche Folge sein. Aber bis dahin werden noch einige Tage vergehen. Ich hoffe aber, dass ich mit diesem 555er-Trick, das inkonsistente Verhalten von Python überwunden habe.

    Die Sache mit der Trittdrehzahl ist von immens wichtiger Bedeutung, was den gesamten Aufwand ein wenig erklärt. Beim Ausdauertraining geht man davon aus, dass eine bestimmte Leistung über einen bestimmten Zeitraum erbracht werden soll. Ändert sich die Trittdrehzahl, ändert sich auch die erbrachte Leistung, wenn nicht der Gegenwiderstand über die tatsächliche Drehzahl nachgeführt wird, so dass die Leistung, die man als Anwender auf dem Ergometer erbringen muss konstant bleibt. Dieser Regelkreis, der sich dahinter verbirgt, erfordert eine einwandfreie Erfassung der durchschnittlichen Trittdrehzahl. Die Anforderungen sind dementsprechend hoch.

    Mein Motörchen für die Seilwinde benötigt 12 Volt. Ein extra Netzteil wollte ich dafür nicht spendieren, da war es sofort klar, dass ich einen Step-Up-Wandler spendieren würde. Die Schaltung habe ich verschiedene Male schon erprobt und liefert maximal 1000 mA. Das Motörchen hat eine Nennleistung von 4 Watt, das macht rein rechnerisch 333,33 mA. Wenn ich die physischen Gegebenheiten berücksichtige, werde ich von der zur Verfügung stehenden Nennleistung nur einen Bruchteil, also weniger als 1% in Anspruch nehmen. Die Schaltung ist also reichlich überdimensioniert. Versorgt wird dieser Zweig auch wieder über die bekannte Kombination aus D201 und F201.

    Bei diesem Teil der Schaltung habe ich einen meiner Grundsätze über Bord geworfen, keine Bauteile zu verwenden, die schon abgekündigt sind. Der Baustein ZXBM52 hat mir so gut gefallen, dass ich von meinen Grundsätzen abgerückt bin. Franky07 hat mich auf den Pfad der Tugend zurückgebracht und davon abgeraten, den Raspberry Pi direkt den Tücken eines Gate-Durchschlages auszusetzen. Aus diesem Grunde steuert der Raspberry Pi den Baustein über Optokoppler an.

    Der Eingang "Vm" wird von der Spannung gespeist, die mittels des Step-Up-Wandlers aus dem vorhergehenden Abschnitts erzeugt wird. Am Eingang "VRef" liegt zwar eine "edle" Spannung an, sie wird aber dort nur aus der Sicht des Pegels gesehen, die Qualität ist nicht entscheidend, weil der Baustein in einem Modus betrieben wird, in dem das PWM-Signal bereits an die Eingänge "FWD" bzw. "REV" geführt wird.

    So bleibt zum Schluss noch die Geschichte über den Empfänger für die Werte des Plusschlages. Ursprünglich hatte ich vorgesehen, den Pulsschlag mittels Bluetooth ins System einzuspeisen. Es hätte verschiedene Möglichkeiten unter Python geben sollen, das Vorhaben auch zu realisieren. Gescheitert ist dies letztendlich an vielen profanen Kleinigkeiten - Inkompatibilitäten der verschiedensten Python-Versionen oder Bluetooth-Libs, die veraltet waren. Ich habe dann den Entschluss gefasst, die 5,3 kHz Technik anzuwenden, dazu gab's schließlich auch einen Schaltungsentwurf, den ich realisiert hatte. Alleine mir fehlte zum Schluss die Geduld den Resonanzkreis zum Laufen zu bringen. Beim China-Ali hatte ich seinerzeit einen RF-Empfänger bestellt, der jetzt zum Tragen kam. Und ich somit die Entwicklung abschließen konnte. UPS hat mir gerade mitgeteilt, dass mich die neue Platine Montag erreichen wird.

    Wenn ich die zurückliegende Zeit überdenke, waren es vielfach Klippen, die mir als Nichtkenner der Raspberry-Szene und dabei besonders als Nichtkenner der Python-Scene einige Mühen bereitet haben. Einige Anlässe dieser Art waren unnötig, da sie sich ausschließlich auf oberflächliche Formalitäten bezogen haben. Ich habe Fortschritte in Python gemacht, ob ich aber bei Python bleibe, erscheint mir eher zweifelhaft. Der angebliche Vorteil einer höheren Abstraktionsebene, kann der Interpreter nicht mehr wett machen. Bei einer deterministischen Vorgehensweise schmilzt dieser Vorteil wie Eis in der prallen Sonne.

    Nun gut, zunächst werde ich das gesamte Projekt noch in Python vorstellen.

    Edited once, last by Batucada2 (June 28, 2024 at 11:43 PM).

  • Moin Batucada2,

    ich finde es toll, wenn Leute ihre Projekte vorstellen.
    Was ich aber nicht so schön finde ist, seinen Beitrag mit diesen Worten einzuleiten:

    Ein Wort richte ich an eine besondere Klientel. Kritik ist jederzeit herzlich willkommen, wenn sie nicht aus dem Munde eines Oberlehrers kommt, dessen Einfallsreichtum darin besteht, dass seine Ideen die besten sind.

    Dann stört "mich" der violette Hintergrund deiner Schaltungen. Kannst du das nicht ändern. Es würde die Lesbarkeit sehr erhöhen.

    73 de Bernd

    Ich habe KEINE Ahnung und davon GANZ VIEL!!
    Bei einer Lösung freue ich mich über ein ":thumbup:"
    Vielleicht trifft man sich in der RPi-Plauderecke.
    Linux ist zum Lernen da, je mehr man lernt um so besser versteht man es.

  • Es sind die ewigen Kommentatoren am Spielfeldrand, deren einzige Freude im Leben wohl darin besteht, anderen in die Suppe zu spucken - dass könnte ich annehmen, nach dem ich den Verlauf des Fadens betrachtet habe.

    Eine kleine Korrektur meines Alters: zur Zeit noch 77 Jahre. Ich will damit nicht kokettieren, aber die Korrektur erscheint mir angebracht.

    Ich habe am Ende meines ersten Beitrages deutlich gemacht, dass ich die Projekt-Vorstellung gerne fortsetzen wollte. Ob das unter diesen Umständen noch gelten wird?

    Zur Sache. DipTrace kennt bei seinen Editoren unterschiedliche Einstellungen. Ich habe mir den tiefschwarzen Hintergrund des Schema-Editors etwas aufgehellt, um den Kontrast etwas abzumildern. Statt tiefschwarz (rgb 0-0-0) habe ich für mich ein augenfreundliches 56-6-53 eingestellt. Die erste Abbildung des gesamten Schemas lässt alleine aufgrund der Größe kaum besser darstellen. Doch ich habe für diesen Beitrag weder Kosten noch Mühen gescheut, damit ich
    a. einen augenkratzenden Kontrast (Hintergrund tief schwarz) zustande bringe und
    b. eine aus mehreren Screenshots zusammengesetzte Gesamtschaltung mit einer höheren Auflösung präsentieren kann.

    Das Original, was ich jetzt nachgeschoben habe, hat eine Größe von 6000x3200 Pixel bei einer Auflösung von 300 dpi. Leider, leider scheint die Forumssoftware nicht gnädig zu sein (kann ich irgendwo auch verstehen, Serverplatz kostet schließlich Geld) und reduziert diese Gesamtschaltung herunter auf eine Größe von 1920x1924 Pixel bei einer Auflösung 72 dpi. Wer gerne eine bessere Auflösung sich ansehen möchte, kann sie bei mir anfordern. Ansonsten gilt das von Bertthias gesagte :thumbup::thumbup::thumbup:

    Was lernt man daraus: bitte nicht beim Autor dieses Fadens beschweren, gelle 🤣


    Und das hier schiebe ich noch nach, das war bislang mein Erprobungsmuster.

    Wer es besser kann, sollte dann doch bitte ähnliches vorweisen können.

  • Zuallererst - und das ist mir mit Abstand das wichtigste zu dem Topic - ich finde die Idee super interessant. Normalerweise mag ich superlative Ausdrucksweisen nicht, aber das Thema hat mich voll erwischt. Hab selbst 2 Ergometer herum stehen, die ich mit Raspi's pimpen möchte. Also werde ich den technischen Aspekt des Projekts (versuchen zu) verfolgen.

    Ob Kritik wirklich so willkommen ist, wie der TO geschrieben hat? Schaun mer mol...
    Ja, auch ich kann mich mit der Farbgebung der technischen Zeichnungen schwer anfreunden, aber ich werd sie am Laptop/PC studieren und da ist schnell die Farbgebung "optimiert" - so what...
    Und der Schreibstil? Da gilt: erst mal die eigene Nase und so weiter :D

    Was ich aus Mangel an Geduld am Tablet noch nicht weiß: war das Projekt jetzt erfolgreich oder nicht? Wo fängt der Teil mit dem Ergometer an und hört das Raspi/Monitor/Strom-Setup auf? Ich werde es in Ruhe erkunden.

    Auf jeden ein Projekt, das ich studieren und entschlüsseln will

    ---

    Alles ist relativ - ob in dieser oder deiner Welt :biggrin:

  • Was ich aus Mangel an Geduld am Tablet noch nicht weiß: war das Projekt jetzt erfolgreich oder nicht? Wo fängt der Teil mit dem Ergometer an und hört das Raspi/Monitor/Strom-Setup auf? Ich werde es in Ruhe erkunden.

    Zu aller erst: Das Projekt läuft bei mir schon lange. Dieser Faden war aber als Einstieg in die gesamte Beschreibung es Projektes gedacht.

    Zur Farbgebung: Die habe ich bis auf wenige Ausnahmen nicht erfunden, sie gehört zur standardisierten Einstellung von DipTrace, die ich speziell nur für zwei Potentiale abgewandelt habe.

    Und ganz klar zum besseren Verständnis: DipTrace keine Software, um Elektroschemata im handelsüblichen Stil zu erstellen. Der Editor ist die Vorstufe zur Erstellung eines PCB-Layouts. Nach der Fertigstellung des PCB-Layouts exportiert DipTrace Daten im ODB++ Format, mit denen ein Leiterplattenhersteller die Platine anfertigen kann.

    Was ich aus Mangel an Geduld am Tablet noch nicht weiß: war das Projekt jetzt erfolgreich oder nicht? Wo fängt der Teil mit dem Ergometer an und hört das Raspi/Monitor/Strom-Setup auf? Ich werde es in Ruhe erkunden.

    Die Vorstufe des Projektes war erfolgreich.

    Sobald die neue Platine (versprochene Lieferung am Montag) geht es in die nächste Erprobungsphase.

  • Ich finde es super, dass du dich aufgerapelt hast und so ein Projekt anzugehen.
    Mach weiter so und ich freue mich, wenn ich noch viele solche Beiträge lesen kann. Habe viel Spass dabei.

    Und auf die Gesundheit achten. Wir pokern nicht mehr mit dem Teufel.:)

    Meine Antwort ist 21 und nicht 42. Oder anders gesagt: 0001 0101 statt 0010 1010

  • Und weiter geht's. Zunächst mit dem Standardbild mit dem die Anwendung starten soll. Für den normalen Ablauf habe ich dieses Bild als für meinen persönlichen Bedarf als ausreichend empfunden. Aus einer logischen Betrachtung heraus habe ich den Bildschirm in 4 logische Bereiche unterteilt, die somit auch an physischen Gegebenheiten orientieren. Bei einem Ausdauertraining interessiert letztendlich die Trainingsleistung, deren inkrementelle Vorgabe über die Pfeiltasten erfolgt. Der jeweils eingestellte Wert wird mit der 7-Segment-Funktion dargestellt. Diese Art der Wertvorgaben gilt auch für die übrigen 3 Bereiche. Der Abschnitt der Trainingsleistung ist auch gleichzeitig der simpelste Abschnitt.

    Der nächste Abschnitt behandelt die Trittdrehzahl, die Vorgabe funktioniert nach bewährtem Muster, die Anzeige ist allerdings umschaltbar. Die Umschaltung wird automatisch vollzogen, wenn das Training gestartet wird, dann zeigt die 7-Segment-Anzeige den Istwert der Trittdrehzahl an, ebenfalls wechselt die Überschrift von "Soll-Trittdrehzahl" in "Ist-Trittdrehzahl". Neben der 7-Segment-Anzeige werden noch 3 LEDs simuliert, die je nach Unter- oder Überdrehzahl "aufleuchten", die dritte LED zeigt dann, wenn das Training im "grünen" Bereich verläuft. Wenn das Training aktiviert worden ist, dann sind die Pfeiltasten ausgegraut und deaktiviert.

    Der dritte Bereich behandelt die Pulsfrequenz, die Funktionalität ist prinzipiell die gleiche wie beim vorherigen Abschnitt.

    Der vierte und letzte Abschnitt hat in sich eine weitere Unterteilung, die aber nur für die Bedienung gilt. Mit den Pfeiltasten wird die Trainingsdauer eingestellt. Die beiden weiteren Tasten sind für den Start (Kreissymbol) oder den Abbruch bzw. Unterbrechung des Trainings (Stopsymbol)vorgesehen. Wurde das Training gestartet läuft die Trainingszeit ab und die 7-Segment-Anzeige zählt abwärts. Mit der Stoptaste kann das Training angehalten werden, die Zeit bleibt stehen, sie zählt erst wieder weiter abwärts, wenn die Starttaste betätigt wurde. Wurde das Training einmal gestartet, kann die Vorgabezeit nicht wieder verändert werden, bis entweder diese Zeit insgesamt abgelaufen ist oder das Training abgebrochen wurde. Den Abbruch des Trainings wird erzwungen, wenn die Stoptaste für mehr als 3 Sekunden betätigt wird.

    Alle Traningseinstellungen werden dauerhaft in einem EEprom gespeichert.

    Links oben in der Ecke gibt es noch 2 kleine Knöpfe, um die ich mich bsiher noch nicht sonderlich gekümmert habe. Das waren bisherige Arbeitsknöpfe, für die ich mir eventuell noch etwas besonders ausdenken muss. Mit dem schwarzen Kopf wechselt man in einen Dialog-Bildschirm - mit dem grünen beendet man den Python-Script.

    Den Dialogbildschirm werde ich bei der Beschreibung der entsprechenden Programmteile spezifisch erläutern. Soviel vorab:

    • Skalierung [W], mit diesem Wert soll die angestrebte Trainingsleistung bewertet werden. Hinter dieser Bewertung verbirgt sich das Prinzip, Regelkreisberechnungen grundsätzlich mit Einheitswerten durchzuführen.
    • Proportionalband kP [%], der Proportionalbeiwert des PI-Regelkreises
    • Nachstellzeit TN [s], der Integralbeiwert des PI-Regelkreises, auch Nachstellzeit genannt
    • Totband ysh [%], eine "tote" Zone, in der keine Verstellung der Seilwinde stattfinden soll
    • Moment-Bewertung fk [-], für die Trittdrehzahl gibt es eine Erfassung des echten Istwertes, für das Drehmoment gibt es das dagegen nicht. Da sich die Leistung aus einem Drehmoment und der Kreisfrequenz (2πf) errechnet, muss der Wert des Drehmoments aus einem Rückführwert ermittelt werden. Als Rückführwert dient die Stellung eines Potentiometers, das mit der Seilwinde gekoppelt ist. Die Seilwinde bewegt eine Schiene mit Dauermagneten, die in einem Schwungrad Wirbelströme erzeugen, welche wiederum das Schwungrad abbremsen. Sofern sich die Trittdrehzahl verändert, muss die Schiene mit den Dauermagneten zum Schwungrad anders zugestellt werden, damit das Produkt aus Drehmoment und Kreisfrequenz konstant bleibt.
    • Rückführung, Anfang und Ende[-], beim Einrichten der Seilwinde werden diese Werte in der Funktion als Endlagenauswertung verwendet.
    • PWM Frequenz [Hz], eine Auswahl der PWM-Frequenz, mit der Motor der Seilwinde betrieben werden soll
    • max. PWM-Einschaltdauer [%], Einstellung des dutycycles bei maximaler Leistung im automatischen Betrieb
    • min. PWM-Einschaltdauer [%], dito bei minimaler Leistung
    • manuelle PWM-Einschaltdauer [%], da die Seilwinde manuell eingerichtet und justiert werden muss, kann man hier den dutycycle für diese Betriebsart vorgeben
    • manuelle Verstellung mit up/down, mit den Pfeiltasten wird dann der Motor des Seilzuges solange bewegt, wie einer der Taster bedient wird
    • x1/y1-Abgleich bei 40% bzw. x2/y2-Abgleich bei 80%, mit den Funktionen soll der Abgleich der Trittdrehzahlerfassung betrieben werden. Das ist mein Sorgenkind, weil mit dem Raspberry in Python keine konstante Frequenz erzeugt werden kann.

    Es gibt eine Übernahmetaste, mit der der Wert dauerhaft in das EEprom gespeichert werden soll.

    Außerdem ist noch ein Taste für Bluetooth vorhanden, die aber bisher nicht zum Einsatz gekommen ist, weil sich Kompatibilität der BT-Apis als Desaster entwickelt hat. Vielleicht noch einmal in einem zweiten Anlauf.

  • Batucada2 Es ist wirklich faszinierend, wieviel Arbeit und Liebe zum technischen Detail Du da in Dein erstmal einfach scheinendes Projekt "Ergometer" steckst. Und es dann noch so ausführlich dokumentierst! :thumbup:

    PS: Aber es macht mir nur Spaß, die sachlichen Beiträge zu lesen. Sobald zu viel Provokation oder Schimpfen drin ist, ist für mich auch ein inhaltlich interessanter Thread erledigt. Damit meine ich ebenso die zwei/drei anderen Autoren, die dazu neigen. ;)

    Edited 2 times, last by simonz (June 29, 2024 at 9:13 PM).

  • Keine Ahnung, was dunkel violette Hintergründe mit anderer Leut's Platinenherstellung zu tun haben, ich werde nicht mehr auf dieses Thema eingehen. Und in Zukunft werde ich mich nur noch mit Sachfragen zu technologischen Inhalten des Projektes befassen.

    Am Anfang meiner Bemühungen eine grafische Benutzeroberfläche für mein Projekt zustande zu bringen, stand die Suche nach einer geeigneten Bibliothek. Da ich einen Teil der GUI-Entwicklung zuvor auf meinem heimischen Mac betrieben habe, sind mir ein paar besondere Umstände nicht näher aufgefallen, so dass diese mir später noch unangenehm auf die Füße gefallen sind. Da QT entsprechende Schnittstellen für Python bereit hält, hatte sich bei mir der Eindruck verfestigt, ich würde schnell zu einem GUI kommen. Das hat auch funktioniert, solange ich meinen Sandkasten nicht verlassen hatte. Solange ich das für die bestimmte Bildschirmgröße des Zielgerätes entwickelte GUI in einem Fensterausschnitt betrieben habe, der auf einem Bildschirm dargestellt wurde, der wesentlich größer war als das Zielgerät, war die Welt noch in Ordnung. Die Ernüchterung kam, als ich mit dem GUI auf den Raspi inkl. dem LCD-Monitor von Waveshare umgezogen bin.

    Es hat ein paar Tage gedauert, bis ich die wahren Zusammenhänge erkannt habe (zumindest glaube ich diese erkannt zu haben), um aufgrund dieser Erkenntnisse, einer unguten Entwicklung entgegen steuern konnte. So wie es sich mir darstellt, hat QT einen Mechanismus eingebaut, der bestimmten Widgets automatisch Rollbalken hinzufügt, wenn die physische Ausdehnung dieser Widgets einen bestimmten Prozentsatz der physischen Ausdehnung des Bildschirms überschreiten. Meine Projektierung der statischen Bildschirmmaske bezog sich auf die physische Ausdehnung des Wavesharebildschirm mit 1024 x 600 Pixel. Wenn ich die statische Bildschirmmaske mit dieser Größe auf dem Mac-Bildschirm mit 4k dargestellt habe, trat dieser Effekt natürlich nicht in Erscheinung. Ok, das Problem ist gelöst, wenn auch mir die Lösung in der vorliegenden Form widerstrebt, weil es letztendlich aus meiner Sicht ein unnötiges Workaround darstellt.

    Die Suche nach einer Alternative zu QT (unter Python) verlief bis zu dem damaligen Zeitpunkt ohne Ergebnis, eine neuerliche Suche mit einer intelligenteren Suchmaschine förderte jedoch zumindest dieses interessante Ergebnis zu Tage: wxPython. Ich habe eine beispielhafte Anwendung unter wxPython gesehen, die diese verflixte Abhängigkeit zu QT auflösen könnte, was erstrebenswert ist. Für den Augenblick bleibe ich jedoch bei den Lösungen und Workarounds, weil ich mein Projekt abschließen möchte, um auch endlich in den Genuß meiner Entwicklung zu kommen.

    Ich werde meine Projektbeschreibung mit dem Modul CommonSubs.py fortsetzen und es zunächst einmal in kompletter Länge präsentieren, bevor ich einzelne Teile näher vorstelle.

    CommonSubs.py

    class SettingsWatch():

    Eine Änderung von Sollwerten oder anderen Parametern zur grundsätzlichen Einstellung des Ergometers sollte schließlich auch dauerhaft gespeichert werden. Damit dies nicht vergessen wird, habe ich einen Automatismus eingeführt, der zeitverzögert einen Speichervorgang auslöst. Der Einbau einer Instanz dieser Klasse erfolgt in "main" mit:

    SWI = SettingsWatch(5)

    bei gleichzeitiger Verknüpfung mit den 4 wichtigsten Modulen:

    Code
        POWP.Watch = SWI
        SPPA.Watch = SWI
        HRCP.Watch = SWI
        TIPA.Watch = SWI

    POWP - Powerpanel, SPPA - Speedpanel, HRCP - HeartRateControlPanel und TIPA - Timerpanel. Wir erinnern uns an die 4 Bereiche, in die ich den Standardbildschirm eingeteilt habe: Trainingsleistung, Trittdrehzahl, Pulsfrequenz und Trainingsdauer. POWP.Watch enthält somit als Element der Instanz POWP einen Verweis auf die Instanz SWI. Wenn also irgendwo ein Sollwert oder ein Parameter verändert wird, läuft immer die gleiche Uhr ab, die in "main" zyklisch kontrolliert wird:

    Code
           if SWI.LookWatch():
               readyToWrite = True               # die Schreib-Anforderung speichern


    class BackGroundPlane(QGraphicsRectItem):

    An dieser Stelle wird klar, dass sich mein GUI auf die Gegebenheiten von QT einstellen musste. Ob das alles im Sinne von Puristen mit ordentlichen Regeln abläuft, ist eine andere Frage, zumindest ist es nicht schädlich. Einen Teil dieser Formulierungen hat mir der Copilot vorgeschlagen, ein von Microsoft installierter KI-Assistent in VS Code, der IDE, die ich für dieses Projekt verwendet habe.
    Die "class BackGroundPlane" ist eine Single-Purpose Klasse, die auf der Basis des QT-Widgets "QGraphicsRectItem" funktioniert. Der erste im Aufruf übergebene Parameter "qsc" sammelt für das im Hintergrund agierende QT alle Areale, die mit diesem Parameter verbunden werden sollen. Siehe hierzu folgender Ausschnitt, der fast selbsterklärend ist:

    Code
           # öffne die Grafik Oberfläche
           self.scene = QGraphicsScene(self)
           # Hintergrund festlegen
           BackGroundPlane(self.scene, 0, 0, _wideBackPlaneWidth_, _upperPanelHeight_, None)
           # Trainingsdauer
           self.HeadlineLabel = TextLineLabel("Trainingsdauer", self.scene, _wideBackPlaneWidth_ / 2,
                                              _HeadlineYPos_, _LabelWidth_, _LabelHeight_, 'M', self.TIPA.TextColor)

    Jedes grafische Element, was einen bestimmten Teil des Bildschirmes ausfüllt, wird so in dem Element "self.scene" verankert, weil der daruf zielende Verweis dem Aufruf als Übergabeparameter mitgegeben wird. In dem Aufruf von BackGroundPlane wird zum Schluss die Anweisung "qsc.addItem(self)" ausgeführt, was diese Verkettung bewirkt.

    class TextLineLabel(QLabel):

    Die Klasse "class TextLineLabel" ist eine Single-Purpose Klasse, die auf der Basis des QT-Widgets "QLabel" funktioniert. Der erste im Aufruf übergebene Parameter "qsc" sammelt für das im Hintergrund agierende QT alle Areale, die mit diesem Parameter verbunden werden sollen. Ein kleine Bonmot am Rande, die Anweisungen:

    Code
           if system() == 'Darwin':
               fh = 24
           else:
               fh = 18
           myFont.setPointSize(fh)

    ließen sich in C so zusammenfassen (abgesehen von verschiedenen Einschränkungen, die in C so nicht erlaubt sind) und so die Anwendung eines ternären Operators grundsätzlich verdeutlichen:

    Code
           myFont.setPointSize(system() == 'Darwin' ? 24:18)

    und dagegen könnte ich die Python-Schreibweise

    Code
           myFont.setPointSize(24 if system() == 'Darwin' else 18)

    verwenden, aber die Syntax gefällt mir nicht.

    class SevenSegmentDisplay(QLCDNumber):

    Die Klasse "class SevenSegmentDisplay" ist wie bisher eine Single-Purpose Klasse, die auf dem QT-Widget "QLCDNumber" beruht. Dieses QT-Widget stellt einen der Gründe dar, warum ich bei QT geblieben bin.

    Ich habe mit den bisherigen Single-Purpose Klassen mir eine Menge Schreibarbeit erspart, in dem ich den Gebrauch der QT-Widgets für mich so typisiert habe, so dass nach ausreichender Erprobung mir die Gestaltung des GUIs leichter von der Hand ging. Anders als sonst üblich habe ich die Positionierung nicht an der linken oberen Ecke eines Objektes ausgerichtet sondern an der Mitte, d.h. die Übergabeparameter eines Aufrufes geben immer den Mittenbezug an.

    class ProjPushBtn(QWidget):

    Anders als bisher ist die Klasse "class ProjPushBtn" keine typische Single-Purpose Klasse, auch wenn sie nur der Handhabung eines einzigen Objektes dient. Das QT-Widget "QWidget" ist dabei aber auch nicht typisch für die realisierte Funktion, sondern dient nur dazu, das Objekt irgendwo im QT-Nirwana zu verankern. Einige ererbte Methoden habe ich übernommen und deren Verwendung durch Kommentare hervorgehoben. Die Klasse "class ProjPushBtn" ist ein Allrounder, den ich auf meine Bedürfnisse abgerichtet habe. Aus diesem Allrounder habe ich anschließend den weiteren Allrounder "class RectPushBtn(QWidget)" entwickelt, sicher hätte ich das noch weiter treiben können, aber irgendwo hört die Liebe auf.
    Die Klasse "class ProjPushBtn" habe ich hauptsächlich für die Pfeiltasten eingesetzt, die Klasse "class RectPushBtn" ist später entstanden, weil ich gemerkt habe, dass die Bedienbarkeit der Radiobuttons im Dialogbildschirm zu wünschen übrig ließ. Die Klasse "class RectPushBtn" habe ich hinter die Textfelder des Dialogbildschirms hinterlegt, um die Bedienung des Dialogbildschirms zu verbessern.

    class ProjRadioBtn(QWidget):

    Die Klasse "class ProjRadioBtn" ist natürlich kein Allrounder aber auch keine typische Single-Purpose Klasse. Ich habe in der Klasse ein Feature verwirklicht, so dass bei der Realisierung einer Reaktion es nicht notwendig ist, für jede Taste ein eigenen Callback zu projektieren. Der Mouse-Event liefert dezidiert eine ID zurück, mit der die auslösende Taste identifiziert werden kann.

    class DisplayLED():

    Anders als bisher basiert diese Klasse auf keinem QT-Widget. Und dennoch kann auf die Beteiligung von QT nicht verzichtet werden. Wenn nicht noch die Methode "def update" wäre, dann wäre es wieder eine klassische Single-Purpose Klasse.

  • Der Begriff „Single-Purpose-Klasse“ scheint ”Klassen” zu beschreiben die eigentlich einfach eine Funktion hätten sein können/sollen.

    “Since, in the long run, every planetary society will be endangered by impacts from space, every surviving civilization is obliged to become spacefaring — not because of exploratory or romantic zeal, but for the most practical reason imaginable: staying alive.” — Carl Sagan, Pale Blue Dot

Participate now!

Don’t have an account yet? Register yourself now and be a part of our community!