TCO maison avec Stellpult / MS2

Bonjour tout le monde,

Je commande mon petit réseau avec une MS2 et ma vingtaine d’aiguillages via CAN Stellput basé sur des M83.

J’aimerais récupérer les informations rouge / vert déjà présentes par exemple sur un stellput pour créer un petit TCO (informatif uniquement, lecture seule) avec des diodes bicolores.

Je pense que ce serait possible en combinant quelques éléments de chez CAN mais suis perdu dans mon approche.

Avez-vous des pistes pour m’aiguiller vers une approche sympa ?

Merci !

2 « J'aime »

Bonjour Sylv1,

Je ne connais pas CAN Stellput mais comme tu l’avances il est à peu près certain que le boitier communique en CAN avec la MS2. A partir de là, ton souhait de réaliser un TCO est assez facile à réaliser.

En effet, tous les messages qui circulent sur un bus CAN peuvent être facilement “sniffés” et l’on peut créer des actions en fonction.

En gros, un Arduino équipé d’une carte CAN lit les messages échangés entre CAN Stellput et MS2 (et vis verça) et allume les leds souhaitées en conséquence.

C’est simple et assez peu couteux, probablement moins de 20€.

Je peux t’aider si tu le souhaites. Il faudrait dans un premier temps que tu publies tout ce que tu as sur les câblages, documentations mais aussi photos des différents raccordements.

Christophe

3 « J'aime »

Merci pour ta réponse Christophe. Je vois effectivement des modules comme celui-ci qui pourraient m’aider : Das CAN digital Bahn Projekt, mais 1) c’est cher ; 2) plus dispo chez les revendeurs habituels ; 3) j’ai dans mes tiroirs un Arduino pas exploité, quelques cartes de lecture NFC et étiquette NFC et du coup on pourrait mixer les deux projets :slight_smile: .

Actuellement :

Aiguillages (une vingtaine) commandés par des M83, soit via la MS2 soit directement sur le Stellput. Ce qui veut dire que mes deux sorties de la Gleisbox sont occupées. Ce serait top de venir sniffer tout ça avec un arduino pour :

  1. Pouvoir commander des leds pour un TCO visuel
  2. Envoyer tout ça sur mon ordinateur pour également le voir sur la page web qui affiche déjà mes caméras
  3. Pouvoir en plus ajouter dans tout ça la détection de locos via NFC pour enrichir tout ce beau monde

Peux-tu me dire quel composant acheter pour sniffer du CAN sur l’arduino ? MCP2515 ? Avec une piste de code sur LOCODUINO - Mise en oeuvre du Bus CAN entre modules Arduino (1) si j’ai bien compris ?

Merci !

Je suis intéressée aussi et pour les mêmes raisons … pouvoir faire un TCO à côté du pupitre de commande.

J’avais aussi démonté le Stellpult pour voir s’il était possible de repiquer les leds. Pas simple.

2 « J'aime »

Une partie de l’info est ici :

On a donc la partie “captation”. La difficulté pour ma part est que les 2 sorties de la gleisbox sont déjà occupée, mais il y a probablement moyen de se servir de la sortie qui permet de mettre des Stellput en série pour récupérer ça…

Ensuite il faudrait un “switch” permettant de gérer les LEDS depuis l’arduino pour afficher ça. Vais déjà acheter la carte pour la captation des signaux CAN pour tester… :slight_smile:

Bonsoir @Julaye et @Sylv1,

Tout d’abord, oui les petits modules CAN NiRen dont je parle dans le post cité feront parfaitement l’affaire pour ce qui est du hard. Tous les branchements sont présentés dans ce post.

Je rappelle bien ce point une nouvelle fois, le CAN fonctionne sur un bus. Donc oui, tous les récepteurs CAN sont en série, on entre dans l’un puis on ressort pour aller dans l’autre. Si donc il y a une sortie CAN disponible sur le CAN Stellput, c’est sur celle-ci que l’on va se brancher. On pensera à activer la résistance de fin de bus de 120Ω car l’Arduino sera de ce fait le dernier à l’une des extrémités du bus.

Ce qui est dommage, c’est que les fonction CAN de commande d’aiguilles ne sont pas documentées dans le « Kommunikationsprotokoll » (protocole CAN publié par Marklin pour la CS2 souvent cité).

Je vais chercher sur les forums, il y a peut-être quelqu’un qui s’est amusé à décrypter les commandes. Sinon, ce n’est pas compliqué mais c’est sans doute un jeu de patience.

Il faut absolument avoir bien compris la structure d’une trame CAN Marklin pour réaliser cela sans difficulté. J’en parle dans ce post : Comprendre et utiliser le protocole CAN de Marklin - #15 par bobyAndCo

La prio, les 4 bits de poids fort dans l’identifiant ne sont pas importants ici.
Par contre, ce qui va nous permettre de savoir si l’on a affaire à une commande d’aiguille sera dans les 8 bits de poids fort suivants.
Le bit suivant (réponse) est aussi intéressant car le Stellput adressera ses commandes avec ce bit à 0 (c’est une commande) et la MS2 répondra avec le même identifiant de commande mais avec ce bit à 1 (c’est une réponse). Elle confirme la bonne réception de l’ordre.
Dans les 16 bits suivants sont contenus les identifiants des appareils qui envoient le message. En fonction du bit de réponse à 0 ou à 1 on déduira vite quel est l’ID de la MS2 et l’ID de CAN Stellput.
Enfin, les 4 derniers bits sont également importants car ils nous indiquent le nombre d’octets de datas qui sont envoyés avec le message. J’ai bien dit octets, c’est-à-dire des séries de 8 bits.

L’adresse de l’aiguille est probablement contenue dans les 4 premiers octets. Je pense que l’état de commutation demandé doit se trouver dans le 5° octets, 0 ou 1 pour dévié ou droit.

Mais ceci apparaitra progressivement en sniffant les trames CAN échangées entre le CAN Stellput et la MS2 et inversement.

Il suffira de commuter progressivement chaque aiguille sur le CAN Stellput. Pour les aiguilles qui se commandent par la MS2, ce sera le même principe, elles seront publiées sur le bus même si elles sont exécutées directement.

Il faudra utiliser les filtres de message CAN car, même si le système est au repos, il adresse néanmoins divers messages qui viennent poluer la recherche que l’on fait. On va donc demander à l’Arduino de les ignorer sur la base de leurs 8 bits de commande.

Quand on aura le message qui correspond à la commande de chaque aiguille avec l’octet de données qui signifie sa position, il sera facile de demander à l’Arduino d’allumer telle ou telle led.

Si cela vous convient, je vous proposerais un petit programme pour sniffer les trames et les afficher sur le moniteur série de l’Arduino. Ca avait beaucoup amusé @Jerome qui voyait cela comme un peu magique.

A+

Christophe

Merci beaucoup pour ces infos et précisions !

Tu peux m’en dire plus sur la résistance de 120 ohms ? J’ai effectivement remarqué que mon connecteur RJ12 dans la sortie du stellput avait une résistance, comment reproduire ça depuis en arrivant en fin de compte dans le module CAN ?

Faire ressortir les câbles du module via les 2 connecteur et y placer la résistance ?

Merci !

Le module NiRen dispose d’une résistance que l’on active ou pas en plaçant ou en retirant un cavalier.

Il faut donc enlever la résistance du Stellup et mettre le cavalier sur le NiRen

Christophe

Top. Merci !

Je reçois ça demain je vérifierai ça à deux fois.

Tu ne crains rien avec le CAN, il n’y a pas de risques de court-jus ou autres si tu inverses de câbles par exemple. Je ne vois pas bien comment détruire un module CAN sauf à marcher dessus.

Pense bien pour les câbles qu’il ne sont jamais croisés comme sur un bus série par exemple. CanH, dans CanH et CanL dans CanL.

Christophe

1 « J'aime »

Oui, et d’ailleurs ce sujet m’interesse aussi. Après s’être nettoyé les narines, rien ne vaut un bon vieux TCO :sweat_smile:

2 « J'aime »

Voici un programme pour Arduino Uno et module CAN NiRen pour sniffer les trames CAN sur le bus.

Il faut (si ce n’est pas déjà fait) installer dans l’IDE Arduino la bibliothèque ACAN2515

Il faut aussi reprendre le câblage qui est dans le post général sur le CAN et s’assurer que CS et INT sont bien respectivement branchés sur le broches 10 et 2.

static const byte MCP2515_CS = 10; // CS input of MCP2515 (adapt to your design)
static const byte MCP2515_INT = 2; // INT output of MCP2515 (adapt to your design)

Je n’ai pas pu tester car je suis encore en vacances. Mais relié directement, dans un premier temps, à l’une des prises mini-DIN de la Gleisbox, cela doit fonctionner si c’est bien câblé. Ne pas hésiter à me dire si problème.

Quand ce premier programme fonctionnera, je vous donnerai le code pour filtrer les seuls messages qui nous intéressent.

Il est normalement possible de ne câbler que le CAN-StellPult avec l’Arduino équipé de son MCP2515. De cette manière, on ne sniffe que les message concernant les aiguilles et il est beaucoup plus facile de décoder les trammes de commandes d’aiguilles.

//——————————————————————————————————————————————————————————————————————————————
//  Marklin CAN sniffer
//  v 0.1 31/07/2025
//  Christophe BOBILLE
//——————————————————————————————————————————————————————————————————————————————

#include <ACAN2515.h>

//——————————————————————————————————————————————————————————————————————————————
//  MCP2515 connections:
//    - standard SPI pins for SCK, MOSI and MISO
//    - a digital output for CS
//    - interrupt input pin for INT
//——————————————————————————————————————————————————————————————————————————————

static const byte MCP2515_CS = 10;  // CS input of MCP2515 (adapt to your design)
static const byte MCP2515_INT = 2;  // INT output of MCP2515 (adapt to your design)

//——————————————————————————————————————————————————————————————————————————————
//  MCP2515 Driver object
//——————————————————————————————————————————————————————————————————————————————

ACAN2515 can(MCP2515_CS, SPI, MCP2515_INT);

//——————————————————————————————————————————————————————————————————————————————
//  MCP2515 Quartz: adapt to your design
//——————————————————————————————————————————————————————————————————————————————

static const uint32_t QUARTZ_FREQUENCY = 8UL * 1000UL * 1000UL;  // 8 MHz compatible with the NiRen module

//——————————————————————————————————————————————————————————————————————————————
//   SETUP
//——————————————————————————————————————————————————————————————————————————————

void setup() {
  //--- Start serial
  Serial.begin(115200);
  //--- Wait for serial
  while (!Serial) {
    delay(50);
  }
  //--- Begin SPI
  SPI.begin();
  //--- Configure ACAN2515
  Serial.println("Configure ACAN2515");
  ACAN2515Settings settings(QUARTZ_FREQUENCY, 250UL * 1000UL);  // Marklin CAN bit rate 250 kb/s
  const uint16_t errorCode = can.begin(settings, [] {
    can.isr();
  });
  if (errorCode == 0) {
    Serial.print("CAN config OK ");
  } else {
    Serial.print("CAN config error ");
  }
}


void loop() {
  CANMessage frame;
  if (can.available()) {
    can.receive(frame);

    uint8_t prio = (frame.id & 0x1E000000) >> 25;
    uint8_t cmd = (frame.id & 0x1FE0000) >> 17;
    uint8_t resp = (frame.id & 0x10000) >> 16;
    uint16_t hash = frame.id & 0xFFFF;

    Serial.print("Received CAN frame from : Ox");
    Serial.println(hash, HEX);

    Serial.print("Command : 0x");
    Serial.println(cmd, HEX);

    Serial.print("Number of data : ");
    Serial.println(frame.len);

    for (byte i = 0; i < frame.len; i++) {
      Serial.print("data["); Serial.print(i); Serial.print("] = "); Serial.println(frame.data[i]);
    }
    Serial.print("------------------------------------------------------------------------------------------\n");
    Serial.print("\n");
  }
}

//——————————————————————————————————————————————————————————————————————————————

Bonjour Sylvain,

Merci pour ta question qui m’intéresse également.
Actuellement, avec ton Stellput, j’imagine que tu commandes tes aiguillages manuellement. Tu envisages d’automatiser ton réseau dans le futur?

Alain

Je précise à ceux qui sont intéressé qu’il est possible de faire sans le CAN-StellPult qui vaut tout de même la bagatelle de 159€ sur le site du constructeur. Ou plus exactement de construire son propre boitier !

Je pourrai apporter des précisions à ceux qui en souhaiteraient.

Christophe

J’ai fait quelques recherches sur internet. Il est probable que les commandes d’aiguilles soient traités globalement comme accessoires dans le protocole Märklin. Voici comment les commandes sont envoyées :

J’ai modifié le post depuis sa publication originale. En cherchant plus, j’ai en effet trouvé que la commande pour les aiguilles est 0x0B (donc placée dans l’identifiant).

Que data length est 6 pour une commande (réponse = 0) et data length est 8 pour une réponse (réponse = 1)

Que l’identifiant de l’aiguille est bien sur les 4 premiers octets de data mais que seuls data[2] et data[3] sont signifiants.

Que data[4] contient la position, 0 pour droit et 1 pour dévié

Les autres datas : data[5], data[6], data[7] ne semblent pas signifiantes pour les aiguilles et n’apportent de toutes façon rien pour le cas d’un TCO où seuls l’adresse et la position sont nécessaires.

A vérifier avec le sniffer.

Je pense qu’à la mise sous tension du réseau, le Stellpult doit envoyer toute la liste des adresses qu’il a enregisté avec sans doute la bonne position.

Christophe

Hello,

@bobyAndCo, merci pour les précisions ! Je reçois le matos aujourd’hui, à voir quand j’aurai un peu de temps libre pour tester tout ça :slight_smile: Peut-être déjà ce week-end… En effet, on pourrait émuler un Stellput et s’en passer, mais je trouvais le truc sympa comme cadeau de Noël avec toutes ses lumières :rofl:

@Alain2 oui, je n’envisage pas d’automatisation pour le moment (le réseau est petit, on peut le voir ici : Hermes : réseau H0 dans la nature - Présentation de vos réseaux - Forum 3Rails et j’y trouve du plaisir à changer les aiguillages)

Je vous tiens au courant :slight_smile:

2 « J'aime »

Du coup, si je ne me suis pas trompé et que la commande CAN pour une commande d’aiguille est bien 0x0B, le code ci-dessous doit permettre d’écrire dans le moniteur série de l’IDE Arduino l’adresse de l’aiguille qui a été commutée et la position de l’aiguille.

//——————————————————————————————————————————————————————————————————————————————
//  Marklin CAN sniffer
//  v 0.2 31/07/2025
//  Christophe BOBILLE
//——————————————————————————————————————————————————————————————————————————————

#include <ACAN2515.h>

//——————————————————————————————————————————————————————————————————————————————
//  MCP2515 connections:
//    - standard SPI pins for SCK, MOSI and MISO
//    - a digital output for CS
//    - interrupt input pin for INT
//——————————————————————————————————————————————————————————————————————————————
// If you use CAN-BUS shield (http://wiki.seeedstudio.com/CAN-BUS_Shield_V2.0/) with Arduino Uno,
// use B connections for MISO, MOSI, SCK, #9 or #10 for CS (as you want),
// #2 or #3 for INT (as you want).
//——————————————————————————————————————————————————————————————————————————————
// Error codes and possible causes:
//    In case you see "Configuration error 0x1", the Arduino doesn't communicate
//       with the 2515. You will get this error if there is no CAN shield or if
//       the CS pin is incorrect.
//    In case you see success up to "Sent: 17" and from then on "Send failure":
//       There is a problem with the interrupt. Check if correct pin is configured
//——————————————————————————————————————————————————————————————————————————————

static const byte MCP2515_CS = 10;  // CS input of MCP2515 (adapt to your design)
static const byte MCP2515_INT = 3;  // INT output of MCP2515 (adapt to your design)

//——————————————————————————————————————————————————————————————————————————————
//  MCP2515 Driver object
//——————————————————————————————————————————————————————————————————————————————

ACAN2515 can(MCP2515_CS, SPI, MCP2515_INT);

//——————————————————————————————————————————————————————————————————————————————
//  MCP2515 Quartz: adapt to your design
//——————————————————————————————————————————————————————————————————————————————

static const uint32_t QUARTZ_FREQUENCY = 8UL * 1000UL * 1000UL;  // 8 MHz compatible with the NiRen module

//——————————————————————————————————————————————————————————————————————————————
//   SETUP
//——————————————————————————————————————————————————————————————————————————————

void setup() {
  //--- Start serial
  Serial.begin(115200);
  //--- Wait for serial
  while (!Serial) {
    delay(50);
  }
  //--- Begin SPI
  SPI.begin();
  //--- Configure ACAN2515
  Serial.println("Configure ACAN2515");
  ACAN2515Settings settings(QUARTZ_FREQUENCY, 250UL * 1000UL);  // Marklin CAN bit rate 250 kb/s
  const uint16_t errorCode = can.begin(settings, [] {
    can.isr();
  });
  if (errorCode == 0) {
    Serial.print("CAN config OK ");
  } else {
    Serial.print("CAN config error ");
  }
}


void loop() {
  CANMessage frame;
  if (can.available()) {
    can.receive(frame);

    uint8_t prio = (frame.id & 0x1E000000) >> 25;
    uint8_t cmd = (frame.id & 0x1FE0000) >> 17;
    uint8_t resp = (frame.id & 0x10000) >> 16;
    uint16_t hash = frame.id & 0xFFFF;

    // Serial.print("Command : 0x");
    // Serial.println(cmd, HEX);

    // Serial.print("Number of data : ");
    // Serial.println(frame.len);

    if (cmd == 0x0B) {
      Serial.print("Received CAN frame from : 0x");
      Serial.println(hash, HEX);

      Serial.println(resp ? "Response" : "Commande");

      Serial.print("Turnout ID : 0x");
      Serial.println(frame.data16[2], HEX);

      Serial.println(data[4] ? "turn" : "straight");
    }
    Serial.print("------------------------------------------------------------------------------------------\n");
    Serial.print("\n");
  }
}

//——————————————————————————————————————————————————————————————————————————————

A tester car je n’ai pas les outils pour cela sur mon lieu de vacances :slight_smile:

Christophe

2 « J'aime »

Profitant d’une annulation de RDV, j’ai pu travailler sur mon montage. Merci @bobyAndCo pour les infos.

Voici le montage du module CAN / Arduino sur base de la sortie du Stellput :

La connectique en sortie du Stellput est du RJ12 avec les câbles suivants (code couleur CAN-Stellput) :

  1. Noir : 12V, attention doit finir sa vie dans un wago, à ne surtout pas cabler dans l’Arduino ! ou ne le cablez pas…
  2. Marron : masse
  3. Jaune : CAN H (semble-t-il en inversion de la doc du module NiRen ?)
  4. Vert : CAN L (semble-t-il en inversion de la doc du module NiRen ?)

Le reste est câblé comme dans la doc fournie par Christophe sur ce forum. Attention que le INT est sur le 2 comme sur la doc, à modifier dans le code ci-dessus.

Pour le code, après import de la librairie ad-hoc (à ne pas confondre avec la acan sans le 2515 :frowning: ), la partie loop est à débugger. La ligne affichant le turnout ID ne connait pas la variable data. Après correction en frame.data[4], l’ID retourné semble toujours être un ID générique pour les M83.

Voici une boucle loop() qui (me) donne satisfaction :

void loop() {
  CANMessage frame;
  if (can.available()) {

    // Frame & subdata
    can.receive(frame);
    uint8_t cmd = (frame.id & 0x1FE0000) >> 17;

    // Check tournout data, don't loop in ack & release
    if ((frame.id == 0x160301) && (cmd == 0x0B) && (frame.data[5] == 1)) {

      // Internal data
      uint8_t decoder_id = frame.data[2];
      uint8_t output = frame.data[3];
      uint8_t position = frame.data[4];

      // Debug
      Serial.print("M83 ID : ");
      Serial.print(decoder_id);
      Serial.print(" / Sortie : ");
      Serial.print(output);
      Serial.print(" / Aiguillage logique : ");
      Serial.print(output + 1);

      // Mhhh green / red not norm compliant ? It's MY norm !
      Serial.print(" => Position : ");
      Serial.println(position ? "Droit (vert)" : "Dévié (rouge)");
      
    }     
    
  }
  
}

Ce qui donne comme résultat (assurez-vous de bien indiquer à la console 115200 bauds pour avoir des caractères lisibles) :

Le résultat me convient donc à 100%, un grand merci ! Il me reste maintenant à commander (en code et dans le commerce :slight_smile: ) mes LEDS pour répercuter cette info. Pour combler le manque de place dans les sorties Arduino, je vais combiner quelques SN74HC595N pour pouvoir commander les ± 20 leds.

Je vous tiens au courant !

2 « J'aime »

C’est super cool tu t’es débrouillé comme un chef.

Oui il faut effectivement utiliser des 74HC595. 1 75HC595 pour 4 aiguilles soit 8 leds. Avec 3 pins ARDUINO tu peux monter 2 74HC en série soit 8 aiguilles donc 16 leds. Si besoin de peux prendre 3 autres pins.

Attention à la puissance des leds. De mémoire, une sortie sur le 74HC supporte 6 mA max. Une led ordinaire de 5 mm, c’est 20 mA. Bien sûr tu peux insérer des ULN 2803 mais ça augmente inutilement le coût et l’encombrement.

Tu peux aussi mettre un résistance assez forte mais la led n’éclairera pas beaucoup.

Hâte de voir la suite

Christophe

Deuxième version du script :slight_smile:

J’ai ajouté 8 LEDS pour faire les aiguillages 1 à 8. En décomptant les dételleurs j’en ai … 17, zut il me faudra 3 74HC595 :frowning:

Voici déjà une version avec 8 aiguillages :

  1. Je n’ai pas encore les LEDS vert / rouge, donc je simule rouge = éteint, vert = allumé
  2. Enregistrement dans la mémoire de l’Arduino comme ça quand je coupe le courant je récupère le dernier état

Plus qu’à chainer les 74HC595 et à changer par des vertes / rouges :slight_smile: Ensuite le tableau TCO…

Le script :

//——————————————————————————————————————————————————————————————————————————————
//  Marklin CAN sniffer + infos aiguillages + infos LED
//  v 0.3 - 01/08/2025
//  Christophe BOBILLE + Sylvain Lombart + GPT-4
//——————————————————————————————————————————————————————————————————————————————

#include <ACAN2515.h>
#include <EEPROM.h>

//——————————————————————————————————————————————————————————————————————————————
//  MCP2515 connections
//——————————————————————————————————————————————————————————————————————————————

static const byte MCP2515_CS = 10;
static const byte MCP2515_INT = 2;
static const uint32_t QUARTZ_FREQUENCY = 8UL * 1000UL * 1000UL;  // 8 MHz

ACAN2515 can(MCP2515_CS, SPI, MCP2515_INT);

//——————————————————————————————————————————————————————————————————————————————
//  LEDs & EEPROM
//——————————————————————————————————————————————————————————————————————————————

const byte latchPin = 6;   // ST_CP / RCK
const byte clockPin = 5;   // SH_CP / SCK
const byte dataPin  = 7;   // DS

byte leds = 0;
byte lastSavedLeds = 0;
unsigned long lastChangeTime = 0;
const unsigned long saveDelay = 1000; // délai avant écriture EEPROM (anti-usure)

const byte EEPROM_ADDR_LEDS = 0;
const byte EEPROM_INIT_MARK = 0x42;

//——————————————————————————————————————————————————————————————————————————————
//  SETUP
//——————————————————————————————————————————————————————————————————————————————

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(50);

  SPI.begin();
  Serial.println("Configure ACAN2515");
  ACAN2515Settings settings(QUARTZ_FREQUENCY, 250UL * 1000UL);
  const uint16_t errorCode = can.begin(settings, [] { can.isr(); });
  if (errorCode == 0) {
    Serial.println("CAN config OK");
  } else {
    Serial.print("CAN config ERROR: 0x");
    Serial.println(errorCode, HEX);
  }

  // Setup LEDs
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);

  // Initialisation EEPROM
  if (EEPROM.read(1) != EEPROM_INIT_MARK) {
    EEPROM.write(EEPROM_ADDR_LEDS, 0x00);
    EEPROM.write(1, EEPROM_INIT_MARK);
    leds = 0;
    lastSavedLeds = 0;
    Serial.println("EEPROM initialisée (LEDs toutes éteintes)");
  } else {
    leds = EEPROM.read(EEPROM_ADDR_LEDS);
    lastSavedLeds = leds;
    Serial.print("LEDs restaurées depuis EEPROM : ");
    Serial.println(leds, BIN);
  }

  updateShiftRegister();
}

//——————————————————————————————————————————————————————————————————————————————
//  LOOP
//——————————————————————————————————————————————————————————————————————————————

void loop() {
  CANMessage frame;

  if (can.available()) {
    can.receive(frame);
    uint8_t cmd = (frame.id & 0x1FE0000) >> 17;

    if ((frame.id == 0x160301) && (cmd == 0x0B) && (frame.data[5] == 1)) {
      uint8_t decoder_id = frame.data[2];
      uint8_t output = frame.data[3];
      uint8_t position = frame.data[4];

      Serial.print("M83 ID : ");
      Serial.print(decoder_id);
      Serial.print(" / Sortie : ");
      Serial.print(output);
      Serial.print(" / Aiguillage logique : ");
      Serial.print(output + 1);
      Serial.print(" => Position : ");
      Serial.println(position ? "Droit (vert)" : "Dévié (rouge)");

      // Mise à jour de l’état LED
      if (position == 0) {
        bitClear(leds, output); // On simule le rouge, LED OFF
      } else {
        bitSet(leds, output); // On simule le vert, LED ONN
      }

      updateShiftRegister();

      // Marque le moment du changement
      if (leds != lastSavedLeds) {
        lastChangeTime = millis();
      }
    }
  }

  // Sauvegarde en EEPROM après délai
  if ((millis() - lastChangeTime) > saveDelay && leds != lastSavedLeds) {
    EEPROM.update(EEPROM_ADDR_LEDS, leds);
    lastSavedLeds = leds;
    Serial.println("LEDs sauvegardés dans EEPROM");
  }
}

//——————————————————————————————————————————————————————————————————————————————
//  Envoie les bits aux LEDs via le 74HC595
//——————————————————————————————————————————————————————————————————————————————

void updateShiftRegister() {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, leds);
  digitalWrite(latchPin, HIGH);
}
3 « J'aime »