[TP] Parking

Ça y est, une page se tourne avec l’acquisition de nombreuses connaissances de base. C’est donc l’occasion idéale pour faire un (gros :diable: ) TP qui utilisera l’ensemble de vos connaissances durement acquises. J’aime utiliser les situations de la vie réelle, je vais donc en prendre une pour ce sujet. Je vous propose de réaliser la gestion d’un parking souterrain… RDV aux consignes pour les détails.

Consigne

Après tant de connaissances chacune séparée dans son coin, nous allons pouvoir mettre en œuvre tout ce petit monde dans un TP traitant sur un sujet de la vie courante : les parkings !

Histoire

Le maire de zCity à décidé de rentabiliser le parking communal d’une capacité de 99 places (pas une de plus ni de moins). En effet, chaque jour des centaines de zTouristes viennent se promener en voiture et ont besoin de la garer quelque part. Le parking, n’étant pour le moment pas rentable, servira à financer l’entretien de la ville. Pour cela, il faut rajouter au parking existant un afficheur permettant de savoir le nombre de places disponibles en temps réel (le système de paiement du parking ne sera pas traité). Il dispose aussi dans la ville des lumières vertes et rouges signalant un parking complet ou non. Enfin, l’entrée du parking est équipée de deux barrières (une pour l’entrée et l’autre pour la sortie). Chaque entrée de voiture ou sortie génère un signal pour la gestion du nombre de places. Le maire vous a choisi pour vos compétences, votre esprit de créativité et il sait que vous aimez les défis. Vous acceptez évidemment en lui promettant de réussir dans les plus brefs délais !

Matériel

Pour mener à bien ce TP voici la liste des courses conseillée :

  • Une carte Arduino (évidemment)
  • 2 LEDs avec leur résistance de limitations de courant (habituellement 330 Ohms) -> Elles symbolisent les témoins lumineux disposés dans la ville
  • 2 boutons (avec 2 résistances de 10 kOhms et 2 condensateurs de 10 nF) -> Ce sont les "capteurs" d’entrée et de sortie.
  • 2 afficheurs 7 segments -> pour afficher le nombre de places disponibles
  • 1 décodeur 4 bits vers 7 segments
  • 7 résistances de 330 Ohms (pour les 7 segments)
  • Une breadboard pour assembler le tout
  • Un paquet de fils
  • Votre cerveau et quelques doigts…

Voici une vidéo pour vous montrer le résultat attendu par le maire :

Bon courage !

Pour ceux qui veulent uniquement se concentrer sur le code, le montage via simulateur, sans code, est disponible dans la correction, juste avant la partie concernant le programme.

Correction !

J’espère que tout s’est bien passé pour vous et que le maire sera content de votre travail. Voilà maintenant une correction (parmi tant d’autres, comme souvent en programmation et en électronique). Nous commencerons par voir le schéma électronique, puis ensuite nous rentrerons dans le code.

Montage

Le montage électronique est la base de ce qui va nous servir pour réaliser le système. Une fois qu’il est terminé on pourra l’utiliser grâce aux entrées/sorties de la carte Arduino et lui faire faire pleins de choses. Mais ça, vous le savez déjà. Alors ici pas de grand discours, il "suffit" de reprendre les différents blocs vus un par un dans les chapitres précédents et de faire le montage de façon simple.

Schéma

Je vous montre le schéma que j’ai réalisé, il n’est pas absolu et peut différer selon ce que vous avez fait, mais il reprend essentiellement tous les "blocs" (ou mini montages électroniques) que l’on a vus dans les précédents chapitres, en les assemblant de façon logique et ordonnée :

TP Parking - Schéma
TP Parking - Schéma
TP Parking - Montage
TP Parking - Montage
Procédure de montage

Voici l’ordre que j’ai suivi pour réaliser le montage :

  • Débrancher la carte Arduino !
  • Mettre les boutons
     - Mettre les résistances de pull-up
     - Puis les condensateurs de filtrage
     - Et tirez des fils de signaux jusqu'à la carte Arduino
     - Enfin, vérifiez la position des alimentations (+5V et masse)
    
  • Mettre les LEDs rouge et verte avec leur résistance de limitation de courant et un fil vers Arduino
  • Mettre les décodeurs
     - Relier les fils ABCD à Arduino
     - Mettre au +5V ou à la masse les signaux de commandes du décodeur
     - Mettre les résistances de limitations de courant des 7 segments
     - Enfin, vérifier la position des alimentations (+5V et masse)
    
  • Puis mettre les afficheurs -> les relier entre le décodeur et leurs segments) -> les connecter au +5V
  • Amener du +5V et la masse sur la breadboard

Ce étant terminé, la maquette est fin prête à être utilisée ! Évidemment, cela fait un montage (un peu) plus complet que les précédents !

Pour ceux qui veulent uniquement faire le code, voici le montage sur simulateur, avec les déclarations de broches. Il ne reste plus qu’à coder !

!(https://www.tinkercad.com/embed/gHgLrsGuuxR)

Programme

Nous allons maintenant voir une solution de programme pour le problème de départ. La vôtre sera peut-être (voire surement) différente, et ce n’est pas grave, un problème n’exige pas une solution unique. Je n’ai peut-être même pas la meilleure solution ! (mais ça m’étonnerait :P :ninja: )

Les variables utiles et déclarations

Tout d’abord, nous allons voir les variables globales que nous allons utiliser ainsi que les déclarations utiles à faire. Pour ma part, j’utilise six variables globales. Vous reconnaîtrez la plupart d’entre elles car elles viennent des chapitres précédents.

  • Celles pour stocker l’état des boutons un coup sur l’autre et une pour le stocker de manière courante
  • Un char stockant le nombre de places disponibles dans le parking
  • Un booléen désignant l’afficheur utilisé en dernier
  • Un long stockant l’information de temps pour le rafraîchissement de l’affichage

Voici ces différentes variables commentées.

// les broches du décodeur 7 segments
const int bit_A = 2;
const int bit_B = 3;
const int bit_C = 4;
const int bit_D = 5;
// les broches des transistors pour l'afficheur des dizaines et des unités
const int alim_dizaine = 6;
const int alim_unite = 7;
// les broches des boutons
const int btn_entree = 8;
const int btn_sortie = 9;
// les leds de signalements
const int led_rouge = 12;
const int led_verte = 11;
// les mémoires d'état des boutons
int mem_entree = HIGH;
int mem_sortie = HIGH;
int etat = HIGH; // variable stockant l'état courant d'un bouton

char place_dispo = 99; // contenu des places dispos
bool afficheur = false;
long temps;
Les variables et constantes
L’initialisation de la fonction setup()

Je ne vais pas faire un long baratin sur cette partie car je pense que vous serez en mesure de tout comprendre très facilement car il n’y a vraiment rien d’original par rapport à tout ce que l’on a fait avant (réglages des entrées/sorties et de leurs niveaux).

void setup()
{
    // Les broches sont toutes des sorties (sauf les boutons)
    pinMode(bit_A, OUTPUT);
    pinMode(bit_B, OUTPUT);
    pinMode(bit_C, OUTPUT);
    pinMode(bit_D, OUTPUT);
    pinMode(alim_dizaine, OUTPUT);
    pinMode(alim_unite, OUTPUT);
    pinMode(led_rouge, OUTPUT);
    pinMode(led_verte, OUTPUT);

    pinMode(btn_entree, INPUT);
    pinMode(btn_sortie, INPUT);

    // Les broches sont toutes mise à l'état bas (sauf led rouge éteinte)
    digitalWrite(bit_A, LOW);
    digitalWrite(bit_B, LOW);
    digitalWrite(bit_C, LOW);
    digitalWrite(bit_D, LOW);
    digitalWrite(alim_dizaine, LOW);
    digitalWrite(alim_unite, LOW);
    digitalWrite(led_rouge, HIGH);
    digitalWrite(led_verte, LOW); // vert par défaut
    // rappel: dans cette configuration, la LED est éteinte à l'état HIGH

    temps = millis(); // enregistre "l'heure"
}
L’initialisation
La boucle principale (loop)

Ici se trouve la partie la plus compliquée du TP. En effet, elle doit s’occuper de gérer d’une part une boucle de rafraichissement de l’allumage des afficheurs 7 segments et d’autre part gérer les évènements. Rappelons-nous de l’organigramme vu dans la dernière partie sur les 7 segments :

Organigramme TP Parking
Organigramme TP Parking

Dans notre application, la gestion d’évènements sera "une voiture rentre-t/sort-elle du parking ?" qui sera symbolisée par un appui sur un bouton. Ensuite, il faudra aussi prendre en compte l’affichage de la disponibilité sur les LEDs selon si le parking est complet ou non… Voici une manière de coder tout cela :


void loop()
{
    // si ca fait plus de 10 ms qu'on affiche, on change de 7 segments
    if((millis() - temps) > 10)
    {
        // on inverse la valeur de "afficheur"
        // pour changer d'afficheur (unité ou dizaine)
        afficheur = !afficheur;
        // on affiche
        afficher_nombre(place_dispo, afficheur);
        temps = millis(); // on met à jour le temps
    }

    // on test maintenant si les boutons ont subi un appui (ou pas)
    // d'abord le bouton plus puis le moins
    etat = digitalRead(btn_entree);
    if((etat != mem_entree) && (etat == LOW))
        place_dispo += 1;
    mem_entree = etat; // on enregistre l'état du bouton pour le tour suivant

    // et maintenant pareil pour le bouton qui décrémente
    etat = digitalRead(btn_sortie);
    if((etat != mem_sortie) && (etat == LOW))
        place_dispo -= 1;
    mem_sortie = etat; // on enregistre l'état du bouton pour le tour suivant

    // on applique des limites au nombre pour ne pas dépasser 99 ou 0
    if(place_dispo > 99)
        place_dispo = 99;
    if(place_dispo < 0)
        place_dispo = 0;

    // on met à jour l'état des leds
    // on commence par les éteindres
    digitalWrite(led_verte, HIGH);
    digitalWrite(led_rouge, HIGH);
    if(place_dispo == 0) // s'il n'y a plus de place
        digitalWrite(led_rouge, LOW);
    else
        digitalWrite(led_verte, LOW);
}

Dans les lignes 4 à 11, on retrouve la gestion du rafraichissement des 7 segments. Ensuite, on s’occupe de réceptionner les évènements en faisant un test par bouton pour savoir si son état a changé et s’il est à l’état bas. Enfin, on va borner le nombre de places et faire l’affichage sur les LED en conséquence. Vous voyez, ce n’était pas si difficile en fait ! Si, un peu quand même, non ? ^^ Il ne reste maintenant plus qu’à faire les fonctions d’affichages.

Les fonctions d’affichages

Là encore, je ne vais pas faire de grand discours puisque ces fonctions sont exactement les mêmes que celles réalisées dans la partie concernant l’affichage sur plusieurs afficheurs. Si elles ne vous semblent pas claires, je vous conseille de revenir sur le chapitre concernant les 7 segments.


// fonction permettant d'afficher un nombre
void afficher_nombre(char nombre, bool afficheur)
{
    long temps;
    char unite = 0, dizaine = 0;
    if(nombre > 9)
        dizaine = nombre / 10; // on recupere les dizaines
    unite = nombre - (dizaine*10); // on recupere les unités

    if(afficheur)
    {
        // on affiche les dizaines
        digitalWrite(alim_unite, LOW);
        digitalWrite(alim_dizaine, HIGH);
        afficher(dizaine);
    }
    else
    {
        // on affiche les unités
        digitalWrite(alim_dizaine, LOW);
        digitalWrite(alim_unite, HIGH);
        afficher(unite);
    }
}

// fonction écriveant sur un seul afficheur
void afficher(char chiffre)
{
    // on commence par écrire 0, donc tout à l'état bas
    digitalWrite(bit_A, LOW);
    digitalWrite(bit_B, LOW);
    digitalWrite(bit_C, LOW);
    digitalWrite(bit_D, LOW);

    if(chiffre >= 8)
    {
        digitalWrite(bit_D, HIGH);
        chiffre = chiffre - 8;
    }
    if(chiffre >= 4)
    {
        digitalWrite(bit_C, HIGH);
        chiffre = chiffre - 4;
    }
    if(chiffre >= 2)
    {
        digitalWrite(bit_B, HIGH);
        chiffre = chiffre - 2;
    }
    if(chiffre >= 1)
    {
        digitalWrite(bit_A, HIGH);
        chiffre = chiffre - 1;
    }
}
Les fonctions d’affichage
Et le code au complet

Si vous voulez tester l’ensemble de l’application sans faire d’erreurs de copier/coller, voici le code complet (qui doit fonctionner si on considère que vous avez branché chaque composant au même endroit que sur le schéma fourni au départ !)

// les broches du décodeur 7 segments
const int bit_A = 2;
const int bit_B = 3;
const int bit_C = 4;
const int bit_D = 5;
// les broches des transistors pour l'afficheur des dizaines et celui des unités
const int alim_dizaine = 6;
const int alim_unite = 7;
// les broches des boutons
const int btn_entree = 8;
const int btn_sortie = 9;
// les leds de signalements
const int led_rouge = 12;
const int led_verte = 11;
// les mémoires d'état des boutons
int mem_entree = HIGH;
int mem_sortie = HIGH;
int etat = HIGH; // variable stockant l'état courant d'un bouton

char place_dispo = 10; // contenu des places dispos
bool afficheur = false;
long temps;

void setup()
{
    // Les broches sont toutes des sorties (sauf les boutons)
    pinMode(bit_A, OUTPUT);
    pinMode(bit_B, OUTPUT);
    pinMode(bit_C, OUTPUT);
    pinMode(bit_D, OUTPUT);
    pinMode(alim_dizaine, OUTPUT);
    pinMode(alim_unite, OUTPUT);
    pinMode(btn_entree, INPUT);
    pinMode(btn_sortie, INPUT);
    pinMode(led_rouge, OUTPUT);
    pinMode(led_verte, OUTPUT);

    // Les broches sont toutes mises à l'état bas (sauf led rouge éteinte)
    digitalWrite(bit_A, LOW);
    digitalWrite(bit_B, LOW);
    digitalWrite(bit_C, LOW);
    digitalWrite(bit_D, LOW);
    digitalWrite(alim_dizaine, LOW);
    digitalWrite(alim_unite, LOW);
    digitalWrite(led_rouge, HIGH);
    digitalWrite(led_verte, LOW); // vert par défaut
    temps = millis(); // enregistre "l'heure"
}

void loop()
{
    // si ca fait plus de 10 ms qu'on affiche, on change de 7 segments
    if((millis() - temps) > 10)
    {
        // on inverse la valeur de "afficheur"
        // pour changer d'afficheur (unité ou dizaine)
        afficheur = !afficheur;
        // on affiche
        afficher_nombre(place_dispo, afficheur);
        temps = millis(); // on met à jour le temps
    }

    // on test maintenant si les boutons ont subi un appui (ou pas)
    // d'abord le bouton plus puis le moins
    etat = digitalRead(btn_entree);
    if((etat != mem_entree) && (etat == LOW))
        place_dispo += 1;
    mem_entree = etat; // on enregistre l'état du bouton pour le tour suivant

    // et maintenant pareil pour le bouton qui décrémente
    etat = digitalRead(btn_sortie);
    if((etat != mem_sortie) && (etat == LOW))
        place_dispo -= 1;
    mem_sortie = etat; // on enregistre l'état du bouton pour le tour suivant

    // on applique des limites au nombre pour ne pas dépasser 99 ou 0
    if(place_dispo > 99)
        place_dispo = 99;
    if(place_dispo < 0)
        place_dispo = 0;

    // on met à jour l'état des leds
    // on commence par les éteindre
    digitalWrite(led_verte, HIGH);
    digitalWrite(led_rouge, HIGH);
    if(place_dispo == 0) // s'il n'y a plus de place
        digitalWrite(led_rouge, LOW);
    else
        digitalWrite(led_verte, LOW);
}

// fonction permettant d'afficher un nombre
void afficher_nombre(char nombre, bool afficheur)
{
    long temps;
    char unite = 0, dizaine = 0;
    if(nombre > 9)
        dizaine = nombre / 10; // on récupère les dizaines
    unite = nombre - (dizaine*10); // on récupère les unités

    if(afficheur)
    {
        // on affiche les dizaines
        digitalWrite(alim_unite, LOW);
        digitalWrite(alim_dizaine, HIGH);
        afficher(dizaine);
    }
    else
    {
        // on affiche les unités
        digitalWrite(alim_dizaine, LOW);
        digitalWrite(alim_unite, HIGH);
        afficher(unite);
    }
}

// fonction écrivant sur un seul afficheur
void afficher(char chiffre)
{
    // on commence par écrire 0, donc tout à l'état bas
    digitalWrite(bit_A, LOW);
    digitalWrite(bit_B, LOW);
    digitalWrite(bit_C, LOW);
    digitalWrite(bit_D, LOW);

    if(chiffre >= 8)
    {
        digitalWrite(bit_D, HIGH);
        chiffre = chiffre - 8;
    }
    if(chiffre >= 4)
    {
        digitalWrite(bit_C, HIGH);
        chiffre = chiffre - 4;
    }
    if(chiffre >= 2)
    {
        digitalWrite(bit_B, HIGH);
        chiffre = chiffre - 2;
    }
    if(chiffre >= 1)
    {
        digitalWrite(bit_A, HIGH);
        chiffre = chiffre - 1;
    }
}
// Fin du programme !
Le code complet

Et voici la correction complète sur simulateur interactif.

!(https://www.tinkercad.com/embed/9l3ird4QSYJ)

Conclusion

Bon, si vous ne comprenez pas tout du premier coup, c’est un petit peu normal, c’est en effet difficile de reprendre un programme que l’on a pas fait soi-même et ce pour diverses raisons. Le principal est que vous ayez cherché une solution par vous-même et que vous soyez arrivé à réaliser l’objectif final. Si vous n’avez pas réussi mais que vous pensiez y être presque, alors je vous invite à chercher profondément le pourquoi du comment votre programme ne fonctionne pas ou pas entièrement, cela vous aidera à trouver vos erreurs et à ne plus en refaire !


Il est pas magnifique ce parking ? J’espère que vous avez apprécié sa réalisation. Nous allons maintenant continuer à apprendre de nouvelles choses, toujours plus sympas les unes que les autres. Un conseil, gardez votre travail quelques part au chaud, vous pourriez l’améliorer avec vos connaissances futures !