Après quelques tests et quelques corrections, le code est maintenant fonctionnel.
A me répéter, le moniteur Série est d'une très grande utilité pour corriger et valider.
J'ai utilisé une carte Arduino MEGA afin de ne pas être limité par le nombre d'E/S.

Il me reste à traiter les points suivants (sur l'aspect fonctionnel):
-Copier le code "en symétrie" pour gérer le test en Block Y
-Gérer le voyant blanc associé à chaque voyant de couleur
-Gérer la cloche et le bouton sonnerie associé
-Documenter encore plus le code
-Optimiser le code
A suivre...
Fabrice
Code : Tout sélectionner
// Name
#define NAME "BMVU"
#define NAME2 "Block Manuel de Voie Unique type SNCF"
#define NAME3 "IN1547 (Règlement S5C) - Articles 101.4 et 102"
// Version & Copyright
#define VERSION "Version 1.4 (Arduino Mega)"
#define COPYRIGHT "Copyright Fabrice Fayolle, Août 2019"
boolean Affichage1 = true;
boolean Affichage2 = true;
const int Levier_SemaphoreX_Normal = 4;
const int Verrou_Levier_SemaphoreX = 5;
const int PedaleX_Non_Active = 6;
const int Levier_SemaphoreY_Normal = 7;
const int Verrou_Levier_SemaphoreY = 8;
const int PedaleY_Non_Active = 9;
class Appareil
{
private:
int BN_BLOCAGE;
int BN_ANNONCE;
boolean VT_ANNONCE_ETAT;
int VT_ANNONCE;
int BN_REDDITION;
boolean VT_REDDITION_ETAT;
int VT_REDDITION;
int BN_TEST;
boolean VT_TEST_ETAT;
int VT_TEST;
int BN_SONNERIE;
int CLOCHE;
public:
boolean SEMAPHORE_OUVERT;
void Setup(int indice);
boolean Blocage();
boolean Test();
boolean Voielibre();
void Vt_Test();
boolean Annonce();
boolean Reddition();
void Vt_Annonce();
void Vt_Reddition();
void Cloche_Block(int etat);
}
;
void Appareil::Setup(int indice)
{
BN_BLOCAGE = 2 + (indice * 20);
pinMode(BN_BLOCAGE, INPUT_PULLUP);
BN_ANNONCE = 3 + (indice * 20);
pinMode(BN_ANNONCE, INPUT_PULLUP);
VT_ANNONCE_ETAT = LOW;
VT_ANNONCE = 4 + (indice * 20);
pinMode(VT_ANNONCE, OUTPUT);
digitalWrite(VT_ANNONCE, LOW);
BN_REDDITION = 5 + (indice * 20);
pinMode(BN_REDDITION, INPUT_PULLUP);
VT_REDDITION_ETAT = LOW;
VT_REDDITION = 6 + (indice * 20);
pinMode(VT_REDDITION, OUTPUT);
digitalWrite(VT_REDDITION, LOW);
BN_TEST = 7 + (indice * 20);
pinMode(BN_TEST, INPUT_PULLUP);
VT_TEST_ETAT = LOW;
VT_TEST = 8 + (indice * 20);
pinMode(VT_TEST, OUTPUT);
digitalWrite(VT_TEST, LOW);
BN_SONNERIE = 9 + (indice * 20);
pinMode(BN_SONNERIE, INPUT_PULLUP);
SEMAPHORE_OUVERT = LOW;
CLOCHE = 10 + (indice * 20);
pinMode(CLOCHE, OUTPUT);
digitalWrite(CLOCHE, LOW);
}
boolean Appareil::Blocage()
{
boolean resultat = false;
resultat = !digitalRead(BN_BLOCAGE);
return resultat;
}
boolean Appareil::Test()
{
boolean resultat = false;
if (!digitalRead(BN_TEST))
{
delay(100);
if (!digitalRead(BN_TEST))
{
Serial.println("Appui du bouton TEST");
while (!digitalRead(BN_TEST))
{
}
resultat = true;
}
}
return resultat;
}
boolean Appareil::Voielibre()
{
boolean resultat = false;
if (!VT_TEST_ETAT && !VT_ANNONCE_ETAT && !VT_REDDITION_ETAT && !SEMAPHORE_OUVERT)
{
resultat = true;
}
return resultat;
}
void Appareil::Vt_Test()
{
VT_TEST_ETAT = !VT_TEST_ETAT;
digitalWrite(VT_TEST, VT_TEST_ETAT);
}
boolean Appareil::Annonce()
{
boolean resultat = false;
if (!digitalRead(BN_ANNONCE))
{
delay(100);
if (!digitalRead(BN_ANNONCE))
{
Serial.println("Appui du bouton ANNONCE");
while (!digitalRead(BN_ANNONCE))
{
}
resultat = true;
}
}
return resultat;
}
void Appareil::Vt_Annonce()
{
VT_ANNONCE_ETAT = !VT_ANNONCE_ETAT;
digitalWrite(VT_ANNONCE, VT_ANNONCE_ETAT);
}
boolean Appareil::Reddition()
{
boolean resultat = false;
if (!digitalRead(BN_REDDITION))
{
delay(100);
if (!digitalRead(BN_REDDITION))
{
Serial.println("Appui du bouton REDDITION");
while (!digitalRead(BN_REDDITION))
{
}
resultat = true;
}
}
return resultat;
}
void Appareil::Vt_Reddition()
{
VT_REDDITION_ETAT = !VT_REDDITION_ETAT;
digitalWrite(VT_REDDITION, VT_REDDITION_ETAT);
}
void Cloche_Block(int etat)
{
switch (etat)
{
case 0:
//Arrêt de la sonnerie
break;
case 1:
//Sonnerie (1 lecture)
break;
case 2:
//Sonnerie (x lectures)
break;
default:
break;
};
}
Appareil BlockX;
Appareil BlockY;
void setup()
{
Serial.begin(9600);
Serial.println(NAME);
Serial.println(NAME2);
Serial.println(NAME3);
Serial.println("---------------------------------------------------------------------------");
Serial.print(VERSION); Serial.print(", "); Serial.println(COPYRIGHT);
Serial.println("---------------------------------------------------------------------------");
Serial.println("");
pinMode(Levier_SemaphoreX_Normal, INPUT_PULLUP);
pinMode(Verrou_Levier_SemaphoreX, OUTPUT);
digitalWrite(Verrou_Levier_SemaphoreX, HIGH);
pinMode(PedaleX_Non_Active, INPUT_PULLUP);
pinMode(Levier_SemaphoreY_Normal, INPUT_PULLUP);
pinMode(Verrou_Levier_SemaphoreY, OUTPUT);
digitalWrite(Verrou_Levier_SemaphoreY, HIGH);
pinMode(PedaleY_Non_Active, INPUT_PULLUP);
BlockX.Setup(1);
BlockY.Setup(2);
Serial.println("Block Manuel de Voie Unique initialisé");
}
void loop()
{
unsigned long Temps;
//Tant qu'un commutateur Blocage est fermé sur un des deux appareils de block, aucun convoi ne peut-être expédié ou réceptionné
//Mise en protection par au moins une des deux extrémités du block (travaux, dérangement, ...)
//Rq: Les manoeuvres effectuées en gare sont protégés par la fermeture du Disque obligeant le convoi entrant à se mettre en marche à vue
//au plus tôt et à s'arrêter à la 1ère aiguille rencontrée
Affichage1 = true;
while (BlockX.Blocage() || BlockY.Blocage())
{
if (Affichage1)
{
Serial.println("Commutateur BLOCAGE d'un des deux appareils de block fermé");
Affichage1 = false;
Affichage2 = true;
}
delay(250);
}
if (Affichage2)
{
Serial.println("Attente d'un test");
Affichage2 = false;
}
// Si l'Agent de circulation appuie sur le bouton TEST du block X alors
if (BlockX.Test())
{
// Si rien ne s'y oppose (Voie libre de X vers Y et Voie libre de Y vers X) alors
if (BlockX.Voielibre() && BlockY.Voielibre())
{
Serial.println("Voie libre");
Temps = millis();
//Voyant TEST du block X passe au Vert
BlockX.Vt_Test();
//Possibilité d'ouvrir le sémaphore X durant 5 secondes (5000 millisecondes)
//Verrou du levier de manoeuvre du sémaphore X ouvert
digitalWrite(Verrou_Levier_SemaphoreX, LOW);
Serial.println("Verrou du levier de manoeuvre du sémaphore ouvert");
while (((millis() - Temps) < 5000) && BlockX.SEMAPHORE_OUVERT == LOW )
{
BlockX.SEMAPHORE_OUVERT = !digitalRead(Levier_SemaphoreX_Normal);
delay(100);
}
//Voyant Test du block X passe au blanc (ou éteint)
BlockX.Vt_Test();
if (BlockX.SEMAPHORE_OUVERT)
{
Serial.println("Manoeuvre du levier du sémaphore");
}
//Verrou du levier de manoeuvre du sémaphore X ouvert
digitalWrite(Verrou_Levier_SemaphoreX, HIGH);
Serial.println("Verrou du levier de manoeuvre du sémaphore fermé");
//Si le sémaphore X a été ouvert durant le laps de temps défini alors
if (BlockX.SEMAPHORE_OUVERT)
{
Serial.println("Sémaphore ouvert");
// Attente de la l'activation de la pédale de voie (canton occupé)
// ou de la fermeture du sémaphore X (levier remis en position Normal)
Affichage1 = true;
while ((digitalRead(PedaleX_Non_Active) == HIGH) && (digitalRead(Levier_SemaphoreX_Normal) == LOW))
{
if (Affichage1)
{
Serial.println("Attente de l'activation de la pédale de voie ou de la fermeture manuelle du sémaphore");
Affichage1 = false;
}
delay(100);
}
// Attente de la confirmation de la fermeture du sémaphore (levier remis en position Normal par l'Agent de circulation)
if (digitalRead(PedaleX_Non_Active) == LOW)
{
Serial.println("Pédale de voie activée");
Serial.println("Sémaphore fermé (aubinage)");
Affichage1 = true;
while (digitalRead(Levier_SemaphoreX_Normal) == LOW)
{
if (Affichage1)
{
Serial.println("Attente de la confirmation de la fermeture du sémaphore");
Affichage1 = false;
}
}
Serial.println("Manoeuvre du levier du sémaphore");
// Attente de l'annonce de X à Y par l'Agent de circulation (appui sur le bouton ANNONCE du block X)
Affichage1 = true;
while (!BlockX.Annonce())
{
if (Affichage1)
{
Serial.println("Attente de l'annonce");
Affichage1 = false;
}
}
Serial.println("Convoi annoncé");
//Voyant REDDITION du block X passe au rouge
BlockX.Vt_Reddition();
//Voyant ANNONCE du block Y passe au bleu
BlockY.Vt_Annonce();
// Activation de la sonnerie du block Y tant que l'agent de circulation en Y n'appuie pas sur le bouton SONNERIE
// Attente de la reddition de Y à X par l'Agent de circulation (appui sur le bouton REDDITION du block Y)
Affichage1 = true;
while (!BlockY.Reddition())
{
if (Affichage1)
{
Serial.println("Attente de la reddition");
Affichage1 = false;
}
}
Serial.println("Convoi réceptionné");
//Voyant ANNONCE du block Y passe au blanc (ou s'éteint)
BlockY.Vt_Annonce();
//Voyant REDDITION du block X passe au blanc (ou s'éteint)
BlockX.Vt_Reddition();
// Activation de la sonnerie du block X durant un temps défini
}
else
{
Serial.println("Manoeuvre du levier du sémaphore");
Serial.println("Sémaphore fermé");
BlockX.SEMAPHORE_OUVERT=LOW;
}
}
Affichage2 = true;
}
}
if (BlockY.Test())
{
// Symétrie de "if (BlockX.Test())
}
}