Erlang pour l'IoT

La vague d'intérêt pour les dispositifs microélectroniques et leur interaction les uns avec les autres pour les besoins industriels et domestiques a conduit au développement d'un grand nombre de concepteurs pour le développement sur la base de SoC (systèmes sur puce) suffisamment puissants, plutôt miniatures par rapport aux solutions de microcontrôleurs, mais contenant déjà un système d'exploitation à part entière. Le développement d'applications pour de tels concepteurs ne diffère pratiquement pas du développement de serveur habituel, sauf que la limite de ressources doit toujours être gardée à l'esprit.



À mesure que la productivité et les capacités augmentent, la pratique consistant à utiliser des langages interprétés de haut niveau tels que Lua, Python et JS pour le développement d'applications prend de plus en plus d'ampleur. Certaines langues pénètrent progressivement dans les «jeunes frères», microcontrôleurs, cependant, sous une forme très limitée.

Il y a plusieurs raisons à cela:

  • prototypage rapide - avec tout le respect dû au langage C, qui développe principalement des microcontrôleurs, il est très difficile de le qualifier de concis. Les langages de haut niveau vous permettent d'écrire moins de code et de simplifier les modifications de ce qui est déjà écrit, ce qui est très important au stade du prototype;
  • gestion automatique de la mémoire et résumé des mécanismes de calcul complexes - je pense qu'il n'a pas besoin de commentaires, les deux processus manuels avec un volume de projet suffisant se transforment en une source de beaucoup de maux de tête;
  • simplification du débogage et des tests - le code interprété est plus facile à vérifier sur le poste de travail jusqu'au test sur le terrain;
  • lutter contre la complexité - souvent, une grande productivité donne lieu à un désir pragmatique naturel de pousser plus de prétraitement et d'analyse sur l'appareil, ce qui n'ajoute pas la simplicité au développement.

Hélas, vous devez payer pour toutes les commodités. Dans ce cas, le prix de la commodité correspond aux ressources, aux performances et à la taille de code les plus précieuses (dans de nombreux cas, vous devez transporter un environnement d'exécution plutôt volumineux). Par conséquent, l'application sur le terrain de langages de haut niveau dans SoC et SoM est une chose ambiguë et, à certains endroits, un compromis.

Nous utilisons le langage Erlang pour le développement, en l'appliquant à la fois pour sa destination (création d'applications serveur et d'un plan de contrôle), et très inhabituel - applications Web. Par conséquent, l'idée d'utiliser l'infrastructure de ce langage pour créer des solutions IoT est née bien avant l'apparition de cartes sur lesquelles le runtime Erlang pouvait fonctionner sans problème.

Les raisons d'utiliser Erlang étaient nombreuses, les plus importantes:

  • Erlang est très pratique pour analyser et créer des séquences binaires. L'appariement des formes associé au traitement des données binaires permet aux protocoles binaires d'être implémentés très rapidement et sans mots;
  • manque de variables globales et immuabilité pour la plupart des données - vous permet d'écrire et, non moins important, de maintenir des applications fiables dans lesquelles il est difficile de modifier accidentellement quelque chose de mal;
  • Processus de messagerie légers très similaires à ceux auxquels les développeurs intégrés doivent faire face. Il s'agit essentiellement d'une procédure d'initialisation et d'une procédure qui traite les messages entrants dans une boucle infinie, modifiant l'état interne. Il est très similaire à Arduino, seulement il peut y avoir de nombreux processus et ils fonctionnent en parallèle, de plus, dans l'intervalle entre les messages, la procédure de traitement peut être modifiée à la volée (rechargement de code à chaud), ce qui est très pratique lorsque vous devez corriger des erreurs mineures ou ajuster le comportement;
  • Je pense que l'environnement isolé et l'allocation automatique de mémoire n'ont pas besoin d'explication;
  • multiplateforme - le code d'octet pour le runtime ERTS peut être compilé sur la machine du développeur, puis transféré vers le périphérique cible sans problème;
  • excellents outils d'introspection - la possibilité de se connecter à une application en cours d'exécution sur le réseau et de voir qu'elle ralentit si souvent est souvent très utile.

La première carte de travail sur laquelle nous avons essayé Erlang était Carambola 2 des développeurs lituaniens de 8 appareils , assemblés sur la populaire puce AR9331. La première version de cette carte, hélas, ne disposait pas de suffisamment de mémoire flash pour s'adapter à l'exécution. Mais la deuxième version s'est déjà tout à fait autorisée à accueillir à la fois ERTS et une petite application.



L’installation a été réalisée par une méthode classique pour ce type d’appareil - assemblage d’une image OpenWRT contenant Erlang, puis flashée dans la mémoire flash de l’appareil. Le premier lancement de l'environnement, hélas, a conduit à la déception - tout a été suspendu. J'ai déjà expliqué les raisons de cela lors de la conférence InoThings 2018 , mais, hélas, comme il s'est avéré plus tard, j'ai trompé mes collègues en nommant à tort la source d'un tel comportement.

Je vais raconter brièvement. Lors de l'utilisation de fichiers, la machine virtuelle ERTS utilise le type off_t , dont la taille dans la distribution est calculée lorsque l'assembly est autoconfiguré (s'il s'exécute sur la plate-forme cible) ou est substitué à partir de l'environnement de compilation croisée, comme cela s'est produit dans le cas d'OpenWRT. Il n'est pas clair pourquoi, mais dans les paramètres des processeurs MIPS et des dérivés, le fichier de configuration d'assemblage est deux fois plus volumineux qu'il ne l'est en fait. Le problème ne se poserait pas si le code de la machine virtuelle n'utilisait pas de directives de préprocesseur comme

#if SIZEOF_OFF_T == 4 

et une banale vérification dans le code (je soupçonne que le résultat final de la compilation serait le même, mais il n'y avait pas assez de fusible pour vérifier):

 if (sizeof(off_t) == 4) { 

Par conséquent, l'ERTS collecté lors de la première itération lors de la tentative de lecture du fichier ~ / .erlang.cookie (une sorte de mot de passe pour l'identification lors de l'interaction réseau) au début a correctement reçu les ordures en ordre élevé et s'est écrasé avec un bang.

Le patch patch et le package avec l'avant-dernière version d'ERTS pour OpenWRT peuvent être téléchargés depuis GitHub . De plus, aucun problème n'a encore été observé, tout s'est déroulé comme prévu.

La deuxième plate-forme matérielle sur laquelle nous avons essayé Erlang était le concepteur LinkIt Smart 7688 de Mediatek et SeeedStudio , spécialement conçu pour le prototypage rapide et l'apprentissage des bases. Cette carte est simplement l'apothéose de la débauche en termes de ressources - la fréquence du noyau MIPS a augmenté de 1,5 fois, plus de RAM (pour ERTS c'est important, GC ne somnole pas) et plus de mémoire flash, ainsi que la présence d'une carte microSD et la possibilité d'utiliser le coprocesseur Atmel Atmega 32U4 dans la version Duo pour travailler avec des périphériques.

En général, la plate-forme était très adaptée pour continuer le banquet et la présence de plug-ins supplémentaires qui se connectent sans soudure vous permet d'assembler rapidement et conditionnellement un banc d'essai sur votre genou.

La plate-forme est livrée avec un logiciel avec sa propre interface Web, ainsi que des bibliothèques Python et NodeJS pour le développement. L'écosystème de l'assemblage n'a pas changé - il s'agit toujours d'OpenWRT. Si pour une raison quelconque vous trouvez toute cette diversité superflue, alors sur le référentiel ci-dessus, il y a des packages contenant un ensemble minimal de composants requis . Après l'assemblage, l'image flash est écrite sur l'appareil et après le redémarrage, vous pouvez utiliser REPL en toute sécurité.

Pour créer des applications sur Erlang pour IoT, un problème architectural doit être résolu.

Le langage a été conçu et développé en tenant compte de ce qui servira de plan de contrôle, c'est-à-dire couche de contrôle, tout en travaillant avec du fer devait passer par FFI. Trois types d'interaction sont prévus à cet effet:

  1. ports (ports) - processus de travail séparés écrits dans n'importe quelle langue, dont l'interaction se produit via des flux d'entrée / sortie. Ils peuvent être redémarrés en cas de chute, mais en raison de la méthode d'interaction, leur productivité en termes de communication est faible (cependant, cela nous suffira);
  2. Fonctions NIF - ressemblent aux fonctions standard du langage, mais leur appel génère l'exécution du code compilé dans l'espace du runtime. En cas d'erreur, ils peuvent faire glisser toute la machine virtuelle derrière eux.
  3. Noeud C - lorsque l'ensemble du travail est effectué dans un processus distinct et que l'interaction est effectuée comme avec un environnement d'exécution fonctionnant séparément sur le réseau. Nous ne considérerons pas cette option en raison des frais généraux suffisamment élevés dans le cadre d'un appareil faible.

Le dilemme est le suivant: nous ne pouvons transporter que des objets vers FFI (c'est-à-dire la prise en charge de GPIO, I2C, SPI, PWM, UART, etc.), et nous pouvons interagir directement avec des capteurs et d'autres appareils sur Erlang, ou au contraire, pour transférer entièrement les pilotes de périphériques vers le code des modules externes, laissant l'application recevoir les données brutes et les traiter, dans ce cas, il peut être judicieux d'utiliser le code déjà écrit.

Nous avons décidé d'utiliser la première option. Il y a plusieurs raisons à cela:

  • parce que nous le pouvons;
  • comme je l'ai déjà mentionné, Erlang est livré avec des outils suffisamment puissants pour assembler et désassembler des séquences binaires, alors que c'est assez simple en mathématiques, ce qui vous permet de ne pas vous soucier de la protection contre les débordements et autres magies utilisées pour traiter les résultats. Non pas que cette magie épouvantail, mais elle affecte les néophytes de manière choquante;
  • intuitivement, il semblait que les pilotes dans un langage de haut niveau seraient plus simples et plus fiables (un processus bloqué redémarrerait le superviseur, ce qui conduirait à la réinitialisation de l'appareil contrôlé).

Par conséquent, la bibliothèque ErlangALE a été rapidement trouvée, qui contenait un support déjà implémenté pour GPIO, I2C et SPI via les interfaces du noyau, et les développeurs de la plate-forme matérielle, à leur tour, s'étaient déjà occupés d'eux. La bibliothèque pour travailler avec UART a déjà été testée, et nous avons ajouté erlexec , une application qui vous permet de créer et de gérer des processus OS.

Toutes ces applications utilisaient des ports (processus binaires lancés séparément) pour fonctionner avec l'équipement et le système d'exploitation, ce qui nécessitait une prise en charge de la compilation croisée pour les langages C et C ++, pour lesquels un script shell assez élaboré a été écrit qui configurait l'environnement de génération pour utiliser les compilateurs nécessaires.

Pour tester les décisions que nous avons prises, nous avons assemblé un simple appareil LinkIt Smart 7866, deux appareils I2C (capteur de température et de pression BMP280 et un écran OLED de 64 pixels 128) et un module GPS USB qui envoie des données via UART. GPIO a été testé sur la LED de la carte, cela fonctionne, et la connexion de l'écran SPI semblait inutile à ce stade de complication.



Il s'est avéré être une application assez compacte et simple, le code source peut être consulté sur Github.

Je ne vais pas me plonger dans les fragments de code, mais essayer de décrire dans un aperçu comment fonctionne l'application.

Les pilotes de tous les périphériques sont réalisés sous la forme de processus gen_server, ce qui est pratique car il vous permet d'ajouter des paramètres supplémentaires et, parfois, l'état du périphérique à l'état. Ce dernier peut être vu dans l'exemple de uart_gps - les données de l'UART arrivent de manière asynchrone, sont analysées par l'analyseur NMEA0183 et les résultats sont écrits dans l'état du processus, d'où ils sont obtenus sur demande.

Le cycle principal de l'application est décrit dans le module gps_temp_display - chaque seconde, le processus lit les données GPS et demande l'état de la température et de la pression au BMP280 et les affiche sur l'écran OLED. Pour votre intérêt, vous pouvez regarder les pilotes d' affichage et de capteur BMP280 - tout s'est avéré assez succinct, 150-170 lignes par module.

En général, la tâche ci-dessus (écrire le code du pilote, tout combiner en une seule application, assemblage et test) a pris environ quatre soirées, deux heures en moyenne, c'est-à-dire à proprement parler, quelques jours ouvrables. Il me semble personnellement que c'est un bon indicateur pour essayer d'utiliser Erlang dans des applications embarquées plus complexes et sérieuses qui ne nécessitent pas de restrictions strictes en temps réel.

Bien sûr, nos tentatives d'utiliser Erlang pour les systèmes embarqués ne sont en aucun cas les seules. Il existe plusieurs projets intéressants à cet égard:

  • nerves-project.org - vise à créer un écosystème intégré basé sur Elixir;
  • www.grisp.org - une approche encore plus radicale, impliquant le lancement d'ERTS directement sur le matériel (système Erlang en métal nu);
  • github.com/bettio/AtomVM et github.com/cloudozer/ling - tente de créer une version compacte d'ERTS, adaptée à une utilisation dans les microcontrôleurs et les SoC / SoM faibles.

Source: https://habr.com/ru/post/fr417245/


All Articles