Raspberry Pi Ant+

Herzfrequenz und Wattwert per Ant+ mit einem Raspberry Pi empfangen und an einen MQTT-Broker senden.

In diesem Beitrag beschreibe ich den Empfang der Herzfrequenz und des Wattwertes und das Senden der Daten an einen MQTT-Broker mit einem Rapberry Pi.

Das Projekt entstand als ich im Winter 2019/2020 mit dem „zwiften“ anfing.

mein Zwift Setup

Ich wollte in erster Linie einen Kühllüfter, in Abhängigkeit der getretenen Leistung auf der Rolle, automatisch ansteuern.
Weiter wird in Abhängigkeit der Herzfrequenz ein LED-Stripe angesteuert.
Der LED-Stripe zeigt in unterschiedlichen Farben an, in welchem Herzfrequenzbereich ich aktuell gerade trainiere.

Der prinzipielle Ablauf der Steuerung des Kühllüfter und des LED-Stripe sieht wie folgt aus:

Der Wattwert wird von dem Wahoo Kickr Snap per Ant+ gesendet.
Die Herzfrequenz wird von dem Bustgrut per Ant+ gesendet.
Die beiden Werte werden von dem Rapberry Pi, an dem einen Ant+ USB-Stick angeschossen ist, empfangen.
Die Node.js Umgebung auf dem Rapberry Pi ermöglicht es ein entsprechendes Javascript laufen zu lassen, dass die Herzfrequenz und den Wattwert entsprechend aufbereitet und dann an einen MQTT-Broker sendet.
Der MQTT-Broker läuft bei mir in einer ioBroker Instanz.
Auf dem ioBroker wird dann mit weiteren Javascripten schliesslich der Kühllüfter und der LED-Stripe angesteuert.

Setup Raspberry Pi:

Hardware: Raspberry Pi Model B Revision 2.0 mit einer 16GB SD-Karte
Betriebssystem: Raspbian Buster Lite
Ant+ USB-Stick: BKOOL ANT+ USB 2.0 Stick
Node Version: v10.15.2
NPM Version: 5.8.0

Wie man eine Grundinstallation auf einem Raspberry Pi macht findet man zu genüge im Internet, daher gehe ich hier nicht weiter darauf ein.

Wenn das Rapberry Pi läuft und per SSH erreichbar ist, geht es per putty auf der Console des Raspberry Pi weiter:

Update des Raspberry Pi:

sudo apt-get update
sudo apt-get upgrade

Nodejs und NPM installieren:

sudo apt-get install nodejs npm

Versionen anzeigen:

pi@powerraspi01:~ $ node -v
v10.15.2

pi@powerraspi01:~ $ npm -v
5.8.0

udev-Regel für den Ant+ USB-Stick einrichten:

idVendor und idProduct des Ant+ USB-Stick anzeigen:

pi@powerraspi01:~ $ lsusb
Bus 001 Device 004: ID 0fcf:1009 Dynastream Innovations, Inc. ANTUSB-m Stick
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp. SMC9512/9514 USB Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

udev-Regel anlegen:

sudo nano /etc/udev/rules.d/ant-usb-m.rules

folgende Zeile in die Datei einfügen, idVendor, idProduct, vendor und product entsprechend anpassen:

SUBSYSTEM=="usb", ATTRS{idVendor}=="0fcf", ATTRS{idProduct}=="1009", RUN+="/sbin/modprobe usbserial vendor=0x0fcf product=0x1009", MODE="0666", OWNER="pi", GROUP="root"

Javascript erstellen um die Ant+ Daten auszuwerten und an einen MQTT-Broker senden:

Ordner erstellen in dem das Script abgelegt wird:

mkdir npm
cd npm 
mkdir zwiftapp
cd zwiftapp

Voraussetzungen für die Libarys schafen:

sudo apt-get install build-essential libudev-dev

benötigte Libarys installiern (mqtt und ant-plus):

npm install
npm install mqtt
npm install ant-plus

Javascript erstellen um die Ant+ Daten auszuwerten und an einen MQTT-Broker senden:

sudo nano heartrate_power_mqtt_V2.js

Inhalt des Scriptes „heartrate_power_mqtt_V2.js“:

/* Wattwert und Herzfrequenz per ANT+ auslesen und 
   an einen MQTT-Broker senden R.R. 21.11.2019

Script: heartrate_power_mqtt_V2.js
Autor: infinityflow.ch
NODE  Verson: v10.15.2
NPM Version: 5.8.0


zusaetzliche Libarys:

ant-plus https://www.npmjs.com/package/ant-plus
mqtt https://www.npmjs.com/package/mqtt

*/

// Variablen fuer mqtt:

var mqtt = require('mqtt')

// IP des MQTT-Broker anpassen
var client = mqtt.connect('mqtt://10.0.0.21')

// Intervall fuer das Senden der Daten an den MQTT-Broker in ms z.B. alle 10s = 10000
var mqttsendeintervall = setInterval(mqttsend, 10000);

// MQTT Topics fuer den Wattwert und die Herzfrequenz
var topic_watt = "fancontroll/power";
var topic_heartrate = "fancontroll/heartrate";

// Hilfsvariablen fuer das Senden per MQTT
var powermqtt = "0";
var heartratemqtt = "0";

// Variablen fuer mqtt Ende


// Wattwert und Herzfrequenz per ant-plus auslesen:

var Ant = require('ant-plus');

function openStick(stick, stickid) {
        var sensor1 = new Ant.HeartRateSensor(stick);

        var dev_id = 0;

        sensor1.on('hbdata', function(data) {
                console.log(stickid, 'sensor 1: ', data.DeviceID, data.ComputedHeartRate);
                heartratemqtt = data.ComputedHeartRate;
        });

        sensor1.on('attached', function() { console.log(stickid, 'sensor1 attached'); });

        var sensor2 = new Ant.BicyclePowerSensor(stick);

        sensor2.on('powerData', function(data) {
                console.log(stickid, 'sensor 2: ', data.DeviceID, data.Power);
                powermqtt = data.Power;
        });

        sensor2.on('attached', function() { console.log(stickid, 'sensor2 attached'); });


        stick.on('startup', function() {
                console.log(stickid, 'startup');

                console.log(stickid, 'Max channels:', stick.maxChannels);

                sensor1.attach(0, 0);

                sensor2.attach(1, 0);
        });


        function tryOpen(stick) {
                let token = stick.openAsync((err) => {
                        token = null;
                        if (err) {
                                console.error(stickid, err);
                        } else {
                                console.log(stickid, 'Stick found');
                        }
                });

                return token;
        }

tryOpen(stick);

}

openStick(new Ant.GarminStick2(), 1);
openStick(new Ant.GarminStick2(), 2);
openStick(new Ant.GarminStick3(), 3);

openStick(new Ant.GarminStick2(), 4);
openStick(new Ant.GarminStick2(), 5);
openStick(new Ant.GarminStick3(), 6);

// Wattwert und die Herzfrequenz per ant-plus auslesen Ende


// Wattwert und die Herzfrequenz per MQTT an den iobroker senden

function mqttsend() {
        var power_string = powermqtt.toString();
        var heartrate_string = heartratemqtt.toString();
        client.publish(topic_heartrate, heartrate_string);
        client.publish(topic_watt, power_string);
        console.log("Wert der Variable heartrate_string:" ,heartrate_string, "Hz");
        console.log("Wert der Variable power_string:" ,power_string, "Watt");
}

mqttsend();

// Wattwert und die Herzfrequenz per MQTT an den iobroker senden Ende

Autostart des Javascript „heartrate_power_mqtt_V2.js“ anlegen:

sudo nano /etc/init.d/power_mqtt

Inhalt des Start-Stop Scriptes:

den Inhalt dieser Init-Script Vorlage in die power_mqtt kopieren

die folgenden Parameter müssen angepasst werden:

dir="/home/pi/npm/zwiftapp"
cmd="node heartrate_power_mqtt_V2.js"
user="root"

Skript ausführbar machen:

sudo chmod 755 /etc/init.d/power_mqtt

Autostart aktivieren:

sudo update-rc.d power_mqtt defaults

manuell kann man das Script wie folgt starten, stopen und neustarten:

sudo /etc/init.d/power_mqtt start
sudo /etc/init.d/power_mqtt stop
sudo /etc/init.d/power_mqtt restart

Somit sollte jetzt der Raspberry Pi automatisch, sobald er gestartet ist, die Ant+ Daten empfangen und alle 10s an einen MQTT-Broker senden.

quick and dirty 🙂

23 Kommentare

  1. Hallo,

    ich habe das Script seit ca 2 Jahren am laufen, es gibt allerdings ein Problem welches ich von Anfang immer hatte. Das hat sich durchgezogen durch 3 verschiedene Raspberrys alle Updates die in dieser Zeit kamen. Auch jetzt alles neu aufgesetzt mit Bullseye, Problem trotzdem da. Und zwar hängt die power_mqtt sich immer nach ca 30 Sekunden auf und sendet keinen Puls mehr, es bleibt dann beim letzten Puls stehen im iobroker. Ich habe das bis jetzt so gelöst, dass ich die power_mqtt per cronjob einfach jede Minute neu starten lasse zwischen 9-22 Uhr. Wäre aber trotzdem schön wenn es dafür eine Lösung geben würde. Statt das Skript zu starten hilft auch Dongle zu ziehen und einstecken, ist aber weniger praktikabel.
    Den Stick den ich habe ist dieser hier:

    https://www.amazon.de/CARDIO-sport-Dongle-Garmin-TrainerRoad/dp/B094RFMJK4/ref=sr_1_7?__mk_de_DE=ÅMÅŽÕÑ&crid=3UG6CJ2G49QQ1&keywords=ant%2B+usb+bkool&qid=1664613097&qu=eyJxc2MiOiIwLjAwIiwicXNhIjoiMC4wMCIsInFzcCI6IjAuMDAifQ%3D%3D&sprefix=ant%2B+usb+bkool%2Caps%2C79&sr=8-7

    Den Stick den du da hast finde ich einfach nicht zum kaufen.

    Grüße

  2. Hi Reto

    Super Sache!

    Die Lösung ist genau das was ich gesucht habe. Ich habe genau wie beschrieben implementiert, läuft es aber bei mir nicht. Bei der .ERR sehe ich „sudo: node_heartrate_power_mqtt_V2.js: command not found“.

    Ich habe leider keine Erfahrung mit Raspberry und komme jetzt nicht weiter. Hast du eine Idee was ich noch versuchen kann?

    Versionen:
    Raspberry Pi OS (Raspbian GNU/Linux 11(bullseye), 11.4
    Node.js v12.22.12
    Npm 7.5.2

    Vielen Dank im Voraus,
    Fernando

    1. Hallo Fernando,

      Hast du in der folgenden Zeile das „node“ drinnen?
      cmd=“node heartrate_power_mqtt_V2.js“

      Kannst du die heartrate_power_mqtt_V2.js manuell mit
      „sudo node heartrate_power_mqtt_V2.js“ ausführen?

      Gruss Reto

    2. Hi Reto

      Sorry dass ich mich nur jetzt melde!

      Ich hatte ein Typpfehler bei dem CMD und nur gemerkt nachedem ich deine Nachricht gelesen habe. Es läuft.

      Vielen Dank!
      Fernando

  3. Für die Berechnung der Herzfrequenzvariabilität sowie der Atmungsrate sind Sensorrohdaten mit R-R peaks notwendig. Bei einer Fenix 6 Uhr kann man einstellen, dass man diese Rohdaten aufzeichnen will. Wie ist es mit dem Rasperri ANT+? Kann man den Code so modifizieren, dass er auch Sensorrohdaten aufzeichnet?

  4. Hallo Reto,
    das Rasperri PI Ant+ wurde so viel ich weiss ursprünglich von Johannes Bader, ehemaliger Doktorand an der ETH Zürich bei Eckart Zitzler und Mitglied der Systems Optimization Group im Computer Engineering und Networks Laboratory (TIK) der ETH Zürich auf einen Rasperri PI portiert wurde, aus seinem Blog veröffentlicht (Open Source), von dir mit MQTT ergänzt und rechtlich geschützt. Ich schlage vor, dass du den Rasperri PI mit allen ANT+ Sensoren ergänzt, auf ein Linux PinePhone portierst und als Produkt verkaufst. Vielleicht würde ich dir eins abkaufen. Das wäre quasi ein IpBike das einen Ip stream über MQTT hat, und für MQTT Anwendungen sehr interessant wäre.

  5. Tolles project, danke vielmals.

    Es hat einmal funktioniert. Das Problem ist, dass /dev/ttyUSB0 verschwindet, wenn der Server power_mqtt gestartet wird (sudo systemctl start power_mqtt)

    Wird dann der Server gestoppt und der USB-Stick ausgenommen und eingesteckt, dann ist /dev/ttyUSB0 wieder vorhanden.

    Wenn ich den Server wieder starte, ist /dev/ttyUSB0 wieder weg, und so weiter.

    Gibt es einen Ausweg aus diesem Problem?

    Noch ein paar Details:
    pi@RPi2:~$ node -v
    v12.22.2

    pi@RPi2:~$ npm -v
    6.14.13

    pi@RPi2:~/npm/zwiftapp$ npm list ant-plus
    /home/pi/npm/zwiftapp
    âââ ant-plus@0.1.24

    pi@RPi2:~/npm/zwiftapp$ npm list mqtt
    /home/pi/npm/zwiftapp
    âââ mqtt@4.2.8

    Grüße, Cor

  6. Hallo Reto,
    eine ganz private Frage. Ist es möglich dein Rasperri ANT+ script zu modifizieren, dass auch Daten von einem ANT+ Foot Pod zu einem MQTT Broker gesendet werden können?

  7. Moin, ab der node.js v13 kann power_mqtt nicht mehr starten. Jemand eine Idee? Sobald ich auf die letzte 12er gehe funktioniert es sofort. Habe auch v14 und v15 probiert leider ohne Erfolg. Grüße

    1. Hallo Jakob,

      Ich habe die folgende Konfiguration erfolgreich getestete:

      Hardware: Raspberry Pi 3 Model B Rev 1.2 mit einer 16GB SD-Karte
      Betriebssystem: Raspbian Buster Lite
      Ant+ USB-Stick: BKOOL ANT+ USB 2.0 Stick
      node Version: v14.16.0
      npm Version: 6.14.11
      npm mqtt Version: 4.2.6
      npm ant-plus Version: 0.1.24

      Hast du dein Raspi UptoDate?
      Hast du mqtt in der Version 4.2.6 und die ant-plus in der Version 0.1.24?

      mqtt und ant-plus Versionen anzeigen mit:

      pi@powerraspi01:~/npm/zwiftapp $ npm list mqtt
      /home/pi/npm/zwiftapp
      └── mqtt@4.2.6

      pi@powerraspi01:~/npm/zwiftapp $ npm list ant-plus
      /home/pi/npm/zwiftapp
      └── ant-plus@0.1.24

      Gruess Reto

    2. Also die Lösung war folgender Befehl:

      sudo npm install usb

      Da wurde dann eine neue Version von usb installiert. Ich denke man bekommt das Problem nicht, wenn man von grundauf das ganze installiert. Das Errorlog hat nämlich folgedes ausgespuckt:

      Error: The module ‚/home/pi/node_modules/usb/build/Release/usb_bindings.node‘
      was compiled against a different Node.js version using
      NODE_MODULE_VERSION 72. This version of Node.js requires
      NODE_MODULE_VERSION 83. Please try re-compiling or re-installing
      the module (for instance, using `npm rebuild` or `npm install`).

  8. Hallo Reto. Ich bin ein grosser Fan von Raspberry Pi Ant+. Möge es eine erfolgreiche Zukunft haben. Hast du dir schon mal überlegt, anstelle eines Raspberry Pi ein Linux Telefon oder eine Linux Uhr zu verwenden?:

    https://petergamma.org/

    so viel ich weiss, sollte das Project auch auf diesen Geräten laufen. Das Problem ist vielleicht der ANT+ Stick, vor allem bei der Uhr. Könnte man das Projekt so modifizieren, dass es auch mit Bluetooth Low Energy Sensoren funktioniert? Das würde das Problem lösen.

    1. Hallo Peter,

      Vielen Dank für deinen Input.
      Der Raspberry Pi Ant+ ist für mich eine reine Hobby Geschichte und soll auch so bleiben.
      Im Moment entwickle ich an dem Raspberry Pi Ant+ nicht weiter, da ich die benötigten Daten, um den Fan und den LED-Stripe steuern zu können, per Home Assistant direkt von Zwift abhole.

      https://infinityflow.ch/zwift-telemetrie/

      Mit sportlichen Grüssen
      Reto

  9. Hätte da noch eine Frage. Ab und zu hängt der Puls an einer Zahl fest, ein restart der power_mqtt schafft Abhilfe. Jetzt habe ich mir gedacht, ich könnte täglich zu einer gewissen Uhrzeit mit blockly das Skript restarten lassen. Das Problem ist, es geht nur mit „sudo“ und das ist in iobroker nicht machbar. Ich habe sowohl die Rechte auf 777 gesetzt und auch den Benutzer sowie Gruppe auf iobroker geändert, keine Chance. Auch über visudo entsprechend eingerichtet, aber auch das nützt nichts. Jemand eine Idee?

  10. Hallo,

    bin gerade über diese Seite gestolpert als ich danach gesucht habe wie ich einen Xiaomi 2s Ventilator per Herzfrequenz steuern kann beim zwiften. Kannst du die Einrichtung und dein JS Script in iobroker noch beschreiben, das wäre super zum nachbauen.

    Grüße Björn

    1. Hi Reto,

      Vielen Dank für die schnelle und ausführliche Antwort mit gleich 2 neuen Blog Beiträgen. Da werd ich mich die Tage mal dran probieren.
      Auf jeden Fall coole Idee die ganze Umsetzung.

      Sportliche Grüße und Ride On 🙂
      Björn

  11. Hier hat es eine Demo der OpenBCI software mit EEG Sensoren:

    https://openbci.com/

    Funktioniert diese Demo auch mit ANT+ oder Bluetooth low energy Sensoren auf dem Linux PinePhone, indem man das Rasperri ANT+ script mit MQTT auf dem Pinephone laufen lässt, um Bluetooth low energy Sensoren erweitert, und mit der OpenBCI Software GUI5 verbindet?

  12. Matlab unterstützt jetzt Bluetooth low energy Sensoren mit dem Beispiel:

    Collect Data from Fitness Monitoring Devices

    Es benützt ein Under Armour Brustgurt und Under Armour Laufschuhe mit integriertem Bluetooth low energy foot pod. Der Matlab Entwickler treibt wohl selten Sport, sonst hätte er wohl kaum so exotische Sportsensoren für sein Beispiel gewählt.

    Das Matlab Beispiel ist, toll und wohl etwas vom Besten was es zur Zeit gibt um Bluetooth low energy Sensoren zu einer Desktop Applikation zu streamen. Es sollte auch für andere Bluetooth low energy Sensoren modifizerbar sein, um es zum Beispiel auch zum Biken verwenden zu können. Aber wer möchte zum Rennen und zum Biken einen PC mit Matlab mitgschleppen?

    Das Rasperri ANT+ script welches Daten zu einem MQTT brocker sendet, könnte sehr hilfreich sein um dieses Problem zu lösen. Rasperri PI Scripte sollten auf dem neuen Linux PinePhone ausführbar sein, und deshalb auch das ANT+ script welches Sensor Daten zu einem MQTT Brocker sendet. Das PinePhone von Pine64 wird zur Zeit als Gerät für Entwickler verkauft, aber es hat ein grosses Potential, da Software Entwicklung unter Linux einfacher ist als unter Android.

    Analysesoftware für Sensor Daten wie OpenSignals oder Matlab laufen nicht auf ARM basierenden Plattformen wie dem Linux Pinephone.

    Nun gibt es aber die OpenBCI Gehirn Computer Interface Software mit der Version GUI5, welche auf BrainFlow basiert. BrainFlow läuft auch auf ARM Platformen:

    https://openbci.com/forum/index.php?p=/discussion/2693/can-gui-visualize-and-analyze-ant-and-ble-sensor-data-on-the-linux-pinephone#latest

    Das Rasperri ANT+ script welches Daten zu einem MQTT brocker sendet, könnte mit diesen Softwarelösungen integriert werden. Dies ergibt eine Echtzeitdatenerfassungssoftware, die mobile ist und auf einem Linux PinePhone lauffähig ist, welche Herzfrequenz, Geschwindigkeit, Kadenz, aber auch Daten vone EEG oder MEG Sensoren verarbeiten kann.

    Eine solche Softwarelösung kann mit Analysesoftware wie OpenSignals oder Matlab mithalten, und hat gegenüber diesen Lösungen den Vorteil, dass sie zum Beispiel auf dem Linux Pinephone lauffähig sind, und das Mitschleppen eines Laptops beim Sport treiben überflüssig macht.

  13. Es gibt ein Skript QBIKE ANT+ Cycling Training on Raspberry Pi, mit einem schönen Echtzeit Display.

    https://www.thisisant.com/forum/viewthread/7371/

    Anstelle von einem Raspberry Pi finde ich das neue Linux Pinephone attraktiv als Gerät.

    https://www.thisisant.com/forum/viewthread/7372/

    Es wäre auch schön alle diese Optionen zum Beispiel in einer Distro für ein Pinephone vereinigt zu haben. Attraktiv wäre für mich persönlich auch eine Option zum Aufzeichnen von Garmin Sensor Daten im .csv Format, da das Garmin .fit Format zum Beispiel mit Matlab schwierig zu verarbeiten ist.

    Das Raspberry Pi Ant+ Skritpt welches hier vorgestellt wird und welches Sensor Daten an einen MQTT-Broker sendet, gefällt mir sehr gut. Vor allem auch deshalb, weil Garmin kein Bluetooth 5.0 SDK support hat, was von Matlab unterstützt würde. Anstelle kann dieses Skript hier verwendet werden.

    Ich habe alle Links zu den verschiedenen Echtzeit Streaming Möglichkeiten für eine Garmin Uhr auf meinem Github aufgelistet, sollten andere ebenfalls auf der Suche nach solchen Möglichkeiten sein.

    https://github.com/PeterGamma/Live-stream-of-sensor-data-from-Garmin-watches

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert