Forums LR PRESSE

Où il est question de trains, petits et grands

  • Advertisement

Arduino - Servomoteurs

Toutes les discussions sur l'Arduino !

Modérateur: MOD

Re: Arduino - Servomoteurs

Publié: Sam 23 Nov 2013, 15:05 
Sélection d’un servomoteur pour le réglage des butées

L'article est aussi disponible sur mon blog : http://modelleisenbahn.triskell.org/spip.php?article61

Le réglage des butées est un point important. En effet, chaque servo moteur va avoir un neutre un peu différent de ses congénères. Il en va de même pour les positions extrêmes. De plus, chaque installation d'aiguille est un peu différente des autres.

Il est donc essentiel de pouvoir, servomoteur par servomoteur, régler les deux positions extrêmes.

Le matériel

Afin de sélectionner l'un des servomoteurs pour régler, au moyen de deux poussoirs, sa position extrême actuelle nous avons besoin d'un dispositif permettant de donner un numéro de servomoteur. Le dispositif le plus adapté est un codeur rotatif à 10 positions comme celui-ci.

Image

Ces codeurs rotatifs ont 6 broches. 2 d'entre-elles sont reliées entre elles et communes, les 4 autres codent en binaire la position du bouton (4 bits).

Comme 5 entrées analogiques sont disponibles et qu'il est aussi possible de les utiliser comme entrée numérique, on ne va pas finasser et simplement utiliser une entrée par bit (On rappelle que les entrées/sorties logiques encore disponibles sont réservées pour les LED témoin). 1 entrée analogique, A1, restera disponible pour les poussoirs de réglage.

Je n'ai pas de codeur rotatif en stock, à la place je vais utiliser un DIP-switch à 4 interrupteurs sur lequel le numéro du servo sera codé en binaire et qui sera raccordé exactement de la même façon que le codeur. Par convention, on décide que la valeur 0 (position 0 du codeur) est le mode d'exploitation. Aucun servo n'est sélectionné par le codeur. Les valeurs de 1 à 8 permettent de sélectionner le servo à régler. La valeur 9 correspond aussi au mode exploitation où aucun servo n'est sélectionné.

Le codeur est donc raccordé de la manière suivante aux entrées analogiques.

Image

Les entrées dispose de résistances de tirage à 5V (on dit pull-up) interne que l'on peut activer par programme. Quand les interrupteurs sont ouverts, C'est à dire à 0, la valeur présente sur les entrées est 5V et non 0V ; Il faut en tenir compte pour construire le logiciel.

Le logiciel

Pour lire la valeur numérique codée et la stocker dans une variable entière servoARegler, il faut donc lire les entrées analogiques A2, A3, A4 et A5 en numérique, en déduire une valeur de bit et positionner le bit correspondant dans servoARegler. C'est l'occasion de voir une nouvelle spécificité du langage C, la manipulation des bits dans une variable.

Le langage C permet de faire des opérations logiques bit à bit. Les opérateurs permettant de telles opérations sont : le et bit à bit, noté &, le ou bit à bit, noté |, le ou exclusif bit à bit, noté ^ et le non (inversion de bit) bit à bit noté ~. Il existe aussi deux opérateurs de décalage : décalage logique vers la gauche d'un certain nombre de bits, noté << et le décalage vers la droite d'un certain nombre de bits, noté >>. Dans les deux cas, les bits laissés libres par le décalage sont à 0 (Seulement si la variable est non signée. Si la variable est signée, le décalage à droite remplit les bits laissés libres avec le bit de signe.), les bits qui sont expulsés sont perdus.

Ainsi, si l'on veut construire un byte dont tous les bits sont à 0 sauf les bits 0, 2 et 4 qui sont à 1 :

Image

On peut écrire : 1 << 4 | 1 << 2 | 1

C'est à dire:

Image

Ce qui est bien le résultat escompté.

On peut se poser la question de savoir que renvoit la lecture d'une entrée en numérique par un digitalRead(...). Eh bien elle vaut soit HIGH, soit LOW. Ces deux constantes sont en fait des entiers qui valent 1 pour HIGH et 0 pour LOW. On peut donc utiliser la valeur renvoyée par un digitalRead(...) dans un calcul. C'est ce que nous allons faire pour calculer la valeur du codeur.

En examinant le schéma de connexion, on voit que la condition permettant de construire le bit correspondant à une des entrées est l'inverse de la valeur renvoyée par digitalRead(...) parce que la position ouverte de l'interrupteur correspond à +5V. Il faut donc inverser cette valeur en utilisant le non logique : !. Cet opérateur retourne un 1 si son opérande est un 0 et un 0 si son opérande est différent de 0.

Donc la valeur du codeur peut être construite par l'expression suivante :

    servoARegler =
        (! digitalRead(A2)) << 3 |
        (! digitalRead(A3)) << 2 |
        (! digitalRead(A4)) << 1 |
        (! digitalRead(A5));



Pour programmer proprement, on va loger ceci dans une fonction, lireReglage() et en profiter pour recadrer la valeur brute. Si la valeur brute est plus grande que 8, elle est remise à 0 et la valeur brute - 1 est retournée. De cette manière -1 signifie « rien à régler » et une valeur positive désigne le numéro du servo à régler.

/*
 * Lecture du codeur de réglage.
 * Retourne -1 si aucun servo n'est sélectionné et
 * le numéro du servo de 0 à 7 si un servo est sélectionné
 */
int lireReglage()
{
    int servoARegler =
        (! digitalRead(A2)) << 3 |
        (! digitalRead(A3)) << 2 |
        (! digitalRead(A4)) << 1 |
        (! digitalRead(A5));
    
    if (servoARegler > 8 ) servoARegler = 0;
    
    return servoARegler - 1;
}



Comme c'est l'état du codeur qui nous intéresse et non les événements de changement d'état, il n'est pas nécessaire de se préoccuper des transitoires et autres rebonds.

Il va aussi falloir activer les résistances interne de pull-up. Faisons ceci dans une fonction qui sera appelée à partir de setup().

void initialiseCodeur()
{
    /* Active le pullup des entrées A2, A3, A4 et A5 */
    digitalWrite(A2,HIGH);
    digitalWrite(A3,HIGH);
    digitalWrite(A4,HIGH);
    digitalWrite(A5,HIGH);
}

Dernière édition par jlb le Sam 23 Nov 2013, 17:14, édité 1 fois au total.
Avatar de l’utilisateur
jlb
Fécond
 
Messages: 679
Inscrit le: Jeu 04 Oct 2012, 15:38
Echelle pratiquée: N
Prénom: Jean-Luc

Re: Arduino - Servomoteurs

Publié: Sam 23 Nov 2013, 16:05 
Bonjour

Les entrées analogiques peuvent être utilisée comme entrées digitales, du moins au niveau matériel. Est ce que Arduino empêche cela ???

Pierre
Avatar de l’utilisateur
Pierre59
Papotier
 
Messages: 147
Inscrit le: Dim 07 Mars 2010, 09:17
Localisation: Villeneuve d'Ascq (59650)
Âge: 75
Echelle pratiquée: HO
Club: Lille Modélisme

Re: Arduino - Servomoteurs

Publié: Sam 23 Nov 2013, 16:21 
Exact oui.

Ça permettrait de bénéficier du pullup et d'économiser des résistances.

Donc

digitalWrite(A2,HIGH) pour activer la resistance de pullup
digitalRead(A2) pour récupérer la valeur.

Je mets à jour tout ça
Avatar de l’utilisateur
jlb
Fécond
 
Messages: 679
Inscrit le: Jeu 04 Oct 2012, 15:38
Echelle pratiquée: N
Prénom: Jean-Luc

Re: Arduino - Servomoteurs

Publié: Sam 23 Nov 2013, 17:15 
Voilà j'ai rectifié. Merci de ta remarque pertinente Pierre, j'étais passé à côté de ça.
Avatar de l’utilisateur
jlb
Fécond
 
Messages: 679
Inscrit le: Jeu 04 Oct 2012, 15:38
Echelle pratiquée: N
Prénom: Jean-Luc

Re: Arduino - Servomoteurs

Publié: Sam 30 Nov 2013, 14:06 
Comme d'habitude l'article est aussi disponible sur mon blog :

http://modelleisenbahn.triskell.org/spip.php?article62

Essayant de ne pas faire de trop gros articles indigestes, je saucissonne un peu plus les sujets. Voici le 2e sur le réglage des butées. Il porte sur la transformation du programme que nous avions écrit pour le clavier analogique afin que le même programme puisse servir pour le clavier de réglage et en fait pour n'importe quel clavier analogique. En informatique on appelle ça la généricité.

Poussoirs de réglage de la position

Les poussoirs de réglage de la position sont mis en œuvre avec un clavier analogique de la même manière que les poussoirs de commande mais nous allons employer 4 poussoirs au lieu de 8 et les connecter sur l'entrée analogique A1. Nous allons voir plus tard pourquoi 4 et pas 2.

Pour avoir les valeurs des résistances, il suffit de faire sauter un poussoir sur deux dans le tableau de « Clavier de réglage des butées » et d'additionner les deux résistances de part et d'autre des poussoirs supprimés. Les valeurs des résistances peuvent être moins précises car les plages de valeurs sont deux fois plus larges. Nous avons donc:

Image

Le logiciel

Nous avons maintenant deux clavier analogiques, l'un pour les 8 poussoirs de changement de positions des servomoteurs et l'autre avec 4 pour le réglage. Nous allons bien entendu utiliser les mêmes fonctions pour les deux claviers et en profiter pour les passer en C++ comme nous l'avons fait pour les servomoteurs car maintenant que nous avons 2 claviers, il est logique d'avoir des objets. Un clavier analogique est jusqu'à présent représenté par 2 variables.

byte etatAutomate = NON_PRESSE;
int etatPoussoir = -1;



Mais les fonctions lirePoussoirs() et lireEvenement() que nous avons écrites ont en dur dans le code le fait qu'il y ait 8 poussoirs. Il en est de même pour la broche de connexion. Par conséquent, pour que les fonctions puissent être génériques, nous allons en plus avoir besoin de la broche de connexion, du nombre de poussoirs et de ce nombre de poussoirs, nous allons déduire la taille d'une plage de valeurs. Voici le début de notre classe DescripteurClavierAnalogique.

class DescripteurClavierAnalogique {
    byte etatAutomate;
    int etatPoussoir;
    int taillePlage;
    int nbPoussoirs;
    int pin;
};


Nous allons ajouter le constructeur qui contient les initialisations indépendantes du nombre de poussoirs et de la broche et une fonction de connexion qui va contenir les initialisations qui en sont dépendantes.

  public:
  
    DescripteurClavierAnalogique()
    {
        etatAutomate = NON_PRESSE;
        etatPoussoir = -1;
    }
    
    void connecte(int pinDeConnexion, int nombreDePoussoirs)
    {
        pin = pinDeConnexion;
        nbPoussoirs = nombreDePoussoirs;
        /*
         * calcule la taille d'une plage
         * le convertisseur a 1024 valeurs, Le taille de plage
         * est égal à 1024/nombreDePoussoirs. Le décalage est 
         * la taille de la plage sur 2
         */
        taillePlage = 1024 / nombreDePoussoirs;
    }



Enfin, nous allons modifier lirePoussoirs() afin de remplacer les valeur en dur par la plage de valeurs ou le nombre de poussoirs. lirePoussoirs() est placé dans la partie privée de la classe car il n'est appelé que part la fonction lireEvenement()

    int lirePoussoirs()
    {
        int resultat;
        int numPoussoir = (analogRead(pin) + taillePlage / 2) /taillePlage;
        
        int nouvelEtatPoussoir = etatPoussoir; /* à priori rien ne change */
     
        switch (etatAutomate) {
            case NON_PRESSE:
                if (numPoussoir < nbPoussoirs)
                    etatAutomate = ENFONCE;
                break;
            case ENFONCE:
                if (numPoussoir < nbPoussoirs) {
                    etatAutomate = PRESSE;
                    nouvelEtatPoussoir = numPoussoir;
                }
                else {
                    etatAutomate = NON_PRESSE;
                }
                break;
            case PRESSE:
                if (numPoussoir == nbPoussoirs) {
                    etatAutomate = NON_PRESSE;
                    nouvelEtatPoussoir = -1;
                }
                break;
        }
    
        return nouvelEtatPoussoir;
    }



Nous pouvons maintenant créer nos deux objets clavierOrdreServo et clavierReglage.

DescripteurClavierAnalogique clavierOrdreServo;
DescripteurClavierAnalogique clavierReglage;



et à appeler la fonction connect(...) dans setup()

    clavierOrdreServo.connecte(0,8); /* pin A0, 8 poussoirs */
    clavierReglage.connecte(1,4);    /* pin A1, 4 poussoirs */



Pour finir, la classe complète de gestion de clavier analogique avec l'ajout de lireEvenement. Cette classe est réutilisable sans modification dans n'importe quel montage comportant un clavier analogique.

class DescripteurClavierAnalogique {
    byte etatAutomate;
    int etatPoussoir;
    int taillePlage;
    int nbPoussoirs;
    int pin;
 
    int lirePoussoirs()
    {
        int resultat;
        int numPoussoir = (analogRead(pin) + taillePlage / 2) /taillePlage;
        
        int nouvelEtatPoussoir = etatPoussoir; /* à priori rien ne change */
     
        switch (etatAutomate) {
            case NON_PRESSE:
                if (numPoussoir < nbPoussoirs)
                    etatAutomate = ENFONCE;
                break;
            case ENFONCE:
                if (numPoussoir < nbPoussoirs) {
                    etatAutomate = PRESSE;
                    nouvelEtatPoussoir = numPoussoir;
                }
                else {
                    etatAutomate = NON_PRESSE;
                }
                break;
            case PRESSE:
                if (numPoussoir == nbPoussoirs) {
                    etatAutomate = NON_PRESSE;
                    nouvelEtatPoussoir = -1;
                }
                break;
        }
    
        return nouvelEtatPoussoir;
    }
 
  public:
  
    DescripteurClavierAnalogique()
    {
        etatAutomate = NON_PRESSE;
        etatPoussoir = -1;
    }
    
    void connecte(int pinDeConnexion, int nombreDePoussoirs)
    {
        pin = pinDeConnexion;
        nbPoussoirs = nombreDePoussoirs;
        /*
         * calcule la taille d'une plage
         * le convertisseur a 1024 valeurs, Le taille de plage
         * est égal à 1024/nombreDePoussoirs. Le décalage est 
         * la taille de la plage sur 2
         */
        taillePlage = 1024 / nombreDePoussoirs;
    }

    /*
     * construction d'un événement en comparant
     * le nouvel état des poussoirs avec l'état précédent.
     */
    byte lireEvenement(int *numPoussoir)
    {
        byte evenement;
        int nouvelEtatPoussoir = lirePoussoirs();
            
        if (nouvelEtatPoussoir == etatPoussoir)
            evenement = AUCUN_EVENEMENT;
        if (nouvelEtatPoussoir >= 0 && etatPoussoir == -1)
            evenement = EVENEMENT_PRESSE;
        if (nouvelEtatPoussoir == -1 && etatPoussoir >= 0)
            evenement = EVENEMENT_RELACHE;
     
        etatPoussoir = nouvelEtatPoussoir;
        *numPoussoir = etatPoussoir;
        
        return evenement;
    }
};



Maintenant que les deux commandes nécessaires au réglage sont mises en place, nous allons pouvoir nous concentrer sur le réglage en lui-même.
Avatar de l’utilisateur
jlb
Fécond
 
Messages: 679
Inscrit le: Jeu 04 Oct 2012, 15:38
Echelle pratiquée: N
Prénom: Jean-Luc

Re: Arduino - Servomoteurs

Publié: Lun 02 Déc 2013, 09:05 
L'article est également disponible sur mon blog avec une vidéo : http://modelleisenbahn.triskell.org/spip.php?article65

Nous voici enfin à l'avant dernière étape de développement de notre système. Dans « Sélection d’un servomoteur pour le réglage des butées », nous avons vu la mise en œuvre d'un codeur pour désigner le servomoteur à régler, dans « Clavier de réglage des butées », nous avons vu l'ajout d'un second clavier analogique destiné à permettre le réglage des butées et, comme nous avions déjà un clavier analogique, la généralisation de la gestion de ce type de clavier par le biais d'une classe C++.

Nous allons maintenant voir comment intégrer cette fonction de réglage au programme de pilotage des 8 servos. Mais tout d'abord quelques éclaircissement sur la fonction désirée.

Le fonctionnement du réglage

Lorsqu'un des servo est désigné au moyen du codeur, le mouvement des servos restent actif. En effet, le réglage ne porte que sur la butée actuelle du servo désigné.

Si le servo est en butée dans le sens horaire, une pression sur le poussoir de droite doit déplacer la butée horaire un peu plus dans le sens horaire. Une pression sur le poussoir de gauche déplace la butée un peu moins. De même, si le servo est en butée dans le sens trigonométrique, une pression sur le poussoir de gauche déplace un peu plus la butée dans le sens trigonométrique et une pression sur le poussoir de droite un peu moins.

Pour passer du réglage d'une butée au réglage de l'autre butée, il suffit de presser le poussoir commandant le mouvement du servomoteur.

Une tentative de réglage alors que le servo est en mouvement est ignorée.

Le réglage en lui même va donc augmenter ou diminuer les angles minimum et maximum. Si on veut un réglage précis, il faut régler de pas en pas (Un pas vaut 1µs puisqu'on utilise la fonction writeMicroseconds(...) pour piloter les servos). Mais régler de pas en pas risque d'être long. On veut donc pouvoir décider de combien de pas la butée est augmentée ou diminuée lors d'une pression sur un des poussoirs de réglage. Voici donc le rôle des 2 boutons supplémentaires du clavier de réglage, ils servent à augmenter et diminuer le nombre de pas de réglage.

Par défaut, lorsqu'on désigne un servo à régler, le nombre de pas sera de 8. Une pression sur le poussoir d'augmentation du pas le multipliera par 2 avec un maximum de 32. Une pression sur le poussoir de diminution du pas le divisera par 2 avec un minimum de 1. Le fait de désigner un nouveau servo réinitialisera le pas de réglage à 8.

Modification de la classe DescripteurServo[b]

La première modification est évidente. Auparavant, les butées étaient déterminées globalement pour les 8 servomoteurs. Maintenant que l'on veut régler les butées de chaque servomoteur individuellement, il faut que le [b]DescripteurServo<
comporte deux données membres angleMin et angleMax comme ceci:

class DescripteurServo {
    Servo objetServo; /* Objet issu de la bibliothèque Servo de l'Arduino */
    int vitesse; /* La vitesse de déplacement du servomoteur */
    int angle; /* L'angle du servo-moteur */
    int angleMin; /* Angle minimum du servo */
    int angleMax; /* Angle maximum du servo */
    int pin; /* La broche sur laquelle il est connecté */
    byte etatServo; /* son état : SERVO_A_ANGLE_MIN, SERVO_A_ANGLE_MAX
                         SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX ou
                         SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN    */
...



On va bien entendu conserver des constantes minimum et maximum absolues afin de limiter le réglage:

const int ANGLE_MIN = 1000;
const int ANGLE_MAX = 2000;



Ces constantes vont être employées dans le constructeur de DescripteurServo pour initialiser les données membres angleMin et angleMax.

La fonction de réglage

Ensuite, il faut écrire une fonction membre de réglage. Cette fonction est appelée de loop() lorsque le codeur désigne le servo concerné. Cette fonction doit
- lire un événement sur le clavier de réglage;
- si l'événement est un changement de pas de réglage, la variable de pas est multipliée par 2 ou divisée par 2;
- si l'événement est un réglage:
- si l'état du servo est SERVO_A_ANGLE_MIN, l'angle minimum est ajusté, la vitesse du servo est mise à 1 ou -1 selon le changement pour que le servo vienne de recaler sur l'angle minimum;
- si l'état du servo est SERVO_A_ANGLE_MAX, l'angle maximum est ajusté, la vitesse du servo est mise à 1 ou -1 selon le changement pour que le servo vienne de recaler sur l'angle maximum.

Afin de séparer clairement les différents cas, nous allons utiliser des switch ... case.

    void regleServo()
    {
        int numPoussoir;
        
        if (clavierReglage.lireEvenement(&numPoussoir) == EVENEMENT_PRESSE) {
            switch(numPoussoir) {
              
              case 0: /* déplacement de la butée dans le sens trigo */
                switch(etatServo) {
                  case SERVO_A_ANGLE_MIN:
                    if (angleMin < angleMax)
                      angleMin += min(pasDeReglage, angleMax - angleMin);
                    objetServo.attach(pin);
                    vitesse = 1;
                     break;
                  case SERVO_A_ANGLE_MAX:
                    if (angleMax < ANGLE_MAX)
                      angleMax += min(pasDeReglage, ANGLE_MAX - angleMax);
                    objetServo.attach(pin);
                    vitesse = 1;
                    break;
                }
                break;
                
              case 1: /* déplacement de la butée dans le sens horaire */
                switch(etatServo) {
                  case SERVO_A_ANGLE_MIN:
                    if (angleMin > ANGLE_MIN)
                      angleMin -= min(pasDeReglage, angleMin - ANGLE_MIN);
                    objetServo.attach(pin);
                    vitesse = -1;
                    break;
                  case SERVO_A_ANGLE_MAX:
                    if (angleMax > angleMin)
                      angleMax -= min(pasDeReglage, angleMax - angleMin);
                    objetServo.attach(pin);
                    vitesse = -1;
                    break;
                }
                break;
                
              case 2: /* diminution du pas de réglage */
                if (pasDeReglage > 1) pasDeReglage /= 2;
                break;
                
              case 3: /* augmentation du pas de réglage */
                if (pasDeReglage < 32) pasDeReglage *= 2;
                break;
            }
        }
    }



La gestion du mouvement

Le réglage est un cas particulier, l'angle du servo peut être en dehors de la fourchette angleMin et angleMax. Or la fonction gereServo() suppose que l'angle est dans la fourchette. Il ne va donc pas fonctionner correctement pour le réglage. Il faut donc l'adapter.

Tout d'abord, il nous faut une variable booléenne qui va indiquer si un réglage est en cours ou non. Cette variable sera modifiée dans loop en fonction de la valeur renvoyée par lireReglage(), la fonction qui renvoie l'état du codeur.

Si il n'y a aucun réglage en cours ou si le servo est dans l'une des états SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX ou SERVO_EN_MOUVEMENT_ VERS_ANGLE_MIN, il s'agit d'un mouvement normal du servo et le code existant fonctionne. Dans l'autre cas, il faut un code spécifique pour gérer le mouvement vers la nouvelle butée. Selon la butée où le servo est et selon le signe de la vitesse, on détermine quand le mouvement doit être arrêté:

            if (etatServo == SERVO_A_ANGLE_MIN) {
                if (vitesse > 0 && angle >= angleMin) {
                    angle = angleMin;
                    vitesse = 0;         
                    objetServo.detach();
                }
                else if (vitesse < 0 && angle <= angleMin) {
                    angle = angleMin;
                    vitesse = 0;
                    objetServo.detach();
                }
            }
            else if (etatServo == SERVO_A_ANGLE_MAX) {
                if (vitesse > 0 && angle >= angleMax) {
                    angle = angleMax;
                    vitesse = 0;         
                    objetServo.detach();
                }
                else if (vitesse < 0 && angle <= angleMax) {
                    angle = angleMax;
                    vitesse = 0;
                    objetServo.detach();
                }
            }



Le fait de tester l'angle par rapport aux angles minimum et maximum avec des >= et <= est une sécurité. En effet, en testant avec une égalité == et si pour une raison indéterminée l'angle dépassait l'angle min ou max, le servo continuerait sont mouvement et irait en butée mécanique, ce qui pourrait l'endommager.

Modification de loop()

loop() est modifié pour prendre en compte la fonction de réglage.

void loop()
{
    int numServo;
  
    /* gestion du mouvement des servos */
    for (numServo = 0; numServo < 8; numServo++) servoMoteur[numServo].gereServo();
 
    /* lecture d'un ordre de mouvement */
    byte evenement = clavierOrdreServo.lireEvenement(&numServo);
  
    /* exécution de l'ordre de mouvement */
    if (evenement == EVENEMENT_PRESSE)
        servoMoteur[numServo].evenementServo();
    
    /* lecture du codeur qui désigne le servo à régler */
    int servoARegler = lireReglage();
    /* si positif ou nul, il y a un servo à régler */
    reglageEnCours = (servoARegler >= 0);
    /* si on a changé de servo à régler, le pas est remis à 8 */
    if (servoARegler != ancienServoARegler) {
      ancienServoARegler = servoARegler;
      pasDeReglage = 8;
    }
    /* exécution de réglage */
    if (reglageEnCours)
        servoMoteur[servoARegler].regleServo();
  
    delay(5);
}



Le code complet du sketch est disponible en téléchargement en bas de la page http://modelleisenbahn.triskell.org/spip.php?article65

Comme expliqué dans la vidéo, il reste une paire de détails à régler. Nous verrons cela dans le prochain article mais on commence à voir le bout du tunnel !
Avatar de l’utilisateur
jlb
Fécond
 
Messages: 679
Inscrit le: Jeu 04 Oct 2012, 15:38
Echelle pratiquée: N
Prénom: Jean-Luc

Re: Arduino - Servomoteurs

Publié: Lun 02 Déc 2013, 09:54 
Voici l'article qui boucle la partie sur le gestion des servomoteurs. Il est également disponible sur mon blog : http://modelleisenbahn.triskell.org/spip.php?article67

Comme indiqué dans la vidéo au bas de l'article « Réglage des butées des servomoteurs », un problème lié à l'inertie mécanique subsiste. Nous avons aussi le problème de la non mémorisation des réglages. Nous allons voir comment régler ces deux problèmes.

Prise en compte de l'inertie mécanique

Quand la consigne de position, via la durée de l'état haut de la PWM, est envoyé au servomoteur, celui-ci met quelques instants à appliquer cette consigne. Or, le programme actuel coupe la PWM est appelant la fonction detach() dès que la consigne atteint l'angle maximum ou l'angle minimum. Le servo n'a donc pas le temps de répondre à cette consigne et pour régler le problème il faut différer le détachement du servo.

Au lieu de détacher immédiatement le servo, il faut initialiser un compteur, que nous appelleront cptArret, qui quand il atteint 0 provoque le détachement du servo. Une fois le servo détaché, le compteur est mis à -1 pour éviter de détacher le servo de manière répétitive. Actuellement dans gereServo() en plusieurs endroits, la vitesse est mise à 0 et le servo est détaché.

Il faut remplacer les objetServo.detach() par l'initialisation du compteur. Pour rationaliser nous allons mettre le vitesse = 0; et l'initialisation du compteur dans une fonction membre séparée :

    void arreteServo()
    {
        vitesse = 0;
        cptArret = 30; 
    }



Avec le délai de 5ms dans loop(), une valeur de 30 engendre un délai de 150ms ce qui est suffisant pour que la mécanique rattrape l'électronique.

Il faut maintenant, dans la fonction gereServo(), décrémenter ce compteur tant qu'il est ≥ à 0 et détacher le servo quand ce compteur est égal à 0. De plus, à chaque fois que nous avons la mise de la vitesse à 0 et le détachement du servo, nous le remplaçons par l'appel à la fonction arreteServo(). gereServo() devient :

void gereServo()
{
    objetServo.writeMicroseconds(angle);
 
    if (vitesse == 0)
        if (cptArret == 0)
            objetServo.detach();
        if (cptArret >= 0)
            cptArret--;
    else
        angle += vitesse;
    
    if (! reglageEnCours ||
        etatServo == SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN ||
        etatServo == SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX) {
        if (angle > angleMax) {
            angle = angleMax;
            arreteServo();
            etatServo = SERVO_A_ANGLE_MAX;
        }
        else if (angle < angleMin) {
            angle = angleMin;
            arreteServo();
            etatServo = SERVO_A_ANGLE_MIN;
        }
    }
    else {
        if (etatServo == SERVO_A_ANGLE_MIN) {
            if (vitesse > 0 && angle >= angleMin) {
                angle = angleMin;
                arreteServo();
            }
            else if (vitesse < 0 && angle <= angleMin) {
                angle = angleMin;
                arreteServo();
            }
        }
        else if (etatServo == SERVO_A_ANGLE_MAX) {
            if (vitesse > 0 && angle >= angleMax) {
                angle = angleMax;
                arreteServo();
            }
            else if (vitesse < 0 && angle <= angleMax) {
                angle = angleMax;
                arreteServo();
            }
        }
    }
}



Il faut également garantir que le servo ne sera pas détaché intempestivement. En effet, si l'utilisateur demande un mouvement du servo dans l'intervalle de temps entre le passage de la vitesse à 0 et le passage du compteur à 0, il faut passer directement le compteur à -1 dans
Code: Tout sélectionner
evenementServo()
car sinon le détachement du servo interviendra pendant le mouvement, ce qui est un dysfonctionnement.

Mémorisation des butées

La plupart des microcontrôleurs dispose d'une mémoire permanente, c'est à dire qui ne s'efface pas à la mise hors tension, sous forme d'EEPROM (Electrically Erasable Programmable Read-Only Memory). La bibliothèque de l'Arduino permet de lire et d'écrire l'EEPROM de manière très simple via deux fonctions : EEPROM.read(...) et EEPROM.write(..., ...). EEPROM.read(...) prend comme argument une adresse d'octet (byte) et retourne l'octet qui se trouve à cet adresse et EEPROM.write(..., ...) prend deux arguments. Le premier est l'adresse de l'octet que l'on souhaite écrire et le second est la valeur que l'on souhaite y écrire. L'EEPROM de l'ATMega328 qui équipe l'Arduino Uno a une taille de 1ko.

Pour chaque servomoteur, il faut stocker les deux butées. Chaque butée est stockée dans un int. Sur l'Arduino Uno, un int prend 2 octets. Par conséquent il faut 4 octets pour chaque servos et donc 32 octets au total. En démarrant à l'adresse 0 de l'EEPROM, l'adresse de stockage des butée d'une servo sera donc égale au numéro du servo × 4. Nous allons donc stocker le premier octet de angleMin à numéro du servo × 4, le second à numéro du servo × 4 + 1, le premier octet de angleMax à numéro du servo × 4 + 2 et le 2e octet à numéro du servo × 4 + 3. Nous allons donc ajouter une donnée membre à la classe DescripteurServo pour y mémoriser cette adresse. Cette donnée est initialisée dans le constructeur.

Il est nécessaire d'être prudent avec les EEPROM. En effet, elle supporte un nombre de cycle d'effacement/écriture réduit. Par exemple, celle de l'ATMega328 qui équipe l'Arduino Uno supporte 100000 cycles d'effacement/écriture. Cela peut sembler une valeur importante mais si nous écrivons imprudemment les butées des servos en EEPROM à chaque passage dans loop(), l'EEPROM sera fichue en 5ms × 100~000 = 500s = 8 minutes et 20 secondes.

Il faut donc n'écrire les butées que si un réglage a été effectué et si la valeur a changé. Nous allons donc ajouter un booléen membre de la classe DescripteurServo, aSauver, qui sera mis à vrai dans regleServo() si l'une des butées a été changée.

Enfin, deux fonctions membres, ecrireEEPROMSiDifferent(...) et enregistre() sont ajoutées pour mémoriser les butées dans l'EEPROM. Le travail n'est effectué que si aSauver est vrai et si les valeurs ont changé.

    void ecrireEEPROMSiDifferent(int adresseEcriture, byte valeur)
    {
        if (EEPROM.read(adresseEcriture) != valeur)
            EEPROM.write(adresseEcriture, valeur);
    }
    
    void enregistre()
    {
        if (aSauver) {
            ecrireEEPROMSiDifferent(adresse, angleMin & 0xFF);
            ecrireEEPROMSiDifferent(adresse + 1, angleMin >> 8);
            ecrireEEPROMSiDifferent(adresse + 2, angleMax & 0xFF);
            ecrireEEPROMSiDifferent(adresse + 3, angleMax >> 8);
            aSauver = false;
        }            
    }



Enfin, au démarrage du système dans connecte(), il faut initialiser la position des servos aux valeurs enregistrées. La valeur lue est recadrée entre ANGLE_MIN et ANGLE_MAX car l'EEPROM peut contenir des valeurs écrites par une autre application ou bien être vierge (Un octet d'EEPROM qui n'a jamais été écrit est à la valeur 255 en décimal ou 0xFF en hexadécimal (tous les bits de l'octet sont à 1)).

        angleMin = EEPROM.read(adresse) | ((int)EEPROM.read(adresse + 1)) << 8;
        angleMax = EEPROM.read(adresse + 2) | ((int)EEPROM.read(adresse + 3)) << 8;
        if (angleMin < ANGLE_MIN || angleMin > ANGLE_MAX) angleMin = ANGLE_MIN;
        if (angleMax < ANGLE_MIN || angleMax > ANGLE_MAX) angleMax = ANGLE_MAX;



Et enfin le sketch de l'application améliorée et une petite vidéo qui montre l'effet de ces deux améliorations est disponible en bas de page de http://modelleisenbahn.triskell.org/spip.php?article67
Dernière édition par jlb le Lun 02 Déc 2013, 20:58, édité 1 fois au total.
Avatar de l’utilisateur
jlb
Fécond
 
Messages: 679
Inscrit le: Jeu 04 Oct 2012, 15:38
Echelle pratiquée: N
Prénom: Jean-Luc

Re: Arduino - Servomoteurs

Publié: Lun 02 Déc 2013, 10:51 
Bravo pour ces deux nouveaux articles :applause: :applause: :applause:

Et les vidéos sont également aussi claires que les précédentes, c'est du beau boulot.

Christian
Avatar de l’utilisateur
Arduino
Prolixe
 
Messages: 1698
Inscrit le: Mer 25 Sep 2013, 16:14

Re: Arduino - Servomoteurs

Publié: Lun 02 Déc 2013, 18:40 
Trop bien expliqué, trop fort.

F E L I C I T A T I O N

:applause:
Cordialement,

Christian.
Avatar de l’utilisateur
likiki
Causant
 
Messages: 270
Inscrit le: Dim 29 Avr 2012, 14:21
Localisation: Corbeil Essonne
Âge: 51
Echelle pratiquée: H0 3R
Prénom: Christian

Re: Arduino - Servomoteurs

Publié: Lun 02 Déc 2013, 18:45 
Merci à vous deux 8)

La suite arrive : la gestion des témoins avec les 5 broches qui me restent ;)
Avatar de l’utilisateur
jlb
Fécond
 
Messages: 679
Inscrit le: Jeu 04 Oct 2012, 15:38
Echelle pratiquée: N
Prénom: Jean-Luc

Re: Arduino - Servomoteurs

Publié: Lun 02 Déc 2013, 20:41 
C'est vrai beau boulot. Cela va me servir bientôt.
Guillaume22
Papotier
 
Messages: 145
Inscrit le: Dim 29 Sep 2013, 08:20

Re: Arduino - Servomoteurs

Publié: Lun 02 Déc 2013, 20:59 
Merci !

Petit bug lors de la lecture de l'EEPROM, les angles lus doivent être comparés aux deux bornes ANGLE_MIN et ANGLE_MAX
Avatar de l’utilisateur
jlb
Fécond
 
Messages: 679
Inscrit le: Jeu 04 Oct 2012, 15:38
Echelle pratiquée: N
Prénom: Jean-Luc

Re: Arduino - Servomoteurs

Publié: Lun 02 Déc 2013, 22:20 
Voici la suite : la présentation de la technique qui va être mise en œuvre pour gérer 16 LED avec seulement 5 broches. L'article est également disponible sur mon blog : http://modelleisenbahn.triskell.org/spip.php?article64

La dernière fonction à mettre en œuvre est la gestion des témoins de position. 16 LED sont nécessaires et il nous reste 6 broches. La broche numérique 13 ayant déjà une LED soudée sur la carte de l'Arduino n'est pas très commode à utiliser. Il nous reste donc en réalité 5 broches.

Comment faire ?

Il existe une technique connue sous le nom de Charliplexing (Le Charlieplexing est une technique proposée en 1995 par Charlie Allen, voir l'article sur Wikipedia) qui permet de multiplexer des LED sur des entrées/sorties numériques. Examinons tout d'abord le cas de deux LED connectées à 2 broches avec 2 résistances de 150Ω.

Image

Si la broche 0 est en sortie à 5V et la broche 1 en sortie à 0V, la LED rouge est allumée. Si la broche 0 est en sortie à 0V et la broche 1 en sortie à 5V, la LED verte est allumée. Toute autre configuration, une des broches ou les deux en entrée ou les deux en sortie au même potentiel, éteint les deux LED.

Ça ne nous avance pas beaucoup me direz vous.

Rajoutons donc une broche 2 et une résistance et ajoutons une paire de LED entre la broche 2 et la broche 1 mais aussi entre la broche 2 et la broche 0, soit 6 LED au total.

Image

Pour allumer la LED D5 par exemple, il faut mettre la broche 0 en sortie à 5V, la broche 2 en sortie à 0V et pour que ni D1, ni D3 ne s'allument, mettre la broche 1 en entrée (donc en haute impédance). On peut remarquer que D1 et D3 sont également alimentées mais comme elle sont en série, elles présentent une tension de seuil 2 fois supérieure (La tension de seuil dépend de la couleur de la LED mais même en mettant en série 2 LED rouge dont la tension de seuil est la plus faible, 1,6V, 2 × 1,6V = 3,2 V reste supérieure à la tension de seuil des LED violettes, 3,1V) et le courant empruntera le chemin de plus faible tension de seuil.

Pour la suite on va noter le fait de mettre une sortie à 5V : 1, à 0V, 0 et en entrée : Z.

La table correspondant à l'allumage de chaque LED est la suivante.

Image

De manière générale, avec N broches, on peut piloter N × N-1 LED. Avec nos 5 broches disponible, on pourra donc piloter 5 × 4 = 20 LED.

Programme de démonstration

Évidemment, seule une LED peut être allumée à un instant. C'est donc l'Arduino qui va s'occuper de balayer les LED et, pour chacune les allumer ou non à une vitesse suffisamment grande pour que l'œil ait l'impression que l'allumage de chaque LED est permanent grâce à la persistance rétinienne. Avant de nous lancer dans l'allumage de 16 LED, voyons comment fonctionne le programme pour 3 broches et 6 LED. Dans un premier temps, nous allons balayer et allumer les 6 LED pour juger de l'effet rendu.

Il faut tout d'abord transposer le table d'états en C dont les lignes correspondront aux LED et les colonnes aux sortie. Les états eux même sont définis par un <code>enum</code>, une manière de donner un nom symbolique à une constante numérique :

enum { OUT0, OUT1, INZ };

const byte etatSortiePourLED[6][3] = {
  { OUT0, OUT1, INZ }, /* D0 */
  { OUT1, OUT0, INZ }, /* D1 */
  { INZ, OUT0, OUT1 }, /* D2 */
  { INZ, OUT1, OUT0 }, /* D3 */
  { OUT0, INZ, OUT1 }, /* D4 */
  { OUT1, INZ, OUT0 }  /* D5 */
};



OUT0 signifie que le broche est en sortie et à 0, OUT1 qu'elle est en sortie et à 1 et INZ en entrée et à haute impédance, c'est à dire sans résistance de pull-up.

Ajoutons une fonction pour programmer une broche en fonction de ce code:

void programmeBroche(int numBroche, byte etat)
{
  switch (etat) {
    case OUT0:
      digitalWrite(numBroche,LOW);
      pinMode(numBroche,OUTPUT);
      break;
    case OUT1:
      digitalWrite(numBroche,HIGH);
      pinMode(numBroche,OUTPUT);
      break;
    case INZ:
      pinMode(numBroche,INPUT);
      digitalWrite(numBroche,LOW); /* pas de pullup */
      break;
  }
}



Pour que l'on puisse tour à tour allumer une LED ou non, il nous faut un tableau qui, pour chaque LED, va indiquer son état, ALLUME ou ETEINT. De nouveau nous allons utiliser unenum.

enum { ALLUME, ETEINT };
 
byte etatLED[6] = {
    ETEINT, ETEINT, ETEINT, ETEINT, ETEINT, ETEINT
};



Enfin, il nous faut une fonction qui, pour un numéro de LED, va chercher son état et, si elle elle allumée, appelle, pour chaque broche, programmeBroche avec les états de broche correspondant à l'allumage de cet LED. Si elle est éteinte, toutes les sorties sont mises dans l'état INZ.

void gereLED(int num)
{
  if (etatLED[num] == ALLUME) {
    programmeBroche(10,etatSortiePourLED[num][0]);
    programmeBroche(11,etatSortiePourLED[num][1]);
    programmeBroche(12,etatSortiePourLED[num][2]);
  }
  else {
    programmeBroche(10,INZ);
    programmeBroche(11,INZ);
    programmeBroche(12,INZ);
  }
}



Il suffit ensuite dans loop() de balayer les LED, de 0 à 5, en donnant leur état avec une période de 2ms.

void loop()

    gereLED(numLED);
 
    numLED++;
    if (numLED > 15)
        numLED = 0;
 
    delay(1);
}



Par dessus on superpose une séquence d'allumage comme, par exemple, un chenillard.

void loop()
{
    compteur1s++;
    if (compteur1s == 500) {
        /* Au bout de 1s, on allume la LED suivante */
        compteur1s = 0;
        /* Éteint la LED qui était allumée */
        etatLED[LEDAllumee] = ETEINT;
        /* Passe à la LED suivante */
        LEDAllumee++;
        if (LEDAllumee > 5)
            LEDAllumee = 0;
        /* Allume la nouvelle LED */
        etatLED[LEDAllumee] = ALLUME;
    }

    gereLED(numLED);
 
    numLED++;
    if (numLED > 15)
        numLED = 0;
 
    delay(1);
}



Le programme de chenillard en Charlieplexing sur 3 broches et 6 LED peut être téléchargé en bas de la page http://modelleisenbahn.triskell.org/spip.php?article64 . Vous y trouverez aussi une vidéo de démonstration.
Dernière édition par jlb le Mar 03 Déc 2013, 13:24, édité 1 fois au total.
Avatar de l’utilisateur
jlb
Fécond
 
Messages: 679
Inscrit le: Jeu 04 Oct 2012, 15:38
Echelle pratiquée: N
Prénom: Jean-Luc

Re: Arduino - Servomoteurs

Publié: Mar 03 Déc 2013, 12:37 
Ce qu'a écrit jlb est intéressant à plus d'un titre. :wink:

En effet, la technique du multiplexage permet d'interfacer plusieurs périphériques avec un nombre de fils restreint (un même bus), ce qui permet de réaliser une économie de moyens ou encore de faire tenir une application gourmande en sorties, sur un microcontrôleur de taille petite. Cette technique est utilisée couramment dans nos automobiles ; avant, il y avait un fil pour chaque organe (feu, essuie-glace, clignotant, ...), aujourd'hui, un même fil commande plusieurs organes.

Cette technique peut être aussi utilisée avec Arduino. Prenons le cas d'un afficheur 7 segment, constitué de 7 petites LED plus le point ; il faut donc 8 fils pour réaliser l'affichage d'un chiffre (de 0 à 9). Si on veut mettre un deuxième afficheur, 8 fils de plus, soit 16 sorties (on est presque à la limite de ce que peut faire un Uno). Avec le multiplexage, on utilise les mêmes huit fils pour les deux afficheurs, plus deux fils pour sélectionner soit l'afficheur 1 soit le 2. Bien sûr, un seul afficheur est allumé à la fois, mais en les sélectionnant l'un après l'autre à une fréquence très élevée, on a l'impression que les deux afficheurs sont allumés en même temps.

Dans ce que décrit jlb, la technique de multiplexage est un peu différente car elle utilise une propriété de certains microcontrôleurs modernes : le bus tri-state. Chaque sortie peut être à l'état LOW, HIGH ou bien haute impédance, ce qui rajoute des possibilités. Le charlieplexing utilise cette propriété ; jlb l'ayant très bien décrite, je ne vais pas revenir sur ce point. 8)

Je vous laisse simplement imaginer ce que vous pouvez faire pour votre réseau avec cette technique et de simples LED. Vous vous rappelez de cette fête foraine où tous les manèges clignotaient de tous leurs feux, fête décrite dans "Le monde selon Arduino" ? (voir forum Arduino et le projet APTE, page 28). Et bien voilà, nous y sommes ! :D
Avatar de l’utilisateur
Arduino
Prolixe
 
Messages: 1698
Inscrit le: Mer 25 Sep 2013, 16:14

Re: Arduino - Servomoteurs

Publié: Mar 03 Déc 2013, 20:45 
Bonsoir :)

Voici le dernier article de la série. Il est également disponible sur mon blog : http://modelleisenbahn.triskell.org/spip.php?article66

Nous avons vu la technique du Charlieplexing dans « Témoins de positions : le Charlieplexing », nous allons maintenant l'appliquer à un système à 5 broches de pilotage et 16 LED puis l'intégrer dans notre application.

Charlieplexing des 16 LED témoins

Le schéma pour allumer 16 LED est le suivant.

Image

On peut noter que le schéma est incomplet. En effet, avec 5 broches, il est possible de piloter 20 LED. Ici les couples de broches 0-3 et 1-4 sont manquants et nous n'allons donc pas les placer dans notre table d'état des broches.

Voici donc la table d'état des broches pour chaque LED

Image

Nous allons donc construire cette table en C comme nous l'avons fait pour la Charlieplexing avec 3 broches de commande.

const byte etatSortiePourLED[16][5] = {
  { OUT0, OUT1, INZ, INZ, INZ }, /* D0 */
  { OUT1, OUT0, INZ, INZ, INZ }, /* D1 */
  { INZ, OUT0, OUT1, INZ, INZ }, /* D2 */
  { INZ, OUT1, OUT0, INZ, INZ }, /* D3 */
  { INZ, INZ, OUT0, OUT1, INZ }, /* D4 */
  { INZ, INZ, OUT1, OUT0, INZ }, /* D5 */
  { INZ, INZ, INZ, OUT0, OUT1 }, /* D6 */
  { INZ, INZ, INZ, OUT1, OUT0 }, /* D7 */
  { OUT0, INZ, OUT1, INZ, INZ }, /* D8 */
  { OUT1, INZ, OUT0, INZ, INZ }, /* D9 */
  { INZ, INZ, OUT0, INZ, OUT1 }, /* D10 */
  { INZ, INZ, OUT1, INZ, OUT0 }, /* D11 */
  { INZ, OUT0, INZ, OUT1, INZ }, /* D12 */
  { INZ, OUT1, INZ, OUT0, INZ }, /* D13 */
  { OUT0, INZ, INZ, INZ, OUT1 }, /* D14 */
  { OUT1, INZ, INZ, INZ, OUT0 }  /* D15 */
};



La fonction programmeBroche(...) reste la même par contre la fonction gereLED(...) doit être modifiée puisque l'on passe de 3 à 5 broches de commande.

void gereLED(int num)
{
  if (etatLED[num] == ALLUME) {
    programmeBroche(8,etatSortiePourLED[num][0]);
    programmeBroche(9,etatSortiePourLED[num][1]);
    programmeBroche(10,etatSortiePourLED[num][2]);
    programmeBroche(11,etatSortiePourLED[num][3]);
    programmeBroche(12,etatSortiePourLED[num][4]);
  }
  else {
    programmeBroche(8,INZ);
    programmeBroche(9,INZ);
    programmeBroche(10,INZ);
    programmeBroche(11,INZ);
    programmeBroche(12,INZ);
  }
}



Et le tableauetatLED doit être agrandi pour contenir 16 éléments.

Intégration dans l'application

Pour intégrer l'allumage des témoins dans l'application il faut tout d'abord insérer l'appel à gereLED() dans loop(). Nous faisons face à un premier problème. Pour que les servomoteurs pivotent suffisamment lentement, l'attente est de 5ms. Cela conduit à un balayage des 16 LED en 80ms ce qui est trop lent pour que la persistance rétinienne opère. Il faudrait balayer au moins 4 fois plus souvent. Par conséquent, le délai dans loop() va être réduit à 1ms et la gestion des poussoirs et des servomoteurs sera appelée toutes les 5 fois alors que la gestion des LED sera appelée toutes les fois. Nous allons donc ajouter un compteur compteurGestionServos initialisé à 0. Comme ceci.

void loop()
{
    compteurGestionServos++;
    
    if (compteurGestionServos == 5) {
        
        compteurGestionServos = 0;

...
/* code de gestion des servos et des poussoir à l'identique */
...
    }
  
    gereLED(numLED);
 
    numLED++;
    if (numLED == 16)
        numLED = 0;
  
    delay(1);
}



Il suffit ensuite de changer l'état des LED en ALLUME ou ETEINT dans le tableau etatLED en fonction des événements. Deux fonctions membre de la classe DescripteurServo sont concernées : evenementServo(...) où on va éteindre la LED de la position que l'on quitte et gereServo() où on va allumer la LED de la position que l'on atteint. Dans le tableau etatLED, les LED sont alternativement rouges et vertes et par conséquent la paire de LED correspondant à un servomoteur est à la position {numéro de servo} × 2 (angle minimum) et {numéro de servo} × 2 + 1 (angle maximum).

    void evenementServo()
    {
        cptArret = -1;
        switch (etatServo) {
          case SERVO_A_ANGLE_MIN:
            etatLED[numServo * 2] = ETEINT;
            objetServo.attach(pin);
          case SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN:
            vitesse =  1;
            etatServo = SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX;
            break;
          case SERVO_A_ANGLE_MAX:
            etatLED[numServo * 2 + 1] = ETEINT;
            objetServo.attach(pin);
          case SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX:
            vitesse = -1;
            etatServo = SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN;
            break;
        }
    }



Dans la fonction membre gereServo(), la partie qui s'occupe du passage de la vitesse à 0 quand les angles minimum ou maximum sont atteint est modifiée comme suit:

        if (! reglageEnCours ||
            etatServo == SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN ||
            etatServo == SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX) {
            if (angle > angleMax) {
                angle = angleMax;
                arreteServo();
                etatServo = SERVO_A_ANGLE_MAX;
                etatLED[numServo * 2 + 1] = ALLUME;
            }
            else if (angle < angleMin) {
                angle = angleMin;
                arreteServo();
                etatServo = SERVO_A_ANGLE_MIN;
                etatLED[numServo * 2] = ALLUME;
            }
        }



Il ne faut pas oublier d'allumer la LED correspondant à l'angle minimum dans la fonction membre connecte(...)

        /* allume la LED correspondante */
        etatLED[numServo * 2] = ALLUME;



Nous sommes parvenus au bout de ce développement. Le sketch de l'application complète est à télécharger sur http://modelleisenbahn.triskell.org/spip.php?article66

Il est accompagné d''une vidéo de démonstration.
Avatar de l’utilisateur
jlb
Fécond
 
Messages: 679
Inscrit le: Jeu 04 Oct 2012, 15:38
Echelle pratiquée: N
Prénom: Jean-Luc

PrécédentSuivant

Retour vers Arduino

Qui est en ligne ?

Utilisateur(s) parcourant actuellement ce forum : Aucun utilisateur inscrit et 4 invité(s)