/*
***********************************************
* Décodeur DCC pour 4 aiguillages à bobines *
* version 20240721 *
* Les BP sont montés en PULL_UP *
* et gérés par Bounce2 *
* sauf ceux connectés à A6 et A7 *
* qui sont des sorties uniquement analogiques *
* *
* Arduino Nano *
***********************************************
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! Ce code est distribué gratuitement sous licence !
! Creativ Commons CC-BY-NC-ND 3.0 !
! !
! Cette licence permet à d'autres utilisateurs d'utiliser ce code !
! à des fins non commerciales, dans la mesure où le nom de l'auteur est !
! mentionné. Merci de m'informer de toute modification du code ce qui !
! me permettra de continuer à apprendre. !
! !
! auteur Philippe GUENET - [email protected] - https://wgnt-train.fr !
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Les solénoides sont simulées par des leds, mais ils devront être alimentés au travers de ULN2803A
*/
#include <Wire.h>
#include <EEPROM.h> // bibliothèque pour gérer la mise en mémoire
#include <Bounce2.h>
String Version = "4 bobines -20240818";
const int numCarte = 1; // ! NUMERO DE LA CARTE A MODIFIER SI EMPLOI DE PLUSIEURS CARTES !
const int Nb_Aiguillages = 4;
const int pinPoussoirCommande[Nb_Aiguillages] = {A0, A1, A2, A3};
const int pinDirectAiguillages[Nb_Aiguillages] = {3, 5, 7, 9};
const int pinDevieAiguillages[Nb_Aiguillages] = {4, 6, 8, 13};
const int pinPoussoirPlus = A4;
const int pinPoussoirMoins = A5;
const uint32_t dureeImpulsion = 180ul;
const int tempoValidation = 3000ul;
int CompteurLoop = 0;
enum EtatDirection{ DIRECT = 1, DEVIE = 2 };
struct aiguillage {
int adresseDCC;
int pinDirect;
int pinDevie;
EtatDirection DernierePosition;
};
aiguillage Aiguillage[Nb_Aiguillages];
int adresseDCCDepart = 1 + (Nb_Aiguillages * (numCarte - 1)); // de 1 à 15 pour la carte 1, de 16 à 31 pour la carte 2
/* Pour les poussoirs gérés par Bounce */
const uint32_t dureeAntiRebond = 30;
Bounce BPclavier[Nb_Aiguillages] = Bounce();
Bounce BP_Plus = Bounce();
Bounce BP_Moins = Bounce();
/* =================================== */
/* GESTION DE 4 74HC595 en série par Arduino */
const byte data_Pin = 10; // Pin de donnée (DS) 14
const byte verrou_Pin = 11; // Pin de verrouillage (ST_CP)
const byte horloge_Pin = 12; // Pin de l'horloge (SH_CP)
// Pin OE 13 => GND
// Pin MR 10 => VCC
const byte Nombre_74hc595 = 1; // Nombre de 74HC595 en série
const byte numOfRegisterPins = Nombre_74hc595 * 8;
boolean Registres[numOfRegisterPins];
/* ========================================= */
void setup() {
Serial.begin(38400);
Serial.print(F("Gestion 4 aiguillages a bobines : ")); Serial.println(Version);
Serial.print(F("Initialisation ... "));Serial.print(F(" Carte : ")); Serial.print(numCarte);
Serial.print(F(" / Adresse depart : ")); Serial.println(adresseDCCDepart);
// Initialiser les pins comme sorties
pinMode(data_Pin, OUTPUT);
pinMode(verrou_Pin, OUTPUT);
pinMode(horloge_Pin, OUTPUT);
// Reset tous les pins du 74HC595
razRegistres();
/* ========================== */
for (int i = 0; i < Nb_Aiguillages; i++) {
/* Attribution de l'adresse DCC pour chaque aiguillage */
Aiguillage[i].adresseDCC = adresseDCCDepart + i;
/* =================================================== */
/* Pour les boutons poussoirs */
BPclavier[i].attach(pinPoussoirCommande[i], INPUT_PULLUP);
BPclavier[i].interval(dureeAntiRebond);
/* ========================== */
/* Configuration des pins des aiguillages */
Aiguillage[i].pinDirect = pinDirectAiguillages[i];
Aiguillage[i].pinDevie = pinDevieAiguillages[i];
pinMode(Aiguillage[i].pinDirect, OUTPUT);
pinMode(Aiguillage[i].pinDevie, OUTPUT);
/* ====================================== */
// Vérification la validité de l'état initial de l'aiguillage
byte valeurStockee = EEPROM.read(i * 10);
if (valeurStockee == static_cast<byte>(DIRECT) || valeurStockee == static_cast<byte>(DEVIE)) {
/* si la valeur est valide on la passe dans la variable DernierePosition */
Aiguillage[i].DernierePosition = static_cast<EtatDirection>(valeurStockee);
/* ===================================================================== */
} else {
/* sinon on force l'état à DIRECT et on l'enregistre */
Aiguillage[i].DernierePosition = DIRECT;
EEPROM.update((i * 10), static_cast<byte>(Aiguillage[i].DernierePosition));
/* ================================================= */
} // Fin de if (valeurStockee == static_cast<byte>(DIRECT) || valeurStockee == static_cast<byte>(DEVIE))
Serial.print(F("Adresse DCC aiguillage ")); Serial.print(i); Serial.print(F(" = "));Serial.print(Aiguillage[i].adresseDCC);
Serial.print(F(" / Position :")); Serial.println(Aiguillage[i].DernierePosition == DIRECT ? "DIRECT" : "DEVIE");
mettreAJourAiguillage(i);
} // Fin de for (int i = 0; i < Nb_Aiguillages; i++)
} // Fin de setup()
void loop() {
CompteurLoop ++; if (CompteurLoop == Nb_Aiguillages) {CompteurLoop = 0;}
/* par précaution on vérifie q'un des pins de sortie ne soit pas resté à HIGH */
if (digitalRead(Aiguillage[CompteurLoop].pinDirect == HIGH)) {digitalWrite(Aiguillage[CompteurLoop].pinDirect, LOW);}
if (digitalRead(Aiguillage[CompteurLoop].pinDevie == HIGH)) {digitalWrite(Aiguillage[CompteurLoop].pinDevie, LOW);}
/* ========================================================================== */
BPclavier[CompteurLoop].update();
/* Si un bouton a été appuyé puis relaché */
if (BPclavier[CompteurLoop].changed() && BPclavier[CompteurLoop].rose()){
/* Basculer de l'état de l'aiguillage */
Aiguillage[CompteurLoop].DernierePosition = (Aiguillage[CompteurLoop].DernierePosition == DIRECT) ? DEVIE : DIRECT;
/* Mettre à jour physiquement l'aiguillage et gérer les leds*/
Serial.print(F("Aiguillage : "));Serial.print(CompteurLoop); Serial.print(F(" => "));
Serial.println(Aiguillage[CompteurLoop].DernierePosition == DIRECT ? "DIRECT" : "DEVIE");
mettreAJourAiguillage(CompteurLoop);
} // Fin de if (BPclavier[CompteurLoop].changed() && BPclavier[CompteurLoop].rose())
/* ====================================== */
} // Fin de loop()
void razRegistres() {
// Place tous les pins du 74HC595 à l'état "OFF"
for (int i = 0; i < numOfRegisterPins; i++) {
Registres[i] = LOW;
}
// Met à jour les registres avec les valeurs initialisées
majRegistres();
} // Fin de void razRegistres()
void ecritRegistres(int index, int value) {
// Enregistrer la valeur dans le registre
Registres[index] = value;
// Met à jour les registres avec la nouvelle valeur
majRegistres();
} // Fin de ecritRegistres(int index, int value)
void majRegistres() {
// Mettre à jour et afficher les valeurs dans le registre
digitalWrite(verrou_Pin, LOW);
for (int i = numOfRegisterPins - 1; i >= 0; i--) {
digitalWrite(horloge_Pin, LOW);
digitalWrite(data_Pin, Registres[i]);
digitalWrite(horloge_Pin, HIGH);
}
digitalWrite(verrou_Pin, HIGH);
} // Fin de majRegistres()
void mettreAJourAiguillage(int indiceAiguillage) {
int etatAiguillage = (Aiguillage[indiceAiguillage].DernierePosition == DIRECT) ? HIGH : LOW;
digitalWrite(Aiguillage[indiceAiguillage].pinDirect, etatAiguillage);
digitalWrite(Aiguillage[indiceAiguillage].pinDevie, !etatAiguillage);
/* allumage des leds correspondantes */
ecritRegistres((indiceAiguillage * 2), etatAiguillage);
ecritRegistres((indiceAiguillage * 2) + 1, !etatAiguillage);
/* ================================= */
delay(dureeImpulsion); // emploi volontaire de delay() bloquant pour qu'aucun autre appel ne puisse se faire pendant l'impulsion
/* remise des deux pins à LOW après l'impulsion */
digitalWrite(Aiguillage[indiceAiguillage].pinDirect, LOW);
digitalWrite(Aiguillage[indiceAiguillage].pinDevie, LOW);
/* ============================================ */
/* sauvegarde du nouvel état dans l'EEPROM */
EEPROM.update((indiceAiguillage * 10), static_cast<byte>(Aiguillage[indiceAiguillage].DernierePosition));
/* ======================================== */
} // Fin de procédure mettreAJourAiguillage()