Wir pumpen das Laufband

Vor kurzem habe ich mich für einen sehr seltsamen Kauf entschieden. Ja, ich habe ein Laufband gekauft.



Und bald wurde mir klar, dass es nicht genug detaillierte Statistiken gab, wie beim Fahrradfahren. Im Falle eines Fahrrads schreibt die Anwendung auf dem Telefon meine Geschwindigkeit, Herzfrequenz, Trittfrequenz und meinen Auftrieb. Es ist sehr neugierig, all diese Parameter während des Trainings zu steuern, um Diagramme anzeigen und von Zeit zu Zeit Ihre Ergebnisse vergleichen zu können.

Deshalb habe ich mich für etwas Ähnliches mit dem Laufband entschieden: Schließen Sie es an ein Smartphone oder Tablet an, um Statistiken zu sammeln und anzuzeigen.

Wie üblich ist meine Geschichte in Form eines traditionellen Textartikels und per Video. Wie du mehr magst.

Video



Artikel


Design


Selbst in dem Moment, als ich das Laufband abholte, bemerkte ich, dass die Fernbedienung und der Laufriemen selbst nur vier Drähte verbanden. Anscheinend werden einige von ihnen zur Stromversorgung der Konsole verwendet, da die Leinwand selbst an das 220-Volt-Netzwerk angeschlossen ist und die verbleibenden Kabel benötigt werden, um Steuersignale in die entgegengesetzte Richtung zu übertragen - von der Konsole zur Leinwand steuern sie die Geschwindigkeit und den Winkel der Spur.

Ich habe das Oszilloskop parallel zu diesen Drähten angeschlossen und verschiedene Kombinationen ausprobiert.

Als Ergebnis stellte ich fest, dass alles ungefähr so ​​war, wie ich es erwartet hatte. Einer der Drähte ist geerdet und ein anderer hat eine Spannung von 12 Volt. Der Rest überträgt digitale Daten.

In einem von ihnen ändert sich das Signal beim Umschalten von Geschwindigkeit und Winkel. Genau das brauche ich! Die Signalamplitude beträgt ca. vier Volt. Das Protokoll sieht jedoch nicht wie ein Standard aus, und das Signal ist sehr verrauscht. Wenn die Spur eingeschaltet ist, müssen Sie es irgendwie filtern.



Der letzte Draht besteht nur aus Impulsen mit einer konstanten Frequenz. Anscheinend soll die Konsole die Verbindung zum Laufband sehen. Wenn Sie dieses Kabel abziehen, gibt die Fernbedienung sofort einen Fehler aus.

Anzeigen vom Impulssensor auf diesen Drähten werden eindeutig nicht übertragen, sind jedoch nicht erforderlich. Es ist besser, einen separaten Brustsensor anzuschließen, den ich schon lange beim Fahrradfahren benutze. Außerdem stellte sich heraus, dass der Herzfrequenzsensor am Laufband selbst viel liegt und die Messwerte unterschätzt.

Gerätebaugruppe


Die nächste Aufgabe besteht also darin, eine Karte zusammenzubauen, die parallel zu diesen Drähten angeschlossen ist, die aktuelle Geschwindigkeit und den aktuellen Winkel liest und sie irgendwie drahtlos auf ein Tablet oder Smartphone überträgt.

Ich habe mich erneut für den Onion Omega2 Single-Board-Computer entschieden. Er muss einen exzellenten Job machen. Es ist nur erforderlich, die Versorgungsspannung auf 3,3 Volt zu senken und die Daten vor Störungen zu filtern.

Um die Spannung zu reduzieren, verwende ich jetzt diese vorgefertigten Karten mit einem DC-DC-Wandler. Sie kosten einen Cent, halten bis zu ein paar Ampere stand und die Ausgangsspannung wird mit einem Dreh eingestellt.



Gleichzeitig hat diese Platine Schlussfolgerungen, direkt auf eine andere Platine zu löten, was sehr praktisch ist. Die Hauptsache ist, die Spannungsverdrehung nach dem Einbau in den Stromkreis nicht zu verdrehen.

Um das Rauschen auf der Datenleitung zu filtern, habe ich ein normales RC-Filter hergestellt: einen 2,2-Kilo-Ohm-Widerstand und einen 22-Picofarad-Kondensator. Dies sollte das Hochfrequenzrauschen herausfiltern und ein Niederfrequenzsignal hinterlassen.

Es stellte sich heraus, dass es ein ziemlich kleiner Schal war.



Ich habe es an die Laufbandkabel angeschlossen, um zu sehen, wie gut das Signal gefiltert wird, wenn es eingeschaltet wird, und anscheinend ist die Wellenform fast perfekt geworden.



Kernelmodul


Es ist jedoch nicht so einfach, die Leistung von Eisen zu überprüfen. Wie wir
bereits am Oszilloskop gesehen haben, gehen die Signale sehr schnell und wir verwenden keinen Mikrocontroller, sondern einen Omega2-Computer mit einer Karte und Linux an Bord. Unter Linux können wir Signale aus dem Benutzerbereich nicht so schnell verarbeiten. Aber von Grund auf können wir! Daher ist es Zeit, ein Linux-Kernelmodul zu schreiben!

Dazu müssen Sie die Linux-Kernelquellen herunterladen. In unserem Fall handelt es sich um eine OpenWRT-Assembly für Omega2, und ein Verzeichnis mit dem Quellcode unseres Moduls erstellen.

Das Schreiben von Modulcode ähnelt dem Programmieren eines Mikrocontrollers. Wir schreiben auch in C, auch alles ist Low-Level, wir arbeiten auch mit Interrupts und wenden uns auch den GPIO-Schlussfolgerungen zu. Nur hier, zusätzlich zu all dem oben Genannten, interagieren wir noch mit dem Benutzerraum über eine Pseudodatei. Somit wird unser Kernelmodul zu einer Art Adapter zwischen der Hardware und gewöhnlichen Anwendungen. Eigentlich nennt man das den Treiber.

Anfangs wusste ich nicht, wie ich die Signale dekodieren sollte, also habe ich einfach ihre Dauer abgeleitet.



Es wurde schnell klar, dass die Signale mit hoher Pegeldauer codiert wurden. Es ist entweder 600 Mikrosekunden lang oder 1200 Mikrosekunden lang. Der niedrige Pegel ist bis auf die Anfangssequenz immer 600 Mikrosekunden lang.

Insgesamt 17 solcher Tropfen fallen auf und ab. Anscheinend sind dies 16 Datenbits plus die Anfangssequenz. Ich habe ihre Dekodierung vorgenommen, wobei ich davon ausgegangen bin, dass lange, hohe Unterschiede eine logische Null sind und kurze eine logische Einheit, und ich habe verstanden, was passiert ist. Ich habe sofort die Daten gesehen, die ich brauchte!



16 Bit sind bekanntlich zwei Bytes. Das erste Byte gibt die Art der übertragenen Daten an: den Neigungs- oder Geschwindigkeitswinkel und das zweite Byte die Daten selbst. Der Fahrer ist sehr einfach.

Der einzige Treiberparameter ist die Portnummer.

/* Module parameters */
static u8 receive_pin = 11;
module_param(receive_pin, byte, S_IRUGO);
MODULE_PARM_DESC(receive_pin,"Treadmill receiver pin number (default 11)");

Konfigurieren Sie es beim Initialisieren für die Eingabe und stellen Sie den Interrupt ein, der jedes Mal ausgelöst wird, wenn sich der Pegel ändert.

/* Allocate and init the timer */
data_recv_timer = kzalloc(sizeof(struct hrtimer), GFP_KERNEL);
if (!data_recv_timer) {
    pr_err("treadmill: can't allocate memory for timer\n");
    treadmill_free();
    return -1;
}
hrtimer_init(data_recv_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
data_recv_timer->function = recv_timer_callback;

In dieser Unterbrechung schauen wir zuerst auf die aktuelle Zeit. Als nächstes verwenden wir diesen Wert, um zu berechnen, wie viel Zeit seit der letzten Interrupt-Operation vergangen ist, und um ihn in ein Array einzufügen. Natürlich erinnern wir uns beim nächsten Mal an die aktuelle Zeit für die Berechnung. Außerdem müssen Sie den speziellen Timer neu starten.

/* IRQ fired every rising/falling edge of receiver pin */
static irq_handler_t treadmill_irq_handler(unsigned int irq,
    void *dev_id, struct pt_regs *regs)
{
    u64 now = ktime_to_us(ktime_get_boottime());
    u8 value = gpio_get_value(receive_pin);
    u64 time_passed;
    reset_recv_timer();

    if ((timings_pos & 1) == value)
    {
        time_passed = now - last_time;
        if (timings_pos < TIMINGS_BUFFER_SIZE)
        {
            timings[timings_pos] = time_passed;
            timings_pos++;
        }
        last_time = now;
    }

    /* Announce that the IRQ has been handled correctly */
    return (irq_handler_t) IRQ_HANDLED;
}

Der Trick besteht darin, dass, wenn der Timer noch funktioniert, lange Zeit keine Pegelabfälle am Pin aufgetreten sind und es dementsprechend an der Zeit ist, die gesammelten Informationen zu verarbeiten. In der Funktion, die der Timer aufruft, wird überprüft, ob genau 34 Tropfen vorhanden sind. Danach sehen wir uns an, wie lang jedes Intervall war. Wenn es 600 Mikrosekunden gibt, dann 1200 Mikrosekunden, dann nehmen wir 900 im Ausland. Wenn das Intervall kürzer ist, schreiben wir eins in das Ergebnis und verschieben es um ein Bit. Nach der Verarbeitung jedes Intervalls senden wir das Ergebnis an geöffnete Pseudodateien und übertragen so Daten in den Benutzerbereich.

/* Timer */
static enum hrtimer_restart recv_timer_callback(struct hrtimer *timer)
{
    int i, p;
    u16 data;

    if (timings_pos != 34) {
        pr_debug("treadmill: invalid edges count: %d", timings_pos);
        timings_pos = 0; 
        return HRTIMER_NORESTART;
    }

    data = 0;   
    for (i = 2; i < timings_pos; i += 2)
    {
        data >>= 1;
        if (timings[i] < 900) // 600us = 1, 1200 us = 0
            data |= 0x8000;
    }
    
    for (p = 0; p < 2; p++) {
        for (i = 0; i < treadmill_number_opens; i++) {
            if (!(opened_files[i]->f_mode & FMODE_READ)) continue;
            ((struct cfile_t*)opened_files[i]->private_data)->receiver_buffer[
                ((struct cfile_t*)opened_files[i]->private_data)->receiver_write_pos++
                % RECEIVER_BUFFER_SIZE] = (data >> (8 * p)) & 0xFF;
        }
    };
    wake_up_interruptible(&wq_data);

    timings_pos = 0; 
   
    return HRTIMER_NORESTART;
}

Python-Server und Geschwindigkeitserkennung


Dann muss noch ein Python-Skript geschrieben werden, das sie aus der Pseudodatei liest und als JSON-Zeichenfolgen über das Netzwerk sendet. Es scheint, dass alles ziemlich einfach ist. Wenn jedoch mit dem Neigungswinkel alles einfach ist und der Wert im zweiten Byte genau dem Neigungswinkel in Prozent entspricht, hat sich mit der Geschwindigkeit alles als viel komplizierter herausgestellt.

Ein Wert von 9 entspricht einem Kilometer pro Stunde und ein Wert von 160 entspricht 18 Kilometern pro Stunde. Das heißt, die Abhängigkeit der Daten von der tatsächlichen Geschwindigkeit ist überhaupt nicht offensichtlich. Ich schrieb alle Werte manuell aus, fuhr sie in Excel, zeichnete sie und erhielt eine sehr ungleichmäßige Kurve.



Und es gibt Geschwindigkeiten, bei denen die Messwerte auf der Fernbedienung unterschiedlich sind, aber die Daten und die Geschwindigkeit der Spur selbst bleiben gleich! Zum Beispiel sind 5,2 km / h und 5,3 km / h tatsächlich die gleichen Geschwindigkeiten. Überall betrügen. Ich frage mich, welche Geschwindigkeit es wirklich gibt. Messen Sie es irgendwie, aber lassen Sie es für später.

Abgesehen von dieser Übertragung von Papageien auf Kilometer pro Stunde erwies sich das Drehbuch als äußerst einfach. Wir lesen die Daten aus der Linux-Pseudodatei, dekodieren sie, akzeptieren Netzwerkverbindungen und übertragen die Daten als JSON-Zeichenfolge an die über das Netzwerk verbundenen Clients.

class TreadmillServer:
    def __init__(self, device = "/dev/treadmill", port = 11010, interface = '0.0.0.0'):
        self._device = device
        self._port = port
        self._interface = interface
        self._working = False
        self._clients = []
        self._server_sock = None
        self.incline = 0
        self.speed = 0

    def start(self):
        self._working = True
        self._server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._server_sock.bind((self._interface, self._port))
        self._server_sock.listen(10)
        print("Listening port", self._port)
        Thread(target=self._port_listener, name="Treadmill port listener", daemon=True).start()
        Thread(target=self._device_listener, name="Treadmill device listener", daemon=True).start()

    def stop(self):
        self._working = False
        if self._server_sock != None:
            try:
                self._server_sock.close()
            except:
                pass
            self._server_sock = None

    def __del__(self):
        self.stop()

    def _port_listener(self):
        while self._working and self._server_sock:
            try:
                conn, addr = self._server_sock.accept()
                print('Connected: {0}'.format(addr))
                TreadmillClientConnection(self, conn, addr)
            except Exception as e:
                print("Error:", e)

Ich denke, hier sind keine Autorisierung und Sicherheit erforderlich. Der Zustand des Laufbandes ist nicht die Art von Daten, die ich vor Hackern schützen möchte.

Wir setzen dieses Skript in den Start und entfernen das Board im Laufband. Leider passt es nur in ein Metallrohr, das die Konsole mit dem Laufband verbindet.



Wie Sie wissen, schirmt Metall das Funksignal ab, also habe ich die WLAN-Antenne aus dem Rohr geholt, aber unter einem Kunststoffgehäuse, das die Drähte verbirgt.



Auf diesem direkt „intelligenten“ Laufband steht bereit. Sie weiß bereits, wie man Statistiken über das Netzwerk verteilt. Es bleibt nur ein Kunde für sie zu schreiben!

Android-Client


Was sollte meiner Meinung nach so ein Kunde sein. Dies ist eine Android-Anwendung, die ich auf einem Tablet oder Smartphone ausführen und auf das Display des Laufbands selbst legen werde. Sie sollte alle Informationen zu den Übungen auf dem Bildschirm anzeigen und das Display des Laufbands selbst ersetzen. Die Anwendung sollte im Hintergrund arbeiten können, damit ich das Video beim Joggen problemlos ansehen kann. Außerdem sollte es detaillierte Statistiken über Läufe führen, alles mit der Wolke synchronisieren und Diagramme der Abhängigkeit des Impulses von Geschwindigkeit und Neigungswinkel zeichnen.

Das Herzstück einer solchen Anwendung sollte ein Dienst sein, der im Hintergrund ausgeführt wird, sich in einer Endlosschleife mit dem Laufband verbindet, Daten empfängt und dekodiert. Hierbei gibt es keine besonderen Schwierigkeiten.

Herzfrequenzsensor


Am schwierigsten war es plötzlich, mit einem Herzfrequenzsensor zu arbeiten. Viele Fallstricke wurden entdeckt. Ich habe hier einen solchen Brustherzfrequenzmesser:



Ich benutze ihn schon lange, wenn ich Fahrrad fahre. Es ist Standard, funktioniert mit BLE a la Bluetooth Low Enegy und kann problemlos mit einem Telefon und einem Garmin-Navigator gekoppelt werden. Ich konnte mir nicht einmal vorstellen, dass die Arbeit mit meiner Anwendung so offensichtlich wäre. Solche Sensoren haben Standard-GUIDs für unterschiedliche Messwerte.

Um eine Herzfrequenz zu erhalten, müssen Sie zuerst Ihren Herzfrequenzmesser so konfigurieren, dass regelmäßig Messwerte gesendet werden. Ich konnte dies nur tun, indem ich nicht funktionierende Beispiele studierte und tippte.
Aus diesem Grund habe ich eine Klasse für die Arbeit mit einem Herzfrequenzsensor geschrieben, der automatisch versucht, eine Verbindung herzustellen, und regelmäßig die aktuelle Herzfrequenz meldet.

Samsung Health SDK


Wie für Statistiken und Grafiken. Ich habe mich entschieden, das Rad nicht neu zu erfinden, sondern das zu verwenden, was ich bereits beim Fahrradfahren benutze, nämlich mich irgendwie mit der wunderbaren Samsung Health App anzufreunden.

Jetzt wird es wahrscheinlich so aussehen, als würde ich wieder für Samsung werben. Aber auf einem Fahrrad hat sich diese Anwendung wirklich sehr gut bewährt. Zu meiner Überraschung lässt es sich problemlos mit allen Sensoren verbinden, zeigt sowohl die Trittfrequenz als auch die Raddrehzahl an und diktiert die Statistiken in den Kopfhörern, zeigt die gleichen Statistiken mit Grafiken an, gibt Erfolge aus und speichert alles in der Cloud.

Die Suche ergab, dass Samsung Health über ein eigenes SDK verfügt, das zwar nicht vollständig verständlich ist, aber dennoch dokumentiert ist: img-developer.samsung.com/onlinedocs/health/android/data/index.html

Die Arbeit damit basiert im Wesentlichen auf einer Datenbank, in der eine Vielzahl von Messwerten gespeichert ist, von durchgeführten Schritten und Herzfrequenzmessungen bis hin zu Blutzucker- und Schlafphasen. Aber jetzt sind wir an Aufzeichnungen von Übungen interessiert, die sowohl skalare Werte wie die Art der Übung, Zeit, Entfernung, Dauer, verbrannte Kalorien als auch Anordnungen von Live-Daten wie die Geschichte von Herzfrequenz, Geschwindigkeit und Koordinaten enthalten.

Alle diese Daten müssen korrekt gespeichert und aufbereitet werden. Einige müssen berechnet werden.

Höhenberechnung


Zum Beispiel Hubhöhe. Vom Laufband kennen wir den Steigwinkel zu jedem Zeitpunkt, der in Prozent gemessen wird. Der Prozentsatz des Höhenwinkels ist das Verhältnis der zurückgelegten Strecke zum Aufstieg. Es stellt sich heraus, dass die vertikale Geschwindigkeit gleich der üblichen Geschwindigkeit multipliziert mit der Steigung in Prozent ist und durch einhundert geteilt wird. Wenn wir die vertikale Geschwindigkeit kennen, können wir die aktuelle Höhe zu jedem Zeitpunkt berechnen. Infolgedessen muss es in die aktuellen Koordinaten eingegeben werden, obwohl sie sich während der Übung nicht ändern und nicht berücksichtigt werden.
In Reaktion auf diese Daten zeigt die Samsung Health App, wie viel ich angeblich geklettert bin, sowie die vertikale Geschwindigkeit zu jedem Zeitpunkt des Trainings.

Kalorienzählen


Aber wie zählt man Kalorien? Darüber hinaus ist die Kalorienzählung ein Muss für Samsung Health. Gleichzeitig ist der Kalorienverbrauch ein sehr ungenauer Indikator, der von vielen verschiedenen Faktoren abhängt. Ich bin mir nicht sicher, ob es Sinn macht, sie zu zählen.

Ich habe mir nichts Eigenes ausgedacht und einfach den Taschenrechner (https://42.195km.net/e/treadsim/) googelt und den Algorithmus aus meinem Javascript (https://42.195km.net/e/treadsim/treadsim107) kopiert .js). Am Eingang nimmt er die zurückgelegte Strecke, den Höhenwinkel und ... das Gewicht.

Ich könnte mein Gewicht manuell einstellen, aber da wir mit Samsung Health arbeiten, kann ich mein aktuelles Gewicht von dort nehmen. Immerhin verwende ich intelligente Waagen von Xiaomi, die mit Google Fit auf meinem Handy synchronisiert sind. Google FIt wird über eine separate Anwendung mit Samsung Health synchronisiert. Samsung Health wird über die Cloud mit sich selbst auf dem Tablet synchronisiert, auf dem meine Anwendung es bereits empfängt.

App Aussehen


Visuell besteht die Aufgabe der Anwendung darin, die Hauptindikationen in großem Maßstab anzuzeigen: Geschwindigkeit, Winkel, Herzfrequenz, Entfernung, Kalorien. Es ist besser, dies in Weiß auf schwarzem Hintergrund zu tun, damit der Batterieverbrauch bei Verwendung des AMOLED-Bildschirms minimal ist, da wir auf jeden Fall darauf hinweisen, dass der Bildschirm bei der Anzeige unserer Aktivitäten ständig eingeschaltet sein sollte.



Die Tasten werden automatisch ausgeblendet, wenn das Laufband aktiv ist. Sie können das Training nur mit der Geschwindigkeit Null starten und beenden.

Und natürlich müssen Sie den Modus „Bild in Bild“ unterstützen. Dies geschieht in nur wenigen Zeilen. Sie müssen nur im Manifest angeben, dass die Aktivität diesen Modus unterstützt, und im Code darauf zugreifen, wenn die Anwendung minimiert wird. So können Sie beispielsweise YouTube ansehen und Laufbandwerte in einer Ecke des Bildschirms anzeigen. Es stellte sich als sehr praktisch heraus.



Aber zu diesem Zeitpunkt wurde ich schließlich von den Schmerzen des Entwicklers für Android überholt, da ich bereits vier verschiedene Bildschirmgrößen habe: das Telefon und das Tablet im normalen Modus und sie befinden sich auch im Modus „Bild in Bild“. Und so kam es, dass, wenn ich die normale Schriftgröße für eine Bildschirmgröße auswähle, in anderen Fällen alles zu klein und dann zu groß ist.

Bei der Entwicklung für Android gibt es mehrere Kategorien von Bildschirmen, und Sie können festlegen, dass unterschiedliche Einstellungen automatisch für sie angewendet werden. In meinem Fall war dies jedoch nicht ausreichend.
Infolgedessen musste ich die Schriftgrößen im Code berechnen und einstellen, was ich für sehr falsch halte. Infolgedessen funktioniert es jedoch perfekt.

Ergebnis


Und hier ist das Ergebnis. Wir öffnen die Anwendung, warten auf die Verbindung mit dem Laufband und dem Herzfrequenzsensor, beginnen mit dem Training und verwenden das Laufband wie gewohnt.
Am Ende des Trainings stoppen wir das Laufband. Bei Erreichen der Geschwindigkeit Null erscheint die Schaltfläche „Training beenden“. Klicken Sie darauf, und die Statistiken werden an Samsung Health gesendet. Öffnen Sie es und sehen Sie alle Daten.







Sie können die Grafik von Puls, Geschwindigkeit und Anstieg sehen, Ihren Fortschritt in verschiedenen Zeitintervallen vergleichen, all dies wird in der Cloud gespeichert und ist von allen Geräten aus zugänglich.

Sie können es mit Google Fit synchronisieren. Die Schönheit. Ich bin mit dem Ergebnis zufrieden. Jetzt ist die Hauptsache, keine Klassen zu werfen. Sie können die Funktionalität der Anwendung erweitern, sodass sie dem Training ähnelt, wenn ich lange Zeit faul bin. Aber ich bin schon zu faul, um diese Funktion auszuführen.

All Articles