Nous pompons le tapis roulant

Récemment, j'ai décidé d'un achat très étrange pour moi. Oui, j'ai acheté un tapis roulant.



Et bientôt, je me suis rendu compte qu'il n'y avait pas assez de statistiques détaillées comme lorsque je faisais du vélo. Dans le cas d'un vélo, l'application sur le téléphone écrit ma vitesse, ma fréquence cardiaque, ma cadence et mon portance. Il est très curieux de contrôler tous ces paramètres pendant l'entraînement, pour pouvoir consulter les graphiques et comparer de temps en temps vos résultats.

J'ai donc décidé de faire quelque chose de similaire avec le tapis roulant: le connecter à un smartphone ou une tablette afin de collecter et d'afficher des statistiques.

Comme d'habitude, mon histoire se présente sous la forme d'un article texte traditionnel, et par vidéo. Comme vous en voulez plus.

Vidéo



Article


Conception


Même au moment où je récupérais le tapis roulant, j'ai remarqué que la télécommande et la courroie elle-même ne connectaient que quatre fils. Apparemment, certains d'entre eux sont utilisés pour alimenter la console, car la toile elle-même est connectée au réseau 220 volts, et les fils restants sont nécessaires pour transmettre des signaux de contrôle dans la direction opposée - de la console à la toile, ils contrôlent la vitesse et l'angle de la piste.

J'ai connecté l'oscilloscope en parallèle à ces fils, en essayant différentes combinaisons.

En conséquence, j'ai découvert que tout était à peu près le même que ce à quoi je m'attendais. L'un des fils est mis à la terre et un autre est de 12 volts. Les autres transmettent des données numériques.

Dans l'un d'eux, le signal change lors du changement de vitesse et d'angle. C'est exactement ce dont j'ai besoin! L'amplitude du signal est d'environ quatre volts. Mais le protocole ne ressemble pas à quelque chose de standard, et le signal est très bruyant, lorsque la piste est activée, vous devez le filtrer d'une manière ou d'une autre.



Le dernier fil n'est que des impulsions à fréquence constante. Apparemment, pour que la console puisse voir la connexion à la ceinture de course. Si vous déconnectez ce fil, la télécommande donne immédiatement une erreur.

Les indications du capteur d'impulsions sur ces fils ne sont clairement pas transmises, mais ce n'est pas nécessaire. Il est préférable de connecter un capteur thoracique séparé, que j'utilise depuis longtemps lorsque je fais du vélo. De plus, il s'est avéré que le capteur de fréquence cardiaque sur le tapis roulant lui-même ment beaucoup, sous-estimant les lectures.

Assemblage de l'appareil


Ainsi, la tâche suivante consiste à assembler une carte qui se connecte en parallèle à ces fils, lit la vitesse et l'angle actuels, et les transfère en quelque sorte sans fil vers une tablette ou un smartphone.

Une fois de plus, j'ai décidé d'utiliser l'ordinateur monocarte Onion Omega2. Il doit faire un excellent travail. Il suffit de baisser la tension d'alimentation à 3,3 volts et de filtrer les données des interférences.

Pour réduire la tension, j'utilise maintenant ces cartes prêtes à l'emploi avec un convertisseur DC-DC. Ils coûtent un sou, peuvent supporter jusqu'à quelques ampères et la tension de sortie est ajustée avec une torsion.



En même temps, cette carte a des conclusions à souder directement sur une autre carte, c'est très pratique. L'essentiel est de ne pas tordre la torsion de tension après l'installation dans le circuit.

Pour filtrer le bruit sur la ligne de données, j'ai fabriqué un filtre RC ordinaire: une résistance de 2,2 kilo-ohms et un condensateur de 22 picofarad. Cela devrait filtrer le bruit haute fréquence, laissant un signal basse fréquence.

Il s'est avéré être une petite écharpe.



Je l'ai connecté aux fils du tapis roulant pour voir à quel point le signal est filtré lorsqu'il est allumé et, apparemment, la forme d'onde est devenue presque parfaite.



Module noyau


Cependant, il n'est pas si facile de vérifier les performances du fer. Comme nous l'avons vu
plus tôt sur l'oscilloscope, les signaux vont très vite, et nous n'utilisons pas de microcontrôleur, mais un ordinateur Omega2 mono-carte avec Linux embarqué. Sous Linux, nous ne pourrons pas traiter les signaux de l'espace utilisateur aussi rapidement. Mais du cœur, nous pouvons! Il est donc temps d'écrire un module du noyau Linux!

Pour ce faire, vous devez télécharger les sources du noyau Linux, dans notre cas, il s'agit d'un assemblage OpenWRT pour Omega2, et créer un répertoire contenant le code source de notre module.

L'écriture de code de module ressemble beaucoup à la programmation d'un microcontrôleur. Nous écrivons également en C, tout est également de bas niveau, nous travaillons également avec des interruptions et nous nous tournons également vers les conclusions GPIO. Seulement ici, en plus de tout ce qui précède, nous interagissons toujours avec l'espace utilisateur via un pseudo-fichier. Ainsi, notre module noyau devient une sorte d'adaptateur entre le matériel et les applications ordinaires. En fait, cela s'appelle le pilote.

Au début, je ne savais pas comment décoder les signaux, j'ai donc simplement déduit leur durée.



Il est vite devenu clair que les signaux étaient codés avec une durée de haut niveau. Elle est longue de 600 microsecondes ou de 1200 microsecondes. Le niveau bas est toujours de 600 microsecondes à l'exception de la séquence initiale.

Un total de 17 de ces gouttes de haut en bas. Apparemment, il s'agit de 16 bits de données plus la séquence initiale. J'ai fait leur décodage, en prenant comme base que les longues différences élevées sont un zéro logique, et les courtes sont une unité logique et j'ai compris ce qui s'est passé. J'ai immédiatement vu les données dont j'avais besoin!



Comme vous le savez, 16 bits sont deux octets. Le premier octet indique le type de données transmises: l'angle d'inclinaison ou de vitesse, et le deuxième octet les données elles-mêmes. Le pilote est extrêmement simple.

Le seul paramètre de pilote est le numéro de port.

/* 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)");

Lors de l'initialisation, configurez-le pour l'entrée et définissez l'interruption, qui sera déclenchée à chaque changement de niveau.

/* 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;

Dans cette interruption, nous regardons d'abord l'heure actuelle. Ensuite, nous utilisons cette valeur pour calculer le temps écoulé depuis le déclenchement de la dernière interruption et la placer dans un tableau. Bien sûr, nous nous souvenons de l'heure actuelle pour le calcul la prochaine fois. De plus, vous devez redémarrer la minuterie spéciale.

/* 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;
}

L'astuce est que si la minuterie fonctionne toujours, cela signifie qu'il n'y a pas eu de baisse de niveau sur la broche pendant une longue période, et qu'il est donc temps de traiter les informations collectées. Dans la fonction appelée par le temporisateur, il est vérifié qu'il y a eu exactement 34 gouttes, après quoi nous regardons la durée de chaque intervalle. S'il y a 600 microsecondes, puis 1200 microsecondes, alors nous prenons 900 à l'étranger. Si l'intervalle est inférieur, alors nous écrivons un dans le résultat, en le décalant d'un bit. Après avoir traité chaque intervalle, nous envoyons le résultat à des pseudo-fichiers ouverts, transférant ainsi les données vers l'espace utilisateur.

/* 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;
}

Serveur Python et détection de vitesse


Il reste ensuite à écrire un script Python qui les lira du pseudo-fichier et les enverra sur le réseau sous forme de chaînes JSON. Il semblerait que tout soit assez simple. Cependant, si tout est simple avec l'angle d'inclinaison et que la valeur dans le deuxième octet correspond exactement à l'angle d'inclinaison en pourcentage, alors avec la vitesse tout s'est avéré beaucoup plus déroutant.

Une valeur de 9 correspond à un kilomètre par heure et une valeur de 160 correspond à 18 kilomètres par heure. Autrement dit, la dépendance des données à la vitesse réelle n'est pas du tout évidente. J'ai écrit toutes les valeurs manuellement, les ai conduites dans Excel, tracé et obtenu une courbe très inégale.



Et il y a des vitesses lorsque les lectures sur la télécommande sont différentes, mais les données et la vitesse de la piste elle-même restent les mêmes! Par exemple, 5,2 km / h et 5,3 km / h sont en fait les mêmes vitesses. Partout tricher. Je me demande quelle vitesse est vraiment là? Mesurez-le en quelque sorte, mais laissez-le pour plus tard.

Hormis ce transfert de perroquets en kilomètres par heure, le script s'est avéré extrêmement simple. Nous lisons les données du pseudo-fichier Linux, les décodons, acceptons les connexions réseau et transférons les données aux clients connectés sur le réseau sous forme de chaîne JSON.

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)

Je pense qu'aucune autorisation et sécurité ne sont nécessaires ici. L'état du tapis roulant n'est pas le type de données que je voudrais protéger contre les pirates.

Nous mettons ce script au démarrage et retirons la planche à l'intérieur du tapis roulant. Hélas, il ne tient que dans un tuyau métallique reliant la console à la bande de roulement.



Comme vous le savez, le métal protège le signal radio, j'ai donc sorti l'antenne Wi-Fi du tuyau, mais sous un boîtier en plastique qui cache les fils.



Sur ce tapis roulant directement «intelligent» est prêt. Elle sait déjà comment diffuser des statistiques sur le réseau. Il ne reste plus qu'à lui écrire un client!

Client Android


Ce qui à mon avis devrait être un tel client. Il s'agit d'une application Android que je vais exécuter sur une tablette ou un smartphone et placer sur le dessus de l'écran du tapis roulant lui-même, respectivement, elle devrait afficher toutes les informations sur les exercices sur l'écran, en remplaçant l'affichage du tapis roulant lui-même. L'application devrait pouvoir fonctionner en arrière-plan, afin que je puisse regarder la vidéo sans aucun problème pendant le jogging. En outre, il devrait conserver des statistiques détaillées sur les courses, tout synchroniser avec le nuage et dessiner des graphiques de la dépendance de l'impulsion à la vitesse et à l'angle d'inclinaison.

Le cœur d'une telle application devrait être un service qui s'exécute en arrière-plan, se connecte au tapis roulant dans une boucle sans fin, reçoit des données et les décode. Il n'y a pas de difficultés particulières à cela.

Capteur de fréquence cardiaque


La chose la plus difficile a été de travailler soudainement avec un capteur de fréquence cardiaque. De nombreux pièges ont été découverts. J'ai un tel moniteur de fréquence cardiaque thoracique ici:



je l'utilise depuis longtemps quand je fais du vélo. Il est assez standard, il fonctionne sur BLE à la Bluetooth Low Enegy, il peut être couplé avec un téléphone et un navigateur Garmin sans aucun problème. Je ne pouvais même pas penser que travailler avec lui depuis ma candidature serait si évident. Ces capteurs ont des GUID standard pour différentes lectures.

Pour commencer à recevoir une fréquence cardiaque, vous devez d'abord configurer votre moniteur de fréquence cardiaque pour envoyer périodiquement des lectures. Je ne pouvais le faire qu'en étudiant des exemples non fonctionnels et en tapant.
En conséquence, j'ai écrit une classe pour travailler avec un émetteur de fréquence cardiaque, qui essaie automatiquement de s'y connecter et rapporte périodiquement la fréquence cardiaque actuelle.

Samsung Health SDK


Quant aux statistiques et aux graphiques. J'ai décidé de ne pas réinventer la roue, mais d'utiliser ce que j'utilise déjà quand je fais du vélo, à savoir me lier d'amitié avec la merveilleuse application Samsung Health.

Maintenant, il semblerait que je fasse de nouveau de la publicité pour Samsung. Mais sur un vélo, cette application a vraiment fait ses preuves. À ma grande surprise, il se connecte à tous les capteurs sans problème, affiche à la fois la cadence et la vitesse des roues, et dicte les statistiques dans les écouteurs, il affiche les mêmes statistiques avec des graphiques, donne des réalisations et stocke tout dans le cloud.

La recherche a montré que Samsung Health possède son propre SDK, qui, bien que pas entièrement intelligible, est toujours documenté: img-developer.samsung.com/onlinedocs/health/android/data/index.html

Travailler avec, c'est essentiellement travailler avec une base de données qui stocke une variété de lectures, des mesures prises et des mesures de la fréquence cardiaque à la glycémie et aux phases de sommeil. Mais maintenant, nous nous intéressons aux enregistrements d'exercices, qui incluent à la fois des valeurs scalaires comme le type d'exercice, le temps, la distance, la durée, les calories brûlées et des tableaux de données en direct comme l'historique de la fréquence cardiaque, de la vitesse et des coordonnées.

Toutes ces données doivent être correctement stockées et préparées. Certains doivent être calculés.

Calcul de la hauteur


Par exemple, la hauteur de levage. Depuis le tapis roulant, nous connaissons l'angle de montée à chaque instant, qui est mesuré en pourcentage. Le pourcentage de l'angle d'élévation est le rapport de la distance parcourue à la montée. Il s'avère que la vitesse verticale est égale à la vitesse habituelle multipliée par la pente en pourcentage et divisée par cent. Connaissant la vitesse verticale, nous pouvons calculer la hauteur actuelle à chaque instant. Par conséquent, il doit être entré dans les coordonnées actuelles, malgré le fait que pendant l'exercice, elles ne changent pas et ne sont pas prises en compte.
En réponse à ces données, l'application Samsung Health montrera combien j'ai soi-disant grimpé, ainsi que la vitesse verticale à chaque moment de l'entraînement.

Comptage des calories


Mais comment compter les calories? De plus, le comptage des calories est un must pour Samsung Health. Dans le même temps, les calories brûlées sont un indicateur très inexact, qui dépend de nombreux facteurs différents. Je ne sais pas s'il est logique de les compter.

Je ne suis pas venu avec quelque chose de moi et juste google la calculatrice (https://42.195km.net/e/treadsim/) et copié l'algorithme de mon javascript (https://42.195km.net/e/treadsim/treadsim107 .js). A l'entrée, il prend la distance parcourue, l'angle d'élévation et ... le poids.

Je pouvais définir mon poids manuellement, mais puisque nous travaillons avec Samsung Health, je peux prendre mon poids actuel à partir de là. Après tout, j'utilise des balances intelligentes de Xiaomi, qui sont synchronisées avec Google Fit sur mon téléphone, Google FIt via une application distincte est synchronisé avec Samsung Health, Samsung Health via le cloud est synchronisé avec lui-même sur la tablette, où mon application le reçoit déjà.

Apparence de l'application


Visuellement, la tâche de l'application est d'afficher à grande échelle les principales indications: vitesse, angle, fréquence cardiaque, distance, calories. Il est préférable de le faire en blanc sur fond noir afin que la consommation de la batterie lors de l'utilisation de l'écran AMOLED soit minimale, car nous indiquons certainement que lors de l'affichage de notre activité, l'écran doit être allumé en permanence.



Les boutons sont automatiquement masqués lorsque le tapis roulant est actif. Vous pouvez démarrer et arrêter l'entraînement uniquement à vitesse nulle.

Et bien sûr, vous devez prendre en charge le mode «image dans l'image». Cela se fait en quelques lignes seulement. Vous avez juste besoin d'indiquer dans le manifeste que l'activité prend en charge ce mode, et dans le code, allez-y lorsque vous réduisez l'application. Par conséquent, vous pouvez regarder, par exemple, YouTube et voir les lectures du tapis roulant dans un coin de l'écran. Cela s'est avéré très pratique.



Mais à ce stade, j'ai finalement été dépassé par la douleur du développeur pour Android, car j'ai déjà quatre tailles d'écran différentes: le téléphone et la tablette en mode normal et ils sont en mode "image dans l'image". Et il se trouve que si je sélectionne la taille de police normale pour une taille d'écran, dans d'autres cas, tout est trop petit, puis trop grand.

Lors du développement pour Android, il existe plusieurs catégories d'écrans et vous pouvez appliquer différents paramètres automatiquement pour eux, mais dans mon cas, cela ne suffisait pas.
En conséquence, j'ai dû calculer et définir les tailles de police dans le code, ce qui, je pense, est très faux. Cependant, cela fonctionne parfaitement en conséquence.

Résultat


Et voici le résultat. Nous ouvrons l'application, attendons la connexion avec le tapis roulant et le capteur de fréquence cardiaque, commençons l'entraînement et utilisons le tapis roulant comme d'habitude.
À la fin de l'entraînement, nous arrêtons le tapis roulant. Une fois la vitesse nulle atteinte, le bouton «terminer l'entraînement» apparaît. Cliquez dessus et les statistiques sont envoyées à Samsung Health. Ouvrez-le et voyez toutes les données.







Vous pouvez voir le graphique du pouls, de la vitesse et de l'élévation, comparer vos progrès à différents intervalles de temps, tout cela est stocké dans le cloud et accessible depuis tous les appareils.

Vous pouvez le synchroniser avec Google Fit. La beauté. Je suis content du résultat. Maintenant, l'essentiel n'est pas de lancer des cours. Vous pouvez ajouter à la fonctionnalité de l'application pour qu'elle ressemble à une formation si je suis paresseux pendant longtemps. Mais je suis déjà trop paresseux pour faire cette fonction.

All Articles