Jump to content
  • This is not a forum but a community!

    20100521_GTX_0003.JPGWe all love this mythical and popular car, because we sailed in one being childrens, because someone in the family had one or just because it always made us want it... because it's endearing, whatever we say.
    Its great success is certainly due to the fact that with a simple mechanics, with little or no electronics, it is easy to maintain by yourself without expensive tools, without forgetting that the parts are not expensive and always easy to find. Even today, for some homes, students and people with "little budget", it is a source of great comfort at a lower price: far from car loans and garage mechanics, which are becoming more and more inaccessible. The small engines models (the most common) are very robust and still consume very little, sometimes less than recent "equivalent" cars, which makes it by definition just as ecological, if not even more by the simple fact that we we always use it instead of buying new ones ... At the level of the official technical control, benefiting from its "seniority", it does not undergo the modern standards much too restrictive, the tranquility is assured.
    Today it is still an interesting car for everyday use, especially and clearly from an economic point of view.
    Collectors are also beginning to take an interest in this 30-year-old "granny": restoration, maintenance or refurbishment, repairs, etc ... make it possible to find "newer than new" models for the pleasure of the eyes and to see this French heritage thus safeguarded.
    We have created this community to bring together all those who are interested and who wish to take part in this adventure to preserve their Renault Super 5, whether by maintenance, repair or restoration: you will find here all useful information and tips on these topics.

    The forum is freely accessible in its entirety: there is no need to contribute, buy, neither pay anything to integrate the community and participate.
    To be able to ask questions and share your interest, all you have to do is register and present yourself properly!
    You do not have to have a Super 5: an interest in auto mechanics is enough

    Welcome !

    (This message disappears if you register into the forum)

    [Translated from french using GoogleTranslation tool]

     

Régulateur de vitesse commandé par Arduino


totojest
 Share

Recommended Posts

Petit complément, je viens de faire un test avec l'oscilloscope branché sur les bornes d'alimentation du moteur, de l'électroaimant et de l'alimentation de l'Arduino (sur le 12V, mais pas sur le 5V) pour détecter d'éventuels pics de tension. J'ai peut être pris une division trop large (1 seconde par division horizontale), mais je ne vois rien de flagrant (Attention, les courbes ne sont pas superposables)

 

Courbe de tension quand je sollicite l'accélération:

IMG_3796.JPG.6abd07b4f9d1587fbbbc54fa76e98e09.JPG

(Au moment où la tension revient à zéro, l'Arduino saute, comme d'hab)

 

Courbe de tension d'alimentation de l'électroaimant

IMG_3798.JPG.a86cf677d87a907434f07723cf1d615a.JPG

(Quand elle est à zéro, c'est que l'Arduino est en train de rebooter, et on voit juste un pic négatif au début du reboot, qui n'est pas apparu sur les deux tests que j'ai fait en suivant)

 

Et je n'ai pas de photo de la courbe d'alimentation de l'Arduino, car elle reste bien propre sur ses 12V...

 

Enfin, j'ai essayé de mettre une résistance de 5kOhm en parallèle du moteur, ça saute toujours... Je vais donc refaire des mesures à l'oscilloscope avec une définition un peu plus fine, et considérer une façon de filtrer mieux l'alimentation de l'Arduino. Car là je sèche!

Link to comment
Share on other sites

Elle est censée fournir combien de jus ton alim réglée sur 5V ? (quelle puissance ou ampérage)
L'idée de la boucle de surveillance est bonne, c'est le bon principe de fonctionnement, après sans réécrire tout ton code j'aurais du mal à te proposer quelque chose de concret qui te soit utile. Faut dire que deux développeurs développeront de deux façons différentes pour obtenir le même résultat, un peu comme des peintres (cf. Nous Les Informaticiens -> Des Artistes), le but c'est toujours celui de faire le moins d'opérations inutiles possible, ou en d'autres mots il vaut mieux ne rien faire que faire un truc inutile (réaffecter la même valeur en boucle, etc...). Gagner du temps CPU pour en avoir lorsqu'il en faut. Enfin bon, ce sont des principes qui peuvent paraître "bateau", mais ça aide vraiment beaucoup...

As-tu testé ton code en simulation "réelle", je veux dire en simulant des entrées/sorties qui fonctionnent comme devrait fonctionner ton moteur/potentiomètre/électroaimant ? Tu tombes peut-être simplement dans une boucle infinie ou un état sans sortie par exemple...

Enfin, en dehors des éventuels problèmes de code, concernant ton branchement qui fait planter, je ne comprends pas bien où t'as branché l'oscillo dans chaque cas, entre quoi et quoi ? C'est du côté "après relai" ? ou toujours dans le circuit arduino ? T'arrives à avoir des sorties console lorsque ça plante ? Si t'enlèves la commande de reset sur erreur de ton code (en la mettant en commentaire par exemple), ça le fait toujours ?

 

Link to comment
Share on other sites

  • 2 weeks later...

Après peut être des heures de recherche et de test, je crois que j'ai cerné mon problème de reset intempestif qui est bel et bien hardware... Pour cela il a fallu que je sorte le moteur pour le mettre sur mon plan de travail:

IMG_0084.JPG.656e674295aaa71fa52489e90099f037.JPG

 

Et je vous présente les "fautifs", tout du moins les composants qui ne sont pas adaptés à l'usage que je souhaitais avoir:

IMG_0085.JPG.1e57c711d37778dc449786f4a3d7603a.JPG

 

En fait pour comprendre le problème, il faut s'intéresser au schéma électrique du montage, qui est actuellement le suivant:

1672560641_Schema5copy.thumb.jpg.dc1ef0173b6ab489c9b6bff1fb58babf.jpg

 

Et plus précisément donc sur les relais, mais côté "puissance":

Schema5.thumb.jpg.7812a48ffdddeef6dfe5254c72258c61.jpg

 

Le relais Electroaimant est encadré, mais hors de cause, je vais le remplacer par un module optocoupleur/MOSFET, car je viens de voir que ça existait "tout fait" aussi:  https://protosupplies.com/product/lr7843-mosfet-control-module/

 

Et pour le moteur, je vais remplacer mes deux relais par un module adapté aux moteurs à courant continu: https://electrotoile.eu/arduino-moteur-DC-shield.php

 

Le problème majeur est que je veux faire tourner mon moteur dans les deux sens. Et je pense que j'ai ponctuellement un court circuit qui se génère quand mes relais ne sont plus commutés et qui fait sauter l'Arduino. Je pourrais trouver un moyen de filtrer ça, mais la lecture et l'usage du module précédent me va beaucoup mieux car il permettrait de moduler la vitesse du moteur...

 

 

Link to comment
Share on other sites

Pour piloter les moteurs t'as plus simple : les ponts en H, ça fait ce que tu veux et c'est fait pour... (Edit: oui, en fait c'est ce que fait ton "module adapté", veille cependant à ce qu'il laisse passer le bon courant pour ton moteur (dans la page de ton lien, ils disent 600mA ce qui me semble peu au vu de la taille et l'épaisseur des fils de ton moteur, sinon tu risques de le griller)

 

Par contre, je ne vois pas bien pourquoi un court-circuit côté moteur dans ton montage actuel, ca ferait sauter l'arduino (?)

Link to comment
Share on other sites

  • 3 weeks later...
Le 25/05/2024 à 22:19, Zorro_X a dit :

Par contre, je ne vois pas bien pourquoi un court-circuit côté moteur dans ton montage actuel, ca ferait sauter l'arduino (?)

Et bien moi non plus, j'étais parti sur une théorie peut être fumeuse pensant que court circuit + bobine = perturbation magnétique auquel l'Arduino aurait été sensible.

 

Concernant le diamètre des fils, il ne faut pas s'y fier. Même quand le moteur force en butée, je ne dépasse pas 700 mA de consommation totale (électroaimant + moteur + Arduino + modules).

 

Enfin bon, j'ai remplacé tous mes relais par des semi conducteurs (le pont en H L293D qui me permet de gérer aussi la vitesse du moteur) et un MOSFET pour l'électroaimant. J'aurais voulu garder un relais pour ce dernier, histoire de pouvoir l'entendre claquer, mais cette saleté laissait quand même passer du courant de temps en temps même quand il était commandé ouvert par l'Arduino! Par contre, quand je faisais un reset il se déconnectait bien. Non finalement les relais et l'Arduino, ça m'a beaucoup déçu, et leur fonctionnement est bien mystérieux (ça marche peut être qu'avec des ampoules finalement)!

 

Et donc ENFIN mon montage fonctionne! Ca a été un véritable chemin de croix en tout cas, il a fallu bouger plein de connexions sur l'Arduino pour prendre en compte les spécificités du pont en H (et la gestion PWM pour réguler la vitesse de rotation du moteur, sur des bornes spécifiques de l'Arduino), déplacer tous les boutons des entrées digitales vers les entrées analogiques pour récupérer des ports, changer les branchements pour le MOSFET, traquer les petits courts jus, bref, pas mal de modifications de dernière minute assez pénibles, mais là je tiens le bon bout!

 

Voici donc le schéma à jour. Pardonnez le vide sidéral autour des modules qui remplacent les relais, comme ils sont déjà tout prêts je me suis simplifié la tache lorsque je les ai dessinés...

Schema6.thumb.jpg.05029be9428852d915256f9bda15ae5d.jpg

 

Demain je teste ça en situation réelle. Malgré tous les branchements visibles sur le schéma, je n'ai toujours pas d'asservissement à la vitesse, et l'écran n'est pas encore installé non plus. Mais si ça me permet déjà de m'aider à maintenir la pression sur l'accélérateur, ce sera déjà top!

  • Like 1
Link to comment
Share on other sites

  • 3 weeks later...

Quelques nouvelles sur ce projet qui avance bien! Je considère la version "V0" du code comme terminée. Pour rappel, cette "V0" devait juste appuyer sur l'accélérateur à ma place, mais c'était encore à moi de jouer du levier si je voulais accélérer ou ralentir. J'ai pu ainsi détecter que sur du plat à vitesse stabilisée, la voiture ralentissait tout de même, comme si le ressort de l'accélérateur était plus fort que le moteur du régulateur. Mais bon, avant d'aller plus loin, j'ai organisé mon code en créant des fonctions, pour simplifier aussi le débogage, et pour structurer les actions, et ça devrait me faciliter les évolutions.

 

Après ça donc, je passe donc à une "V1" du code où je commence donc à intégrer de l'asservissement. Pour le moment, toujours pas de lecture de vitesse, mais l'idée est bien de garder l'accélérateur en position, à partir d'une position mémorisée. Pour se faire, j'utilise le potentiomètre intégré au moteur. Dans le fonctionnement, je sollicite ma commande "PLUS" jusqu'à la position de l'accélérateur désirée, et une fois que ça me convient, je lâche la commande, et le système s'occupe de maintenir la pédale en position. Je peux ensuite ajuster (avec "PLUS" ou "MOINS") si besoin, et ça prend en compte la nouvelle valeur de la position de la pédale. Je peux aussi appuyer sur "OFF" (ou freiner ou débrayer), ça coupera aussitôt la régulation, et un appui bref sur "MEMO" me permet de récupérer mon ancienne valeur enregistrée. Plus tard, un appui long sur "MEMO" capturera la vitesse en cours plutôt que de rappeler la vitesse mémorisée précédemment (mais pour le moment ça n'a pas d'intérêt, vu que je ne travaille pas encore avec la vitesse, c'est juste pour comprendre pourquoi c'est présent dans le code).

 

Cette "V1" c'est aussi la mise en place de l'écran, que j'avais déjà pas mal paramétré à côté et qui est assez fonctionnel. Il y a 5 zones sur cet écran: une zone pour un logo qui me permet de voir en un coup d'oeil si on est en phase d'initialisation (avec un dessin sablier ), si la régulation est active (avec un dessin Play ▶️) ou inactive (avec un dessin Pause ⏸️), ou s'il y a une erreur détectée (avec un panneau attention ⚠️), et 4 lignes de texte que j'utilise surtout pour suivre le fonctionnement de mon régulateur.

 

Côté fonctionnement, cette V1 est déjà pas mal aboutie je trouve, du moins en statique. Je dois encore jouer sur des paramètres tels que la vitesse de rotation du moteur à des phases données (pour éviter qu'il ne fasse trop le yoyo en phase de régulation de vitesse, ou éviter un trop grand décalage entre la lecture de la position du potard et la position actuelle de l'accélérateur -oui c'est bizarre dit comme ça, mais la mémorisation a un "temps de retard"-) ou sur des valeurs de tolérance, mais ça va se faire au fur et à mesure de mes essais (ça va se faire à tâtons, évidemment). Bien entendu, je vais tacher de filmer ça, j'ai beaucoup papoté, mais j'imagine bien que ça reste pas mal abstrait!

 

Sinon, voici le schéma électrique actuel, revu sur le câblage du potentiomètre:

Schema6bis.thumb.jpg.0242fd87d2b6beaa999de64876b3d817.jpg

 

Et voici le code actuel. Il prend 82% de mémoire, à cause des paramètres pour l'affichage. Il va falloir que je revois ça, ça ne me laisse pas des masses de marge pour les évolutions (et surtout la lecture de la vitesse). Bon après il y aura des bouts qui seront remplacés ou qui vont disparaître, mais ce ne sera pas pour tout de suite (du moins tant que ça ne sera pas fiable).

//#####################################################################
//## Déclaration des variables d'environnement                       ##
//#####################################################################
#include <EEPROM.h>  // Bibliothèque mémoire
#include <UTFT.h>    // Bibliothèque écran
   
#define TEMPS_MAINTIENLONG 1500                                       // Temps de maintien en appui long
#define TEMPS_REMISEZERO 30000                                        // Durée de remise à zéro
#define TEMPS_CHRONOOFF 1000                                          // Durée d'attente pour la fonction Off
#define TEMPS_DELAY 1000                                              // Durée de délai (Pas encore utilisée)
#define CMDELECTROAIMANT 2                                            // Commande relais électroaimant de sécurité
#define CMDDECELERATION 3                                             // Commande sens décélération (A-)
#define CMDACCELERATION 6                                             // Commande sens accélération (A+)
#define ECRANRES 8                                                    // Ecran RES  
#define PWMMOTEUR 9                                                   // PWM Moteur pour variation de vitesse
#define ECRANCS 10                                                    // Ecran CS
#define ECRANSDA 11                                                   // Ecran SDA
#define ECRANDC 12                                                    // Ecran DC
#define ECRANSCL 13                                                   // Ecran SCL
#define VITESSE 1                                                     // (Analogique) Compte tours ou vitesse
#define POSITIONMOTEUR 3                                              // (Analogique) Résistance asservissement servomoteur
#define BTNMEMO 4                                                     // (Analogique) Bouton MEMO
#define BTNPLUS 5                                                     // (Analogique) Bouton +
#define BTNMOINS 6                                                    // (Analogique) Bouton -
#define BTNOFF 7                                                      // (Analogique) Bouton OFF (relais des pédales de frein et d'embrayage + bouton Off de la commande au volant)

//----------------------------------------------------------------------------------------------------------------------------
// Structure permettant de définir les propriétés d'un bouton
//----------------------------------------------------------------------------------------------------------------------------
struct Bouton {
  int entree;                   // Entrée où le bouton est connecté
  bool boutonAppuye;            // Etat du bouton
  unsigned long date;           // Date à laquelle on a commencé à appuyer sur le bouton 
};

//----------------------------------------------------------------------------------------------------------------------------
// Définition des boutons
//----------------------------------------------------------------------------------------------------------------------------
Bouton off;   // Bouton OFF (relais des pédales de frein et d'embrayage + bouton Off de la commande au volant)
Bouton memo;  // Définition du bouton de mémorisation vitesse
Bouton plus;  // Définition du bouton +
Bouton moins; // Définition du bouton -

//----------------------------------------------------------------------------------------------------------------------------
// Définition des fonctions qu'on associe aux boutons
//----------------------------------------------------------------------------------------------------------------------------
enum FonctionBouton {
  RIEN,      // Fontion neutre, il n'y a rien à faire
  OFF,       // Fonction OFF
  MEMO,      // Fonction mémorisation vitesse
  MEMOLONG,  // Fonction associée à l'appui long sur le bouton mémorisation
  PLUS,      // Fonction +
  PLUSLONG,  // Fonction appui long sur +
  MOINS,     // Fonction -
  MOINSLONG, // Fonction appui long sur -
};

//----------------------------------------------------------------------------------------------------------------------------
// Type énuméré permettant de définir les états des boutons
//----------------------------------------------------------------------------------------------------------------------------
enum EtatBouton {
  RELACHE,                // Le bouton est relâché (circuit ouvert)
  APPUYE,                 // Le bouton est appuyé (circuit fermé)
  MAINTIEN_LONG,          // Le bouton est maintenu appuyé (circuit fermé)
};

//----------------------------------------------------------------------------------------------------------------------------
// Déclarations relatives à l'affichage
//----------------------------------------------------------------------------------------------------------------------------
extern uint8_t SmallFont[]; // Police
extern uint8_t BigFont[];   // Police
UTFT myGLCD(ST7735S_4L_80160,ECRANSDA,ECRANSCL,ECRANCS,ECRANRES,ECRANDC);    // Initialisation écran LCD:  4Line  serial interface      SDI  SCL  /CS  /RST  D/C    NOTE:Only support  DUE   MEGA  UNO
int color = 0;
word colorlist[] = {VGA_WHITE, VGA_BLACK, VGA_RED, VGA_BLUE, VGA_GREEN, VGA_FUCHSIA, VGA_YELLOW, VGA_AQUA};
int  bsize = 4;
char voltageDisplay[7] = "";

//void drawColorMarkerAndBrushSize(int col) {
//  myGLCD.setColor(VGA_BLACK);
//  myGLCD.fillRect(25, 0, 31, 239);
//  myGLCD.fillRect(myGLCD.getDisplayXSize()-31, 161, myGLCD.getDisplayXSize()-1, 191);
//  myGLCD.setColor(VGA_WHITE);
//  myGLCD.drawPixel(25, (col*30)+15);
//  for (int i=1; i<7; i++)
//    myGLCD.drawLine(25+i, ((col*30)+15)-i, 25+i, ((col*30)+15)+i);
//  
//  if (color==1)
//    myGLCD.setColor(VGA_WHITE);
//  else
//    myGLCD.setColor(colorlist[col]);
//  if (bsize==1)
//    myGLCD.drawPixel(myGLCD.getDisplayXSize()-15, 177);
//  else
//    myGLCD.fillCircle(myGLCD.getDisplayXSize()-15, 177, bsize);
//    
//  myGLCD.setColor(colorlist[col]);
//};

//----------------------------------------------------------------------------------------------------------------------------
// Déclaration des variables globales utilisées dans le code
//----------------------------------------------------------------------------------------------------------------------------
bool erreur = true;                       // Variable d'erreur
bool erreurmemo = false;                  // Variable d'erreur pour le bouton MEMO et empêcher le reset automatique s'il est détecté bloqué à l'initialisation
int vitessemini = 1500;                   // Vitesse minimum pour l'activation du régulateur de vitesse (en tours par minute)
int vitessemaxi = 6400;                   // Vitesse maximum pour l'activation du régulateur de vitesse (en tours par minute)
int vitessememoire = 0;                   // Vitesse mise en mémoire pour la régulation
String vitesseaffichage = String(0);      // Vitesse convertie en type String pour l'affichage
int pasvitesse = 50;                      // Pas avec lequel on incrémente ou diminue la valeur de vitesse
int gdpasvitesse = 250;                   // Pas avec lequel on incrémente ou diminue la valeur de vitesse lors d'appui long
int margevitesse = 5;                     // Marge de vitesse permettant de ne pas trop solliciter la régulation de vitesse
int positionservo = 0;                    // Position du servomoteur lue au potentiomètre
int margeposition = 5;                    // Marge pour la position du potentiomètre (en %)
int chronoservo = 0;                      // Chronomètre pour évaluer les mouvements du servomoteur
int variateurManuPlus = 100;              // Variateur pour le moteur via la pin PWM pour l'accélération manuelle. (Valeur comprise entre 0 et 255)
int variateurManuMoins = 80;              // Variateur pour le moteur via la pin PWM pour la décélération manuelle. (Valeur comprise entre 0 et 255)
int variateurRegulPlus = 80;              // Variateur pour le moteur via la pin PWM pour l'accélération en mode régulation. (Valeur comprise entre 0 et 255)
int variateurRegulMoins = 65;             // Variateur pour le moteur via la pin PWM pour la décélération en mode régulation. (Valeur comprise entre 0 et 255)
bool regulation = false;                  // Régulateur actif ou non
bool etatantelectro = false;              // Etat antérieur de l'électroaimant (pour éviter le clignotement des dessins play et pause)
bool reinitialisation = false;            // Autorisation de réinitialisation en cas d'erreur détectée
String espace = " ";
String txtcommande = "commande";
String txtappuyee = "appuyee";
String txtvitesse = "Vitesse";
String txtinsuffisante = "insuff";
String txton = "On";
String txtoff = "Off";
String txtmemo = "Memo";
String txtplus = "Plus";
String txtmoins = "Moins";
String txtloop = "Loop:";
String txtlong = "Long";
String txtpotard = "Potard (%)";
FonctionBouton precfonc = RIEN;           // Fonction bouton à n-1
unsigned long chronoOff = 0;              // Chronomètre pour la fonction Off (au lieu d'utiliser un delay)

//#####################################################################
//## Déclaration des fonctions                                       ##
//#####################################################################

///////////////////
// Affichage     //
///////////////////

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "dessinSablier" de dessin du symbole attente
//----------------------------------------------------------------------------------------------------------------------------
void dessinSablier () {
  int xcoinsuperieur = 5;
  int ycoinsuperieur = 5;
  int xcoininferieur = 72;
  int ycoininferieur = 72;
  int x1 = 0;
  int y1 = 0;
  int x2 = 0;
  int y2 = 0;
  int x3 = 0;
  int y3 = 0;
  int x4 = 0;
  int y4 = 0;
  int x5 = 0;
  int y5 = 0;
  int y6 = 0;
  int y7 = 0;
  int y8 = 0;
  int y9 = 0;
  int ya = 0;
  
  // Arrière plan du symbole
  myGLCD.setColor(255, 255, 255);           // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillRoundRect(xcoinsuperieur, ycoinsuperieur, xcoininferieur, ycoininferieur);       // Position x1, y1, x2, y2
  // Sablier: Deux rectangles et deux triangles, remplis par un triangle et un rectangle
  x1 = 0.3 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x2 = 0.7 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x3 = 0.5 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x4 = 0.35 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x5 = 0.65 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  y1 = 0.1 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y2 = 0.3 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y3 = 0.7 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y4 = 0.9 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y5 = 0.55 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y6 = 0.45 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y7 = 0.5 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y8 = 0.8 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y9 = 0.85 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;

  // Deux rectangles (à l'échelle)
  myGLCD.setColor(200, 100, 100);           // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillRect(x1, y1, x2, y2);          // Premier rectangle coordonnées x1, y1, x2, y2
  myGLCD.fillRect(x1, y3, x2, y4);          // Deuxième rectangle coordonnées x1, y1, x2, y2

  // Deux triangles
  myGLCD.fillTriangle(x1, y2, x2, y2, x3, y5); // Triangle plein
  myGLCD.fillTriangle(x1, y3, x2, y3, x3, y6); // Triangle plein

  // Un triangle et un rectangle
  myGLCD.setColor(230, 230, 020);                // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillTriangle(x4, y2, x5, y2, x3, y7);   // Triangle plein
  myGLCD.fillRect(x4, y8, x5, y9);               // Premier rectangle coordonnées x1, y1, x2, y2
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "dessinPlay" de dessin du symbole play
//----------------------------------------------------------------------------------------------------------------------------
void dessinPlay () {
  int xcoinsuperieur = 5;
  int ycoinsuperieur = 5;
  int xcoininferieur = 72;
  int ycoininferieur = 72;
  int x1 = 0;
  int y1 = 0;
  int y2 = 0;
  int x3 = 0;
  int y3 = 0;
  // Arrière plan du symbole
  myGLCD.setColor(255, 255, 255);           // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillRoundRect(xcoinsuperieur, ycoinsuperieur, xcoininferieur, ycoininferieur);       // Position x1, y1, x2, y2
  // Triangle (à l'échelle)
  myGLCD.setColor(0, 255, 0);               // Couleur du symbole dessiné (red, green, blue)
  x1 = 0.1 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x3 = 0.9 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  y1 = 0.1 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y2 = 0.9 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y3 = 0.5 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  myGLCD.fillTriangle(x1, y1, x1, y2, x3, y3); // Triangle plein
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "dessinPause" de dessin du symbole pause
//----------------------------------------------------------------------------------------------------------------------------
void dessinPause () {
  int xcoinsuperieur = 5;
  int ycoinsuperieur = 5;
  int xcoininferieur = 72;
  int ycoininferieur = 72;
  int x1 = 0;
  int y1 = 0;
  int x2 = 0;
  int y2 = 0;
  int x3 = 0;
  int x4 = 0;
  // Arrière plan du symbole
  myGLCD.setColor(255, 255, 255);           // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillRoundRect(xcoinsuperieur, ycoinsuperieur, xcoininferieur, ycoininferieur);       // Position x1, y1, x2, y2
  // Deux rectangles (à l'échelle)
  myGLCD.setColor(100, 020, 255);               // Couleur du symbole dessiné (red, green, blue)
  x1 = 0.15 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x2 = 0.42 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x3 = 0.57 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x4 = 0.85 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  y1 = 0.15 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y2 = 0.85 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  myGLCD.fillRect(x1, y1, x2, y2);          // Premier rectangle coordonnées x1, y1, x2, y2
  myGLCD.fillRect(x3, y1, x4, y2);          // Deuxième rectangle coordonnées x1, y1, x2, y2
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "dessinErreur" de dessin du symbole attention
//----------------------------------------------------------------------------------------------------------------------------
void dessinErreur () {
  int xcoinsuperieur = 5;
  int ycoinsuperieur = 5;
  int xcoininferieur = 72;
  int ycoininferieur = 72;
  int x1 = 0;
  int y1 = 0;
  int x2 = 0;
  int y2 = 0;
  int x3 = 0;
  int x4 = 0;
  int y4 = 0;
  int x5 = 0;
  int y5 = 0;
  int y6 = 0;
  int y7 = 0;
  // Arrière plan du symbole
  myGLCD.setColor(255, 255, 255);           // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillRoundRect(xcoinsuperieur, ycoinsuperieur, xcoininferieur, ycoininferieur);       // Position x1, y1, x2, y2
  // Triangle (à l'échelle)
  myGLCD.setColor(255, 0, 0);               // Couleur du symbole dessiné (red, green, blue)
  x1 = 0.1 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x2 = 0.5 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x3 = 0.9 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  y1 = 0.9 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y2 = 0.1 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  myGLCD.fillTriangle(x1, y1, x2, y2, x3, y1); // Triangle plein
  // Point exclamation (à l'échelle)
  x4 = 0.5 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur - 0.05 * (xcoininferieur - xcoinsuperieur);
  x5 = 0.5 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur + 0.05 * (xcoininferieur - xcoinsuperieur);
  y4 = 0.4 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y7 = 0.85 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y6 = y7 - (x5 - x4);
  y5 = y6 - 0.85 * (x5 - x4);
  myGLCD.setColor(255, 255, 050);               // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillRect(x4, y4, x5, y5);            // Premier rectangle coordonnées x1, y1, x2, y2
  myGLCD.fillRect(x4, y6, x5, y7);            // Deuxième rectangle coordonnées x1, y1, x2, y2
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "ecrtitureMessage1" qui écrit sur la première ligne de l'écran (10 caractères max visibles)
//----------------------------------------------------------------------------------------------------------------------------
void ecritureMessage1 (String message) {
    myGLCD.setBackColor(0, 0, 0);         // Couleur de l'arrière plan de la zone de texte (red, green, blue)
    myGLCD.setColor(255, 255, 255);       // Couleur de texte (red, green, blue)
    myGLCD.setFont(SmallFont);
    if (message.length() > 9) {           // Tronquer le message s'il dépasse
      message[10] = NULL;
    }
    myGLCD.print(F("          "), 75, 5); // Effacement du texte précédent
    myGLCD.print(message, 75, 5);         // text, position x, position y
    Serial.print(F("Message 1: "));
    Serial.println(message);
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "ecritureMessage2" qui écrit sur la deuxième ligne de l'écran (5 caracères max visibles)
//----------------------------------------------------------------------------------------------------------------------------
void ecritureMessage2 (String message) {
    myGLCD.setBackColor(0, 0, 0);         // Couleur de l'arrière plan de la zone de texte (red, green, blue)
    myGLCD.setColor(255, 255, 255);       // Couleur de texte (red, green, blue)
    myGLCD.setFont(BigFont);
    if (message.length() > 4) {           // Tronquer le message s'il dépasse
      message[5] = NULL;
    }
    myGLCD.print(F("     "), 75, 20);     // Effacement du texte précédent
    myGLCD.print(message, 75, 20);        // text, position x, position y
    Serial.print(F("Message 2: "));
    Serial.println(message);
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "ecritureMessage3" qui écrit sur la troisième ligne de l'écran (10 caracères max visibles)
//----------------------------------------------------------------------------------------------------------------------------
void ecritureMessage3 (String message) {
    myGLCD.setBackColor(0, 0, 0);          // Couleur de l'arrière plan de la zone de texte (red, green, blue)
    myGLCD.setColor(255, 255, 255);        // Couleur de texte (red, green, blue)
    myGLCD.setFont(SmallFont);
    if (message.length() > 9) {            // Tronquer le message s'il dépasse
      message[10] = NULL;
    }
    myGLCD.print(F("          "), 75, 39); // Effacement du texte précédent
    myGLCD.print(message, 75, 39);         // text, position x, position y
    Serial.print(F("Message 3: "));
    Serial.println(message);
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "ecritureMessage4" qui écrit sur la quatrième ligne de l'écran (5 caracères max visibles)
//----------------------------------------------------------------------------------------------------------------------------
void ecritureMessage4 (String message) {
    myGLCD.setBackColor(125, 125, 125);         // Couleur de l'arrière plan de la zone de texte (red, green, blue)
    myGLCD.setColor(0, 0, 0);                   // Couleur de texte (red, green, blue)
    myGLCD.setFont(BigFont);
    if (message.length() > 4) {                 // Tronquer le message s'il dépasse
      message[5] = NULL;
    }
    myGLCD.print(F("     "), 75, 54);           // Effacement du texte précédent
    myGLCD.print(message, 75, 54);              // text, position x, position y
    Serial.print(F("Message 4: "));
    Serial.println(message);
}

///////////////////
// Debogage      //
///////////////////

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "debugSerialPlotter" qui retransctrit les états des commandes et des boutons pour les afficher sur le moniteur Plotter
//----------------------------------------------------------------------------------------------------------------------------
void debugSerialPlotter() {
  // Débug avec Serial Plotter
  float SignalOff = analogRead(BTNOFF);
  float SignalMemo = 2+!analogRead(BTNMEMO);
  float SignalPlus = 4+!analogRead(BTNPLUS);
  float SignalMoins = 6+!analogRead(BTNMOINS);
  float SignalAccel = 8+!digitalRead(CMDACCELERATION);
  float SignalDecel = 10+!digitalRead(CMDDECELERATION);
  float SignalElec = 12+digitalRead(CMDELECTROAIMANT);
  
  Serial.print("Signal_OFF:");
  Serial.print(SignalOff);
  Serial.print(',');
  Serial.print("Signal_MEMO:");
  Serial.print(SignalMemo);
  Serial.print(',');
  Serial.print("Signal_PLUS:");
  Serial.print(SignalPlus);
  Serial.print(',');
  Serial.print("Signal_MOINS:");
  Serial.print(SignalMoins);
  Serial.print(',');
  Serial.print("Signal_PLUS:");
  Serial.print(SignalPlus);
  Serial.print(',');
  Serial.print("Signal_Accel:");
  Serial.print(SignalAccel);
  Serial.print(',');
  Serial.print("Signal_Decel:");
  Serial.print(SignalDecel);
  Serial.print(',');
  Serial.print("Signal_Elec:");
  Serial.print(SignalElec);
  Serial.print(',');
  Serial.println();
  // Fin débug
}

////////////////////////
// Lecture paramètres //
////////////////////////

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "etatDuBouton" qui gère les états des boutons (revoie un des états contenus dans la variable énumérée EtatBouton)
//----------------------------------------------------------------------------------------------------------------------------
EtatBouton etatDuBouton (Bouton *boutonATraiter) {
  EtatBouton etat = RELACHE;                                                   // Valeur qui sera renvoyée comme résultat de cette fonction, par défaut RELACHE
  bool etatSignal = !analogRead(boutonATraiter->entree);                       // Lire l'état de l'entrée dans une nouvelle variable temporaire

  if ( etatSignal ) {                                                          // Si le bouton semble avoir été appuyé (pas de signal, ou signal = 0/false)
    if (!boutonATraiter->boutonAppuye) {                                       // Si c'est la première fois que le bouton est détecté appuyé, on lance un chrono
      boutonATraiter->date = millis();
    }
    boutonATraiter->boutonAppuye = true;                                       // On attribue au bouton l'état appuyé
    if (millis() - boutonATraiter->date > TEMPS_MAINTIENLONG) {                // Si le bouton est appuyé depuis plus du temps défini
      etat = MAINTIEN_LONG;                                                    // Renvoyer l'état appui long
    }
    else {                                                                     // Sinon on est sur un simple appui
      etat = APPUYE;                                                           // Renvoyer l'état appuyé
    }
  }
  else {                                                                       // Si le bouton n'est pas appuyé
    boutonATraiter->boutonAppuye = false;                                      // On attribue au bouton l'état non appuyé
    etat = RELACHE;                                                            // Renvoyer l'état relaché
  }
  return etat;                                                                 // Renvoyer l'état du bouton en sortie de fonction
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "lectureCommande" qui contrôle les instructions données par le bouton multiposition
//----------------------------------------------------------------------------------------------------------------------------
FonctionBouton lectureCommande () {
  FonctionBouton fonction = RIEN; // Fonction renvoyée (par défaut: RIEN)
  // Lecture des états des boutons
  EtatBouton etatoff   = etatDuBouton( &off );
  EtatBouton etatmemo  = etatDuBouton( &memo );
  EtatBouton etatplus  = etatDuBouton( &plus );
  EtatBouton etatmoins = etatDuBouton( &moins );
    
  // Définition des fonctions en fonction de l'état des boutons
  // Etat OFF
  if ( etatoff == RELACHE ) {
    fonction = OFF;
  }

  // Etat MEMO
  if ( etatmemo == APPUYE ) {
    fonction = MEMO;
  } else if ( etatmemo == MAINTIEN_LONG ) {
    fonction = MEMOLONG;
  }

  // Etat PLUS
  if ( etatplus == APPUYE ) {
    fonction = PLUS;
  } else if ( etatplus == MAINTIEN_LONG ) {
    fonction = PLUS; //PLUSLONG
  }

  // Etat MOINS
  if ( etatmoins == APPUYE ) {
    fonction = MOINS;
  } else if ( etatmoins == MAINTIEN_LONG ) {
    fonction = MOINS; //MOINSLONG
  }

  // Renvoi de la fonction associée
   return fonction;
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "lecturePotentiometre" qui lit et transforme en % la valeur du potentiomètre du moteur du régulateur pour la position
//----------------------------------------------------------------------------------------------------------------------------
int lecturePotentiometre() {
  int valeurLue = analogRead(POSITIONMOTEUR);
  int positionMoteur = 0;
  if ((valeurLue < 0) || (valeurLue > 1023)) {    // Si la valeur est incohérente (en dehors de la plage 0 - 1023), on lève l'erreur
    erreur = true;
  }
  positionMoteur = map(valeurLue,0,1023,100,0);   // Conversion en pourcentage
  Serial.print(F("Position régulateur (%):"));
  Serial.println(positionMoteur);
  return positionMoteur;
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "lectureVitesse" qui lit la valeur de vitesse de la voiture et la retranscrit en valeur exploitable (en tours minutes)
//----------------------------------------------------------------------------------------------------------------------------
int lectureVitesse () {
  int valeurLue = analogRead(VITESSE);
  int valeurVitesse = 0;
  if ((valeurLue < 0) || (valeurLue > 1023)) {      // Si la valeur est incohérente (en dehors de la plage 0 - 1023), on lève l'erreur
    erreur = true;
  }
  valeurVitesse = map(valeurLue, 0, 1023, 0, 6500); // Conversion en tours minutes
  Serial.print(F("Vitesse:"));
  Serial.println(valeurVitesse);
  return valeurVitesse;
}

///////////////////
// Actions       //
///////////////////

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionOff" qui gère les sorties pour couper le régulateur
//----------------------------------------------------------------------------------------------------------------------------
void fonctionOff() {
  digitalWrite(CMDACCELERATION, LOW);
  digitalWrite(CMDDECELERATION, LOW);
  digitalWrite(CMDELECTROAIMANT, LOW);    // Rappel: pour le module relais, "Low level trigger". Donc HIGH = repos, LOW = allumé.
  regulation = false;                     // On coupe la régulation
  //dessinPause();                          // Indication à l'écran que la régulation est coupée // Pour le gain de temps d'exécution
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionArret" pour arrêter le régulateur un peut quand on veut
//----------------------------------------------------------------------------------------------------------------------------
void fonctionArret() {
  fonctionOff();
  if (precfonc != OFF) {      // Si ce n'était pas déjà "OFF" 
    chronoOff = millis();  // On réinitialise le chronomètre pour Off (utilisé pour maintenir la fonction Off quelques temps)
    //Serial.println(F("Loop: OFF"));
    ecritureMessage1 (txtloop);
    ecritureMessage2 (txtoff);
    ecritureMessage3 (espace);
    ecritureMessage4 (espace);
    regulation = false;   // Déjà déclarée dans fonctionOff, mais au moins on assure le coup
    dessinPause();        // Indication à l'écran que la régulation est coupée
  }
  precfonc = OFF;
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionAccel" qui gère les sorties pour accélérer (variable d'entrée: valeur du PWM)
//----------------------------------------------------------------------------------------------------------------------------
void fonctionAccel(int variation) {
  digitalWrite(CMDACCELERATION, HIGH);
  digitalWrite(CMDDECELERATION, LOW);
  analogWrite(PWMMOTEUR, variation);
  digitalWrite(CMDELECTROAIMANT, HIGH);    // Rappel: pour le module relais, "Low level trigger". Donc HIGH = repos, LOW = allumé.
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionDecel" qui gère les sorties pour décélérer (variable d'entrée: valeur du PWM)
//----------------------------------------------------------------------------------------------------------------------------
void fonctionDecel(int variation) {
  digitalWrite(CMDACCELERATION, LOW);
  digitalWrite(CMDDECELERATION, HIGH);
  analogWrite(PWMMOTEUR, variation);
  digitalWrite(CMDELECTROAIMANT, HIGH);    // Rappel: pour le module relais, "Low level trigger". Donc HIGH = repos, LOW = allumé.
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionOn" qui gère les sorties pour maintenir l'accélération
//----------------------------------------------------------------------------------------------------------------------------
void fonctionOn() {
  digitalWrite(CMDACCELERATION, LOW);
  digitalWrite(CMDDECELERATION, LOW);
  digitalWrite(CMDELECTROAIMANT, HIGH);    // Rappel: pour le module relais, "Low level trigger". Donc HIGH = repos, LOW = allumé.
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionRegulation" qui ajuste la position de l'accélérateur
//----------------------------------------------------------------------------------------------------------------------------
void fonctionRegulation() {
  int positionLue = lecturePotentiometre();
  String positionAffich = String(positionLue); // Conversion de la valeur int en String pour l'affichage // ****** Temporaire
  int positionMemo = positionservo;
  String memoAffich = String(positionMemo);    // Conversion de la valeur int en String pour l'affichage // ****** Temporaire
  int vitesseLue = lectureVitesse();
    
// ****** POUR LE MOMENT on garde juste la pression sur la pédale d'accélérateur, le compte tours n'est pas encore branché. Cette partie du code est amenée à être remplacée par une surveillance de la plage de la position pour ne pas accélérer quand on est en butée  
  ecritureMessage1 (txtmemo);
  ecritureMessage2 (memoAffich);
  ecritureMessage3 (txtpotard);
  ecritureMessage4 (positionAffich);
  
  while (positionLue > positionMemo + margeposition) { // Position lue au dela de la position mémorisée + marge: il faut décélérer
    fonctionDecel(variateurRegulMoins);
    positionLue = lecturePotentiometre();
    if (lectureCommande() == OFF) { // Si OFF est déclanché, on sort de la boucle et on coupe tout au plus vite
      fonctionArret();
      positionLue = 0;
      positionMemo = 0;
    }  
    
    // *** Pour debug, n'est pas voué à rester
    positionAffich = String(positionLue); // Conversion de la valeur int en String pour l'affichage 
    if (lectureCommande() == OFF) { // Je remets ce bloc car l'affichage prend du temps et il faut être réactif sur l'arrêt
      fonctionArret();
      positionLue = 0;
      positionMemo = 0;
    }
    ecritureMessage1 (txtmemo);
    if (lectureCommande() == OFF) { // Je remets ce bloc car l'affichage prend du temps et il faut être réactif sur l'arrêt
      fonctionArret();
      positionLue = 0;
      positionMemo = 0;
    }
    ecritureMessage2 (memoAffich);
    if (lectureCommande() == OFF) { // Je remets ce bloc car l'affichage prend du temps et il faut être réactif sur l'arrêt
      fonctionArret();
      positionLue = 0;
      positionMemo = 0;
    }
    ecritureMessage3 (txtpotard);
    if (lectureCommande() == OFF) { // Je remets ce bloc car l'affichage prend du temps et il faut être réactif sur l'arrêt
      fonctionArret();
      positionLue = 0;
      positionMemo = 0;
    }
    ecritureMessage4 (positionAffich);
    if (lectureCommande() == OFF) { // Je remets ce bloc car l'affichage prend du temps et il faut être réactif sur l'arrêt
      fonctionArret();
      positionLue = 0;
      positionMemo = 0;
    }  
    // *** Fin de "pour debug" 
  }
  
  while (positionLue < positionMemo - margeposition) { // Position lue inférieure à la position mémorisée - marge: il faut accélérer
    fonctionAccel(variateurRegulPlus);
    positionLue = lecturePotentiometre();
    if (lectureCommande() == OFF) { // Si OFF est déclanché, on sort de la boucle et on coupe tout au plus vite
      fonctionArret();
      positionLue = 0;
      positionMemo = 0;
    } 
    
    // *** Pour debug, n'est pas voué à rester
    positionAffich = String(positionLue); // Conversion de la valeur int en String pour l'affichage 
    if (lectureCommande() == OFF) { // Je remets ce bloc car l'affichage prend du temps et il faut être réactif sur l'arrêt
      fonctionArret();
      positionLue = 0;
      positionMemo = 0;
    }
    ecritureMessage1 (txtmemo);
    if (lectureCommande() == OFF) { // Je remets ce bloc car l'affichage prend du temps et il faut être réactif sur l'arrêt
      fonctionArret();
      positionLue = 0;
      positionMemo = 0;
    }
    ecritureMessage2 (memoAffich);
    if (lectureCommande() == OFF) { // Je remets ce bloc car l'affichage prend du temps et il faut être réactif sur l'arrêt
      fonctionArret();
      positionLue = 0;
      positionMemo = 0;
    }
    ecritureMessage3 (txtpotard);
    if (lectureCommande() == OFF) { // Je remets ce bloc car l'affichage prend du temps et il faut être réactif sur l'arrêt
      fonctionArret();
      positionLue = 0;
      positionMemo = 0;
    }
    ecritureMessage4 (positionAffich);
    if (lectureCommande() == OFF) { // Je remets ce bloc car l'affichage prend du temps et il faut être réactif sur l'arrêt
      fonctionArret();
      positionLue = 0;
      positionMemo = 0;
    }
    // *** Fin de "pour debug"
  }
// ****** Fin du code temporaire

}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionNormal" qui s'exécute en temps normal, quand il n'y a pas d'erreur
//----------------------------------------------------------------------------------------------------------------------------
void fonctionNormal() {
  if (millis() - chronoOff < TEMPS_CHRONOOFF) { // Si on est toujours dans le timing pour la prise en charge de la fonction OFF, on la maintient histoire d'être sur que l'electroaimant soit bien désactivé
      fonctionOff();
    }
    else {                                      // Sinon on lit la commande et on traite les cas correspondants, puis on gère la régulation de vitesse
      fonctionLectureCmd();
      if (regulation) {                         // Si la régulation est activée, on régule la vitesse
        fonctionRegulation();
      }
//      debugSerialPlotter();    
  }
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionErreur" qui s'exécute quand il y a une erreur détectée, et autorise la réinitialisation
//----------------------------------------------------------------------------------------------------------------------------
void fonctionErreur() {
  fonctionOff();       // On coupe tout par sécurité
  dessinErreur();      // Affichage du logo sur l'écran
  Serial.println(F("Arrêt d'Urgence dans le loop"));
  // Si on appuie longtemps sur MEMO, on peut réinitialiser le régulateur
  if (!erreurmemo){    // Si le bouton n'était pas bloqué à l'initalisation, on autorise la réinitialisation
    if (lectureCommande() == MEMOLONG) { // Si on a appuyé longtemps sur MEMO, on va autoriser la réinitialisation
      reinitialisation = true;
      Serial.println(F("Réinitialisation autorisée"));
    }
    if (reinitialisation) {
      while (lectureCommande() == MEMOLONG) { // Tant qu'on maintient en position longue le bouton MEMO (pour la réinitialisation), on actuallise le chrono (pour éviter de reset l'Arduino alors qu'on est encore appuyé et ainsi risquer de lever "erreurmemo") 
        chronoOff = millis(); // (On recycle le chronoOff pour éviter de déclarer une nouvelle variable)
        Serial.println(F("MEMOLONG demandé, réinitialisation 3 secondes après lacher du bouton"));
        ecritureMessage1 (espace);
        ecritureMessage2 ("3");
        ecritureMessage3 ("secondes");
        ecritureMessage4 (espace);        
      }
      if (millis() - chronoOff  > 3000) { // Après 3 secondes
        asm volatile("jmp 0x00");  // Réinitialisation depuis le début
      }
    }
  }
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionLectureCmd" qui lit et interprête la lecture de la commande
//----------------------------------------------------------------------------------------------------------------------------
void fonctionLectureCmd() {
  switch (lectureCommande()){       // On lit la commande qui retournera quelque chose différent de RIEN si un bouton a été appuyé
    
    case OFF : {
      fonctionArret();
    }
    break;
    
    case MEMO : {
      if (precfonc != MEMO) {                  // Pour éviter d'afficher le message à chaque passage dans le loop
        //Serial.println(F("Loop: MEMO"));
        ecritureMessage1 (txtloop);
        ecritureMessage2 (txtmemo);
        ecritureMessage3 (espace);
        ecritureMessage4 (espace);
      }  
      //positionservo = lecturePotentiometre(); // Mémorisation de la position du servomoteur pour l'asservissement // Ne pas faire cette lecture pour éviter d'écraser la valeur en mémoire (rappel de la vitesse précédente)
      precfonc = MEMO;                        // Pour éviter d'afficher le message à chaque passage dans le loop
      // On ne fait rien pour le moment
    }
    break;
    
    case MEMOLONG : {
      if (precfonc != MEMOLONG) {             // Pour éviter d'afficher le message à chaque passage dans le loop
        //Serial.println(F("Loop: MEMOLONG"));
        ecritureMessage1 (txtloop);
        ecritureMessage2 (txtmemo);
        ecritureMessage3 (txtlong);
        ecritureMessage4 (espace);
      }
      positionservo = lecturePotentiometre(); // Mémorisation de la position du servomoteur pour l'asservissement // Pour ce cas, on écrase la valeur mémorisée pour la remplacer par la nouvelle
      precfonc = MEMOLONG;                    // Pour éviter d'afficher le message à chaque passage dans le loop
      // On ne fait rien pour le moment
    }
    break;
    
    case PLUS : {
      if (precfonc != PLUS) {                 // Pour éviter d'afficher le message à chaque passage dans le loop
        //Serial.println(F("Loop: PLUS"));
        ecritureMessage1 (txtloop);
        ecritureMessage2 (txtplus); 
        ecritureMessage3 (espace);
        ecritureMessage4 (espace);
      }
      fonctionAccel(variateurManuPlus);
      positionservo = lecturePotentiometre(); // Mémorisation de la position du servomoteur pour l'asservissement
      precfonc = PLUS;                        // Pour éviter d'afficher le message à chaque passage dans le loop
    }
    break;
    
    case MOINS : {
      if (precfonc != MOINS) {                // Pour éviter d'afficher le message à chaque passage dans le loop
        //Serial.println(F("Loop: MOINS"));
        ecritureMessage1 (txtloop);
        ecritureMessage2 (txtmoins);
        ecritureMessage3 (espace);
        ecritureMessage4 (espace);
      }
      fonctionDecel(variateurManuMoins);
      positionservo = lecturePotentiometre(); // Mémorisation de la position du servomoteur pour l'asservissement
      precfonc = MOINS;                       // Pour éviter d'afficher le message à chaque passage dans le loop
    }
    break;
    
    default : {
      if (precfonc != RIEN) {                 // Pour éviter d'afficher le message à chaque passage dans le loop
          //Serial.println(F("Loop: ON"));
          ecritureMessage1 (txtloop);
          ecritureMessage2 (txton);
          ecritureMessage3 (espace);
          ecritureMessage4 (espace);
          if (precfonc == MOINS || precfonc == PLUS || precfonc == MEMO || precfonc == MEMOLONG) { // Activation de la régulation dès que l'une des précédentes fonctions: Moins Plus Memo Memolong (A remplacer pour prendre en compte la vitesse au lieu de la position de la pédale)
            regulation = true;                      // Activation de la régulation
            dessinPlay();                           // Indication à l'écran que la régulation est active
          }
        }
      fonctionOn();
      precfonc = RIEN;                        // Pour éviter d'afficher le message à chaque passage dans le loop
    }
    break;
  }
}


//#####################################################################
//## Initialisation du circuit (fonction setup)                      ##
//#####################################################################

void setup() {
  Serial.begin(9600);                    // Ouvre le port série
  
  // 1) Initialisation de l'écran
  myGLCD.InitLCD();
  myGLCD.fillScr(125, 125, 125);         // Fond écran
  Serial.println(F("Ecran initialisé"));
  
  // 2) Déclaration des pins:
    // a) pour les boutons
  pinMode(BTNOFF, INPUT);     // On déclare le pin "BTNOFF" comme un pin d'entrée 
  pinMode(BTNMEMO, INPUT);    // On déclare le pin "BTNMEMO" comme un pin d'entrée
  pinMode(BTNPLUS, INPUT);    // On déclare le pin "BTNPLUS" comme un pin d'entrée
  pinMode(BTNMOINS, INPUT);   // On déclare le pin "BTNMOINS" comme un pin d'entrée
  off.entree   = BTNOFF;
  memo.entree  = BTNMEMO;
  plus.entree  = BTNPLUS;
  moins.entree = BTNMOINS;
    // b) pour les relais et module L293D
  pinMode(CMDELECTROAIMANT, OUTPUT);
  pinMode(PWMMOTEUR, OUTPUT);
  pinMode(CMDACCELERATION, OUTPUT);
  pinMode(CMDDECELERATION, OUTPUT);
    // c) pour l'asservissement du servomoteur et le capteur de vitesse: pas besoin, ce sont des entrées analogiques
  Serial.println(F("Pins initialisés"));

  // 3) Initialisation erreur
  erreur = false;

  // 4) Initialisation commandes
  fonctionOff();
  
  // 5) Dessin sablier pour identifier le début de l'initialisation et remplacement du symbole pause
  dessinSablier ();
  etatantelectro = true; // On change cette variable pour permettre de redessiner le logo pause après l'initialisation
  
//  // 6) Test de lecture des paramètres d'entrée
//  // Vitesse:
//  if (lectureVitesse () < 100 || lectureVitesse () > vitessemaxi){ // Si la vitesse est hors clous
//    // Message "Lecture vitesse impossible réinitialiser le régulateur"
//    ecritureMessage1 ("Lect vitesse");
//    ecritureMessage2 ("impossible");
//    ecritureMessage3 ("reinitialiser");
//    ecritureMessage4 ("regul");
//    // Ne pas laisser le régulateur fonctionner si la vitesse ne peut être lue
//    erreur = true;
//    arretUrgence (erreur);
//  }
//  else {
//    ecritureMessage1 (espace);
//    ecritureMessage2 ("...");
//    ecritureMessage3 (espace);
//    ecritureMessage4 (espace);
//    Serial.println(F("Vitesse OK"));
//  }
//  delay (1000); // On prend 1 seconde pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
  
  
  // 7) Commandes:
    // a) Vérification de la commande MEMO
  if (!analogRead (memo.entree)){
    erreurmemo = true; // Le bouton MEMO détecté "bloqué" au démarrage doit pouvoir empêcher la réinitialisation dans le loop
    // Affichage sur l'écran
    ecritureMessage1 (txtcommande);
    ecritureMessage2 (txtmemo);
    ecritureMessage3 (txtappuyee); // "Commande Memo appuyée
    ecritureMessage4 (espace);
    delay (5000); // On prend 5 secondes pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
    // Ne pas laisser le régulateur fonctionner si ce bouton est appuyé au départ
    erreur = true;
  }
    // b) Vérification de la commande MOINS
  if (!analogRead (moins.entree)){
    // Affichage sur l'écran
    ecritureMessage1 (txtcommande);
    ecritureMessage2 (txtmoins);
    ecritureMessage3 (txtappuyee); // "Commande Moins appuyée"
    ecritureMessage4 (espace);
    delay (5000); // On prend 5 secondes pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
    // Ne pas laisser le régulateur fonctionner si ce bouton est appuyé au départ
    erreur = true;
  }
    // c) Vérification de la commande PLUS
  if (!analogRead (plus.entree)){
    // Affichage sur l'écran
    ecritureMessage1 (txtcommande);
    ecritureMessage2 (txtplus);
    ecritureMessage3 (txtappuyee); // "Commande Plus appuyée"
    ecritureMessage4 (espace);
    delay (5000); // On prend 5 secondes pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
    // Ne pas laisser le régulateur fonctionner si ce bouton est appuyé au départ
    erreur = true;
  }
    // d) Vérification de la commande OFF (on ne bloque pas celle là, car on peut rester appuyé sur le frein ou l'embrayage, par contre on met juste un message à l'écran)
  if (analogRead (off.entree)){
    // Affichage sur l'écran
    ecritureMessage1 (txtcommande);
    ecritureMessage2 (txtoff);
    ecritureMessage3 (txtappuyee); // "Commande Off appuyée"
    ecritureMessage4 (espace);
    delay (1000); // On prend 1 seconde pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
  }
  // 8) Si on n'a pas eu d'erreur (dans les if précédents) et qu'on n'a pas appuyé sur OFF, on affiche qu'aucune commande n'a été appuyée
  else if (!erreur) {
    // Affichage sur l'écran
    ecritureMessage1 ("Aucune");
    ecritureMessage2 (txtcommande);
    ecritureMessage3 (txtappuyee); // "Aucune commande appuyée"
    ecritureMessage4 (espace);
    delay (1000); // On prend 1 seconde pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
  }
    
  // 9) Si pas d'erreur à l'initialisation, on informe que tout est prêt
  if (!erreur) {
    // Affichage sur l'écran
    ecritureMessage1 (espace);
    ecritureMessage2 ("Pret");
    ecritureMessage3 (espace);
    ecritureMessage4 (espace);
    dessinPause ();
  }
  else {
    // Affichage sur l'écran
    ecritureMessage1 (espace);
    ecritureMessage2 ("Erreur");
    ecritureMessage3 ("relevee");
    ecritureMessage4 (espace);
  }
}


//#####################################################################
//## Fonction loop                                                   ##
//#####################################################################

void loop() {
  if (!erreur){                          // Si pas d'erreur détectée soit dans le setup soit dans le loop
    fonctionNormal();
  }
  else {                                 // En cas d'erreur relevée, on stoppe le régulateur
    fonctionErreur();
  }
}

 

Link to comment
Share on other sites

C'est normal que la voiture n'avance pas à une vitesse constante avec une position de pédale constante, l'effort à fournir par le moteur en pente ou contre le vent n'est pas le même qu'en descendant une pente ou le vent en faveur. D'où l'utilité d'un asservissement vis-à-vis de la vitesse, qui est la cible à réguler. Le fait d'avoir une position cohérente de la pédale/accélérateur est seulement une fonction (utile et important) qui permet de mettre en place l'asservissement. Disons que lorsque tu captureras la vitesse, t'auras enfin tous les outils pour pouvoir mettre en place ton régulateur.
Dans tous les cas, tu avances visiblement bien et dans la bonne direction. J'ai cependant l'impression que tu laisses la capture de la vitesse "pour la fin", alors que ce n'est que lorsque tu l'auras que tu pourras commencer...

Link to comment
Share on other sites

J’ai fait un test même en légère descente et je te confirme que même dans cette configuration la voiture ralentissait. Et (effet placebo ou pas?) si je gardais mon pied effleuré sur la pédale, je sentais la pression devenir plus forte :hein:

 

En tout cas oui la vitesse vient en dernier, car je veux déjà que tout ce qui touche à la pédale d’accélérateur soit réglé au poil, et aussi parce que je dois tout coder « de zéro » juste pour la lecture de vitesse. En fait vu qu’il s’agira de lire le compte tours (dont la seule valeur qui fluctue est la fréquence du signal, c’est pas la même que juste lire une valeur de résistance :laugh:), et ce via l’optocoupleur (je ne sais donc pas à quoi m’attendre comme type de signal), j’ai tout à prendre en main. Je ferai donc un code juste pour ça le temps du test avant de l'intégrer dans mon code « V1 abouti », et je ne pourrais le tester que en conditions réelles.

Link to comment
Share on other sites

  • 3 weeks later...
Posted (edited)

Mesdames, messieurs, j'ai l'honneur de vous partager ma fierté du moment: mon régulateur de régime moteur est parfaitement fonctionnel :wub: :wub: :wub:

 

Je ne suis pas mécontent du résultat, loin de là! Bon certes il y a quelques petits détails qui mériteraient d'être revus, qui sont surtout dus à des limitations techniques. Mais globalement, j'ai un truc fonctionnel dont l'ergonomie s'approche de très près d'un régulateur qu'on trouve sur des voitures récentes et surtout (point crucial!) qui réagisse au doigt et à l'oeil pour se désactiver!

 

Bien entendu, il faudra aussi que je peaufine certains paramètres, mais là, je crois que mon code est suffisamment robuste pour ne pas avoir à subir de modifications majeures. J'ai essayé de l'optimiser au maximum de mes compétences dans ce langage, et quand je compare la réactivité du rafraîchissement de l'affichage et de la réponse aux commandes avec ce que j'avais au début, je me dis que ce sera difficile de faire mieux.

 

Je suis aussi content de voir que la prise en compte de l'optocoupleur n'a pas été si difficile que ça. J'ai eu un moment de frayeur au moment de mesurer la tension "de sortie" avec mon oscilloscope, car on passait de 6,5V (au compte tours, et qui est une tension trop élevée pour l'Arduino, d'où le pont diviseur de tension et l'optocoupleur) à 2,5V (ce qui est limite il me semble pour que l'Arduino puisse détecter quelque chose), mais ça marche impeccable!

 

Avant de partager mon code (pour lequel je fais quand même une déclaration de non responsabilité s'il devait y avoir une réutilisation et un accident lié à son usage), voici deux vidéos du régulateur en action:

https://dai.ly/krgwIx6xSHEzYNB8Vj2

 

https://dai.ly/k1PN0CIP65nboyB8Vj4

 

Et donc le code, adapté à mon montage et à ma voiture, et donc pour lequel je décline toute responsabilité encore une fois s'il devait y avoir un usage qui occasionnerait un quelconque accident ou détérioration:

//#####################################################################
//## Déclaration des variables d'environnement                       ##
//#####################################################################
#include <UTFT.h>    // Bibliothèque écran
   
#define TEMPS_MAINTIENLONG 1500                                       // Temps de maintien en appui long
#define TEMPS_CHRONOOFF 2000                                          // Durée d'attente pour la fonction Off
#define TEMPS_CHRONOLIMIT 3000                                        // Durée d'affichage du message LIMITE
#define TAILLETABLE 5                                                 // Nombre de valeurs de vitesse enregistrées pour faire le lissage
#define CMDELECTROAIMANT 2                                            // Commande relais électroaimant de sécurité
#define CMDDECELERATION 3                                             // Commande sens décélération (A-)
#define CMDACCELERATION 6                                             // Commande sens accélération (A+)
#define ECRANRES 8                                                    // Ecran RES  
#define PWMMOTEUR 9                                                   // PWM Moteur pour variation de vitesse
#define ECRANCS 10                                                    // Ecran CS
#define ECRANSDA 11                                                   // Ecran SDA
#define ECRANDC 12                                                    // Ecran DC
#define ECRANSCL 13                                                   // Ecran SCL
#define VITESSE A1                                                    // (Analogique) Compte tours ou vitesse
#define POSITIONMOTEUR 3                                              // (Analogique) Résistance asservissement servomoteur
#define BTNMEMO 4                                                     // (Analogique) Bouton MEMO
#define BTNPLUS 5                                                     // (Analogique) Bouton +
#define BTNMOINS 6                                                    // (Analogique) Bouton -
#define BTNOFF 7                                                      // (Analogique) Bouton OFF (relais des pédales de frein et d'embrayage + bouton Off de la commande au volant)

#define VITESSEMINI 1000                                              // Vitesse minimum pour l'activation du régulateur de vitesse (en tours par minute, à arrondir à la dizaine)
#define VITESSEMAXI 4000                                              // Vitesse maximum pour l'activation du régulateur de vitesse (en tours par minute, à arrondir à la dizaine)
#define CTRLVITMINI 100                                               // Vitesse minimum pour déclanchement de l'erreur à la lecture de la vitesse (en tours par minute, à arrondir à la dizaine)
#define CTRLVITMAXI 5500                                              // Vitesse maximum pour déclanchement de l'erreur à la lecture de la vitesse (en tours par minute, à arrondir à la dizaine)
#define PASVITESSE 10                                                 // Pas avec lequel on incrémente ou diminue la valeur de vitesse (en tours par minute, à arrondir à la dizaine)
#define GDPASVITESSE 50                                               // Pas avec lequel on incrémente ou diminue la valeur de vitesse lors d'appui long (en tours par minute, à arrondir à la dizaine)
#define POSITIONSERVOMIN 1                                            // Position du servomoteur minimum au potentiomètre (en %)
#define POSITIONSERVOMAX 99                                           // Position du servomoteur minimum au potentiomètre (en %)
#define MARGESUPVITLENT 10                                            // Marge supérieure de vitesse avant décélération lente (en tours par minute, à arrondir à la dizaine)
#define MARGEINFVITLENT 10                                            // Marge inférieure de vitesse avant accélération lente (en tours par minute, à arrondir à la dizaine)
#define MARGESUPVITRAPID 40                                           // Seuil au delà duquel on calculera une valeur de décélération adaptée à l'écart entre l'actuel et la cible (en tours par minute, à arrondir à la dizaine)
#define MARGEINFVITRAPID 30                                           // Seuil au delà duquel on calculera une valeur d'accélération adaptée à l'écart entre l'actuel et la cible (en tours par minute, à arrondir à la dizaine)
#define MARGEINFPOTLENT 5                                             // Marge inférieure de position accélérateur avant accélération rapide (pour accélération initiale) (en %)
#define MARGEINFPOTRAPID 1                                            // Marge inférieure de position accélérateur avant accélération lente (pour accélération initiale) (en %)
#define VARIATRAPIDPLUS 255                                           // Variateur maximum pour le moteur via la pin PWM pour l'accélération "rapide". (Valeur comprise entre 0 et 255)
#define VARIATRAPIDMOINS 170                                          // Variateur maximum pour le moteur via la pin PWM pour la décélération "rapide". (Valeur comprise entre 0 et 255)
#define VARIATLENTPLUS 50                                             // Variateur pour le moteur via la pin PWM pour l'accélération lente. (Valeur comprise entre 0 et 255, mini 60)
#define VARIATLENTMOINS 25                                            // Variateur pour le moteur via la pin PWM pour la décélération lente. (Valeur comprise entre 0 et 255)
#define MARGEACCELINIT 25                                             // Valeur ajoutée à la variation VARIATLENTPLUS pour l'accélération initiale (à calculer de façon à avoir 70 minimum)

//----------------------------------------------------------------------------------------------------------------------------
// Structure permettant de définir les propriétés d'un bouton
//----------------------------------------------------------------------------------------------------------------------------
struct Bouton {
  int entree;                   // Entrée où le bouton est connecté
  bool boutonAppuye;            // Etat du bouton
  unsigned long date;           // Date à laquelle on a commencé à appuyer sur le bouton 
};

//----------------------------------------------------------------------------------------------------------------------------
// Définition des boutons
//----------------------------------------------------------------------------------------------------------------------------
Bouton off;   // Bouton OFF (relais des pédales de frein et d'embrayage + bouton Off de la commande au volant)
Bouton memo;  // Définition du bouton de mémorisation vitesse
Bouton plus;  // Définition du bouton +
Bouton moins; // Définition du bouton -

//----------------------------------------------------------------------------------------------------------------------------
// Définition des fonctions qu'on associe aux boutons
//----------------------------------------------------------------------------------------------------------------------------
enum FonctionBouton {
  RIEN,      // Fontion neutre, il n'y a rien à faire
  OFF,       // Fonction OFF
  MEMO,      // Fonction mémorisation vitesse
  MEMOLONG,  // Fonction associée à l'appui long sur le bouton mémorisation
  PLUS,      // Fonction +
  PLUSLONG,  // Fonction appui long sur +
  MOINS,     // Fonction -
  MOINSLONG, // Fonction appui long sur -
};

//----------------------------------------------------------------------------------------------------------------------------
// Type énuméré permettant de définir les états des boutons
//----------------------------------------------------------------------------------------------------------------------------
enum EtatBouton {
  RELACHE,                // Le bouton est relâché (circuit ouvert)
  APPUYE,                 // Le bouton est appuyé (circuit fermé)
  MAINTIEN_LONG,          // Le bouton est maintenu appuyé (circuit fermé)
};

//----------------------------------------------------------------------------------------------------------------------------
// Type énuméré permettant de définir les messages affichés
//----------------------------------------------------------------------------------------------------------------------------
enum MessageAffiche {
  MSGVIDE,
  INITMEMO,
  INITMOINS,
  INITPLUS,
  INITOFF,
  INITOK,
  INITVITKO,
  INITPOTKO,
  INITERR,
  PRET,
  APPUIOFF,
  REGUL,
  LIMITE,
  REINIT,
  MSGERR,
};

//----------------------------------------------------------------------------------------------------------------------------
// Type énuméré permettant de définir les dessins affichés
//----------------------------------------------------------------------------------------------------------------------------
enum DessinAffiche {
  DESVIDE,
  SABLIER,
  PAUSE,
  PLAY,
  ERREUR,
};

//----------------------------------------------------------------------------------------------------------------------------
// Déclarations relatives à l'affichage
//----------------------------------------------------------------------------------------------------------------------------
extern uint8_t SmallFont[]; // Police
extern uint8_t BigFont[];   // Police
UTFT myGLCD(ST7735S_4L_80160,ECRANSDA,ECRANSCL,ECRANCS,ECRANRES,ECRANDC);    // Initialisation écran LCD:  4Line  serial interface      SDI  SCL  /CS  /RST  D/C    NOTE:Only support  DUE   MEGA  UNO
int color = 0;
word colorlist[] = {VGA_WHITE, VGA_BLACK, VGA_RED, VGA_BLUE, VGA_GREEN, VGA_FUCHSIA, VGA_YELLOW, VGA_AQUA};
int  bsize = 4;
char voltageDisplay[7] = "";

//----------------------------------------------------------------------------------------------------------------------------
// Déclaration des variables globales utilisées dans le code
//----------------------------------------------------------------------------------------------------------------------------
bool erreur = true;                                   // Variable d'erreur
bool erreurmemo = false;                              // Variable d'erreur pour le bouton MEMO et empêcher le reset automatique s'il est détecté bloqué à l'initialisation
bool boolsetup = false;                               // Indicateur de la phase setup, pour l'affichage des messages d'initialisation
bool regulation = false;                              // Régulateur actif ou non
bool reinitialisation = false;                        // Autorisation de réinitialisation en cas d'erreur détectée
bool accelInitial = false;                            // Autorisation de l'accélération sur la base de la position (course en %) de l'accélérateur
int vitesseMemoire = 0;                               // Vitesse mise en mémoire pour la régulation
int vitesseLue = 0;                                   // Vitesse actuelle (lue) (pour rafraichissement écran)
int tableVitesse[TAILLETABLE] = {0, 0, 0, 0, 0};      // Liste des valeurs de vitesse pour le calcul de leur moyenne
byte n = 0;                                           // Curseur pour parcourir la table lors de l'acquisition de la vitesse
FonctionBouton precfonc = RIEN;                       // Fonction bouton à n-1
DessinAffiche desEnCours = DESVIDE;                   // Dessin affiché à l'écran (pour éviter les rafraichissements intempestifs)
unsigned long chronoOff = 0;                          // Chronomètre pour la fonction Off (au lieu d'utiliser un delay)
unsigned long chronoLimit = 0;                        // Chronomètre pour l'affichage du message LIMITE
String espace = "";
String message1prec = espace;                         // Message 1 précédement affiché (pour rafraichissement écran)
String message2prec = espace;                         // Message 2 précédement affiché (pour rafraichissement écran)
String message3prec = espace;                         // Message 3 précédement affiché (pour rafraichissement écran)
String message4prec = espace;                         // Message 4 précédement affiché (pour rafraichissement écran)

//#####################################################################
//## Déclaration des fonctions                                       ##
//#####################################################################

///////////////////
// Affichage     //
///////////////////

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "dessinVide" de dessin d'un rectangle blanc
//----------------------------------------------------------------------------------------------------------------------------
void dessinVide () {
  int xcoinsuperieur = 5;
  int ycoinsuperieur = 5;
  int xcoininferieur = 72;
  int ycoininferieur = 72;
  // Arrière plan du symbole
  myGLCD.setColor(255, 255, 255);           // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillRoundRect(xcoinsuperieur, ycoinsuperieur, xcoininferieur, ycoininferieur);       // Position x1, y1, x2, y2
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "dessinSablier" de dessin du symbole attente
//----------------------------------------------------------------------------------------------------------------------------
void dessinSablier () {
  int xcoinsuperieur = 5;
  int ycoinsuperieur = 5;
  int xcoininferieur = 72;
  int ycoininferieur = 72;
  int x1 = 0;
  int y1 = 0;
  int x2 = 0;
  int y2 = 0;
  int x3 = 0;
  int y3 = 0;
  int x4 = 0;
  int y4 = 0;
  int x5 = 0;
  int y5 = 0;
  int y6 = 0;
  int y7 = 0;
  int y8 = 0;
  int y9 = 0;
  int ya = 0;
  
  // Arrière plan du symbole
  myGLCD.setColor(255, 255, 255);           // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillRoundRect(xcoinsuperieur, ycoinsuperieur, xcoininferieur, ycoininferieur);       // Position x1, y1, x2, y2
  // Sablier: Deux rectangles et deux triangles, remplis par un triangle et un rectangle
  x1 = 0.3 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x2 = 0.7 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x3 = 0.5 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x4 = 0.35 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x5 = 0.65 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  y1 = 0.1 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y2 = 0.3 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y3 = 0.7 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y4 = 0.9 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y5 = 0.55 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y6 = 0.45 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y7 = 0.5 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y8 = 0.8 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y9 = 0.85 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;

  // Deux rectangles (à l'échelle)
  myGLCD.setColor(200, 100, 100);           // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillRect(x1, y1, x2, y2);          // Premier rectangle coordonnées x1, y1, x2, y2
  myGLCD.fillRect(x1, y3, x2, y4);          // Deuxième rectangle coordonnées x1, y1, x2, y2

  // Deux triangles
  myGLCD.fillTriangle(x1, y2, x2, y2, x3, y5); // Triangle plein
  myGLCD.fillTriangle(x1, y3, x2, y3, x3, y6); // Triangle plein

  // Un triangle et un rectangle
  myGLCD.setColor(230, 230, 020);                // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillTriangle(x4, y2, x5, y2, x3, y7);   // Triangle plein
  myGLCD.fillRect(x4, y8, x5, y9);               // Premier rectangle coordonnées x1, y1, x2, y2
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "dessinPlay" de dessin du symbole play
//----------------------------------------------------------------------------------------------------------------------------
void dessinPlay () {
  int xcoinsuperieur = 5;
  int ycoinsuperieur = 5;
  int xcoininferieur = 72;
  int ycoininferieur = 72;
  int x1 = 0;
  int y1 = 0;
  int y2 = 0;
  int x3 = 0;
  int y3 = 0;
  // Arrière plan du symbole
  myGLCD.setColor(255, 255, 255);           // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillRoundRect(xcoinsuperieur, ycoinsuperieur, xcoininferieur, ycoininferieur);       // Position x1, y1, x2, y2
  // Triangle (à l'échelle)
  myGLCD.setColor(0, 255, 0);               // Couleur du symbole dessiné (red, green, blue)
  x1 = 0.1 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x3 = 0.9 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  y1 = 0.1 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y2 = 0.9 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y3 = 0.5 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  myGLCD.fillTriangle(x1, y1, x1, y2, x3, y3); // Triangle plein
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "dessinPause" de dessin du symbole pause
//----------------------------------------------------------------------------------------------------------------------------
void dessinPause () {
  int xcoinsuperieur = 5;
  int ycoinsuperieur = 5;
  int xcoininferieur = 72;
  int ycoininferieur = 72;
  int x1 = 0;
  int y1 = 0;
  int x2 = 0;
  int y2 = 0;
  int x3 = 0;
  int x4 = 0;
  // Arrière plan du symbole
  myGLCD.setColor(255, 255, 255);           // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillRoundRect(xcoinsuperieur, ycoinsuperieur, xcoininferieur, ycoininferieur);       // Position x1, y1, x2, y2
  // Deux rectangles (à l'échelle)
  myGLCD.setColor(100, 020, 255);               // Couleur du symbole dessiné (red, green, blue)
  x1 = 0.15 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x2 = 0.42 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x3 = 0.57 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x4 = 0.85 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  y1 = 0.15 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y2 = 0.85 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  myGLCD.fillRect(x1, y1, x2, y2);          // Premier rectangle coordonnées x1, y1, x2, y2
  myGLCD.fillRect(x3, y1, x4, y2);          // Deuxième rectangle coordonnées x1, y1, x2, y2
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "dessinErreur" de dessin du symbole attention
//----------------------------------------------------------------------------------------------------------------------------
void dessinErreur () {
  int xcoinsuperieur = 5;
  int ycoinsuperieur = 5;
  int xcoininferieur = 72;
  int ycoininferieur = 72;
  int x1 = 0;
  int y1 = 0;
  int x2 = 0;
  int y2 = 0;
  int x3 = 0;
  int x4 = 0;
  int y4 = 0;
  int x5 = 0;
  int y5 = 0;
  int y6 = 0;
  int y7 = 0;
  // Arrière plan du symbole
  myGLCD.setColor(255, 255, 255);                // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillRoundRect(xcoinsuperieur, ycoinsuperieur, xcoininferieur, ycoininferieur);       // Position x1, y1, x2, y2
  // Triangle (à l'échelle)
  myGLCD.setColor(255, 0, 0);                    // Couleur du symbole dessiné (red, green, blue)
  x1 = 0.1 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x2 = 0.5 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  x3 = 0.9 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur;
  y1 = 0.9 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y2 = 0.1 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  myGLCD.fillTriangle(x1, y1, x2, y2, x3, y1);   // Triangle plein
  // Point exclamation (à l'échelle)
  x4 = 0.5 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur - 0.05 * (xcoininferieur - xcoinsuperieur);
  x5 = 0.5 * (xcoininferieur - xcoinsuperieur) + xcoinsuperieur + 0.05 * (xcoininferieur - xcoinsuperieur);
  y4 = 0.4 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y7 = 0.85 * (ycoininferieur - ycoinsuperieur) + ycoinsuperieur;
  y6 = y7 - (x5 - x4);
  y5 = y6 - 0.85 * (x5 - x4);
  myGLCD.setColor(255, 255, 050);               // Couleur du symbole dessiné (red, green, blue)
  myGLCD.fillRect(x4, y4, x5, y5);              // Premier rectangle coordonnées x1, y1, x2, y2
  myGLCD.fillRect(x4, y6, x5, y7);              // Deuxième rectangle coordonnées x1, y1, x2, y2
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "dessinLogo" qui dessine le logo associé au paramètre d'entrée
//----------------------------------------------------------------------------------------------------------------------------
void dessinLogo (DessinAffiche typeDessin) {
  switch (typeDessin) {

    case DESVIDE : {
      if (!erreur) {                   // Si on n'est pas en erreur (pour éviter d'écraser le dessin du symbole d'erreur)
        if (desEnCours != DESVIDE) {      // Si le symbole "VIDE" est déjà affiché, inutile de le dessiner à nouveau
          dessinVide();
          desEnCours = DESVIDE;
        }
      }
    }
    break;

    case SABLIER : {
      if (!erreur) {                   // Si on n'est pas en erreur (pour éviter d'écraser le dessin du symbole d'erreur)
        if (desEnCours != SABLIER) {   // Si le sablier est déjà affiché, inutile de le dessiner à nouveau
          dessinSablier();
          desEnCours = SABLIER;
        }
      }
    }
    break;

    case PAUSE : {
      if (!erreur) {                   // Si on n'est pas en erreur (pour éviter d'écraser le dessin du symbole d'erreur)
        if (desEnCours != PAUSE) {     // Si le symbole pause est déjà affiché, inutile de le dessiner à nouveau
          dessinPause();
          desEnCours = PAUSE;
        }
      }
    }
    break;

    case PLAY : {
      if (!erreur) {                   // Si on n'est pas en erreur (pour éviter d'écraser le dessin du symbole d'erreur)
        if (desEnCours != PLAY) {      // Si le symbole play est déjà affiché, inutile de le dessiner à nouveau
          dessinPlay();
          desEnCours = PLAY;
        }
      }
    }
    break;

    case ERREUR : {
      if (desEnCours != ERREUR) {      // Si le symbole erreur est déjà affiché, inutile de le dessiner à nouveau
        dessinErreur();
        desEnCours = ERREUR;
      }
    }
    break;

    default : {
      if (!erreur) {                   // Si on n'est pas en erreur (pour éviter d'écraser le dessin du symbole d'erreur)
        if (desEnCours != DESVIDE) {      // Si le symbole "VIDE" est déjà affiché, inutile de le dessiner à nouveau
          dessinVide();
          desEnCours = DESVIDE;
        }
      }
    }
    break;
  }
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "ecritureMessage1" qui écrit sur la première ligne de l'écran (10 caractères max visibles)
//----------------------------------------------------------------------------------------------------------------------------
void ecritureMessage1 (String message) {
    if (message == espace) {
      myGLCD.setBackColor(125, 125, 125);       // Couleur de l'arrière plan de la zone de texte (red, green, blue), ici gris
    }
    else {
      myGLCD.setBackColor(0, 0, 0);             // Couleur de l'arrière plan de la zone de texte (red, green, blue), ici noir
    }
    myGLCD.setColor(255, 255, 255);             // Couleur de texte (red, green, blue)
    myGLCD.setFont(SmallFont);
    if (message.length() > 9) {                 // Tronquer le message s'il dépasse
      message[10] = NULL;
    }
    myGLCD.print(F("          "), 75, 5);       // Effacement du texte précédent
    if (message != espace) {                    // N'écrire le texte que s'il est différent d'un espace
      myGLCD.print(message, 75, 5);             // text, position x, position y
    }
    Serial.print(F("Message 1: "));
    Serial.println(message);
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "ecritureMessage2" qui écrit sur la deuxième ligne de l'écran (5 caracères max visibles)
//----------------------------------------------------------------------------------------------------------------------------
void ecritureMessage2 (String message) {
    if (message == espace) {
      myGLCD.setBackColor(125, 125, 125);       // Couleur de l'arrière plan de la zone de texte (red, green, blue), ici gris
    }
    else {
      myGLCD.setBackColor(0, 0, 0);             // Couleur de l'arrière plan de la zone de texte (red, green, blue), ici noir pour quand on affichera le texte
    }
    myGLCD.setColor(255, 255, 255);             // Couleur de texte (red, green, blue)
    myGLCD.setFont(BigFont);
    if (message.length() > 4) {                 // Tronquer le message s'il dépasse
      message[5] = NULL;
    }
    myGLCD.print(F("     "), 75, 20);           // Effacement du texte précédent
    if (message != espace) {                    // N'écrire le texte que s'il est différent d'un espace
      myGLCD.print(message, 75, 20);            // text, position x, position y
    }  
    Serial.print(F("Message 2: "));
    Serial.println(message);
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "ecritureMessage3" qui écrit sur la troisième ligne de l'écran (10 caracères max visibles)
//----------------------------------------------------------------------------------------------------------------------------
void ecritureMessage3 (String message) {
    myGLCD.setBackColor(125, 125, 125);         // Couleur de l'arrière plan de la zone de texte (red, green, blue)
    myGLCD.setColor(0, 0, 0);                   // Couleur de texte (red, green, blue)
    myGLCD.setFont(SmallFont);
    if (message.length() > 9) {                 // Tronquer le message s'il dépasse
      message[10] = NULL;
    }
    myGLCD.print(F("          "), 75, 39);      // Effacement du texte précédent
    if (message != espace) {                    // N'écrire le texte que s'il est différent d'un espace
      myGLCD.print(message, 75, 39);            // text, position x, position y
    }
    Serial.print(F("Message 3: "));
    Serial.println(message);
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "ecritureMessage4" qui écrit sur la quatrième ligne de l'écran (5 caracères max visibles)
//----------------------------------------------------------------------------------------------------------------------------
void ecritureMessage4 (String message) {
    myGLCD.setBackColor(125, 125, 125);         // Couleur de l'arrière plan de la zone de texte (red, green, blue)
    myGLCD.setColor(0, 0, 0);                   // Couleur de texte (red, green, blue)
    myGLCD.setFont(BigFont);
    if (message.length() > 4) {                 // Tronquer le message s'il dépasse
      message[5] = NULL;
    }
    myGLCD.print(F("     "), 75, 54);           // Effacement du texte précédent
    if (message != espace) {                    // N'écrire le texte que s'il est différent d'un espace
      myGLCD.print(message, 75, 54);            // text, position x, position y
    }  
    Serial.print(F("Message 4: "));
    Serial.println(message);
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "ecritureMessage" qui écrit les 4 lignes sur l'écran en fonction du type de message
//----------------------------------------------------------------------------------------------------------------------------
void ecritureMessage (MessageAffiche typeMessage) {
  String message1 = espace;
  String message2 = espace;
  String message3 = espace;
  String message4 = espace;
  String memoAffich = String(vitesseMemoire);
  vitesseLue = lectureVitesse();
  String actuelAffich = String(vitesseLue);
  
  if ((!boolsetup) && (millis() - chronoLimit < TEMPS_CHRONOLIMIT)) { // Si on est toujours dans le timing pour l'affichage du message LIMITE, on le force (sauf pour Off, les cas d'erreurs, et l'initialisation)
    if ((typeMessage == MSGVIDE) || (typeMessage == PRET) || (typeMessage == REGUL)) {
      typeMessage = LIMITE;
    }
  }

  switch (typeMessage) {
  // Message vide: on n'affiche rien
    case MSGVIDE : {
      if (vitesseMemoire != 0) {
        message1 = "Memo:";
        message2 = memoAffich;
      }
      else {
        message1 = "Actuel:";
        message2 = actuelAffich;
      }
      message3 = espace;
      message4 = espace;
    }
    break;
  // Message "cmde Memo" à l'initialisation
    case INITMEMO : {
      message1 = "Cmde";
      message2 = "Memo";
      message3 = espace;
      message4 = espace;
    }
    break;
  // Message "cmde Moins" à l'initialisation    
    case INITMOINS : {
      message1 = "Cmde";
      message2 = "Moins";
      message3 = espace;
      message4 = espace;
    }
    break;
  // Message "cmde Plus" à l'initialisation    
    case INITPLUS : {
      message1 = "Cmde";
      message2 = "Plus";
      message3 = espace;
      message4 = espace;
    }
    break;
  // Message "cmde Off" à l'initialisation    
    case INITOFF : {
      message1 = "Cmde";
      message2 = "Off";
      message3 = espace;
      message4 = espace;
    }
    break;
  // Message "Erreur" à l'initialisation      
    case INITERR : {
      message1 = espace;
      message2 = "Erreur";
      message3 = espace;
      message4 = espace;
    }
    break;
  // Message "Lect vitesse impossible" à l'initialisation      
    case INITVITKO : {
      message1 = "Lecture";
      message2 = "vit.";
      message3 = "impossible";
      message4 = espace;
    }
    break;
  // Message "Lect vitesse impossible" à l'initialisation      
    case INITPOTKO : {
      message1 = "Lecture";
      message2 = "posit";
      message3 = "impossible";
      message4 = espace;
    }
    break;
  // Message "..." à l'initialisation (Vitesse OK)
    case INITOK : {
      message1 = espace;
      message2 = espace;
      message3 = espace;
      message4 = "...";
    }
    break;
  // Message "Pret"
    case PRET : {
      if (vitesseMemoire != 0) {
        message1 = "Memo:";
        message2 = memoAffich;
      }
      else {
        message1 = "Actuel:";
        message2 = actuelAffich;
      }
      message3 = espace;
      message4 = "Pret";
    }
    break;
  // Message "Off"  
    case APPUIOFF : {
      if (vitesseMemoire != 0) {
        message1 = "Memo:";
        message2 = memoAffich;
      }
      else {
        message1 = "Actuel:";
        message2 = actuelAffich;
      }
      message3 = espace;
      message4 = "Off";
    }
    break;
  // Message affichant l'état des paramètres lors de la régulation de vitesse  
    case REGUL : {
      message1 = "Memo:";
      message2 = memoAffich;
      message3 = "Actuel:";
      message4 = actuelAffich;
    }
    break;
  // Message avertissant du dépassement des limites de régulation autorisées
    case LIMITE : {
      if (vitesseMemoire != 0) {
        message1 = "Memo:";
        message2 = memoAffich;
      }
      else {
        message1 = "Actuel:";
        message2 = actuelAffich;
      }
      message3 = "Hors";
      message4 = "limit";
    }
    break;
  // Message "Reinitialisation OK" dans le cas d'erreur
    case MSGERR : {
      if (vitesseMemoire != 0) {
        message1 = "Memo:";
        message2 = memoAffich;
      }
      else {
        message1 = "Actuel:";
        message2 = actuelAffich;
      }
      message3 = "Autorise";
      message4 = "reset";
    }
    break;
  // Message "3 sec." au moment de la réinitialisation après erreur
    case REINIT : {
      message1 = espace;
      message2 = "3";
      message3 = "sec.";
      message4 = espace;
    }
    break;
  // Par défaut: on n'affiche rien
    default : {
      if (vitesseMemoire != 0) {
        message1 = "Memo:";
        message2 = memoAffich;
      }
      else {
        message1 = "Actuel:";
        message2 = actuelAffich;
      }
      message3 = espace;
      message4 = espace;
    }
    break;
  }

  // Ecriture des messages pour tous les cas, en ne rafraichissant que les lignes nécessaires
  if (message1prec != message1) {
    ecritureMessage1(message1);
    message1prec = message1;
  }
  if (message2prec != message2) {
    ecritureMessage2(message2);
    message2prec = message2;
  }
  if (message3prec != message3) {
    ecritureMessage3(message3);
    message3prec = message3;
  }
  if (message4prec != message4) {
    ecritureMessage4(message4);
    message4prec = message4;
  }
}

////////////////////////
// Lecture paramètres //
////////////////////////

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "etatDuBouton" qui gère les états des boutons (revoie un des états contenus dans la variable énumérée EtatBouton)
//----------------------------------------------------------------------------------------------------------------------------
EtatBouton etatDuBouton (Bouton *boutonATraiter) {
  EtatBouton etat = RELACHE;                                                   // Valeur qui sera renvoyée comme résultat de cette fonction, par défaut RELACHE
  bool etatSignal = !analogRead(boutonATraiter->entree);                       // Lire l'état de l'entrée dans une nouvelle variable temporaire

  if (etatSignal) {                                                            // Si le bouton semble avoir été appuyé (pas de signal, ou signal = 0/false)
    if (!boutonATraiter->boutonAppuye) {                                       // Si c'est la première fois que le bouton est détecté appuyé, on lance un chrono
      boutonATraiter->date = millis();
    }
    boutonATraiter->boutonAppuye = true;                                       // On attribue au bouton l'état appuyé
    if (millis() - boutonATraiter->date > TEMPS_MAINTIENLONG) {                // Si le bouton est appuyé depuis plus du temps défini
      etat = MAINTIEN_LONG;                                                    // Renvoyer l'état appui long
    }
    else {                                                                     // Sinon on est sur un simple appui
      etat = APPUYE;                                                           // Renvoyer l'état appuyé
    }
  }
  else {                                                                       // Si le bouton n'est pas appuyé
    boutonATraiter->boutonAppuye = false;                                      // On attribue au bouton l'état non appuyé
    etat = RELACHE;                                                            // Renvoyer l'état relaché
  }
  return etat;                                                                 // Renvoyer l'état du bouton en sortie de fonction
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "lectureCommande" qui contrôle les instructions données par le bouton multiposition
//----------------------------------------------------------------------------------------------------------------------------
FonctionBouton lectureCommande () {
  FonctionBouton fonction = RIEN; // Fonction renvoyée (par défaut: RIEN)
  // Lecture des états des boutons
  EtatBouton etatoff   = etatDuBouton( &off );
  EtatBouton etatmemo  = etatDuBouton( &memo );
  EtatBouton etatplus  = etatDuBouton( &plus );
  EtatBouton etatmoins = etatDuBouton( &moins );
    
  // Définition des fonctions en fonction de l'état des boutons
  // Etat OFF
  if (etatoff == RELACHE) {
    fonction = OFF;
  }

  // Etat MEMO
  if (etatmemo == APPUYE) {
    fonction = MEMO;
  } else if (etatmemo == MAINTIEN_LONG) {
    fonction = MEMOLONG;
  }

  // Etat PLUS
  if (etatplus == APPUYE) {
    fonction = PLUS;
  } else if (etatplus == MAINTIEN_LONG) {
    fonction = PLUSLONG;
  }

  // Etat MOINS
  if (etatmoins == APPUYE) {
    fonction = MOINS;
  } else if (etatmoins == MAINTIEN_LONG) {
    fonction = MOINSLONG;
  }

  // Renvoi de la fonction associée
   return fonction;
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "lecturePotentiometre" qui lit et transforme en % la valeur du potentiomètre du moteur du régulateur pour la position
//----------------------------------------------------------------------------------------------------------------------------
int lecturePotentiometre () {
  int valeurLue = analogRead(POSITIONMOTEUR);
  int positionMoteur = 0;
  acquisitionVitesse();                           // Continuer en permanence à faire l'acquisition des vitesses pour les mettre à jour (surtout dans les boucles while)
  if ((valeurLue < 0) || (valeurLue > 1023)) {    // Si la valeur est incohérente (en dehors de la plage 0 - 1023), on lève l'erreur
    erreur = true;
  }
  positionMoteur = map(valeurLue,0,1023,100,0);   // Conversion en pourcentage
  Serial.print(F("Position régulateur (%):"));
  Serial.println(positionMoteur);
  return positionMoteur;
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "acquisitionVitesse" qui lit la valeur de vitesse de la voiture et l'enregistre dans une table utilisée pour la lecture
//----------------------------------------------------------------------------------------------------------------------------
void acquisitionVitesse () {
  unsigned long etat_haut = pulseIn(VITESSE, HIGH);         // Mesure de la durée de l'impulsion haute
  unsigned long etat_bas = pulseIn(VITESSE, LOW);           // Mesure de la durée de l'impulsion basse
  long periode = etat_haut + etat_bas;                      // Calcul de la période
  long frequence = 0;
  if (periode == 0) {
    frequence = 0;
  }
  else {
    frequence = 1000000 / periode; // Calcul de la fréquence
  }  
  int vitesse = map(frequence, 0, 900, 0, 4000);      // Conversion en tours minute (fréquence mini, frequence maxi, RPM mini, RPM maxi)
  vitesse = round(vitesse / 10) * 10;                 // Arrondi de la valeur de la vitesse à la dizaine
  tableVitesse[n] = vitesse;                          // Remplissage de la table des vitesses
  n++;                                                // Incrémentation de l'ordre
  if (n == TAILLETABLE) {                             // Quand on arrive à la taille maximale de la table, on repart à zéro (pour écraser les valeurs précédentes)
    n = 0;
  }
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "lectureVitesse" qui lisse les valeurs de vitesse relevées
//----------------------------------------------------------------------------------------------------------------------------
int lectureVitesse () {
  int valeurVitesse = 0;     // Vitesse calculée
  int sommeVitesse = 0;      // Somme des vitesses contenues dans la table
  byte i = 0;                // Curseur de la table

  acquisitionVitesse();                                                 // Continuer en permanence à faire l'acquisition des vitesses pour les mettre à jour (surtout dans les boucles while)
  for (i=0; i<TAILLETABLE; i++) {                                       // Addition de toutes les valeurs contenues dans la table
    sommeVitesse = sommeVitesse + tableVitesse[i];
  }
  valeurVitesse = sommeVitesse / TAILLETABLE;                           // Division par le nombre d'entrées pour obtenir la moyenne 
  valeurVitesse = round(valeurVitesse / 10) * 10;                       // Arrondi de la valeur de la vitesse à la dizaine
  if ((valeurVitesse < CTRLVITMINI) || (valeurVitesse > CTRLVITMAXI)) { // Si la valeur de la vitesse est en dehors des clous, on lève l'erreur
    erreur = true;
  }
  
  // Pour info: calcul des vitesses associées en km/h
  int vitesseTrois = map(valeurVitesse, 0, 4000, 0, 100);               // Conversion en km/h pour la 3ème (RPM mini, RPM maxi, vitesse mini, vitesse maxi)
  int vitesseQuatre = map(valeurVitesse, 0, 4000, 0, 138);              // Conversion en km/h pour la 4ème (RPM mini, RPM maxi, vitesse mini, vitesse maxi)
  int vitesseCinq = map(valeurVitesse, 0, 4000, 0, 160);                // Conversion en km/h pour la 5ème (RPM mini, RPM maxi, vitesse mini, vitesse maxi)

  Serial.print(F("Vitesse RPM:"));
  Serial.println(valeurVitesse);
  Serial.print(F("Vitesse en 3:"));
  Serial.println(vitesseTrois);
  Serial.print(F("Vitesse en 4:"));
  Serial.println(vitesseQuatre);
  Serial.print(F("Vitesse en 5:"));
  Serial.println(vitesseCinq);

  return valeurVitesse;
}

///////////////////
// Actions       //
///////////////////

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionOff" qui gère les sorties pour couper le régulateur
//----------------------------------------------------------------------------------------------------------------------------
void fonctionOff () {
  digitalWrite(CMDACCELERATION, LOW);
  digitalWrite(CMDDECELERATION, LOW);
  digitalWrite(CMDELECTROAIMANT, LOW);    // Rappel: pour le module relais, "Low level trigger". Donc HIGH = repos, LOW = allumé.
  regulation = false;                     // On coupe la régulation
  accelInitial = true;                    // On autorise le déclanchement de la fonction d'accélération initiale
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionAccelInitiale" qui accélère la première fois pour assurer le relais à l'activation du régulateur
//----------------------------------------------------------------------------------------------------------------------------
void fonctionAccelInitale (int vitesse) {
  int positionMemo = map(vitesse, 0, 1750, 0, 50);          // Position approximative de l'accélérateur à avoir en fonction de la vitesse demandée
  int positionLue = lecturePotentiometre();
  FonctionBouton surveillCmd = lectureCommande();           // Pour surveiller si on appuie sur un bouton pendant la régulation
  int variationPwm = 0;                                     // Pour le calcul de la valeur de variation PWM
  
  // Accélération
  if (positionMemo - positionLue > MARGEINFPOTLENT) {
    while (positionMemo - positionLue > MARGEINFPOTLENT) {                                         // Si l'écart entre la position désirée et la postion actuelle est supérieure à la marge "lente": il faut accélérer 
      if (positionMemo - positionLue > MARGEINFPOTRAPID) {                                         // Si on est au delà de la marge rapide, on va calculer la valeur de variation à adopter
        variationPwm = map((positionMemo - positionLue), 0, 100, (VARIATLENTPLUS + MARGEACCELINIT), VARIATRAPIDPLUS); // Calcul de la valeur de variation à adopter (sur un écart de 0 à 100 % entre la valeur cible et la valeur actuelle)
      }
      else {                                                                                       // Sinon, on conserve la valeur minimum
        variationPwm = (VARIATLENTPLUS + MARGEACCELINIT);
      }
      fonctionAccel(variationPwm);
      positionLue = lecturePotentiometre();
      surveillCmd = lectureCommande();
      if (surveillCmd != RIEN) {                                                                   // Si une commande est sollicitée, on sort de la boucle
        fonctionLectureCmd(surveillCmd);
        positionLue = 0;
        positionMemo = 0;
        break;
      } 
    }
    if (surveillCmd != OFF) {
      fonctionOn();                                                                                // Pour stopper l'accélération (sauf si on a demandé le OFF)
    }
  }
  variationPwm = 0;                                                                                // Réinitialisation de la valeur à 0 par sécurité
  accelInitial = false;                                                                            // Une fois qu'elle a tourné, on n'y revient plus
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionAccel" qui gère les sorties pour accélérer (variable d'entrée: valeur du PWM)
//----------------------------------------------------------------------------------------------------------------------------
void fonctionAccel (int variation) {
  if (lecturePotentiometre() < POSITIONSERVOMAX) {  // Si le potentiomètre est avant la position maximum, on peut accélérer
    digitalWrite(CMDACCELERATION, HIGH);
  } else {                                          // Sinon, le potentiomètre est en position maximum, on n'accélère plus
    digitalWrite(CMDACCELERATION, LOW);
  }
  digitalWrite(CMDDECELERATION, LOW);
  analogWrite(PWMMOTEUR, variation);
  digitalWrite(CMDELECTROAIMANT, HIGH);    // Rappel: pour le module relais, "Low level trigger". Donc HIGH = repos, LOW = allumé.
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionDecel" qui gère les sorties pour décélérer (variable d'entrée: valeur du PWM)
//----------------------------------------------------------------------------------------------------------------------------
void fonctionDecel (int variation) {
  digitalWrite(CMDACCELERATION, LOW);
  if (lecturePotentiometre() > POSITIONSERVOMIN) {  // Si le potentiomètre est après la position minimum, on peut décélérer
      digitalWrite(CMDDECELERATION, HIGH);
  } else {                                          // Sinon, le potentiomètre est en position minimum, on ne décélère plus
      digitalWrite(CMDDECELERATION, LOW);
  }
  analogWrite(PWMMOTEUR, variation);
  digitalWrite(CMDELECTROAIMANT, HIGH);    // Rappel: pour le module relais, "Low level trigger". Donc HIGH = repos, LOW = allumé.
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionOn" qui gère les sorties pour maintenir l'accélération
//----------------------------------------------------------------------------------------------------------------------------
void fonctionOn () {
  digitalWrite(CMDACCELERATION, LOW);
  digitalWrite(CMDDECELERATION, LOW);
  digitalWrite(CMDELECTROAIMANT, HIGH);    // Rappel: pour le module relais, "Low level trigger". Donc HIGH = repos, LOW = allumé.
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionRegulation" qui ajuste la position de l'accélérateur
//----------------------------------------------------------------------------------------------------------------------------
void fonctionRegulation () {
 
  int positionLue = lectureVitesse();
  int positionMemo = vitesseMemoire;
  FonctionBouton surveillCmd = lectureCommande(); // Pour surveiller si on appuie sur un bouton pendant la régulation
  int variationPwm = 0;                           // Pour le calcul de la valeur de variation PWM
  
  if (surveillCmd == RIEN) {                      // Si aucune commande n'est sollicitée, on gère la régulation  
    if (accelInitial) {                           // Si l'accélération initiale est autorisée, on l'exécute
      fonctionAccelInitale(positionMemo);
    }
    // Décéleration
    if (positionLue - positionMemo > MARGESUPVITLENT) {
      while (positionLue - positionMemo > MARGESUPVITLENT) {                                            // Si l'écart entre la vitesse actuelle et la vitesse désirée est supérieure à la marge "lente": il faut décélérer
        if (positionLue - positionMemo > MARGESUPVITRAPID) {                                            // Si on est au dessus de la marge définie pour une décélération "rapide", on calcule la valeur du PWM à adopter
          variationPwm = map((positionLue - positionMemo), MARGESUPVITRAPID, 2000, VARIATLENTMOINS, VARIATRAPIDMOINS); // Calcul de la valeur de variation à adopter (sur un écart de MARGESUPVITRAPID à 2000 tours entre la valeur actuelle et la valeur cible)
        }
        else {                                                                                          // Sinon on reste sur la valeur minimum
          variationPwm = VARIATLENTMOINS;
        }
        fonctionDecel(variationPwm);
        positionLue = lectureVitesse();
        surveillCmd = lectureCommande();
        if (surveillCmd != RIEN) {                                                                      // Si une commande est sollicitée, on sort de la boucle while
          fonctionLectureCmd(surveillCmd);
          positionLue = 0;
          positionMemo = 0;
          break;
        }  
      }
      if (surveillCmd != OFF) {
        fonctionOn();                                                                                   // Pour stopper la décélération (sauf si on a demandé le OFF)
      }
    }
    // Accélération
    else if (positionMemo - positionLue > MARGEINFVITLENT) {
      while (positionMemo - positionLue > MARGEINFVITLENT) {                                            // Si l'écart entre la vitesse désirée et la vitesse actuelle est supérieure à la marge "lente": il faut accélérer
        if (positionMemo - positionLue > MARGEINFVITRAPID) {                                            // Si on est au dessus de la marge définie pour une accélération "rapide", on calcule la valeur du PWM à adopter
          variationPwm = map((positionMemo - positionLue), MARGEINFVITRAPID, 2000, VARIATLENTPLUS, VARIATRAPIDPLUS);   // Calcul de la valeur de variation à adopter (sur un écart de MARGEINFVITRAPID à 2000 tours entre la valeur actuelle et la valeur cible)
          if (variationPwm > 255) {                                                                     // Si la valeur calculée dépasse la valeur maximum (255), on force à 255
            variationPwm = 255;
          }
        }
        else {                                                                                          // Sinon on reste sur la valeur minimum
          variationPwm = VARIATLENTPLUS;
        }
        fonctionAccel(variationPwm);
        positionLue = lectureVitesse();
        surveillCmd = lectureCommande();
        if (surveillCmd != RIEN) {                                                                      // Si une commande est sollicitée, on sort de la boucle
          fonctionLectureCmd(surveillCmd);
          positionLue = 0;
          positionMemo = 0;
          break;
        } 
      }
      if (surveillCmd != OFF) {
        fonctionOn();                                                                                   // Pour stopper l'accélération (sauf si on a demandé le OFF)
      }
      variationPwm = 0;                                                                                 // Réinitialisation de la valeur à 0 par sécurité
    }
  }
  else {                                          // Sinon on traite la commande
    fonctionLectureCmd(surveillCmd);
  }
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionNormal" qui s'exécute en temps normal, quand il n'y a pas d'erreur
//----------------------------------------------------------------------------------------------------------------------------
void fonctionNormal () {
  acquisitionVitesse();                              // Remplissage de la table des vitesses
  if (millis() - chronoOff < TEMPS_CHRONOOFF) {      // Si on est toujours dans le timing pour la prise en charge de la fonction OFF, on la maintient histoire d'être sur que l'electroaimant soit bien désactivé
      fonctionOff();
      dessinLogo(PAUSE);
  }
  else {                                             // Sinon on gère la régulation de vitesse
    if (regulation) {                                // Si la régulation est activée, on régule la vitesse (qui gère d'elle même la lecture des commandes si elle sont sollicitées)
      int controleVitesse = lectureVitesse();        // Contrôle de la vitesse pour n'effectuer la régulation que dans la plage autorisée
      if ((controleVitesse > CTRLVITMINI) && (controleVitesse < CTRLVITMAXI)) {
        dessinLogo(PLAY);                            // On dessine le logo Play
        fonctionRegulation(); 
        ecritureMessage(REGUL);                      // Affichage de l'état de régulation 
      }
      else {
        regulation = false;
        ecritureMessage(LIMITE);          
        chronoLimit = millis();                      // Top chrono pour l'affichage du message LIMITE
      }
    }
    else {                                           // Si la régulation n'est pas active, on lit la commande
      dessinLogo(PAUSE);                             // On dessine le logo Pause car la régulation est désactivée
      ecritureMessage(PRET);                         // Affichage du message Pret
      fonctionLectureCmd(lectureCommande());
    }  
  }
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionErreur" qui s'exécute quand il y a une erreur détectée, et autorise la réinitialisation
//----------------------------------------------------------------------------------------------------------------------------
void fonctionErreur () {
  fonctionOff();       // On coupe tout par sécurité
  dessinLogo(ERREUR);  // Affichage du logo sur l'écran
  Serial.println(F("Arrêt d'Urgence dans le loop"));
  // Si on appuie longtemps sur MEMO, on peut réinitialiser le régulateur
  if (!erreurmemo) {    // Si le bouton n'était pas bloqué à l'initalisation, on autorise la réinitialisation
    ecritureMessage(MSGERR);
    if (lectureCommande() == MEMOLONG) { // Si on a appuyé longtemps sur MEMO, on va autoriser la réinitialisation
      reinitialisation = true;
      Serial.println(F("Réinitialisation autorisée"));
    }
    if (reinitialisation) {
      while (lectureCommande() == MEMOLONG) { // Tant qu'on maintient en position longue le bouton MEMO (pour la réinitialisation), on actuallise le chrono (pour éviter de reset l'Arduino alors qu'on est encore appuyé et ainsi risquer de lever "erreurmemo") 
        chronoOff = millis(); // (On recycle le chronoOff pour éviter de déclarer une nouvelle variable)
        Serial.println(F("MEMOLONG demandé, réinitialisation 3 secondes après lacher du bouton"));
        ecritureMessage(REINIT);        
      }
      if (millis() - chronoOff  > 3000) { // Après 3 secondes
        asm volatile("jmp 0x00");  // Réinitialisation depuis le début
      }
    }
  }
  else {
    ecritureMessage(MSGVIDE); // Effacement de l'écran (sauf valeurs mémorisées)
  }
}

//----------------------------------------------------------------------------------------------------------------------------
// Fonction "fonctionLectureCmd" qui interprête la commande demandée
//----------------------------------------------------------------------------------------------------------------------------
void fonctionLectureCmd (FonctionBouton commandeDemandee) {
  switch (commandeDemandee){                           // On lit la commande qui retournera quelque chose différent de RIEN si un bouton a été appuyé
    
    case OFF : {
      fonctionOff();
      if (precfonc != OFF) {                           // Si ce n'était pas déjà "OFF" 
        chronoOff = millis();                          // On réinitialise le chronomètre pour Off (utilisé pour maintenir la fonction Off quelques temps)
        //Serial.println(F("Loop: OFF"));
        ecritureMessage(APPUIOFF);
      }
      precfonc = OFF;
    }
    break;
    
    case MEMO : {
      if (precfonc != MEMO) {                          // Si on n'était pas en MEMO au tour précédent
        //Serial.println(F("Loop: MEMO"));
      }  
      if (vitesseMemoire == 0) {                       // Si aucune vitesse n'a déjà été mémorisée, on va lire la vitesse actuelle. Sinon ça rappellera automatiquement la vitesse précédemment enregistrée
        vitesseMemoire = lectureVitesse();             // Mémorisation de la vitesse actuelle
        if (vitesseMemoire < VITESSEMINI) {            // Si la vitesse mémorisée est inférieure à la vitesse minimum
          vitesseMemoire = VITESSEMINI;                // On force la vitesse minimum
        }
        if (vitesseMemoire > VITESSEMAXI) {            // Si la vitesse mémorisée est supérieure à la vitesse maximum
          vitesseMemoire = VITESSEMAXI;                // On force la vitesse maximum
        }
      }
      precfonc = MEMO;                                 // Pour le "if" du début du cas
    }
    break;
    
    case MEMOLONG : {
      if (precfonc != MEMOLONG) {                      // Si on n'était pas en MEMOLONG au tour précédent
        //Serial.println(F("Loop: MEMOLONG"));
      }
      vitesseMemoire = lectureVitesse();               // Mémorisation de la vitesse actuelle. Dans ce cas, on écrase la valeur mémorisée pour la remplacer par la nouvelle
      if (vitesseMemoire < VITESSEMINI) {              // Si la vitesse mémorisée est inférieure à la vitesse minimum
        vitesseMemoire = VITESSEMINI;                  // On force la vitesse minimum
      }
      if (vitesseMemoire > VITESSEMAXI) {              // Si la vitesse mémorisée est supérieure à la vitesse maximum
        vitesseMemoire = VITESSEMAXI;                  // On force la vitesse maximum
      }
      precfonc = MEMOLONG;                             // Pour le "if" du début du cas
    }
    break;
    
    case PLUS : {
      if (precfonc != PLUS) {                          // Si on n'était pas en PLUS au tour précédent
        //Serial.println(F("Loop: PLUS"));
        if (!regulation) {                             // Si on n'était pas en régulation
          vitesseMemoire = lectureVitesse();           // Mémorisation de la vitesse actuelle
          if (vitesseMemoire < VITESSEMINI) {          // Si la vitesse mémorisée est inférieure à la vitesse minimum
            vitesseMemoire = VITESSEMINI;              // On force la vitesse minimum
          }
          if (vitesseMemoire > VITESSEMAXI) {          // Si la vitesse mémorisée est supérieure à la vitesse maximum
            vitesseMemoire = VITESSEMAXI;              // On force la vitesse maximum
          }
        }
      }
      
      vitesseMemoire = vitesseMemoire + PASVITESSE;    // Incrémentation de la vitesse
      if (vitesseMemoire > VITESSEMAXI) {              // Si la vitesse mémorisée est supérieure à la vitesse maximum
        vitesseMemoire = VITESSEMAXI;                  // On force la vitesse maximum
      }
      precfonc = PLUS;                                 // Pour le "if" du début du cas
    }
    break;

    case PLUSLONG : {
      if (precfonc != PLUSLONG) {                      // Si on n'était pas en PLUSLONG au tour précédent
        //Serial.println(F("Loop: PLUSLONG"));
      }
      
      vitesseMemoire = vitesseMemoire + GDPASVITESSE;  // Incrémentation de la vitesse rapidement
      
      if (vitesseMemoire > VITESSEMAXI) {              // Si la vitesse mémorisée est supérieure à la vitesse maximum
        vitesseMemoire = VITESSEMAXI;                  // On force la vitesse maximum
      }
      precfonc = PLUSLONG;                             // Pour le "if" du début du cas
    }
    break;
    
    case MOINS : {
      if (precfonc != MOINS) {                         // Si on n'était pas en MOINS au tour précédent
        //Serial.println(F("Loop: MOINS"));
        if (!regulation) { 
          vitesseMemoire = lectureVitesse();             // Mémorisation de la vitesse actuelle
  
          if (vitesseMemoire < VITESSEMINI) {            // Si la vitesse mémorisée est inférieure à la vitesse minimum
            vitesseMemoire = VITESSEMINI;                // On force la vitesse minimum
          }
          if (vitesseMemoire > VITESSEMAXI) {            // Si la vitesse mémorisée est supérieure à la vitesse maximum
            vitesseMemoire = VITESSEMAXI;                // On force la vitesse maximum
          }
        }
      }
      
      vitesseMemoire = vitesseMemoire - PASVITESSE;    // Décrémentation de la vitesse
      if (vitesseMemoire < VITESSEMINI) {              // Si la vitesse mémorisée est inférieure à la vitesse minimum
        vitesseMemoire = VITESSEMINI;                  // On force la vitesse minimum
      }

      precfonc = MOINS;                                // Pour le "if" du début du cas
    }
    break;

    case MOINSLONG : {
      if (precfonc != MOINSLONG) {                     // Si on n'était pas en MOINSLONG au tour précédent
        //Serial.println(F("Loop: MOINSLONG"));
      }
      
      vitesseMemoire = vitesseMemoire - GDPASVITESSE;  // Déncrémentation de la vitesse rapidement

      if (vitesseMemoire < VITESSEMINI) {              // Si la vitesse mémorisée est inférieure à la vitesse minimum
        vitesseMemoire = VITESSEMINI;                  // On force la vitesse minimum
      }

      precfonc = MOINSLONG;                            // Pour le "if" du début du cas
    }
    break;
    
    default : {
      if (precfonc != RIEN) {                          // Si on n'était pas en RIEN au tour précédent
          //Serial.println(F("Loop: ON"));
          if (precfonc != OFF) {                       // Activation de la régulation dès que l'une des précédentes fonctions: Moins Moinslong Plus Pluslong Memo Memolong
            regulation = true;                         // Activation de la régulation
          }
        }
      fonctionOn();
      precfonc = RIEN;                                 // Pour le "if" du début du cas
    }
    break;
  }
}


//#####################################################################
//## Initialisation du circuit (fonction setup)                      ##
//#####################################################################

void setup () {
  Serial.begin(9600);                    // Ouvre le port série
  boolsetup = true;
  // 1) Initialisation de l'écran
  myGLCD.InitLCD();
  myGLCD.fillScr(125, 125, 125);         // Fond écran
  ecritureMessage(MSGVIDE);              // Ecriture message vide (pour initialiser la fonction)
  dessinLogo(DESVIDE);                   // Dessin symbole vide (pour initialiser la fonction)
  Serial.println(F("Ecran initialisé"));
  
  // 2) Déclaration des pins:
    // a) pour les boutons
  pinMode(BTNOFF, INPUT);     // On déclare le pin "BTNOFF" comme un pin d'entrée 
  pinMode(BTNMEMO, INPUT);    // On déclare le pin "BTNMEMO" comme un pin d'entrée
  pinMode(BTNPLUS, INPUT);    // On déclare le pin "BTNPLUS" comme un pin d'entrée
  pinMode(BTNMOINS, INPUT);   // On déclare le pin "BTNMOINS" comme un pin d'entrée
  off.entree   = BTNOFF;
  memo.entree  = BTNMEMO;
  plus.entree  = BTNPLUS;
  moins.entree = BTNMOINS;
    // b) pour les relais et module L293D
  pinMode(CMDELECTROAIMANT, OUTPUT);
  pinMode(PWMMOTEUR, OUTPUT);
  pinMode(CMDACCELERATION, OUTPUT);
  pinMode(CMDDECELERATION, OUTPUT);
    // c) pour l'asservissement du servomoteur et le capteur de vitesse: pas besoin, ce sont des entrées analogiques. AMD: la vitesse est déclarée en entrée dans l'exemple trouvé pour la détermination de la fréquence
  pinMode(VITESSE, INPUT);
  Serial.println(F("Pins initialisés"));

  // 3) Initialisation erreur
  erreur = false;

  // 4) Initialisation commandes
  fonctionOff();
  
  // 5) Dessin sablier pour identifier le début de l'initialisation
  dessinLogo(SABLIER);
  
  // 6) Test de lecture des paramètres d'entrée
  // Vitesse:
  for (n=0; n<TAILLETABLE; n++) {                 // Lecture de premières vitesses pour tester la fonction
    acquisitionVitesse();
  }
  n=0;                                            // Réinitialisation de n
  int testVitesse = lectureVitesse();
  if ((testVitesse >= CTRLVITMINI) && (testVitesse <= CTRLVITMAXI)) {   // Si la vitesse dans les clous
    ecritureMessage(INITOK);
    Serial.println(F("Vitesse OK"));
    delay (500); // On prend 1/2 seconde pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
  }
  else {                                                      // Sinon on informe que l'erreur est levée
    // Message "Lecture vitesse impossible" -> réinitialiser le régulateur (l'erreur est levée directement dans la fonction lecture)
    ecritureMessage(INITVITKO);
    delay (3000); // On prend 3 secondes pour lire le message d'erreur (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
  }

  // Potentiomètre:
  int testPotentiometre = lecturePotentiometre();
  if ((testPotentiometre >= 0) && (testPotentiometre <= 100)) {   // Si la position (en %) dans les clous
    ecritureMessage(INITOK);
    Serial.println(F("Potard OK"));
    delay (1000); // On prend 1 seconde pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
  }
  else {                                                      // Sinon on informe que l'erreur est levée
    // Message "Lecture position impossible" -> réinitialiser le régulateur (l'erreur est levée directement dans la fonction lecture)
    ecritureMessage(INITPOTKO);
    delay (3000); // On prend 3 secondes pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
  }
  
  // 7) Commandes:
    // a) Vérification de la commande MEMO
  if (!analogRead(memo.entree)) {
    erreurmemo = true; // Le bouton MEMO détecté "bloqué" au démarrage doit pouvoir empêcher la réinitialisation dans le loop
    // Affichage sur l'écran
    ecritureMessage(INITMEMO);
    delay (5000); // On prend 5 secondes pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
    // Ne pas laisser le régulateur fonctionner si ce bouton est appuyé au départ
    erreur = true;
  }
    // b) Vérification de la commande MOINS
  if (!analogRead(moins.entree)) {
    // Affichage sur l'écran
    ecritureMessage(INITMOINS);
    delay (5000); // On prend 5 secondes pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
    // Ne pas laisser le régulateur fonctionner si ce bouton est appuyé au départ
    erreur = true;
  }
    // c) Vérification de la commande PLUS
  if (!analogRead(plus.entree)) {
    // Affichage sur l'écran
    ecritureMessage(INITPLUS);
    delay (5000); // On prend 5 secondes pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
    // Ne pas laisser le régulateur fonctionner si ce bouton est appuyé au départ
    erreur = true;
  }
    // d) Vérification de la commande OFF (on ne bloque pas celle là, car on peut rester appuyé sur le frein ou l'embrayage, par contre on met juste un message à l'écran)
  if (analogRead(off.entree)) {
    // Affichage sur l'écran
    ecritureMessage(INITOFF);
    delay (1000); // On prend 1 seconde pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
  }
    // Si on n'a pas eu d'erreur (dans les if précédents) et qu'on n'a pas appuyé sur OFF, on affiche qu'aucune commande n'a été appuyée
  else if (!erreur) {
    // Affichage sur l'écran
    ecritureMessage(INITOK);
    delay (1000); // On prend 1 seconde pour lire le message (et tant pis si on stoppe le code, on n'est qu'au stade de l'initialisation, ce n'est pas grave)
  }
    
  // 8) Si pas d'erreur à l'initialisation, on informe que tout est prêt
  if (!erreur) {
    // Affichage sur l'écran
    ecritureMessage(PRET);
  }
  else {
    // Affichage sur l'écran
    ecritureMessage(INITERR);
  }
  boolsetup = false;
}


//#####################################################################
//## Fonction loop                                                   ##
//#####################################################################

void loop () {
  if (!erreur) {                         // Si pas d'erreur détectée soit dans le setup soit dans le loop
    fonctionNormal();
  }
  else {                                 // En cas d'erreur relevée, on stoppe le régulateur
    fonctionErreur();
  }
}

 

Edited by totojest
Code plus joli
  • Well done! 4
Link to comment
Share on other sites

  • 2 months later...

Petite évolution du montage pour me permettre de faciliter le débogage (si besoin, car ça fonctionne toujours très bien), et faire évoluer le code (sur le maintien de la vitesse en côte par exemple, il y faudrait que je fasse un peu différemment pour réduire le temps de réaction sur la variation d'accélération): ajout d'un module Bluetooth qui permet de remplacer le port USB classique en passant par le port série Bluetooth :)

 

(Dans la foulée, j'ai aussi fait l'Arduino qui commande mes autoradios via la commande au volant)

IMG_2836.JPG.c442459c4f00e47e34ec2f5ef7a0801d.JPG

 

Quand j'ai le contact mis, j'ai bien mes modules visibles sur mon téléphone (par contre on ne peut s'y connecter qu'avec un Android, j'ai pris des modules HC-05 non compatibles avec des appareils à la Pomme).

1150881550_BluetoothArduino.thumb.jpg.fe6fbd186c8cf5173130bd95a1b8f83e.jpg

 

Le schéma électrique devient le suivant:

Schema7.thumb.jpg.3a94732ddbeaabf403239d902c688f67.jpg

 

C'est vraiment pratique comme ça, plus besoin de démonter le cache du tableau de bord derrière lequel il est planqué, ça reste propre tout le temps dans la voiture :D

 

Côté code, le seul truc qui change c'est l'ouverture du port série à mettre avec une vitesse qui corresponde au modèle d'Arduino et au module Bluetooth (57600 bauds en ce qui me concerne). Pour le reste, il faut juste paramétrer le module HC-05 mais c'est relativement facile. J'ai cependant passé un temps infini à trouver le bon cablage pour déclencher la remise à zéro de l'Arduino à chaque chargement de code. Il y a plein de schémas proposés sur Internet, mais le seul qui a été valable, c'est celui avec un transistor. Et cette vidéo a été un véritable salut au milieu de toutes les m*rdes qu'on pouvait trouver sur le sujet et qui m'ont fait perdre un temps précieux:

 

  • Like 3
Link to comment
Share on other sites

Le genre de ptit gadget dont j'aurais bien besoin sur la lag et sa commande au volant spécifique et donc il me manque les schémas pour adapter la commande au volant 👀

Link to comment
Share on other sites

Alors j’ai suivi pour le régulateur de vitesse, par contre j’avoue que je ne comprends pas bien ce que les commandes d’autoradio viennent faire là-dedans ?

Link to comment
Share on other sites

il y a 36 minutes, Zorro_X a dit :

Alors j’ai suivi pour le régulateur de vitesse, par contre j’avoue que je ne comprends pas bien ce que les commandes d’autoradio viennent faire là-dedans ?

C'est le module du dessus sur la photo :) La partie régulateur de vitesse est dans la boite en plastique.

 

Ce sont les deux Arduino que j'utilise pour l'Espace. J'ai donc fait d'une pierre deux coups.

Link to comment
Share on other sites

ok... donc il y a 2 arduinos, soit... Mais du coup ton arduino pour l'autoradio fait quoi ? il convertit les commandes du volant en commandes interprétées par l'auto-radio ?

Link to comment
Share on other sites

Il y a 5 heures, Zorro_X a dit :

ok... donc il y a 2 arduinos, soit... Mais du coup ton arduino pour l'autoradio fait quoi ? il convertit les commandes du volant en commandes interprétées par l'auto-radio ?

Exact! Je pensais avoir créé un topic au sujet de cet Arduino, mais en fait je n'en ai parlé que sur le topic de l'Espace. Rien de bien folichon cela dit, c'est ni plus ni moins qu'une télécommande infrarouge (vu que les deux autoradios on un récepteur IR) qui envoie donc le code correspondant à la fonction choisie sur la commande au volant. Mais actuellement je ne me sert que des boutons de volume, car les autres commandes ne sont pas "cohérentes" en fonction du mode choisi (radio, K7 ou CD...) Il va donc y avoir de l'amélioration à venir sur le code de cet Arduino là aussi, d'où l'intérêt du BT :)

Link to comment
Share on other sites

ok ! je comprends mieux. C'est sympa aussi comme projet, aussi plus simple que le régulateur de vitesse pour commencer... Ceci dit, t'as certainement du apprendre à gérer l'IR avec l'arduino (ce qui n'est pas inutile).

Link to comment
Share on other sites

Il y a 1 heure, Zorro_X a dit :

ok ! je comprends mieux. C'est sympa aussi comme projet, aussi plus simple que le régulateur de vitesse pour commencer... Ceci dit, t'as certainement du apprendre à gérer l'IR avec l'arduino (ce qui n'est pas inutile).

Oui, il m'a fallu d'abord lire les codes que je voulais répliquer pour ma télécommande, comprendre les protocoles de communication IR (il y en a plusieurs), j'avais même tenté de lire les codes IR des plips Renault, mais mon récepteur n'arrivait pas à l'interpréter (avec le code standard qui était fourni dans la bibliothèque correspondante), comme quoi c'est un minimum "sécurisé" :D

Link to comment
Share on other sites

Sympa, au premier abord j'aurais pourtant parié plutôt pour des commandes câblées (il n'y a rien sur la connectique de l'autoradio qui permette de commander 2-3 trucs ?). Mais à vrai dire je ne me suis pas encore penché sur la question. Et je dis "encore" car mes parents ont une voiture avec commandes au volant, mais sans autoradio installée, et j'ai comme dans l'idée d'y ajouter un autoradio et de le câbler dessus...

Link to comment
Share on other sites

Et non, sur les autoradios que j'ai, les seules interfaces d'entrée que j'ai sont en IR, pas possible de connecter quoi que ce soit en filaire. Mais bon, l'un est un autoradio chinois dont la moitié des choses fonctionne (je le garde surtout pour le BT audio et l'écran qui se déploie avec la marche arrière), et l'autre est un chouette Pioneer (avec chargeur 6 CD :lunettes:) qui doit bien avoir 25 ans, une époque où les commandes au volant n'étaient pas encore totalement légion...

Link to comment
Share on other sites

ok, il va falloir que je fasse attention à ce type de détails lorsque je prendrai l'autoradio à mes parents...

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

By using this site, you agree to our terms Terms of Use of use and privacy policy Privacy Policy.