////////////////////////////////////////////////////////////////////
// Programme ARDUINO pour le pilotage d'aiguillages //
// équipés de servomoteurs //
// en DCC ou Analogique //
// //
// Paramètrage via le port Série //
// //
// UAICF Nevers-Vauzelles //
// http://www.modelisme58.fr //
// //
// Mai 2026 //
// Version 3.1 //
// //
// Configuration //
// pour commander 6 aiguillages //
// //
/////////////////////////////////////////////////////////////////////
//*** A modifier si besoin ****************************************************
#define DEBUG_Wokwi_Test
//#define DEBUG_ProgrammeAnalogique
//#define Serial_DEBUG_Serie
#define DEBUG_ProgrammeDCC
//*** Fin des modifications ***************************************************
// *** Librairies nécessaires ***
// *** Bibliothèque à installer : http://mrrwa.org ou http://sourceforge.net/projects/mrrwa/files/MRRwA-2011-12-31.zip/download
#include <NmraDcc.h> // DCC
#include <Servo.h> // Commande des Servomoteurs
#include <EEPROM.h> // Gestion de la mémoire
#include <SoftwareSerial.h> // Ajout d'un port série
#include <avr/wdt.h> // Pour le RESET
NmraDcc Dcc ; // DCC
DCC_MSG Packet ; // DCC
const char Date_Compilation[] = __DATE__;
// *** Plages des adresses en mémoire ***
#define PlageNbreMaxServos 0 // Nbre de servomoteurs
#define PlageServoTempo 1 // Vitesse de déplacement des servomoteurs
#define PlageCentrale 2 // Type de centrale DCC : 0 => Roco | 1 => Lenz
#define PlageAdresse 61 // Adresse DCC sur enregistrées sur 2 adresses EEPROM
#define PlageMin 90 // Position extrême mini du servomteur (valeur mini = 0)
#define PlageMax 100 // Position extrême maxi du servomteur (valeur maxi = 180)
#define PlageInvDCC 130 // True ou 1 => inversion des positions mini et maxi en DCC
#define PlageInvMinMax 140 // True ou 1 => inversion des positions mini et maxi pour logiciel
#define PlageTriple 110 // True ou 1 => aiguillage triple
#define PlagePosition 120 // True ou 1 => position du 2éme moteur si aiguillage triple
#define Tempo 1500 // Tempo entre 2 consignes DCC
// *** Définition des variables, constantes ***
byte NbreMaxServos; // Nombre de servomoteurs connectés à la carte ARDUINO
byte ServoTempo ; // Vitesse de déplacement du servomoteur - Plus la valeur est faible, plus la vitesse est rapide
boolean PremierMouvement = true;
int AddrOld = 0; // Decodeur DCC NMRA (valeur précédente)
int BoardAddrOld = 0; // Decodeur DCC NMRA (valeur précédente)
int OutputAddrOld = 0; // Decodeur DCC NMRA (valeur précédente)
int StateOld = 0; // Decodeur DCC NMRA (valeur précédente)
int ArduinoBorne[6]; // Tableau de définition des adresses DCC des accessoires pilotés
int AIGUILLAGE = 0; // Numéro de l'aiguillage commandé
boolean SENSAIGUILLAGE = 0; // Position de l'aiguillage commandé
boolean MouvementAig = 0; // Variable = 1 si un ou pluseurs aiguillages du décodeur en mouvement
int Compteur; // Compteur nombre de pas
int CompteurMax; // Maximum écart entre position mini et maxi des servomteurs
byte capteurAncienVal[6]; // Etat n-1 des boutons
byte Centrale; // Type de centrale DCC (problème de décalage des adresses DCC des accessoires)
boolean ModeManuel;
byte capteurVal[6];
unsigned long compteurtemps, compteurDCC;
struct DCCAccessoryAddress
{
int address; // Adresse DCC
boolean output; // Etat de l'accesoire : 1=on / 0=off
};
DCCAccessoryAddress accessory[6];
struct servo
{
byte angle;
byte setpoint;
byte minangle;
byte maxangle;
boolean invertedDCC;
boolean invertedMinMax;
boolean triple;
boolean positionTriple;
Servo servo;
byte functionnumber;
int sens;
boolean Actif;
};
servo servos[6];
#include "analogique.h"
#include "dcc.h"
#include "configuration.h"
#include "portserie.h"
#include "wokwi_test.h"
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
delay (1000);
Serial.begin (9600);
Serial.println(F("***"));
Serial.println(F("UAICF Nevers-Vauzelles - http://modelisme58.free.fr"));
Serial.println(Date_Compilation);
Serial.println(F("Decodeur de SERVOMOTEURS compatible DCC - V3.0"));
Serial.print(F("Commande de servomoteurs en Analogique, Port COM et "));
Centrale = EEPROM.read(PlageCentrale);
if (Centrale < 0 || Centrale > 1)
{
Centrale = 0;
EEPROM.update(PlageCentrale, Centrale);
}
switch (Centrale)
{
case 0:
Serial.println(F("Centrale DCC (Roco, NanoX,...)\n"));
break;
case 1:
Serial.println(F("Centrale DCC LENZ\n"));
break;
}
NbreMaxServos = EEPROM.read(PlageNbreMaxServos);
if (NbreMaxServos < 1 || NbreMaxServos > 6)
{
NbreMaxServos = 6;
EEPROM.update(PlageNbreMaxServos, NbreMaxServos);
}
ServoTempo = EEPROM.read(PlageServoTempo);
if (ServoTempo < 1 || ServoTempo > 254)
{
ServoTempo = 50;
EEPROM.update(PlageServoTempo, ServoTempo);
}
for (int n = 0; n < 6; n++)
{
byte m = 14 + n;
byte p = 3 + n;
pinMode(m, INPUT_PULLUP); // Bornes Arduino pour bouton à levier
pinMode(p, OUTPUT); // Bornes Arduino pour pilotage servomoteur
delay (10);
}
ConfigureDecodeurServos(); // Configuration des paramètres des caractéristiques des servomoteurs
#ifdef DEBUG_Wokwi_Test
Config_Test_Wokwi();
#endif
CalculEcart();
Initialisation();
Serial.print(F("Nbre Servos = "));
Serial.print(NbreMaxServos);
Serial.print(F("\t\tVitesse = "));
Serial.println(ServoTempo);
for (int n = 0; n < NbreMaxServos; n++)
{
Serial.print(n + 1);
Serial.print(F(" -> DCC = "));
Serial.print(accessory[n].address);
Serial.print(F(" | Min = "));
Serial.print(servos[n].minangle);
Serial.print(F(" | Max = "));
Serial.print(servos[n].maxangle);
Serial.print(F(" | InvDCC = "));
Serial.print(servos[n].invertedDCC);
Serial.print(F(" | InvMinMax = "));
Serial.print(servos[n].invertedMinMax);
Serial.print(F(" | Triple = "));
Serial.print(servos[n].triple);
Serial.print(F(" | Position = "));
Serial.println(servos[n].positionTriple);
}
Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE, 0 ); // Appel de la fonction initiale pour recevoir les adresses DCC
pinMode(2, INPUT_PULLUP); // Interruption 0 - Ne pas oublier de mettre une résistance de 10 k raccordée au +5 Volts)
#ifdef DEBUG_Wokwi_Test
Serial.println(F("\n$qXY pour simuler une commande DCC : X adresse DCC | Y position 0 ou 1"));
#endif
Serial.println(F("--- Fin Initialisation ---"));
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
static int addr = 0;
//*************************************************************************************
// Commande en mode Analogique
CommandeAnalogique();
//*************************************************************************************
// Commande en mode DCC
Dcc.process();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// A chaque plage de temps en millisecondes, modification d'un pas et déplacement du servomoteur d'un pas (si nécessaire)
unsigned long currentMillis = millis();
if (currentMillis > compteurtemps && ModeManuel == 1)
{
ModeManuel = 0;
// Serial.println(F("--> Debrayage des servomoteurs"));
for (int n = 0; n < NbreMaxServos; n++)
{
servos[n].servo.detach(); // Débrayage de tous les servomoteurs
}
}
if (currentMillis > compteurtemps && MouvementAig == 1)
{
compteurtemps = millis() + ServoTempo;
Compteur ++;
for (int n = 0; n < NbreMaxServos; n++)
{
/*
if (accessory[servos[n].functionnumber].output)
{
if (!servos[n].invertedDCC) servos[n].setpoint = servos[n].minangle;
else servos[n].setpoint = servos[n].maxangle;
} else {
if (!servos[n].invertedDCC) servos[n].setpoint = servos[n].maxangle;
else servos[n].setpoint = servos[n].minangle;
}
*/
if (servos[n].Actif == true)
{
if (servos[n].angle < servos[n].setpoint)
{
servos[n].angle++;
}
if (servos[n].angle > servos[n].setpoint)
{
servos[n].angle--;
}
if (servos[n].angle == servos[n].setpoint)
{
servos[n].servo.detach();
//Serial.println("servo.detach();");
servos[n].Actif = false;
}
servos[n].servo.write(servos[n].angle);
//Serial.println(servos[n].angle);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Débrayage de tous les servomoteurs si délai dépassé
if (Compteur > CompteurMax + 2 && MouvementAig == 1)
{
Compteur = 0;
MouvementAig = 0; // Aucun aiguillage en mouvement
// Serial.println(F("--> Debrayage des servomoteurs"));
for (int n = 0; n < NbreMaxServos; n++)
{
servos[n].servo.detach(); // Débrayage de tous les servomoteurs
servos[n].Actif = false;
}
}
} //Fin de la boucle LOOPCommande de 6 Servomoteurs pour des aiguillages
DCC et/ou Analogique
1
2
3
4
5
6
1
2
3
6
5
4
Signal DCC sur borne 2