Table des matières:

Aller au-delà de la normeFirmata - Revisited: 5 Steps
Aller au-delà de la normeFirmata - Revisited: 5 Steps

Vidéo: Aller au-delà de la normeFirmata - Revisited: 5 Steps

Vidéo: Aller au-delà de la normeFirmata - Revisited: 5 Steps
Vidéo: Comment Aller au-delà de l'ego et se reconnecter au réel ? 2024, Juillet
Anonim
Aller au-delà de la normeFirmata - Revisited
Aller au-delà de la normeFirmata - Revisited

Il y a peu de temps, j'ai été contacté par le Dr Martyn Wheeler, un utilisateur de pymata4, pour obtenir des conseils sur l'ajout de la prise en charge du capteur d'humidité/température DHT22 à la bibliothèque pymata4. La bibliothèque pymata4, en conjonction avec son homologue Arduino, FirmataExpress, permet aux utilisateurs de contrôler et de surveiller leurs appareils Arduino à distance. En quelques séries d'échanges d'e-mails, le Dr Wheeler a réussi à modifier à la fois pymata4 et FirmataExpress. En conséquence, la prise en charge des capteurs DHT22 et DHT11 fait désormais partie intégrante de pymata4 et FirmataExpress.

En mai 2014, j'ai écrit un article sur l'ajout de la prise en charge de Firmata pour des appareils supplémentaires. En réfléchissant à cet article, j'ai réalisé à quel point cela avait changé depuis que j'ai pris le stylo sur papier pour cet article. En plus de cet article, le Dr Wheeler a documenté ses efforts, et vous voudrez peut-être également le vérifier.

FirmataExpress est basé sur StandardFirmata, et la structure du répertoire StandardFirmata a évolué. De plus, l'API pymata4 est également assez différente de l'API PyMata originale de 2014. J'ai pensé que ce serait le moment idéal pour revoir et mettre à jour cet article. En utilisant le travail du Dr Wheeler comme base, explorons comment étendre les fonctionnalités de pymata4/FirmataExpress.

Avant de commencer - Quelques informations générales sur Arduino/Firmata

Alors, qu'est-ce que Firmata ? Citant la page Web de Firmata, "Firmata est un protocole générique pour communiquer avec des microcontrôleurs à partir d'un logiciel sur un ordinateur hôte."

Arduino Firmata utilise une interface série pour transporter à la fois les informations de commande et de rapport entre un microcontrôleur Arduino et un PC, généralement à l'aide d'une liaison série/USB définie sur 57600 bps. Les données transférées via ce lien sont binaires et le protocole est implémenté dans un modèle client/serveur.

Le côté serveur est téléchargé sur un microcontrôleur Arduino sous la forme d'un croquis Arduino. L'esquisse StandardFirmata, incluse avec l'IDE Arduino, contrôle les broches d'E/S Arduino, selon les commandes du client. Il signale également les changements de broche d'entrée et d'autres informations de rapport au client. FirmataExpress est une version étendue de StandardFirmata. Il fonctionne à une vitesse de liaison série de 115200 bps.

Le client Arduino utilisé pour cet article est pymata4. C'est une application Python qui s'exécute sur un PC. Il envoie à la fois des commandes et reçoit des rapports du serveur Arduino. Étant donné que pymata4 est implémenté en Python, il fonctionne sur les ordinateurs Windows, Linux (y compris Raspberry Pi) et macOS.

Pourquoi utiliser Firmata ?

Les microcontrôleurs Arduino sont de merveilleux petits appareils, mais les ressources processeur et mémoire sont quelque peu limitées. Pour les applications gourmandes en processeur ou en mémoire, il n'y a souvent pas d'autre choix que de décharger la demande de ressources sur un PC pour que l'application réussisse.

Mais ce n'est pas la seule raison d'utiliser StandardFirmata. Lors du développement d'applications Arduino plus légères, un PC peut fournir des outils et des capacités de débogage qui ne sont pas directement disponibles sur un microcontrôleur Arduino. L'utilisation d'un client et d'un serveur « fixes » permet de confiner la complexité de l'application à un PC, qui est plus facilement géré. Une fois l'application perfectionnée, elle peut être traduite en une esquisse Arduino personnalisée et autonome.

Pourquoi utiliser pymata4 ?

Étant son auteur, bien sûr, je suis partial. Cela étant dit, c'est le seul client Firmata basé sur Python qui a été maintenu en permanence au cours des dernières années. Il fournit une API intuitive et facile à utiliser. En plus des croquis basés sur StandardFirmata, il prend en charge Firmata via WiFi pour des appareils tels que l'ESP-8266 lors de l'utilisation du croquis StandardFirmataWifI.

De plus, pymata4 a été conçu pour être facilement étendu par un utilisateur afin de prendre en charge des capteurs et actionneurs supplémentaires non actuellement pris en charge par StandardFirmata.

Étape 1: Comprendre le protocole Firmata

Comprendre le protocole Firmata
Comprendre le protocole Firmata

Le protocole de communication Arduino Firmata est dérivé du protocole MIDI, qui utilise un ou plusieurs octets 7 bits pour représenter les données.

Firmata a été conçu pour être extensible par l'utilisateur. Le mécanisme qui fournit cette extensibilité est le protocole de messagerie System Exclusive (SysEx).

Le format d'un message SysEx, tel que défini par le protocole Firmata, est indiqué dans l'illustration ci-dessus. Il commence par un octet START_SYSEX avec une valeur fixe de 0xF0 hexadécimal et est suivi d'un octet de commande SysEx unique. La valeur de l'octet de commande doit être dans la plage hexadécimale 0x00-0x7F. L'octet de commande est alors suivi d'un nombre indéterminé d'octets de données de 7 bits. Enfin, le message se termine par un octet END_SYSEX, avec une valeur fixe de 0xF7 hexadécimal.

Codage/décodage des données Firmata

Étant donné que la partie des données utilisateur d'un message SysEx se compose d'une série d'octets de 7 bits, vous vous demandez peut-être comment on représente une valeur supérieure à 128 (0x7f) ? Firmata code ces valeurs en les désassemblant en plusieurs blocs d'octets de 7 bits avant que les données ne soient rassemblées sur la liaison de données. L'octet le moins significatif (LSB) d'un élément de données est envoyé en premier, suivi par les composants de plus en plus significatifs de l'élément de données par convention. L'octet de poids fort (MSB) de la donnée est la dernière donnée envoyée.

Comment cela marche-t-il?

Disons que nous souhaitons incorporer une valeur 525 dans la partie données d'un message SysEx. Étant donné qu'une valeur de 525 est clairement supérieure à une valeur de 128, nous devons la diviser ou la désassembler en « morceaux » d'octets de 7 bits.

Voici comment cela se fait.

La valeur de 525 en décimal équivaut à la valeur hexadécimale de 0x20D, une valeur de 2 octets. Pour obtenir le LSB, nous masquons la valeur en la faisant ET avec 0x7F. Les implémentations "C" et Python sont présentées ci-dessous:

// Implémentation "C" pour isoler LSB

int max_distance_LSB = max_distance & 0x7f; // masque l'octet inférieur # Implémentation Python pour isoler LSB max_distance_LSB = max_distance & 0x7F # masque l'octet inférieur

Après masquage, max_distance_LSB contiendra 0x0d. 0x20D & 0x7F = 0x0D.

Ensuite, nous devons isoler le MSB pour cette valeur de 2 octets. Pour ce faire, nous allons décaler la valeur de 0x20D vers la droite, de 7 places.

// Implémentation "C" pour isoler le MSB d'une valeur de 2 octets

int max_distance_MSB = max_distance >> 7; // décaler l'octet de poids fort # Implémentation Python pour isoler le MSB de la valeur de 2 octets max_distance_MSB = max_distance >> 7 # décaler pour obtenir l'octet supérieur Après le décalage, max_distance_MSB contiendra une valeur de 0x04.

Lorsque les données marshalées « en morceaux » sont reçues, elles doivent être réassemblées en une valeur unique. Voici comment les données sont réassemblées à la fois en "C" et en Python

// Implémentation "C" pour réassembler les 2 octets, // 7 valeurs de bits en une seule valeur int max_distance = argv[0] + (argv[1] << 7); # Implémentation Python pour réassembler les valeurs de 2 octets, # 7 bits en une seule valeur max_distance = data[0] + (data[1] << 7)

Après remontage, la valeur est à nouveau égale à 525 décimal ou 0x20D hexadécimal.

Ce processus de désassemblage/réassemblage peut être effectué par le client ou le serveur.

Étape 2: Commençons

La prise en charge d'un nouveau périphérique nécessite des modifications à la fois du serveur résident Arduino et du client Python résident du PC. Les travaux du Dr Wheeler seront utilisés pour illustrer les modifications nécessaires.

L'étape la plus importante est peut-être de décider si vous souhaitez intégrer une bibliothèque de périphériques de support existante dans le côté Arduino de l'équation ou écrire la vôtre. Il est recommandé que si vous pouvez trouver une bibliothèque existante, il est beaucoup plus simple de l'utiliser que d'écrire la vôtre à partir de zéro.

Pour la prise en charge des appareils DHT, le Dr Wheeler a basé son code d'extension sur la bibliothèque DHTNew. Très intelligemment, le Dr Wheeler a divisé les fonctionnalités de la bibliothèque DHTNew entre les côtés Arduino et pymata4 de l'équation pour fournir un blocage minimal du côté Arduino.

Si nous regardons DHTNew, il effectue tout ce qui suit:

  • Définit le mode de sortie numérique de la broche sélectionnée.
  • Horloge un signal codé pour récupérer les dernières valeurs d'humidité et de température.
  • Vérifie et signale toute erreur.
  • Calcule les valeurs de température et d'humidité lisibles par l'homme à partir des données brutes récupérées.

Pour que les choses restent aussi efficaces que possible du côté de FirmataExpress, le Dr Wheeler a déchargé les routines de conversion de données de l'Arduino vers Pymata4.

Étape 3: Modification de FirmataExpress pour la prise en charge DHT

L'arborescence du répertoire FirmataExpress

Vous trouverez ci-dessous tous les fichiers qui composent le référentiel FirmataExpress. Cette arborescence est identique à celle de StandardFiramata, juste que certains des noms de fichiers reflètent le nom du référentiel.

Les fichiers qui doivent être modifiés sont ceux qui ont un astérisque (*) à côté d'eux.

FirmataExpress

├── * Tableaux.h

exemples

└── FirmataExpress

│ ├── boardx

├── * FirmataExpress.ino

│ ├── LICENCE.txt

└── Makefile

├── * FirmataConstants.h

├── * FirmataDefines.h

FirmataExpress.cpp

FirmataExpress.h

FirmataMarshaller.cpp

FirmataMarshaller.h

FirmataParser.cpp

FirmataParser.h

Examinons chacun des fichiers et les modifications qui ont été apportées.

Tableaux.h

Ce fichier contient des définitions de macro de type broche pour chacun des types de cartes pris en charge. Il définit le nombre maximum d'appareils pris en charge lorsque plusieurs appareils doivent être pris en charge.

Pour l'appareil DHT, jusqu'à 6 appareils peuvent être connectés à la fois et cette valeur est définie comme:

#ifndef MAX_DHTS

#définir MAX_DHTS 6 #endif

En outre, des macros de type pin peuvent être définies en option pour le nouveau périphérique, soit pour tous les types de cartes, soit uniquement pour celles qui vous intéressent. Ces macros sont principalement utilisées à des fins de rapport et ne sont pas utilisées pour contrôler les appareils. Ces macros définissent à la fois les broches qui prennent en charge l'appareil:

#définir IS_PIN_DHT(p) (IS_PIN_DIGITAL(p) && (p) - 2 < MAX_DHTS)

Ainsi qu'une macro pour définir une conversion de numéro de broche.

#define PIN_TO_DHT(p) PIN_TO_DIGITAL(p)

FirmataConstants.h

Ce fichier contient le numéro de version du firmware, que vous souhaiterez peut-être modifier pour garder une trace de la version que vous avez chargée sur votre Arduino. Il contient également les valeurs des messages Firmata, y compris les messages Firmata SysEx.

Vous devrez attribuer un nouveau message ou un ensemble de messages pour votre appareil dans ce fichier. Pour la DHT, deux messages ont été ajoutés. L'un configure une broche en tant que broche « DHT », et l'autre, en tant que message de rapporteur, lors du renvoi des dernières données DHT au client.

statique const int DHT_CONFIG = 0x64;

statique const int DHT_DATA = 0x65;

Les modes de broche sont également spécifiés dans ce fichier. Pour la DHT, un nouveau mode pin a été créé:

statique const int PIN_MODE_DHT = 0x0F; // broche configurée pour DHT

Lors de l'ajout d'un nouveau mode de broche, le TOTAL_PIN_MODES doit être ajusté:

statique const int TOTAL_PIN_MODES = 17;

FirmataDefines.h

Ce fichier doit être mis à jour pour refléter les nouveaux messages ajoutés à FirmataConstants.h:

#ifdef DHT_CONFIG#undef DHT_CONFIG #endif #define DHT_CONFIG firmata::DHT_CONFIG // Requête DHT #ifdef DHT_DATA #undef DHT_DATA #endif #define DHT_DATA firmata::DHT_DATA // Réponse DHT #ifdef PIN_MODE_DHT_undef PINHT_MODE #undef PINHT_MODE::PIN_MODE_DHT

FirmataExpress.ino

Dans cette discussion, nous couvrirons les « points forts » des modifications apportées à cette esquisse Arduino.

Pour que FirmataExpress puisse prendre en charge jusqu'à six appareils DHT simultanément, 3 matrices ont été créées pour garder une trace de chaque numéro de broche associé à l'appareil, sa valeur WakeUpDelay et le type d'appareil, c'est-à-dire DHT22 ou DHT11:

// Capteurs DHT

int numActiveDHTs = 0; // nombre de DHT attachés uint8_t DHT_PinNumbers[MAX_DHTS]; uint8_t DHT_WakeUpDelay[MAX_DHTS]; uint8_t DHT_TYPE[MAX_DHTS];

Étant donné que les deux types d'appareils nécessitent environ 2 secondes entre les lectures, nous devons nous assurer que nous lisons chaque DHT une seule fois dans le laps de temps de 2 secondes. Certains appareils, tels que les appareils DHT et les capteurs de distance HC-SR04, ne sont accessibles que périodiquement. Cela leur laisse le temps d'interagir avec leur environnement.

uint8_t nextDHT = 0; // index dans dht pour le prochain périphérique à lire

uint8_t currentDHT = 0; // Garde une trace du capteur actif. int dhtNumLoops = 0; // Nombre cible de fois dans la boucle b4 accédant à un DHT int dhtLoopCounter = 0; // Compteur de boucles

Configuration et lecture du périphérique DHT

Lorsque FirmataExpress reçoit une commande SysEx pour configurer une broche pour le fonctionnement DHT, il vérifie que le nombre maximum d'appareils DHT n'a pas été dépassé. Si le nouveau DHT peut être pris en charge, les tableaux DHT sont mis à jour. Si le type DHT est inconnu, un message de chaîne SysEx est créé et renvoyé à pymata4

cas DHT_CONFIG: int DHT_Pin = argv[0]; int DHT_type = argv[1]; if (numActiveDHTs < MAX_DHTS) { if (DHT_type == 22) { DHT_WakeUpDelay[numActiveDHTs] = 1; } else if (DHT_type == 11) { DHT_WakeUpDelay[numActiveDHTs] = 18; } else { Firmata.sendString("ERREUR: TYPE DE CAPTEUR INCONNU, LES CAPTEURS VALABLES SONT 11, 22"); Pause; } // teste le capteur DHT_PinNumbers[numActiveDHTs] = DHT_Pin; DHT_TYPE[numActiveDHTs] = DHT_type; setPinModeCallback(DHT_Pin, PIN_MODE_DHT);

FirmataExpress tente alors de communiquer avec le périphérique DHT. S'il y a des erreurs, il forme un message SysEx avec les données d'erreur et renvoie le message SysEx à pymat4. La variable _bits contient les données renvoyées par le périphérique DHT pour un traitement supplémentaire par pymata4 si vous le souhaitez.

Firmata.write(START_SYSEX);

Firmata.write(DHT_DATA); Firmata.write(DHT_Pin); Firmata.write(DHT_type); pour (uint8_t i = 0; i > 7 & 0x7f); } Firmata.write(abs(rv)); Firmata.write(1); Firmata.write(END_SYSEX);

Si des données valides sont renvoyées, le nombre de DHT actives est incrémenté. Une variable qui garde une trace du nombre d'itérations de boucle à terminer avant de vérifier la prochaine DHT pour les données est également ajustée. Cette variable garantit que quel que soit le nombre de DHT ajoutés au système, ils seront tous lus dans un délai de 2 secondes.

int rv = readDhtSensor(numActiveDHTs);

if (rv == DHTLIB_OK) { numActiveDHTs++; dhtNumLoops = dhtNumLoops / numActiveDHTs; // tout va bien }

Si un ou plusieurs appareils DHT ont été configurés dans la fonction de boucle du sketch, le prochain appareil DHT est lu. Soit les données valides, soit leur état d'erreur sont renvoyées à pymata4 sous la forme d'un message SysEx:

if (dhtLoopCounter++ > dhtNumLoops) { if (numActiveDHTs) { int rv = readDhtSensor(nextDHT); uint8_t current_pin = DHT_PinNumbers[nextDHT]; uint8_t current_type = DHT_TYPE[nextDHT]; dhtLoopCounter = 0; currentDHT = nextDHT; if (nextDHT++ >= numActiveDHTs - 1) { nextDHT = 0; } if (rv == DHTLIB_OK) { // TEST CHECKSUM uint8_t sum = _bits[0] + _bits[1] + _bits[2] + _bits[3]; if (_bits[4] != somme) { rv = -1; } } // renvoie le message avec un état d'erreur Firmata.write(START_SYSEX); Firmata.write(DHT_DATA); Firmata.write(current_pin); Firmata.write(current_type); for (uint8_t i = 0; i < sizeof(_bits) - 1; ++i) { Firmata.write(_bits); // Firmata.write(_bits; } Firmata.write(abs(rv)); Firmata.write(0); Firmata.write(END_SYSEX); } }

Le code utilisé pour communiquer avec l'appareil DHT est directement dérivé de la bibliothèque DHTNew:

int readDhtSensor(int index){

// INIT BUFFERVAR POUR RECEVOIR LES DONNÉES uint8_t mask = 128; uint8_t idx = 0; // TAMPON VIDE // memset(_bits, 0, sizeof(_bits)); for (uint8_t i = 0; i 5 BYTES for (uint8_t i = 40; i != 0; i--) { loopCnt = DHTLIB_TIMEOUT; while (digitalRead(pin) == LOW) { if (--loopCnt == 0) return DHTLIB_ERROR_TIMEOUT; } uint32_t t = micros(); loopCnt = DHTLIB_TIMEOUT; while (digitalRead(pin) == HIGH) { if (--loopCnt == 0) return DHTLIB_ERROR_TIMEOUT; } if ((micros() - t) > 40) { _bits[idx] |= masque; } masque >>= 1; if (masque == 0) // octet suivant? { masque = 128; idx++; } } renvoie DHTLIB_OK; }

Étape 4: Modification de Pymata4 pour la prise en charge DHT

private_constants.h

Pour prendre en charge le DHT, nous devons ajouter à la fois le nouveau type de broche et les messages SysEx à ce fichier:

# modes de broche INPUT = 0x00 # broche définie comme entrée OUTPUT = 0x01 # broche définie comme sortie ANALOG = 0x02 # broche analogique en mode analogInput PWM = 0x03 # broche numérique en mode de sortie PWM SERVO = 0x04 # broche numérique en mode de sortie Servo I2C = 0x06 # broche incluse dans la configuration I2C STEPPER = 0x08 # n'importe quelle broche en mode pas à pas SERIAL = 0x0a PULLUP = 0x0b # N'importe quelle broche en mode pullup SONAR = 0x0c # N'importe quelle broche en mode SONAR TONE = 0x0d # N'importe quelle broche en mode tonalité PIXY = 0x0e # réservé au mode caméra pixy DHT = 0x0f # capteur DHT IGNORE = 0x7f # messages de commande DHT SysEx DHT_CONFIG = 0x64 # commande de configuration dht DHT_DATA = 0x65 # réponse du capteur dht

Le type de broche ajouté et les commandes SysEx doivent correspondre aux valeurs de FirmataConstants.h ajoutées à FirmataExpress.

pymata4.py

Pymata4 utilise un dictionnaire Python pour associer rapidement un message Firmata entrant à un gestionnaire de messages. Le nom de ce dictionnaire est report_dispatch.

Le format d'une entrée de dictionnaire est:

{MessageID: [message_handler, nombre d'octets de données à traiter]}

Une entrée a été ajoutée au dictionnaire pour gérer les messages DHT entrants:

{Constants privés. DHT_DATA: [self._dht_read_response, 7]}

Les 7 octets de données dans le message sont le numéro de broche numérique Arduino, le type de périphérique DHT (22 ou 11) et les 5 octets de données brutes.

La méthode _dht_read_response vérifie les erreurs signalées. S'il n'y a pas d'erreurs signalées, l'humidité et la température sont calculées à l'aide de l'algorithme porté à partir de la bibliothèque Arduino DHTNew.

Les valeurs calculées sont rapportées via une méthode de rappel fournie par l'utilisateur. Ils sont également stockés dans la structure de données interne pin_data. La dernière valeur signalée peut être rappelée en interrogeant pin_data à l'aide de la méthode dht_read.

Configuration d'un nouveau périphérique DHT

Lors de l'ajout d'un nouveau périphérique DHT, la méthode set_pin_mode_dht est appelée. Cette méthode met à jour le pin_data pour les broches numériques. Il crée et envoie également un message DHT_CONFIG SysEx à FirmataExpress.

Étape 5: Conclusion

Comme nous l'avons vu, l'ajout de la prise en charge de Firmata pour un nouvel appareil nécessite de modifier le code du serveur Arduino FirmataExpress et le code client pymata4 basé sur Python. Le code FirmataExpress peut être difficile à déboguer. Une méthode appelée printData a été ajoutée à FirmataExpress pour faciliter le débogage. Cette méthode vous permet d'envoyer des valeurs de données depuis FirmataExpress et de les imprimer sur la console pymata4.

Cette fonction nécessite à la fois un pointeur vers une chaîne de caractères et la valeur que vous souhaitez afficher. Si la valeur des données est contenue dans une variable appelée argc, vous pouvez appeler printData avec les paramètres suivants.

printData((char*)"argc= ", argc);

Si vous avez des questions, n'hésitez pas à laisser un commentaire, je me ferai un plaisir de vous répondre.

Bon codage !

Conseillé: