Quand j'ai repris cet article en 2021, je me suis rendu compte que mon LEGO Mindstorms EV3: Java, leJOS et Eclipse (Programmation) n'était plus à jour en ce qui concerne la version d'Eclipse et ma manière de travailler. J'y ai donc ajouté quelques remarques.

Lorsque j'ai commencé de jouer avec la balise infrarouge (IR) de l'EV3, avec les exemples qui viennent avec la plateforme Java leJOS et avec sa classe EV3IRSensor ... je n'ai pas vraiment compris grand chose.

L'idée m'est donc venue d'écrire une première application, toute petite, pour comprendre cette classe et les événements générés en pressant les différents boutons. Ensuite, je l'ai faite évoluée, en fonction des résultats, pour obtenir quelque chose de plus concret, comme de faire rouler mon robot en le commandant depuis la balise IR.

Le véhicule nécessaire doit être équipé au minimum des deux grands servomoteurs et du capteur infrarouge à gauche:

 

 

 

Les boutons de la balise infrarouge sont expliqués ci-dessous.

Attention, et ce n'est pas forcément évident: la balise infrarouge a besoin du capteur infrarouge pour fonctionner (voir le code de l'Exercice1b ci-dessous).    


Le capteur infrarouge doit être connecté au port 4.

 

Pour comprendre ce code, j'assume que le lecteur possède déjà de bonnes connaissances en Java.

La documentation API de la classe EV3IRSensor est décrite ici:
http://www.lejos.org/ev3/docs/index.html?lejos/hardware/sensor/EV3IRSensor.html. 

J'utilise un dongle USB Wifi de TP-Link connecté à la brique EV3 pour pouvoir télécharger, exécuter, voir déboguer, les exercices directement depuis Eclipse. Mes autres articles de cette catégorie sont à consulter pour la partie installation et l’environnement Eclipse.

L'indentation de 3 définie dans l'éditeur de code Java dans Eclipse est un peu faible, mais nécessaire pour montrer correctement le code source sur un site Web comme celui-ci.

import lejos.hardware.lcd.LCD;
import lejos.hardware.port.SensorPort;
import lejos.hardware.sensor.EV3IRSensor;
import lejos.utility.Delay;

public class Ev3Exercice1a {
   public static void main(String[] args) {
      EV3IRSensor ir = new EV3IRSensor(SensorPort.S4);

      boolean continuing = true;  //Stop for particular key pressed
      int channel        = 0;     //The red switch
      int loopCount      = 0;     

      while (continuing) {
         Delay.msDelay(25);
         loopCount++;

         // Get the IR commands
         byte [] cmds = new byte[4];
         ir.getRemoteCommands(cmds, 0, cmds.length);

         // Find out the active channel and button pressed
         int theCmd = 0;
         for (int i=0; i < 4; i++) {
            if (cmds[i] > 0) {
              channel = i + 1;
               theCmd  = cmds[i];
            }
         }

         LCD.drawString("Command:" + theCmd + " ", 0, 2);
         LCD.drawString("Channel:" + channel,      0, 3);
         LCD.drawString("Counter:" + loopCount,    0, 4);

         LCD.drawString("Both red to stop",        0, 6);

         if (theCmd == 10) {
            continuing = false; 
         }
      }

      ir.close();
   }
}

 

En jouant avec ce programme, nous allons comprendre précisément comment fonctionne cette balise (assignée à l'entrée 4 de la brique) et chacun des boutons.

Nous pourrons alors nous imaginer des combinaisons de commandes pour, par exemple, actionner des moteurs ou stopper le programme. 

  • L'interrupteur rouge à quatre positions, 1 en haut et 4 en bas, (valeur marquée d'ailleurs dans le petit indicateur rouge).

    Dans la classe Exercice1a, c'est la variable channel.
    En lisant la documentation API de la classe EV3IRSensor, mon interprétation est que nous pourrions aussi "jouer" avec 4 balises.
    Ici nous allons simplement accepter les commandes venant de n'importe quelles balises, donc de n'importe quelle position du curseur rouge.    


  • Les quatre petits boutons gris nous retournent une valeur différente, mais seulement s'ils sont tenus pressés.
    Les valeurs seront de 1 (côté rouge en haut) à 4 (côté bleu en bas).

  • En pressant plusieurs de ces 4 petits boutons, nous obtiendrons une valeur composée.
    En tenant par exemple les deux à gauche en même temps (côté marqueur rouge), le getRemoteCommands() au travers de theCmd retournera la valeur de 10. Ce cas est traité ici pour stopper le programme. Dans l'exercice suivant on considérera aussi les deux boutons à droite pour la même fonction.  

  • Le gros bouton gris est différent. Il retourne la valeur de 9, mais restera actif. C'est aussi indiqué par la petite lampe verte en haut de la balise.
    Pour le désactiver, nous presserons ce bouton à nouveau, une seconde fois, ou alors un des 4 petits boutons.   

Nous comprendrons facilement que le gros bouton gris pourrait être utilisé afin d'activer différents modes de commandes.  

Il n'y a que peu de remarques pour ce premier exemple:

  • Le constructeur EV3IRSensor ir = new EV3IRSensor(SensorPort.S4); nous indique sur quel port de la brique est connecté le capteur infrarouge.

  • L'instruction ir.getRemoteCommands(cmds, 0, cmds.length); nous retourne 0 (pas de boutons pressés au moment de la demande) ou alors une des 11 valeurs comme décrit dans l'API.
    cmds a une dimension de 4 pour les 4 canaux.

  • Les valeurs 0 et 2 de LCD.drawString("Command:" + theCmd + " ", 0, 2); correspondent à la position sur le petit écran où sera affiché le texte.

Nous allons passer à la classe Exercice1b en réutilisant le code de la précédente afin d'activer les deux grands servomoteurs.

import lejos.hardware.lcd.LCD;
import lejos.hardware.motor.Motor;
import lejos.hardware.port.SensorPort;
import lejos.hardware.sensor.EV3IRSensor;
import lejos.robotics.RegulatedMotor;
import lejos.utility.Delay;

public class Ev3Exercice1b {

   static RegulatedMotor leftMotor  = Motor.B;
   static RegulatedMotor rightMotor = Motor.C;

   public static void main(String[] args) {
      int speedMotor = 400;

      EV3IRSensor ir = new EV3IRSensor(SensorPort.S4);

      leftMotor.setSpeed(speedMotor);
      rightMotor.setSpeed(speedMotor);

      LCD.drawString("EV3 Ready", 0, 2);

      boolean continuing = true; // Stop for particular key pressed
         while (continuing) {
         Delay.msDelay(25);

         int theCmd = ir.getRemoteCommand(0); // Get the command from channel 1

         if ((theCmd > 0) && (theCmd < 10)) {   
            LCD.drawString("Command:" + theCmd + " ", 0, 2);
            LCD.drawString("Both red to stop", 0, 6);
         }

         switch (theCmd) {
            case 0:
               leftMotor.stop();
               rightMotor.stop();
               break;

            case 1:
               leftMotor.forward();
               rightMotor.stop();
               break;
            case 2:
               leftMotor.backward();
               rightMotor.stop();
               break;
            case 3:
               rightMotor.forward();
               leftMotor.stop();
               break;
            case 4:
               rightMotor.backward();
               leftMotor.stop();
               break;
            case 5:
               leftMotor.forward();
               rightMotor.forward();
               break;
            case 8:
               leftMotor.backward();
               rightMotor.backward();
               break;

            default:
               break;
         }

         if ((theCmd == 10) || (theCmd == 11)) {
            continuing = false; //Stop the program
            leftMotor.stop();
            rightMotor.stop();
         }
      }
      
      ir.close(); //The IR sensor  
   }
}


Ces quelques remarques vont nous aider à comprendre cet second exercice:

  • J'ai tout de suite ajouté LCD.drawString("EV3 Ready", 0, 2); afin de savoir à quel moment le programme était prêt pour accepter les premières commandes depuis la balise IR.
    Depuis Eclipse, cela prend un peu plus de temps pour le téléchargement.

  • Le getRemoteCommand(0) permet de récupérer la commande de la balise IR qui doit avoir ici, impérativement, l'interrupteur rouge sur la position 1 (index 0).

  • Le case 0 du switch va stopper les deux moteurs même s'ils ne sont pas actifs, c'est plus facile pour le code. Nous sommes dans le cas où aucun bouton est pressé.

  • Les cases 1, 2, 3 et 4 vont activer un des deux moteurs dans une des deux directions. L'autre moteur est stoppé, même s'il n'était pas actif.
    Nous pourrons donc faire tourner le robot pour lui faire changer de direction.

  • Les case5 et 8 vont activer les deux moteurs en même temps. Le robot avancera ou reculera ... tout droit.

  • Comme précédemment, en pressant sur la balise les deux boutons ensemble sur la gauche (marque rouge), nous quitterons le programmes, avant d'éventuellement stopper la bique EV3 (bouton en haut à gauche sur le brique et un V final).  


En jouant avec la balise IR, nous nous rendrons compte rapidement du problème. Pour les case5 et 8, le robot aura tendance à faire une petite rotation en fin de parcours. C'est tout à fait explicable: lorsqu'on lâche les deux boutons, il est difficile de le faire précisément, en même temps, aussi à cause du délais de 25ms (lecture de la balise 40 fois par seconde). Un des boutons restera donc pressé un court instant. Il faudrait rajouter du code pour analyser ce cas, où alors faire avancer ou reculer le robot avec un seul bouton.

Un des Exercice1a ou Exercice1b peut, à tout moment, être définis comme programme par défaut de la brique EV3 (Run Default). C'est bien expliqué ici (Écran Files sur la brique EV3, sélectionner le fichier .jar désiré et naviguer avec le bouton droit sur Set as Default) ).

Finalement j'ai eu beaucoup de plaisir à écrire ce code, le comprendre et l'expliquer ici.