Intégration du BSEC dans CircuitPython (un travail en cours)

Quelques conseils sur l'utilisation de l'indicateur de construction USER_C_MODULES

Cet article de blog abordera deux sujets différents. D'une part, je parlerai de l'approche générale à adopter pour inclure et tester votre propre code C dans votre version personnalisée de l'outil de gestion de l'environnement de travail. CircuitPython . D'autre part, je parlerai des défis spécifiques à l'utilisation de la BME688 Breakout BoardNous avons conçu la carte d'extension spécifique et la bibliothèque BSEC sur la RP2040. Cette bibliothèque permet une analyse avancée des données du capteur fournies par le BME688 et des calculs basés sur ces données.

Les avantages d'envelopper votre propre code C ou vos bibliothèques statiques dans CircuitPython sont assez évidents. Beaucoup de cartes micro-contrôleurs, dans ce cas le Raspberry Pi Pico qui est basé sur le RP2040, permettent l'exécution du code C. Écrire du C pur peut être décourageant, surtout pour les programmeurs qui n'ont pas encore passé beaucoup de temps avec ce langage. CircuitPython, en revanche, offre un accès facile via la syntaxe Python, de nombreuses bibliothèques bien écrites et une grande communauté pour vous aider.

Notre objectif était de tester s'il est théoriquement possible d'envelopper la bibliothèque BSEC. Cette bibliothèque est fournie pour une multitude de plateformes et de compilateurs, dont le Cortex M0+ et le compilateur arm-none-eabi-gcc.

Bien que le contournement du BSEC puisse être spécifique à notre cas, je vais fournir des indications plus générales sur la façon d'inclure un module C personnalisé dans CircuitPython pour tester rapidement sa fonctionnalité, sur la façon de lier une bibliothèque binaire supplémentaire et sur la façon de construire CircuitPython pour que le module C et la bibliothèque soient inclus. Il y a quelques petits pièges le long du chemin, qui, je l'espère, seront couverts par cet article de blog.

Si vous voulez écrire votre propre module C de CircuitPython, il existe actuellement deux approches différentes. Vous pouvez lire la première dans les directives officielles sur le site web de CircuitPython. Site web d'Adafruit qui sont actuellement obsolètes mais contiennent encore beaucoup d'informations utiles. L'approche recommandée est donc de regarder le code source de CircuitPython et de le suivre comme exemple. Sans entrer dans les détails, il s'agit de placer vos fichiers d'en-tête et de code source dans les répertoires 'shared-bindings' et 'shared-module' respectivement, tout en utilisant les méthodes de wrapper fournies.

Si votre objectif est une utilisation à long terme ou une contribution à la version officielle, vous devez suivre ces directives. Si vous voulez d'abord voir si le code fonctionne sur une plateforme spécifique ou faire quelques tests rapides, l'approche décrite ici est un peu plus facile et demande moins de travail.

Comment utiliser USER_C_MODULES ?

Tout d'abord, nous allons suivre le processus de construction de CircuitPython en utilisant les instructions officielles qui se trouvent à l'adresse suivante ici. Après s'être assuré que tout fonctionne comme prévu, que CircuitPython se construit et fonctionne sur votre plateforme cible, nous pouvons commencer à travailler sur l'inclusion de notre propre code. Cela se fait via le drapeau de construction USER_C_MODULES. Cette fonctionnalité est héritée de MicroPython. Si vous êtes intéressé, vous pouvez jeter un coup d'œil à la version officielle de MicroPython. Documentation MicroPython qui semble s'appliquer également à CircuitPython.

D'abord nous créons un nouveau dossier dans le répertoire CircuitPython et le remplissons avec les fichiers nécessaires :

circuitpython
├──circuitpython/
| ├──data/
| ├───devices/
| ├───docs/
... ...
|
└───externalCmodules/
        └───BSEC_Test
                ├─── bsec_datatypes.h
                ├─── bsec_interface.h
                ├── libalgobsec.a
                ├─── micropython.mk
                └─── testingBSEC.c

Dans ce cas, 'bsec_datatypes.h', 'bsec_interface.h' et 'libalgobsec.a' sont fournis par Bosh. Nous ne devons nous préoccuper que de mircopython.mk et testingBSEC.c.

Il s'avère que CircuitPython inclut déjà un exemple en C (et C++) et son Makefile respectif dans le code source. Vous pouvez les trouver à l'adresse suivante :

circuitpython/exemples/usercmodule/cexample/

Nous pouvons simplement copier le code et le modifier pour notre propre usage. Pour ce faire, nous incluons les fichiers d'en-tête de la bibliothèque que nous voulons utiliser (dans notre cas, #include "bsec_interface.h") et adaptons le code à l'intérieur. Le fichier 'examplemodule.c' est un exemple bien choisi, car il contient une fonction très basique qui prend un nombre entier en entrée et produit un nombre entier en sortie. Pour l'adapter à nos besoins, il a suffi de modifier le nombre de variables d'entrée et de renommer certaines fonctions. Si votre objectif est uniquement de tester la viabilité d'une intégration à grande échelle, cela devrait probablement être suffisant. Cela peut être facilement réalisé en retournant les codes d'erreur et quelques exemples de sortie et en vérifiant si tout se passe comme prévu. Sinon, il n'est pas très difficile d'ajouter de nouvelles fonctions ou d'apporter des changements plus fondamentaux à celles qui existent déjà, et la documentation de MicroPython le décrit bien.

Vous pouvez déjà en rester là. Selon la complexité de votre cas de test, il peut être nécessaire d'ajouter d'autres fonctions, mais dans notre cas, le but principal était simplement de voir si le code fonctionne.

De plus, nous modifions le Makefile - micropython.mk - pour qu'il ressemble à quelque chose comme ceci :

BSEC_TEST_DIR := $(USERMOD_DIR)
# Ajoutez notre fichier C à SRC_USERMOD.
SRC_USERMOD += $(BSEC_TEST_DIR)/testingBSEC.c
# Ajout de la bibliothèque pré-compilée à SRC_USERMOD.
SRC_USERMOD += $(BSEC_TEST_DIR)/libalgobsec.a

Nous allons ensuite simplement construire CircuitPython comme décrit dans la documentation officielle :

  • 1. Allez dans le répertoire du port pour lequel vous voulez construire (Dans notre cas circuitpython/ports/raspberrypi)
  • 2. make BOARD=raspberry_pi_pico USER_C_MODULES=../../../externalCmodules

L'indicateur de construction pointe vers le répertoire dans lequel nous plaçons notre code et nos bibliothèques.

Problèmes pendant le processus de construction

Si vous avez suivi les instructions à la lettre jusqu'à présent, vous rencontrerez le premier problème. Le système de construction ne trouve pas la bibliothèque (libalgobsec.a).

Il semble que le système de construction le recherche à deux endroits différents pendant le processus de construction. Dans notre cas, aux deux endroits suivants :

circuitpython
├──circuitpython/
| ├──data/
| ├───devices/
| ├───docs/
| └───BSEC_Test
| └── libalgobsec.a
...
|
└──externalCmodules/
        └───BSEC_Test
                ├─── bsec_datatypes.h
                ├─── bsec_interface.h
                ├─── libalgobsec.a
                ├── micropython.mk
                └─── testingBSEC.c

Cet obstacle trouve apparemment son origine dans la façon dont le système de compilation interagit avec le drapeau de compilation USER_C_MODULE. Même après avoir passé un certain temps à étudier la question, toutes les solutions que j'ai essayées ont échoué d'une manière ou d'une autre ou ont introduit une complexité supplémentaire. Si quelqu'un lit ceci et trouve une solution plus propre, j'apprécierais que vous la partagiez ! Dans ce cas, je modifierai le texte en conséquence.

Heureusement, le problème peut être facilement évité en dupliquant la bibliothèque et en la plaçant également au deuxième emplacement. Bien que ce ne soit pas la solution la plus propre, il n'y a pas de différence de taille dans la construction finale.

Si vous essayez de faire fonctionner votre propre code, cela devrait être tout ce dont vous avez besoin pour permettre des tests supplémentaires. En contournant ce petit problème, votre propre code et vos bibliothèques devraient déjà être compilés, liés correctement, construits et appelables.

Mais qu'en est-il de notre cas type, le BSEC ?

Si vous suivez ce processus, vous serez rapidement déçu. Vous vous retrouverez confronté à des messages d'erreur concernant des fonctions non définies :

/home/hanno/System/bin/ARM_GCC/gcc-arm-none-eabi-10-2020-q4-major/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld : IaqEstimator.c :(.text+0xea) : référence indéfinie à `fminf'.
/home/hanno/System/bin/ARM_GCC/gcc-arm-none-eabi-10-2020-q4-major/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld : IaqEstimator.c :(.text+0x112) : référence indéfinie à `fmaxf'.
/home/hanno/System/bin/ARM_GCC/gcc-arm-none-eabi-10-2020-q4-major/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../../arm-none-eabi/bin/ld : IaqEstimator.c :(.text+0x162) : référence indéfinie à `fmaxf'.
/home/hanno/System/bin/ARM_GCC/gcc-arm-none-eabi-10-2020-q4-major/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../../arm-none-eabi/bin/ld : IaqEstimator.c :(.text+0x190) : référence indéfinie à `fmaxf'.
/home/hanno/System/bin/ARM_GCC/gcc-arm-none-eabi-10-2020-q4-major/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../../arm-none-eabi/bin/ld : IaqEstimator.c :(.text+0x198) : référence indéfinie à `fminf'.
........

Eh bien, il s'avère qu'il existe actuellement un obstacle majeur. Puisque CircuitPython est destiné à fonctionner sur des microcontrôleurs et que la mémoire est souvent limitée sur ceux-ci, les développeurs ont écrit et utilisent leur propre bibliothèque mathématique. Bien que cela puisse ne pas poser de problème selon votre cas d'utilisation personnel, le BSEC utilise la fonctionnalité fournie par la bibliothèque mathématique C standard. Et bien qu'il existe un buildflag pour construire CircuitPython avec la bibliothèque mathématique C standard (circuitpython/ports/raspberrypi/mpconfigport.mk → INTERNAL_LIBM), il semble actuellement être défunt. Le faire fonctionner à nouveau nécessiterait du temps supplémentaire, une bonne connaissance du système de construction et est allé un peu trop loin pour notre petit cas de test.

Mais il existe une solution (un peu "bricolée") pour que le BSEC s'initialise et fournisse quelques informations de base, prouvant que son utilisation est probablement possible pour l'intégrer en théorie. Il suffit d'envelopper ou de réimplémenter les fonctions manquantes dans notre code C en utilisant les fonctions mathématiques intégrées fournies par internal_libm. Cela peut ressembler à ce qui suit :

float fminf ( float x, float y ) {
    if (isnan(x))
		retourne y ;
	si (isnan(y))
		retourne x ;
	// gère les zéros signés, voir C99 Annexe F.9.9.2
	if (signbit(x) != signbit(y))
		retourne signbit(x) ? x : y ;
	return x < y ? x : y ;
}

Dans la plupart des cas, le code a été repris ou légèrement adapté à partir du document compilateur arm-none-eabi-gcc qui est open source et peut être consulté sans problème.

Maintenant que CircuitPython se construit, le BSEC est appelable et retourne les valeurs appropriées. Malgré cela, certains problèmes persistent si l'on essaie d'utiliser certaines fonctionnalités. Il semble probable que ces problèmes soient le résultat du processus de compilation de CircuitPython, car ils disparaissent si le BSEC est utilisé nativement avec C. Pour que le BSEC fonctionne dans son intégralité et soit facilement appelable depuis CircuitPython, un travail supplémentaire est nécessaire et probablement une certaine coordination pour avoir accès à une version correcte du BSEC (qui est actuellement construit en utilisant la version 9-2019-q4-major de la chaîne d'outils ARM GNU, alors que CircuitPython utilise la version 10-2020-q4-major) et pour modifier et adapter légèrement le processus de construction de CircuitPython. Il reste à voir si cela sera fait par nous à l'avenir, mais j'espère que ce billet de blog pourra fournir quelques indications aux développeurs qui souhaitent tester leur propre code en utilisant le buildflag USER_C_MODULES et qui veulent éviter certains pièges.

S'il y a une mise à jour de nos progrès, cette page sera mise à jour en conséquence.

Si vous avez d'autres questions que je n'ai pas abordées suffisamment en détail (ou pas du tout), n'hésitez pas à laisser un commentaire ou à m'envoyer un courriel ! Je serai heureux d'y répondre.

Laissez un commentaire

Vous devez être connectés afin de publier un commentaire.