Construction du contrôleur d’aiguillesLe contrôleur d’aiguilles est construit sur une base Arduino Mega 2560 associé à une carte bus CAN et un ensemble de 40 relais qui commandent les 18 aiguilles (2 relais par aiguille) et 2 dételeurs (1 relai par dételeur).
L’Arduino est disposé coté tableau de bord tandis que les relais, disposés de chaque coté d’une planche de CP monté sur charnière, sont placés à l’intérieur, sous le circuit. Des câbles en nappes relient l’Arduino aux relais, comme expliqué pour le TCO, avec une carte prototype permettant la soudure des nappes pour un maximum de fiabilité.
Comme les aiguillages choisis sont des modèles Fleischmann à moteur à courant alternatif, j'ai utilisé 2 relais par aiguille, avec le schéma suivant :
Le relai Impulsion est activé 150 millisecondes après que le relai Sens est positionné selon le sens direct/droit ou dévié.
Les relais en couples sont affectés aux pins de l’Arduino (tout est noté dans un tableau):
Relais d'aiguilles 4,5(A),6,7(B),8,9(C),10,11(D) - 22,23(G),24,25(H),26,27(I),28,29(J) - 30,31(K),32,33(L),34,35(M),36,37(N) - 38,39(O),40,41(P),42,43(Q),44,45(R) - 12,13(E),14,15(F),46,47(S),48(T1),49(T2).
La carte CAN est raccordée par les pins : 50 (SO), 51 (SI), 52 (SCK), 53 (CS), 2 (INT).
Les pins A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15 restent libres pour le moment. Elles sont réservées pour des relais supplémentaires (ça va arriver, il y en a 4 maintenant installés) et des détecteurs complémentaires (RFID, Hall, IR).
Comme pour le TCO, le logiciel utilise les routines CAN décrites ci-dessus, avec des messages dont les Ids sont :
- Réception Id = 0x4x (commande aiguille)
- Emission, Id = 0x12 (etat aiguille)
Les aiguilles sont déclarées sous forme d'objets :
class Aiguille
{
private:
byte index; // 0, .. MaxAIG-1
char nom; // 'A' ou 'B' ...
byte pin_sens; // sens droit (HIGH) ou devie (LOW)
byte pin_pulse; // impulsion de commande : actif LOW
bool etat; // true = droit, false = devie
bool change; // rafraichir la position en fonction de etat avec envoi CAN
bool echoCan; // rafraichir la position en fonction de etat sans envoi CAN si false
bool curstep; // true = pulse en cours, false = cycle fini
unsigned long milli; // temps systeme au debut de l'impulsion
public:
// ----------- constructeur complet ------------
Aiguille(byte a_index, char a_nom, byte a_pin_sens, byte a_pin_pulse);
void Setup();
void Armer();
void Rafraichir();
void Caner();
void Preparer(bool a_sens); // sur reception message CAN: mise en liste
void Bouger(); // execute suivant sens avec envoi CAN
void Positionner(bool a_sens); // si changement - cas recurent
void Forcer(bool a_sens); // cas setup lecture EEPROM
void MiseAJour(); // din de la periode : desarmement relais
byte NomAig();
bool EtatAig();
};
Une des astuces que j'aime bien est le rafraichissement des positions de chaque aiguille, une toutes les 10 secondes. Cela permet d'éviter quelques déraillements en cas de changement de position provoqué par exemple par un convoi entrant par un talon positionné differemment.
/*
* Toutes les 10 secondes : rafraichissement d'une position d'aiguille
*/
if (_Minute + 10000 < millis()) { // toutes les 10 secondes
_Minute = _Minute + 10000;
ListeAig[RafraichAiguille].Rafraichir(); // rafraichissment d'une position d'aiguille sans envoi CAN
if (_debug) {
Serial.print(" 10SEC : rafraich: ");Serial.print(RafraichAiguille);
Serial.print(ListeAig[RafraichAiguille].NomAig());
Serial.print(" ram ");Serial.println(_RamFree());
}
RafraichAiguille++;
if (RafraichAiguille > MaxAIG-1) RafraichAiguille = 0; // pas de rafraichisement des Deteleurs, evidemment !
}
Voici le traitement des messages CAN:
/*
* Récupération d'un seul message par loop dans la memoire circulaire _Circule
*/
while (_Ncan > 2) { // messages dans _Circule : au moins 3 bytes
//digitalWrite(LedV, HIGH); // led activité CAN reste allumée jusqu'à loop suivante
_Ncan--;
RId = _Circule[_indexR]; // recup Id
_indexR++;
if (_indexR == sizeof(_Circule)) {_indexR = 0;}
_Ncan--;
Rlen = _Circule[_indexR]; // recup longueur
_indexR++;
if (_indexR == sizeof(_Circule)) {_indexR = 0;}
if (_dumpCan) {
Serial.print("CAN id ");
Serial.print(RId);
Serial.print(", data ");
}
for (int k = 0; k < Rlen; k++) {
_Ncan--;
Rbuf[k] = _Circule[_indexR]; // recup octets message
_indexR++;
if (_indexR == sizeof(_Circule)) {_indexR = 0;}
if (_dumpCan) {
Serial.print("0x");
Serial.println(Rbuf[k], HEX);
}
} // le message est dans les globales RId, Rlen et Rbuf[..]
curAIG = Rbuf[0] & 0x3F; // garder que les bits 0..5 = index aiguille
/*
* commandes aiguilles CAN sur ID 0x40
*/
if (RId == RId_AIGUILLE) {
// commandes relatives aux aiguilles
// 1 octet donnees: bit 7,6 -> 1,0 = devie ou 1,1 = droit, bits 5..0 = numero aiguille
if (bitRead(Rbuf[0], 7)) { // commande de changement d'aiguille
if (curAIG < MaxAIG) { // aiguille
if (bitRead(Rbuf[0], 6) != ListeAig[curAIG].EtatAig()) {
ListeAig[curAIG].Positionner(bitRead(Rbuf[0], 6)); // bit 6 = position
BougeAIG[curAIG] = true;
if (_debug) {
Serial.print(" CAN : curAIG ");Serial.print(curAIG);
Serial.print(" Aiguille "); Serial.print(ListeAig[curAIG].NomAig());
if (bitRead(Rbuf[0], 6)) {
Serial.println(" DROIT");
} else {
Serial.println(" DEVIE");
}
}
}
}
}
}
}
Les commandes d'aiguilles ne sont pas directement émises par le TCO (les clés) mais par le Gestionnaire.
Imaginons qu’une aiguille soit protégée pour laisser passer un convoi prioritaire : cette aiguille ne pourra être manœuvrée qu’après le passage du convoi. C’est ce que fera le gestionnaire : si je bascule la clé de l’aiguille sur le TCO, rien ne se passe ! Dès que le convoi est passé, l’aiguille change automatiquement et la Led sur le TCO, à coté de la clé, répercute ce changement.
Je vais donc bientôt décrire le Gestionnaire !