dimanche 26 janvier 2020

Robot Raspberry Pi


Le robot que nous allons assembler dans ce tutoriel est une plate-forme mobile dont le mouvement est contrôlé par un Rasbperry Pi. Pour l'instant, le robot ne comportera pas de capteurs qui lui permettraient de prendre des décisions de façon autonome: il sera plutôt contrôlé par des commandes  que nous lui procurerons par SSH à partir d'un autre ordinateur ou d'un téléphone. Bien entendu, des capteurs pourront être ajoutés au robot dans une étape subséquente.

 Matériel
  • Un Raspberry Pi capable de communiquer en WiFi. N'importe quel modèle fera l'affaire, j'en ai même profité pour mettre à contribution mon antique Raspberry Pi 1 modèle B (muni d'une petite clé WiFi). Il faut que la communication SSH soit activée dans le logiciel "Configuration du Raspberry Pi".


  • Une plate-forme mobile munie de deux roues, chaque roue étant contrôlée par son propre moteur à courant continu. C'est relativement facile de s'en procurer une toute faite. Si vous préférez la construire vous-même, assurez-vous d'utiliser des moteurs munis d'une boîte d'engrenages, sinon les roues tourneront beaucoup trop vite.

  • Un pilote de moteur: il s'agit d'un petit circuit qui sert d'intermédiaire entre le Raspberry Pi et les moteurs, car le courant nécessaire pour faire tourner un moteur est beaucoup trop intense pour les délicates broches GPIO du Raspberry Pi. Dans ce tuto, j'utilise le module L298N photographié ci-contre, mais il existe plusieurs autres possibilités­. Si vous préférez utiliser le L293D, par exemple, vous trouverez des informations utiles dans cet article concernant le contrôle de moteurs à courant continu avec un Rasperry Pi. Le script en python fourni un peu plus loin dans le présent billet devrait fonctionner correctement peu importe le pilote de moteur que vous utiliserez.
  • Une source d'alimentation pour le Raspberry Pi. J'ai utilisé un chargeur mobile pour téléphone ("USB power bank") qui a rempli son rôle à la perfection. 

  • Une source d'alimentation pour les moteurs: on évite bien des problèmes potentiels en utilisant une source d'alimentation dédiée aux moteurs, distincte de celle qui alimente le Raspberry Pi. Pour mes tests, j'ai utilisé un accumulateur NiMH de 9 V, qui n'est certainement pas le meilleur choix (avec sa capacité de 175 mAh, mon robot n'ira pas très loin). Une batterie LiPo ou Li-Ion de 7,4 V ou 6 piles de format AA auraient été des choix plus appropriés.
Schéma du circuit

Le module L298N comporte des broches d'alimentations (GND et +12), 6 entrées qui permettent au Raspberry Pi de contrôler les moteurs (ENA, IN1, IN2, IN3, IN4, ENB), et 4 sorties vers les moteurs (OUT1, OUT2, OUT3, OUT4). Il y a également une sortie 5 V, que je n'ai pas utilisée.
  • Broche +12 du L298N: Borne positive de l'alimentation des moteurs (12 V est un maximum).
  • Broche GND du L298N: Borne négative de l'alimentation des moteurs et une broche GND du Raspberry Pi.
  • OUT1 et OUT2 du L298N branchées à un des moteurs.
  • OUT3 et OUT4 du L298N branchées à l'autre moteur
  • ENA du L298N broche BCM 25 (BOARD 22) du Raspberry Pi
  • IN1 du L298N  broche BCM 23 (BOARD 16) du Raspberry Pi
  • IN2 du L298N broche BCM 12 (BOARD 18) du Raspberry Pi
  • IN3 du L298N broche BCM 10 (BOARD 19) du Raspberry Pi
  • IN4 du L298N broche BCM 9 (BOARD 21) du Raspberry Pi
  • ENB du L298N broche BCM 11 (BOARD 23) du Raspberry Pi


Script en Python

Pour contrôler le robot, j'ai installé dans le Raspberry Pi le script en Python ci-dessous, et je l'ai exécuté par SSH au moyen d'un téléphone ou d'un autre ordinateur.

Les commandes sont des chiffres de 0 à 9, dont la disposition respecte une certaine logique si vous utilisez un pavé numérique:
    numeric-keypad
  • 5: Arrêt. Les deux moteurs sont à l'arrêt (broche "enable" au niveau logique bas).
  • 8: Marche avant en ligne droite: les deux moteurs tournent dans le même sens.
  • 2: Marche arrière en ligne droite: les deux moteurs tournent dans le même sens.
  • 4: Rotation vers la gauche: les deux moteurs tournent en sens contraire
  • 6: Rotation vers la droite: les deux moteurs tournent en sens contraire
  • 7: Virage à gauche en avançant: le moteur de droite tourne, mais pas celui de gauche
  • 9: Virage à droite en avançant: le moteur de gauche tourne, mais pas celui de droite
  • 1: Virage à gauche en reculant: le moteur de droite tourne, mais pas celui de gauche
  • 3: Virage à droite en reculant: le moteur de gauche tourne, mais pas celui de droite
  • 0: Arrêt du programme
-
-


Pour terminer, une courte vidéo du véhicule en action:



À lire également:

En utilisant un Arduino plutôt qu'un Raspberry Pi, j'avais fait un robot qui se déplace selon une trajectoire pré-programmée, un robot téléguidé, un robot suiveur de ligne, un robot éviteur d'obstacles...


Yves Pelletier (TwitterFacebook)

samedi 18 janvier 2020

ESP32-CAM: première utilisation avec l'IDE Arduino


L'ESP32-CAM est carte offerte à prix très modique (moins de 10 euros) qui comporte un microcontrôleur ESP32 et une caméra OV2640. Une des caractéristiques les plus intéressantes de l'ESP32 étant la possibilité de communiquer en WiFi, une utilisation évidente de l'ESP-32 CAM consiste à transmettre en direct des images vidéo par WiFi (caméra de surveillance, etc.).

En plus d'une caméra OV2640, le module est équipé d'un lecteur de cartes micro SD qui pourra éventuellement servir à stocker des images ou des séquences vidéo.

Si vous êtes déjà familier avec les modules ESP-32 conventionnels, vous remarquerez toutefois que le module ESP32-CAM ne comporte aucun connecteur USB: pour programmer le microcontrôleur, vous devez utiliser un convertisseur USB-Série fonctionnant à un niveau logique de 3,3 V.

De plus, puisque la caméra accapare un certain nombre d'entrées/sorties du microcontrôleur, les broches GPIO disponibles sont beaucoup moins nombreuses que sur un module ESP-32 conventionnel (il n'y en a que 8, et 6 d'entre elles sont déjà connectées au lecteur de carte SD).

Le module que j'ai reçu était déjà assemblé: nul besoin de souder soi-même les connecteurs, ni même de connecter la caméra.

Dans ce court tutoriel, je vous explique comment, dans les minutes suivant la réception de mon module ESP32-CAM, j'ai pu utiliser un exemple fourni avec l'IDE Arduino pour diffuser en direct sur une page web la vidéo captée par la caméra.

Connexions de l'ESP32-CAM au convertisseur USB-série

Pour programmer mon module ESP32-CAM, j'ai utilisé un convertisseur USB-série qui fonctionne à un niveau logique de 3,3 V.


Pour la phase de programmation, les connexions étaient les suivantes:

  • Sortie 5 V du convertisseur USB-série:  Entrée 5 V de l'ESP32-CAM
  • Broche GND du convertisseur USB-série: Broche GND de l'ESP32-CAM
  • Broche TXD du convertisseur USB-série: Broche UDR de l'ESP32-CAM
  • Broche RXD du convertisseur USB-série: Broche UDT de l'ESP32-CAM
  • Broche IO0 branchée à GND



Le module est donc alimenté en 5 V pendant la programmation (je n'ai pas testé, mais j'ai lu sur des forums de discussion que certaines personnes ont eu des problèmes en tentant de programmer sous une alimentation de 3,3 V).

L'information transite de l'ordinateur vers le microcontrôleur (TXD / UDR) et du microcontrôleur vers l'ordinateur (RXD / UDT) à un niveau logique de 3,3 V (si votre convertisseur USB-série est muni d'un commutateur permettant de sélectionner le niveau logique, veillez à le régler à 3,3 V).

Pour mettre l'ESP32-CAM en mode programmation, il faut que la broche IO0 (ou GPIO 0) soit reliée à la masse (GND); on appuie ensuite sur le bouton "reset". Pour exécuter le programme, on débranche la broche IO0 et on appuie à nouveau sur le bouton "reset".

Après quelques heures de développement sur cette carte, j'ai bricolé un rudimentaire support en carton qui permet de maintenir la caméra à un angle pertinent tout en laissant le bouton "reset" facilement accessible. De plus, j'ai placé un interrupteur entre la broche IO0 et la masse pour éviter de devoir sans cesse débrancher et rebrancher un fil.





Réglages de l'IDE Arduino

Les cartes de la famille ESP32 ne sont pas incluses par défaut avec l'IDE Arduino; il faut les installer en passant par le "Gestionnaire de cartes". Si ce n'est pas déjà fait, vous trouverez la marche à suivre détaillée dans ce précédant billet.


Parmi les nombreuses cartes ESP32 disponibles, vous sélectionnez "AI Thinker ESP32-CAM". (À vrai dire, rien sur mon module ou sur la description offerte par le vendeur ne mentionne "AI Thinker", mais ce choix a fonctionné correctement.)

Vous ouvrez ensuite l'exemple "CameraWebServer" (Menu Fichier - Exemples - ESP32 - Camera - CameraWebServer).


Par défaut, le sketch est réglé pour être utilisé avec le modèle WROVER: utilisez plutôt la ligne 14 "#define CAMERA_MODEL_AI_THINKER".

De plus, écrivez les paramètres de votre réseau WiFi aux lignes 18 et 19.


Après vous être assuré que la broche IO0 est reliée à la masse GND et appuyé sur le bouton "reset", vous pouvez maintenant verser le sketch dans votre ESP32.


Si tout se passe bien, vous devriez voir, au bout de quelques dizaines de secondes, un message indiquant la réussite du téléversement.



Pour démarrer le programme, vous débranchez le fil reliant la broche IO0 à la masse et appuyez sur le bouton "reset".

Le moniteur série affiche l'adresse IP qui permettra de visionner l'image dans un navigateur web.


On copie cette adresse dans le navigateur web de notre choix. Dans la page web qui s'affiche, il s'agit de cliquer sur le bouton "Start Stream" pour que l'image captée par le module ESP32-CAM s'affiche à l'écran. Plusieurs contrôles situés à la gauche de la fenêtre permettent de modifier les caractéristiques de l'image.




En ce qui me concerne, cette première exploration du module ESP32-CAM s'est effectué sans le moindre pépin. J'espère bien, dans un proche avenir, apprendre à personnaliser les programmes pour, par exemple, enregistrer des images sur une carte SD, etc.

À lire également

Mon article sur sur le module caméra du Raspberry Pi, et la liste des articles impliquant l'ESP32.

Yves Pelletier (TwitterFacebook)

dimanche 12 janvier 2020

Analyse d'une communication I2C


Tout comme nous l'avons déjà fait pour les communications UART et SPI, nous allons aujourd'hui décrire les principales caractéristiques d'une communication I2C , et observer une transaction I2C typique au moyen d'un analyseur logique.

Mis au point par la compagnie Philips en 1982,  I2C est un protocole de communication série qui permet à un microcontrôleur d'interagir avec une grande quantité de périphériques en utilisant uniquement deux lignes de transmission (on l'appelle d'ailleurs parfois "Two Wire Interface"). Le nom " I2C " est l'acronyme de "inter-integrated circuit"; donc "IIC" apparemment transformé en "I2C" pour faire plus original.

Il s'agit d'une communication série synchrone bidirectionnelle half-duplex...

"Série" car les bits qui forment un octet sont transmis un à la suite de l'autre sur la même ligne de transmission (par opposition à une communication parallèle, dans laquelle chaque bit serait transmis sur sa propre ligne). UART et SPI sont deux autres exemples de communication série.

"Synchrone" car un signal d'horloge sert de référence à tous les appareils impliqués dans la communication. Ce signal d'horloge est géré par le "maître" et partagé sur la ligne SCL ("serial clock").

"Bi-directionnelle" car l'information peut être transmise du maître à l'esclave et de l'esclave vers le maître.

"Half-duplex" car, puisque qu'une seule ligne de transmission est utilisée pour les deux directions (la ligne SDA "serial data"), les informations ne peuvent circuler que dans une direction à la fois.

Puisqu'il peut y avoir un très grand nombre d'esclaves, chaque esclave dispose d'une adresse unique, constituée de 7 bits (ce qui donne une possibilité théorique de 128 esclaves). La présence de plusieurs maîtres est également possible.

Quelques exemples de périphériques  I2

Le nombre de capteurs et d'afficheurs qui peuvent communiquer avec l'Arduino par I2C est assez impressionnant: écran OLED SH1106,  accéléromètre MMA7455capteur de lumière TSL2561récepteur radio FM RDA5807convertisseur analogique-numérique PCF8591,  accéléromètre/gyro MPU-6050, capteur de pression atmosphérique BMP180magnétomètre/boussole HMC5883L, horloge temps réel DS1307, mémoire EEPROM, etc.

Drain ouvert 

Tous les appareils  I2C  sont connectés à la ligne SDA et à la ligne SCL par une sortie à drain ouvert (ou à collecteur ouvert, si vous préférez la terminologie des transistors bipolaires). Le circuit est schématisé ci-contre. Il s'agit d'un circuit inverseur, puisque sa sortie est au niveau logique '0' lorsque l'entrée est au niveau logique '1', et sa sortie est au niveau '1' lorsque l'entrée est au niveau '0'.

Lorsque l'entrée (la grille du transistor) est à 0 V, le trajet source-drain ne conduit pas le courant, aucun courant ne traverse la résistance et la sortie se trouve à un potentiel de 5 V. Lorsque l'entrée est à 5 V, le trajet source-drain devient conducteur et relie la sortie à la masse (GND).

Imaginez plusieurs circuits de ce genre dont la sortie est reliée à la même ligne (SDA ou SCL): il s'agit qu'un seul de ces circuits relie la ligne de transmission à la masse pour qu'elle se retrouve au niveau logique '0', même si tous les autres circuits tentent de la maintenir au niveau logique '1'.  Nous verrons un peu plus loin que cette caractéristique est utilisée, entre autres,  pour permettre à un esclave d'envoyer un acquittement (aknowledge): pendant que le maître maintient la ligne SDA à '1', l'esclave l'abaisse à '0' pour indiquer qu'il a reçu l'information.

La résistance pull-up doit être assez grande pour limiter le courant circulant à travers les lignes SDA et SCL. Par contre, plus elle est grande, plus la transition entre le niveau logique '0' et le niveau logique '1' sera longue, ce qui limite la vitesse de transmission du signal. Plus le bus  I2C comporte un grand nombre d'appareils, plus la capacité est grande , ce qui nous oblige à diminuer la valeur de la résistance pull-up.

Lorsqu'il n'y a aucune communication  I2C , les lignes SDA et SCL sont au niveau logique '1', puisque c'est dans cet état qu'aucun courant ne circule à travers la résistance pull-up.

Observation d'une communication  I2

J'ai profité de mes récentes expérimentations avec une mémoire EEPROM 24LC01B branchée à une carte Arduino pour observer une communication  I2C  typique au moyen d'un analyseur logique et du logiciel Sigrok Pulseview. La communication  I2C  étant, comme d'habitude, gérée par la bibliothèque Wire.


L'EEPROM 24LC01B était branché à l'Arduino de la façon suivante:
  • Les broches 1, 2 et 3 du 24LC01B ne sont pas branchées. 
  • Broche 4 du 24LC01B branchée à la masse (GND).
  • Broche 5 du 24LC01B (SDA) branchée à la broche A4 de l'Arduino Uno, ainsi qu'à 5 V par l'entremise d'une résistance de tirage de 4,7 kΩ.
  • Broche 6 du 24LC01B  (SCL) branchée à la broche A5 de l'Arduino Uno, ainsi qu'à 5 V par l'entremise d'une résistance de tirage de de 4,7 kΩ.
  • Broche 7 du 24LC01B (WP) reliée à la masse (GND).
  • Broche 8 du 24LC01B reliée à 5 V.

Première communication: écriture d'une valeur en mémoire

Dans un premier temps, j'ai exécuté un sketch qui écrit la valeur "44" à l'adresse "63" de l'EEPROM (dont l'adresse I2C est 0x50) .


Et j'ai obtenu ce chronogramme sur Seegrok Pulseview, qui montre l'émission de 3 octets consécutifs.

La vue d'ensemble est jolie, mais c'est tout petit: on va zoomer un peu...


Première constatation: avant l'établissement de la communication I2C, les deux signaux (SCL et SDA) étaient au niveau logique '1'.

Le maître envoie ensuite un bit "start", qui consiste à mettre la ligne SDA au niveau '0' pendant que la ligne SCL demeure au niveau '1'.

Le maître envoie ensuite un octet constitué des 7 bits de l'adresse I2C du périphérique (ici 50 en hexadécimal, ou 1010000 en binaire), et d'un huitième bit nommé R/W. Le bit R/W prend la valeur '0' si le maître s'apprête à envoyer de l'information à l'esclave, et la valeur '1' si le maître demande à l'esclave de lui envoyer de l'information. Ici, ce 8e bit est à 0: les prochains octets seront donc générés par le maître et non par l'esclave.  Chaque bit est lu sur la ligne SDA au moment où la ligne SCL passe de 0 à 1 (j'ai ajouté des points noirs pour indiquer le moment de la lecture de chacun des 8 bits).

Pour terminer l'envoi de ce premier octet, le maître laisse la ligne SDA à 1, et l'esclave force cette ligne à '0' pour indiquer qu'il a bien reçu l'information et qu'il est prêt à recevoir les données (c'est le signal d'acquittement ACK, symbolisé par le "A" rose à l'extrême droite du chronogramme). Si d'autres esclaves sont présents, ils ne tiennent pas compte du reste de cette communication, puisque l'adresse énoncée n'est pas la leur.


Suite à cette confirmation que l'esclave est prêt à recevoir les données, le maître envoie un deuxième octet, qui est l'adresse de la mémoire dont on désire modifier le contenu. Il s'agit ici de 63, qui se traduit par 00111111 en binaire. À la fin, l'esclave met la ligne SDA à '0' pour indiquer qu'il a bien reçu l'information (acquittement).

Le maître envoie ensuite un troisième octet qui contient la valeur qui désire enregistrer en mémoire. Il s'agit ici de 44, qui se traduit par 00101100 en binaire (ou par 0x2C en hexadécimal, tel qu'indiqué par le logiciel PulseView sur le chronogramme ci-dessus).

C'est suivi par un dernier signal d'acquittement de la part de l'esclave et, finalement, le maître clôt la transaction par le signal "stop" (identifié par le P à l'extrême droite du chronogramme), qui consiste à faire passer la ligne SDA de '0' à '1' pendant que le signal SCL demeure à '1'.

La communication étant terminée, les deux lignes (SDA et SCL) demeurent à '1' en attendant la prochaine transaction.

Remarque: la fréquence par défaut de la communication I2C est de 100 kHz. Au besoin, j'aurais pu modifier cette fréquence en utilisant Wire.setClock() dans mon sketch. Remarquons en passant que cette fréquence est environ 10 fois plus lente que celle d'une communication SPI typique.



Deuxième communication: lecture d'une valeur en mémoire

Dans un deuxième temps, j'ai exécuté un sketch qui lit la valeur se trouvant à l'adresse "63" de l'EEPROM:


Cette fois, la transaction implique 4 octets: le maître doit d'abord indiquer à l'esclave qu'il lui envoie de l'information (l'adresse dont il veut connaître le contenu), puis lui indiquer qu'il attend la réponse.


Faisons un zoom sur chaque octet:


Encore une fois, le signal "Start", qui consiste à faire passer la ligne SDA de '1' à '0' pendant que la ligne SCL demeure à '1'.

Ensuite, l'envoi des 7 bits de l'adresse I2C de l'esclave (1010000), suivi du bit R/W qui est de '0' car le maître devra d'abord envoyer de l'information à l'esclave.

Cette partie de la transaction se termine par l'émission du signal d'acquittement par l'esclave.


Dans un deuxième temps, le maître envoie sur la ligne SDA l'adresse de la mémoire EEPROM dont il désire connaître le contenu (00111111 qui correspond à 63 en décimal). Si à la réception de cette adresse, l'esclave génère le bit d'acquittement, et le maître clôt la transaction par un bit "stop" (la ligne SDA passe de '0' à '1' pendant que SCL reste à '1').


Le 3e octet permet au maître d'aviser l'esclave que c'est maintenant à lui, l'esclave, d'envoyer l'information (qui sera le contenu de l'adresse qu'on lui a précédemment communiqué). Ce troisième octet est identique au premier, sauf qu'il se termine par '1' plutôt que par '0'. Le bit R/W de '1' indique que le prochain échange s'effectuera de l'esclave vers le maître.


Le 4e octet est généré par l'esclave. 00101100 correspond à 44 en décimal, qui est la valeur précédemment enregistrée à l'adresse 63.

Cette fois, c'est le maître qui est responsable d'émettre un signal d'acquittement. S'il le fait, l'esclave lui enverra une autre donnée (le contenu de l'adresse 64). Sinon (signal "N" de non-acquittement), l'esclave cesse d'envoyer des données, et le maître clôt la transaction en faisant passer la ligne SDA de '0' à '1' pendant que SCL est maintenu à '1'.

En cas de mauvaise adresse

Qu'arrive-t-il si le maître envoie une instruction à une adresse qui ne correspond à aucun esclave connecté? Pour vérifier, j'ai modifié mon sketch pour qu'il tente d'écrire une valeur à un esclave (inexistant) dont l'adresse serait 0x60 plutôt que 0x50.


Cette fois, la transaction se limite à un seul octet. Le maître envoie un message constitué des 7 bits de l'adresse i2c 1100000 (qui ne correspond à aucun esclave connecté) et du bit R/W de 0. N'ayant détecté aucun signal d'acquittement, le maître met fin à la transaction.

Avantages/inconvénients

Une communication I2C est moins rapide qu'une communication SPI, entre autres parce que la fréquence de l'horloge est plus faible, parce que l'information ne peut circuler que dans une direction à la fois, parce qu'une partie de la communication consiste à transmettre l'adresse de l'esclave concerné, etc.  Par contre on peut brancher un grand nombre de périphériques en utilisant seulement 2 broches du microcontrôleur.

À lire également

Des articles similaire ont été publiés concernant la communication SPI et la communication UART.


Yves Pelletier (TwitterFacebook)