Aujourd’hui au programme de notre nouveau chapitre du cours de programmation pour makers avec Arduino et Protocoder : l’animation d’une matrice de LED utilisable sur un robot. Nous allons ainsi créer un œil contrôlable à distance via Protocoder.
Matériel
- Carte ZUM BT-328
- Matrice de LED (nous avons utilisé une carte basée sur la puce MAX72XX)
- Comodoro (les fichiers de notre robot sont à télécharger à la fin du chapitre)
Code Arduino
Le code Arduino permet de recevoir les données à partir de l’application Protocoder. En fonction de ces données, l’œil s’anime dans une position spécifique. L’œil clignote également de manière aléatoire.
Pour contrôler la matrice de LED nous utiliserons la bibliothèque LedControl. Tu peux la télécharger sur GitHub et elle est également disponible avec le code source à la fin du chapitre. Pour en savoir plus sur les possibilités offertes par cette bibliothèque, rendez-vous sur le site Arduino Playground.
Pour commencer à programmer avec Arduino, nous devons intégrer la bibliothèque, définir les pins (broches) et créer un objet LedControl, que nous nommerons ici LM :
1 2 3 4 5 6 7 8 9 10 11 |
//Led Display #include "LedControl.h" #define MAX_DEVICES 1 #define CLK_PIN 13 #define DATA_PIN 11 #define CS_PIN 12 LedControl LM = LedControl(DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES); |
Dans la dernière ligne du code ci-dessus, nous avons défini l’objet LM que nous utiliserons pour contrôler la matrice de LED.
Créons maintenant la variable inString, qui stockera les données reçues par Bluetooth. Les fonctions readFromAndroid et writeData ont pour mission respective de stocker les données dans la variable inString et de définir la direction dans laquelle l’œil doit regarder. Pour en savoir plus sur ces fonction tu peux consulter cet article (pour le moment uniquement disponible en anglais et en espagnol).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
String inString = ""; // Chaîne de caractères à lire à partir de Protocoder void readFromAndroid() { char inChar; while (Serial.available() > 0) { inChar = (char) Serial.read(); Serial.flush(); if (inChar == '='){ inString = ""; LM.clearDisplay(0); }else if (inChar != '+'){ inString += inChar; } } } void writeData(){ if (inString == "up") { lookUp(); } if (inString == "down") { lookDown(); } if (inString == "center") { lookCenter(); } if (inString == "left") { lookLeft(); } if (inString == "right") { lookRight(); } } |
Une fois le message entièrement lu (après avoir reçu le caractère ‘=’), nous pouvons effacer les informations à l’écran avec la commande LM.clearDisplay(0); afin d’être certain que l’œil sera correctement positionné.
Dans la fonction setup nous lançons le port série, nous activons la matrice de LED (qui est en mode économie d’énergie par défaut), nous configurons la luminosité au maximum, nous « effaçons » la matrice de LED et enfin nous faisons regarder l’œil droit devant.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void setup() { Serial.begin(19200); /* La puce MAX72XX est en mode économie d’énergie au démarrage, Nous devons changer de mode. // Fonction pour passer en mode économie d’énergie : LM.shutdown(0,true); */ LM.shutdown(0, false); /* Définir la luminosité au maximum, valeur={0,15} */ LM.setIntensity(0, 15); /* et effacer l'écran */ LM.clearDisplay(0); lookCenter(); // Routines de test //testPos(250); //test(); } |
Nous avons inclus deux routines de test : testPos(), qui change la position de l’œil, et test(), qui allume chaque LED l’une après l’autre.
Afin de prédéfinir les différentes positions de l’œil, nous pouvons nous aider de la matrice à télécharger à la fin du chapitre et marquer quelles LED doivent être allumées puis traduire notre idée en code. Voici la fonction à utiliser :
1 |
LM.setLed(0,ligne, colonne, statut) |
Le statut peut être true ou false selon que tu veux allumer ou éteindre les LED.
Dans le cas de la fonction lookCenter(), qui permet de faire regarder l’oeil droit devant, le schéma est le suivant :
Voici le code Arduino correspondant à cette position :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
void lookCenter() { LM.setLed(0, 3, 3, false); LM.setLed(0, 3, 4, false); LM.setLed(0, 4, 3, false); LM.setLed(0, 4, 4, false); LM.setLed(0, 1, 3, true); LM.setLed(0, 1, 4, true); LM.setLed(0, 2, 2, true); LM.setLed(0, 2, 3, true); LM.setLed(0, 2, 4, true); LM.setLed(0, 2, 5, true); LM.setLed(0, 3, 1, true); LM.setLed(0, 3, 2, true); LM.setLed(0, 3, 5, true); LM.setLed(0, 3, 6, true); LM.setLed(0, 4, 1, true); LM.setLed(0, 4, 2, true); LM.setLed(0, 4, 5, true); LM.setLed(0, 4, 6, true); LM.setLed(0, 5, 2, true); LM.setLed(0, 5, 3, true); LM.setLed(0, 5, 4, true); LM.setLed(0, 5, 5, true); LM.setLed(0, 6, 3, true); LM.setLed(0, 6, 4, true); } |
Nous pouvons donc prédéfinir différentes positions. Voici les fonctions qui ont été réalisées pour modifier la position de la pupille de notre œil (lookLeft, lookRight, lookUp, lookDown et lookCenter) et pour le faire cligner (blinkClose, blinkOpen et fullBlink).
La variable delayBlink permet de modifier la vitesse de transition entre la fermeture et l’ouverture de l’œil. Nous l’utiliserons pour augmenter ou réduire la vitesse du clignement. La fonction fullblink prend en compte comme paramètre l’intervalle de temps entre l’état ouvert et l’état fermé. Nous l’utiliserons pour augmenter ou réduire la fréquence des clignements.
1 2 3 4 5 6 7 |
int delayBlink = 10; void fullBlink(int timeClosed){ blinkClose(); delay(timeClosed); blinkOpen(); } |
Les fonctions blinkOpen et blinkClose permettent respectivement d’ouvrir et de fermer l’œil. Elles utilisent la variable delayBlink pour déterminer la vitesse de clignement.
Enfin la fonction loop permet de contrôler le clignement de l’œil et la réception des données via Bluetooth, à l’aide des fonctions readFromAndroid et writeData.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void loop() { if (millis()>nextTimeDouble){ fullBlink(60); delay(100); fullBlink(60); nextTimeDouble = millis() + random(30000, 60000); writeData(); }else if (millis() > nextTime){ fullBlink(60); nextTime = millis() + random(1500, 4000); writeData(); } if (Serial.available() > 0) { readFromAndroid(); } writeData(); } |
La première partie de ce code permet de contrôler le clignement de l’œil :
1 2 3 4 5 6 7 8 9 10 11 |
if (millis()>nextTimeDouble){ fullBlink(60); delay(100); fullBlink(60); nextTimeDouble = millis() + random(30000, 60000); writeData(); }else if (millis() > nextTime){ fullBlink(60); nextTime = millis() + random(1500, 4000); writeData(); } |
Afin d’obtenir un clignement différent à chaque exécution du code, deux états ont été inclus (simple et double) avec un fonctionnement similaire. Premièrement il est vérifié si le temps d’exécution actuel du programme est supérieur au temps défini pour le prochain clignement. Si la condition est vraie (true), la portion de code correspondant au clignement (routine) sera exécutée et le temps suivant sera défini en ajoutant avec la fonction random une quantité aléatoire à la variable concernée.
La seconde partie du code permet de lire les données envoyées depuis l’application Protocoder via Bluetooth :
1 2 3 4 |
if (Serial.available() > 0) { readFromAndroid(); } writeData(); |
Code Protocoder
L’application que nous allons réaliser avec Protocoder enverra les données à Arduino via Bluetooth.
Pour commencer à programmer, nous utilisons l’entête habituelle de nos applications et nous ajouterons le contrôle de la connexion Bluetooth. Si tu souhaites en savoir plus sur le fonctionnement de ce code, tu peux consulter ce chapitre (uniquement disponible en espagnol pour le moment).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
ui.toolbar.title("Robot-Eyes"); ui.toolbar.bgColor(55, 155, 155, 255); ui.toolbar.show(true); //ui.screenMode("fullscreen"); ui.screenOrientation("portrait"); ui.allowScroll(false); device.screenAlwaysOn(true); var margin = 10; var w = ui.screenWidth - 2*margin; var h = 150 + margin; var y = h + margin; var btStatus=0; // Variable qui stocke le statut de connexion du Bluetooth (1 pour connecté et 0 pour déconnecté) var btClient; // "objet" que nous utiliserons pour nous connecter/déconnecter et utiliser le Bluetooth var btnConectar = ui.addButton("Connexion via Bluetooth", margin, margin,w,h).onClick(function() { // Nous créons le bouton de connexion btClient = network.bluetooth.connectSerial(function(status) { console.log("connected " + status); if (status){ ui.toast("Connected"); btStatus = status; console.log("btStatus = " +btStatus); } }); }); var btnDesconectar = ui.addButton("Déconnexion", margin, y,w,h).onClick(function() { // Nous créons le bouton de déconnexion btClient.disconnect(); // permet de nous déconnecter une fois le bouton appuyé btStatus= false; // La variable de statut de connexion correspond maintenant à 0 (soit déconnecté) }); |
Pour envoyer les données sur la carte ZUM, nous utilisons la fonction send qui prend comme paramètre la chaîne de caractères (string) à envoyer. Par exemple, pour que l’oeil regarde à droite, la fonction enverra seulement le mot right, soit send(“right”);. Voici ci-dessous le code de la fonction send :
1 2 3 4 5 6 |
function send(str) { // La fonction send qui envoie la chaîne de caractères (string) if (btStatus) { // Si le Bluetooth est connecté, le code suivant est exécuté btClient.send("="+str+"+"); // Nous envoyons la chaîne de caractères entre les symboles = et + console.log("="+str+"+"); // Nous affichons également sur la console la chaîne de caractères envoyée. } } |
Nous créons ensuite les boutons qui détermineront la position de l’œil. Nous uploadons dans Protocoder les fichiers que nous voulons utiliser, puis nous créons les boutons à l’aide du code suivant :
1 2 3 |
ui.addImageButton(positionX,positionY,width,height,"file name",false).onClick(function(){ }); |
Pour ajuster la taille et la position des boutons, nous utilisons deux variables :
- WidthImg, qui détermine la largeur et la hauteur du bouton.
- posX, qui détermine la position du bouton.
Nous intégrons la fonction send(); à la fonction à exécuter après avoir appuyé sur le bouton afin d’envoyer l’instruction correspondant à la position souhaitée :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
ui.addImageButton(posX,3*y,widthImg,widthImg,"up.png",false).onClick(function(){ send("up"); }); ui.addImageButton(10*margin,3*y + widthImg,widthImg,widthImg,"left.png",false).onClick(function(){ send("left"); }); ui.addImageButton(posX,3*y + widthImg,widthImg,widthImg,"center.png",false).onClick(function(){ send("center"); }); ui.addImageButton(posX + widthImg,3*y + widthImg,widthImg,widthImg,"right.png",false).onClick(function(){ send("right"); }); ui.addImageButton(posX,3*y + 2*widthImg,widthImg,widthImg,"down.png",false).onClick(function(){ send("down"); }); |
Modélisation 3D : Comodoro
Pour créer l’objet 3D Comodoro, nous avons utilisé le logiciel Blender.
Pour imprimer notre pièce avec un support, nous utilisons Cura avec les réglages suivants :
Ce support est facile à retirer et permet d’avoir plus de stabilité lors de l’impression.
Toujours dans Cura, nous avons paramétré l’échelle à 40,79 afin que notre objet soit adapté à la taille de notre matrice de LED. Tu peux bien évidemment ajuster cette échelle afin que la pièce corresponde à ta matrice de LED.