Réalisation du logiciel avec l’IDE Arduino


Installation de l’IDE Arduino

Tous les détails de l’installation sont décrits sur le site Arduino : http://arduino.cc/en/Main/Software

L’installation est possible sous MacOS, Linux et Windows.

Pour plus de détails : http://arduino.cc/en/Guide/Environment

Et en français : http://www.mon-club-elec.fr/pmwiki_reference_arduino/pmwiki.php?n=Main.DebuterPresentationLogiciel



Pour ma part, j’ai installé également le logiciel Sublime Text : http://www.sublimetext.com

et son extension Arduino Stino : https://github.com/Robot-Will/Stino

Celle-ci apporte un confort supplémentaire par rapport à l’IDE Arduino seul.

Seule petite restriction : cela impose de placer les dossiers de librairies dans le dossier 



On trouvera ici le site français d’Arduino avec les références su langage C / C++ utilisé : http://www.mon-club-elec.fr/pmwiki_reference_arduino/pmwiki.php?n=Main.ReferenceEtendue

Ensuite il faut installer les librairies nécessaires, par la méthode décrite ici : http://arduino.cc/en/Guide/Libraries


Définition des librairies Arduino utilisées


// LIBRAIRIE CMDRARDUINO

#include <DCCPacket.h>              // librairie CmdrArduino

#include <DCCPacketQueue.h>         // idem

#include <DCCPacketScheduler.h>     // idem

Cette librairie prend en charge la génération du signal DCC sur la Pin 9 (elle se charge de l’initialisation de cette Pin).

On trouvera cette librairie ici : https://github.com/Railstars/CmdrArduino

Cette librairie est basée sur la gestion du Timer 1 (sous interruption) qui mobilise aussi la Pin 10. Celle-ci ne DOIT donc plus être utilisée.



// AUTRES LIBRAIRIES

#include <EEPROM.h>                 // librairie de gestion memoire EEPROM

Cette librairie permet de lire et écrire dans la mémoire EEPROM qui est non-volatile.

#include <Bounce.h>                 // librairie deboucing pour boutons et inters

Cette librairie permet de lire de façon fiable les actions sur les inters et boutons. En effet, ces contacts mécaniques ont l’inconvénient de « rebondir » et d’entrainer de fausses comandes que cette librairie permet d’éviter.

#include <avr/pgmspace.h>           // librairie de gestion memoire Flash

Cette librairie permet d’accéder à certaines variables internes

#include <SoftwareSerial.h>

Cette librairie est nécessaire pour la librairie serLCD

#include <serLCD.h>                 // librairie de gestion d'un LCD 16x2 via port Tx

Cette librairie permet la gestion d’un écran LCD (2 lignes de 16 caractères) avec les ordres habituels Serial.print et des commandes de positionnement simples.


Définition des Ports utilisés de l’Arduino


// Pins ARDUINO

#define Pot_AV             A0    // Analog IN : potar vitesse Avant

#define Pot_AR             A1    // Analog IN : potar vitesse Arriere

#define Current_Sense      A2    // Analog IN : courant Booster

#define Thermal_Sense      A3    // Analog IN : temperature Booster

#define Pin_AutoManu       A4    // IN  : commutateur de mode manuel (LOW) 

// ou automatique (HIGH)

#define Pin_SW_FL          A5    // IN  : commutateur d'eclairage (HIGH = ON, LOW = OFF)

                                 // les Pins 0 et 1 sont réservées pour Rx 

// et Tx (port série)

#define Pin_Tx             1     // Pour connexion au LCD via serLCD                                 

#define Pin_MAV            2     // OUT : led marche avant

#define Pin_MAR            3     // OUT : led marche arriere

#define Pin_Arret          4     // OUT : led arret

#define Pin_ZoneAV         5     // IN  : detecteur zone d'arret avant

#define Pin_ZoneAR         6     // IN  : detecteur zone d'arret arriere

#define Fin_De_Voie        7     // IN  : detecteur de fin de voie (les 2 combinées)

#define Pin_Mode           8     // IN  : poussoir de mode

#define Pin_Out_DIR        9     // OUT : Signal DIR pour LMD18200 

// pilote par CmdrArduino/Timer1

                                 // pin 10 reservee Timer 1 (ne peut être utilisée)

#define Pin_Out_PWM        11    // OUT : Signal PWM pour LMD18200

// HIGH en marche, LOW = stop

#define Pin_DCC            12    // IN  : commutateur marche/arret signal DCC 

#define Pin_Led_Booster    13    // OUT : led DCC OFF (continu) 

// ou surcharge booster (clignotant)


Définitions des variables


Pour fonctionner, le programme a besoin de manipuler des données dites « variables » qui sont stockées dans le SRAM. 

Les principales variables utilisées sont :


Pour la librairie CmdrArduino

// DCC handler

#define _stop 1                        // pour CmdrArduino 0->eStop et 1->regular stop

DCCPacketScheduler dps;                 // structure pour la librairie CmdrArduino

boolean DCC_EnService = false;          // autorise le Booster si true

boolean _erreurDPSS, _erreurDPSL = false; // pour une éventuelle détection d’erreur


Pour la configuration

typedef struct

{

    int dcc_adresse;                // @ DCC 128 crans. Attention, elle occupe 2 octets.

    byte vitesse_min;               // 2..10 a determiner en pilotage manuel

    byte increment_acceleration;    // 1..10

    byte duree_arret_gare1;         // 0..255 secondes

    byte duree_arret_gare2;         // 0..255 secondes

    byte distance_ligne;            // 1..255 centimetres

    byte distance_gare1;            // 1..255 centimetres

    byte distance_gare2;            // 1..255 centimetres

    byte pas_tempo_ligne;           // 1..255 pas de 1/2 seconde

    byte Nb_Vmin;                   // 1..20  nb de 1/2 secondes à Vmin avant Stop

} configuration;

configuration Config_Init;          // record de configuration copié en EEPROM


Pour le mouvement du train

unsigned int AV_value = 0;           // lecture du potentiometre vitesse avant

unsigned int AR_value = 0;           // lecture du potentiometre vitesse arriere

byte speed_AV, old_speed_AV = 0;     // vitesse AV (courante, précédente)

byte speed_AR, old_speed_AR = 0;     // vitesse AR

byte _speed, vitesse = 0;            // vitesse instantannee (selon direction)

boolean change_V = false;            // si true => change donc nouvelle commande DCC

byte DIR = 1;                        // marche avant: gare1>gare2 (1) 

    // ou arriere: gare2>gare1 (0)

byte Canton = 0;                     // 0=arret quai gare1, 1=depart acceleration, 

    // 2=ligne, 3=arrivee ralentissement, 

    // 4= vitesse min, 5=arret quai gare2

#define NBCantons 6                  // 6 cantons = 6 etats automate

#define NBPAS 10                     // nombre de pas de 1/2 sec par etat automate et 

    // par defaut si le detecteur n'a pas fonctionne

byte PasAV[NBCantons];               // pas en Avant : 1 valeur par canton 

byte PasAR[NBCantons];               // pas en Ariere : 1 valeur par canton 

byte TO;                             // time-out passage canton ligne calculé d'après 

    // le temps de passage entre capteurs

unsigned long TC, MTC, VKM, VCM = 0; // duree de passage entre capteurs, moyenne, 

    // vitesse (km/h) et vitesse (cm/s)

unsigned int MMA, DDS;               // Nb de millimetre restant a parcourir 

    // jusqu’a arret et decrement par 1/2 seconde


Boutons et Capteurs

Bounce B_Mode = Bounce(Pin_Mode, 100); // bouton MODE

byte prev_BM_state = 1;                // etat anterieur du poussoir Mode

byte BM = 0;                           // inactif (0) ou actif (1)

boolean Mode_Config = false;

byte Mode_EnCours = 0;                 // mode 0 : normal, affichage "DCC V DIR AV AR "

                                       // mode 1 : normal, affichage "V DIR Canton Pas"

                                       // mode 2 : normal, affichage vitesse reelle

                                       // mode 3 : config : @ dcc

                                       // mode 4 : config : V min

                                       // mode 5 : config : acceleration

                                       // mode 6 : config : t arret gare 1

                                       // mode 7 : config : t arret gare 2

                                       // mode 8 : config : l canton ligne

                                       // mode 9 : config : l canton gare 1

                                       // mode 10: config : l canton gare 2

                                       // mode 11: config : time-out n 1/2s

                                       // mode 12: config : nb 1/2s a Vmin

                                       

Bounce B_AutoManu = Bounce(Pin_AutoManu, 20); // clé AUTO/MANUEL

byte prev_AutoManu_state = 0;          // etat anterieur du commutateur auto-manuel

byte AutoManu = 0;                     // manuel (0) ou automatique (1)

Bounce B_SW_FL = Bounce(Pin_SW_FL, 20);// clé LUMIERE

byte prev_FL_state = 0;                // etat anterieur du bouton lumiere FL

byte FL = 0;                           // eclairage éteint (0) ou allumé (1)

Bounce B_DCC = Bounce(Pin_DCC, 20);    // clé ARRET/MARCHE DCC

byte prev_ON_DCC_state = 0;            // etat anterieur du bouton de signal DCC (pour etre OFF à l'init)

byte ON_DCC = 0;                       // DCC off (0) ou on (1)


Pour la tâche de clignotement des Leds

byte surchargeBooster = 0;     // 0 = OK, >0 = surcharge : clignote 1 éteint ou 3 allumé

byte Rouge_Clignotant = 0;     // 0 = normal, >0 (true) =  clignote 1 éteint ou 3 allumé

byte Jaune_Clignotant = 0;     // 0 = normal, >0 (true) =  clignote 1 éteint ou 3 allumé

byte Vert_Clignotant = 0;      // 0 = normal, >0 (true) =  clignote 1 éteint ou 3 allumé


Pour les Détecteurs de passage

boolean alarme_temperature = true;      // LOW si T>145°C au capteur, 

      //inversé à la mesure

int mesure_courant = 0; 

int detecteur_ZAV, prev_detecteur_ZAV, etat_detecteur_ZAV = 0;      

// detecteur de zones AV (gare 2)

int detecteur_ZAR, prev_detecteur_ZAR, etat_detecteur_ZAR = 0;      

// detecteur de zones AR (gare 1)

unsigned long IRdebounce_time;

#define IRdebtime  100                  // 10 milliseconde

byte IRdebounceAVH, IRdebounceAVL, IRdebounceARH, IRdebounceARL = 0;


Pour les taches périodiques

unsigned long time500, looptime;

int loopduration;                      // pour avoir idee de la duree de LOOP

int max_loopduration;


Pour l’écran LCD

serLCD lcd(Pin_Tx);

Cette ligne définit la classe « lcd » et initialise le port 1 


On définit ici les chaines de caractères qui seront affichées en 1ère ligne sur l’écran LCD

Ces chaines sont définies pour être stockées en mémoire Flash (avec le programme) et récupérées au coup par coup et une seule à la fois en SRAM, pour éviter de la saturer (plantage assuré).


prog_char string_0[]  PROGMEM = "DCC V DIR AV AR ";   // Mode_EnCours = 0

prog_char string_1[]  PROGMEM = "V DIR Canton Pas";   // Mode_EnCours = 1

prog_char string_2[]  PROGMEM = "Vit  Km/h  Cm/s ";   // Mode_EnCours = 2

prog_char string_3[]  PROGMEM = "Adresse DCC :   ";   // Mode_EnCours = 3  mode configuration à partir de 3

prog_char string_4[]  PROGMEM = "Vitesse min :   ";   // Mode_EnCours = 4

prog_char string_5[]  PROGMEM = "Acceleration :  ";   // Mode_EnCours = 5

prog_char string_6[]  PROGMEM = "T arret gare 1: ";   // Mode_EnCours = 6

prog_char string_7[]  PROGMEM = "T arret gare 2: ";   // Mode_EnCours = 7

prog_char string_8[]  PROGMEM = "L canton ligne: ";   // Mode_EnCours = 8

prog_char string_9[]  PROGMEM = "L canton gare 1:";   // Mode_EnCours = 9

prog_char string_10[] PROGMEM = "L canton gare 2:";   // Mode_EnCours = 10

prog_char string_11[] PROGMEM = "Time-out n 1/2s:";   // Mode_EnCours = 11

prog_char string_12[] PROGMEM = "Nb 1/2s Vmin :  ";   // Mode_EnCours = 12

PROGMEM const char *string_table[] =    // table des adresses

{   

  string_0,

  string_1,

  string_2,

  string_3,

  string_4,

  string_5,

  string_6,

  string_7,

  string_8,

  string_9,

  string_10,

  string_11,

  string_12

 };

char buffer[16];    // tampon de recopie en RAM (doit etre au moins aussi grand que le plus grand STRING


Initialisations (fonction setup)


void setup() {

  lcd.setBrightness(25);// rétro-éclairage du LCD 0..30

  lcd.clear();// effacement du LCD

  lcd.print("Initialisations ");// affichage en 1ère ligne

  lcd.selectLine(2);// positionnement en 2è ligne

  ///////////// initialisation des Pins

  //pinMode(Pot_AV, INPUT);  // entree analogique donc pas nécessaire

  //pinMode(Pot_AR, INPUT);  // entree analogique donc pas nécessaire

  //pinMode(Current_Sense, INPUT);  // entree analogique donc pas nécessaire

  pinMode(Thermal_Sense, INPUT_PULLUP);

  pinMode(Pin_AutoManu, INPUT_PULLUP);

  pinMode(Pin_SW_FL, INPUT_PULLUP);

  pinMode(Pin_Mode, INPUT_PULLUP);

  pinMode(Pin_DCC, INPUT_PULLUP);

  pinMode(Pin_Out_PWM, OUTPUT);

  //pinMode(Pin_Out_DIR, OUTPUT);       // initialisé par CmdrArduino

  digitalWrite(Pin_Out_PWM, LOW);       // DCC OFF

  pinMode(Pin_Led_Booster, OUTPUT);

  digitalWrite(Pin_Led_Booster, HIGH);  // Booster OFF (allume) ou CLIGNOTANT (surcharge)

  pinMode(Pin_MAV, OUTPUT);

  pinMode(Pin_MAR, OUTPUT);

  pinMode(Pin_Arret, OUTPUT);

  pinMode(Pin_ZoneAV, INPUT_PULLUP);

  pinMode(Pin_ZoneAR, INPUT_PULLUP);

  pinMode(Fin_De_Voie, INPUT_PULLUP);

  ////////////// initialisation de la librairie CmdrArduino

  dps.setup();                   // initialise les Pins 9 et 10

  prev_ON_DCC_state = B_DCC.read();   // force l'etat DCC off

// il faut une transition bas -> haut de Alim Rails

// pour autoriser l’alimentation des rails

   

  ////////////// Recuperation des valeurs de configuration en EEPROM 

  _Recup_EEPROM();// voir le détail de cette fonction plus loin

  

  lcd.print("DCC:");// affichage de l’adresse DCC en configuration

  lcd.print(Config_Init.dcc_adresse);

  Mode_EnCours = 0;// initialisation de la variable

  Mode_Config = false;// pas de mode configuration 

  

  ///////////// test de la configuration : si l’adresse DCC == 0 ou FF (EEPROM vierge):       

  ///////////// passage obligatoire en configuration

  if ((Config_Init.dcc_adresse == 0) || (Config_Init.dcc_adresse == 255))

  {

    Mode_EnCours = 3;

    Mode_Config = true;

    _Configuration();    // voir le détail de cette fonction plus loin

  }

  ///////////// iniialisation des compteurs de temps pour les taches periodiques

  time500 = millis();// pour déclencher la fonction SR_demiseconde()

  looptime = micros();// pour mesurer la durée moyenne de la LOOP()

  IRdebounce_time = millis();// pour l’anti-rebond des boutons

  lcd.print(" RAM:");

  lcd.print(_RamFree());

  delay(2000);

  Print_EEPROM();   // voir le détail de cette fonction plus loin

}// fin de la fonction setup()


La fonction setup() a fait appel aux 3 fonctions suivantes :

_Recup_EEPROM() à laquelle on ajoutera la fonction symétrique _Program_EEPROM(), puis Print_EEPROM()

et enfin _Configuration()


//////////////////////////////////////

void _Recup_EEPROM()

{

  int i;

  int s = sizeof(Config_Init);

  byte b[s];// tableau de lecture de l’EEPROM

  

  for (i = 0; i < s; i++)

  {

    b[i] = EEPROM.read(i);

    delay(10);

  }

// puis affectation des variables

  Config_Init.dcc_adresse = b[0]*256 + b[1]; 

// on assemble les 2 octets pour faire un ‘int'

  Config_Init.vitesse_min = b[2];

  Config_Init.increment_acceleration = b[3]; 

  Config_Init.duree_arret_gare1 = b[4];

  Config_Init.duree_arret_gare2 = b[5];

  Config_Init.distance_ligne = b[6];

  Config_Init.distance_gare1 = b[7];

  Config_Init.distance_gare2 = b[8];

  Config_Init.pas_tempo_ligne = b[9];

  Config_Init.Nb_Vmin = b[10];

}


//////////////////////////////////////

void _Program_EEPROM()

{

  int i;

  int s = sizeof(Config_Init);

  byte b[s];

  

  b[0] = highByte(Config_Init.dcc_adresse);  // séparation du int en 2 bytes

  b[1] = lowByte(Config_Init.dcc_adresse);

  b[2] = Config_Init.vitesse_min;

  b[3] = Config_Init.increment_acceleration;

  b[4] = Config_Init.duree_arret_gare1;

  b[5] = Config_Init.duree_arret_gare2;

  b[6] = Config_Init.distance_ligne;

  b[7] = Config_Init.distance_gare1;

  b[8] = Config_Init.distance_gare2;

  b[9] = Config_Init.pas_tempo_ligne;

  b[10] = Config_Init.Nb_Vmin;

 for (i = 0; i < s; i++)

  {

    EEPROM.write(i, b[i]);        //

    delay(10);                    // un petit délai pour garantir une bonne écriture

  }

}


///////////////////////////////////////

void Print_EEPROM()// cette fonction n’est pas très esthétique

{// mais elle permet de vérifier le bon fonctionnement

  lcd.clear();

  lcd.print("EEP ");                             //4

  lcd.print(Config_Init.dcc_adresse);            //+3=7

  lcd.print(" ");

  lcd.print(Config_Init.vitesse_min);            //+2=9

  lcd.print(" ");

  lcd.print(Config_Init.increment_acceleration); //+2=11

  lcd.print(" ");

  lcd.print(Config_Init.duree_arret_gare1);      //+2=13

  lcd.print(" ");

  lcd.print(Config_Init.duree_arret_gare2);      //+2=15

  lcd.selectLine(2);

  lcd.print(Config_Init.distance_ligne);         //3

  lcd.print(" ");

  lcd.print(Config_Init.distance_gare1);         //+3=6

  lcd.print(" ");

  lcd.print(Config_Init.distance_gare2);         //+3=9

  lcd.print(" ");

  lcd.print(Config_Init.pas_tempo_ligne);        //+3=12

  lcd.print(" ");

  lcd.print(Config_Init.Nb_Vmin);                //+3=15

  delay(2000);

}


La fonction _Configuration() est plus compliquée. Elle met en oeuvre un écran LCD par paramètre. 

Le texte de la question apparait sur la 1ere ligne du LCD

La valeur pré-existante est affichée sur la 2e ligne, suivie de « -> » 

Puis la valeur définie par l’opérateur à l’aide des 2 potentiomètres de vitesse !


Cette méthode permet de se passer d’un clavier 0..9. On utilise les potentiomètres car il est possible de lire leur position angulaire qui, après conversion analogique-digitale, donnera une valeur comprise entre 0 et 1023. Mais la précision des potentiomètres n’est pas bonne et il est nécessaire de « démultiplier » la valeur lue pour la ramener dans l’échelle 0..127.

Pour ce faire, on ajoute les lectures des 2 potentiomètres (résultat compris entre 0 et 2048) puis on divise le résultat par 16 : on obtient une plage comprise entre 0 et 127

Ce résultat est affiché à droite de « -> ».


La fonction tourne en boucle en affichant cette valeur tant qu’on n’appuie pas sur le bouton MODE.

Pour enregistrer une nouvelle valeur (affichée à droite de « -> »), il faut lever la clé LUMIERE puis appuyer sur MODE, puis abaisser la clé LUMIERE.

Dans ce cas, la valeur est écrite en EEPROM (« OK » apparait pendant 1 seconde) puis la configuration passe au paramètre suivant.

Sinon, rien n’est changé et le passage au paramètre suivant est réalisé. On peut ainsi consulter la configuration en laissant la clé LUMIERE en bas et en appuyant successivement sur MODE.


La fonction _Configuration() fait appel aux fonctions _questionB() et _questionC().

_questionB() traite uniquement le cas de l’adresse DCC qui occupe 2 octets de configuration en EEPROM

_questionC() traite les autres paramètres qui occupent seulement 1 octet.


void _Configuration()

{

  // version LCD (remplace la version Console via USB)

  // entrée : mode manuel, LUMIERE=0 - si OK suite de questions

  // sortie avec suite d’appuis sur bouton mode

  // Mode_EnCours = 3 à 13 (index des questions)

  

  // arret du train, stop DCC booster, vitesse AV/AR=0, pots à 0

  vitesse = _stop;

  dps.setSpeed128(Config_Init.dcc_adresse,DCC_SHORT_ADDRESS,vitesse);

  ON_DCC = 0;

  digitalWrite(Pin_Out_PWM, ON_DCC);      // 0 = arret

  digitalWrite(Pin_Led_Booster, !ON_DCC); // 1 = led allumee 

  lcd.clear();

  

  // recopie de la chaine en Flash vers notre buffer

  // Cast et dereferencement nécessaire

  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[Mode_EnCours]))); 

  lcd.print(buffer);                    // affichage 1ere ligne

  lcd.setCursor(2, 1);// adresse DCC

  _questionB();// attente d’appui sur MODE

  Mode_EnCours++;// question suivante

  lcd.clear();

  

  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[Mode_EnCours])));

  lcd.print(buffer);                    // affichage 1ere ligne

  lcd.setCursor(2, 1);// vitesse minimale

  Config_Init.vitesse_min = _questionC(Config_Init.vitesse_min, 2);   

  Mode_EnCours++;

  lcd.clear();

  

  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[Mode_EnCours])));

  lcd.print(buffer);                    // affichage 1ere ligne

  lcd.setCursor(2, 1);// incréments d’accélération

  Config_Init.increment_acceleration = _questionC(Config_Init.increment_acceleration, 3);   

  Mode_EnCours++;

  lcd.clear();

  

  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[Mode_EnCours]))); 

  lcd.print(buffer);                    // affichage 1ere ligne

  lcd.setCursor(2, 1); // 1/2 durée arrêt en gare 1

  Config_Init.duree_arret_gare1 = _questionC(Config_Init.duree_arret_gare1, 4);   

  Mode_EnCours++;

  lcd.clear();

  

  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[Mode_EnCours]))); 

  lcd.print(buffer);                    // affichage 1ere ligne

  lcd.setCursor(2, 1);// 1/2 durée arrêt en gare 2

  Config_Init.duree_arret_gare2 = _questionC(Config_Init.duree_arret_gare2, 5);   

  Mode_EnCours++;

  lcd.clear();

  

  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[Mode_EnCours]))); 

  lcd.print(buffer);                    // affichage 1ere ligne

  lcd.setCursor(2, 1);// Distance zone Ligne en cm

  Config_Init.distance_ligne = _questionC(Config_Init.distance_ligne, 6);   

  Mode_EnCours++;

  lcd.clear();

  

  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[Mode_EnCours]))); 

  lcd.print(buffer);                    // affichage 1ere ligne

  lcd.setCursor(2, 1);// Distance zone Gare 1 en cm

  Config_Init.distance_gare1 = _questionC(Config_Init.distance_gare1, 7);   

  Mode_EnCours++;

  lcd.clear();

  

  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[Mode_EnCours]))); 

  lcd.print(buffer);                    // affichage 1ere ligne

  lcd.setCursor(2, 1);// Distance zone Gare 2 en cm

  Config_Init.distance_gare2 = _questionC(Config_Init.distance_gare2, 8);   

  Mode_EnCours++;

  lcd.clear();

  

  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[Mode_EnCours]))); 

  lcd.print(buffer);                    // affichage 1ere ligne

  lcd.setCursor(2, 1);// Nombre de 1/2 secondes maximum sur zone Ligne

  Config_Init.pas_tempo_ligne = _questionC(Config_Init.pas_tempo_ligne, 9);   

  Mode_EnCours++;

  lcd.clear();

  

  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[Mode_EnCours]))); 

  lcd.print(buffer);                    // affichage 1ere ligne

  lcd.setCursor(2, 1);// Nombre de 1/2 secondes à vitesse Vmin

  Config_Init.Nb_Vmin = _questionC(Config_Init.Nb_Vmin, 10);   

  Mode_EnCours = 0;

  Mode_Config = false;

  lcd.clear();

  

  lcd.print("Fin Config ");

  lcd.print(_RamFree());

  lcd.setCursor(2, 1); 

  lcd.print(max_loopduration);

  max_loopduration = 0;

  delay(2000);

}


_questionB() sert à renseigner uniquement la valeur de l’adresse DCC de la loco en service et de l’enregistrer en EEPROM. Elle n’a pas de paramètre en entrée.

La clé LUMIERE doit être positionnée en bas (lumière éteinte).

Après affichage de la valeur initiale une boucle « while » tourne tant que le booléen RXIT est faux.

Le booléen ROK passe à vrai si on lève la clé LUMIERE.

RXIT passe à vrai si on appuie sur le bouton MODE : on sort de la boucle sans modification si ROK est faux ou en modifiant le paramètre si ROK est vrai.


////////////////////////////

void _questionB()                             // adresse DCC, Mode_EnCours = 3

{

  boolean ROK = false;

  boolean RXIT = false; 

  byte Valeur = 0;

  

  lcd.print(Config_Init.dcc_adresse);       // ancienne valeur sur 2e ligne, max 3 cars

  lcd.print(" ->");                         // choix à afficher en ligne 2, colonne 8

  while (!RXIT) 

  {

                                 // on se sert de la somme des potentiometres

    Valeur = (analogRead(Pot_AV) + analogRead(Pot_AR)) >> 4; 

//division par 16 pour ramener le champ 0-2047 à 0-127

    lcd.setCursor(2, 6);

    lcd.print(Valeur);

    lcd.print("  ");             // pour effacer les caracteres suivants eventuels

    if (B_Mode.update())         // Poussoir Mode change-t-il ?

    {

      if (B_Mode.risingEdge()) 

      {

        RXIT = true;             // sortie de la boucle while

     }

    }

    if (B_SW_FL.update())        // COMMUTATEUR DE LUMIERE

    {

      if (B_SW_FL.read() == HIGH)

      {        

        Config_Init.dcc_adresse = Valeur; // validation 

        ROK = true;

      } 

    }

  }        // while !ROK

  

  if (ROK) 

  {

// enregistrement en EEPROM

    EEPROM.write(0, highByte(Config_Init.dcc_adresse)); 

    delay(10);

    EEPROM.write(1, lowByte(Config_Init.dcc_adresse));

    lcd.print(" Ok!");

    delay(1000);

  }

}


_questionC() sert à renseigner les autres paramètres et les enregistrer en EEPROM. Elle a 2 paramètres en entrée : l’index du paramètre et sa valeur initiale.

La clé LUMIERE doit être positionnée en bas (lumière éteinte).

Après affichage de la valeur initiale une boucle « while » tourne tant que le booléen RXIT est faux.

Le booléen ROK passe à vrai si on lève la clé LUMIERE.

RXIT passe à vrai si on appuie sur le bouton MODE : on sort de la boucle sans modification si ROK est faux ou en modifiant le paramètre si ROK est vrai.


/////////////////////////////////////

int _questionC(int _VAL, int _index)        // Mode_EnCours > 3

{

  boolean ROK = false;

  boolean RXIT = false;

  byte Valeur = 0;

  

  lcd.print(_VAL);                // ancienne valeur sur 2e ligne, max 3 cars

  lcd.print(" ->");               // choix à afficher en ligne 2, colonne 8

  while (!RXIT) 

  {

                                  // on se sert de la somme des potentiometres

    Valeur = (analogRead(Pot_AV) + analogRead(Pot_AR)) >> 4;

//division par 16 pour ramener le champ 0-2047 à 0-127

    lcd.setCursor(2, 6);

    lcd.print(Valeur);

    if (B_Mode.update())          // Poussoir Mode change-t-il ?

    {

      if (B_Mode.risingEdge()) 

      {

        RXIT = true;              // sortie de la boucle while

     }

    }

    if (B_SW_FL.update())         // COMMUTATEUR DE LUMIERE

    {

      if (B_SW_FL.read() == HIGH)

      {

        _VAL = Valeur;// validation

        ROK = true;

      } 

    }

  }        // while !ROK

  if (ROK) 

  {

// enregistrement en EEPROM

    EEPROM.write(_index, _VAL);

    lcd.print(" Ok!");

    delay(1000);

  }

  return(_VAL);// retour avec le paramètre modifié ou non

}


Voici des exemples d’écrans obtenus : 



En fin de configuration, le dernier écran affiche la quantité de mémoire SRAM restant et la plus grande durée de la boucle principale LOOP (en micro-secondes) depuis le dernier affichage.