Auteur Sujet: projet de reseau etagere analogique  (Lu 4286 fois)

palou

  • Newbie
  • *
  • Messages: 10
  • presque à la retraite ...
    • Voir le profil
projet de reseau etagere analogique
« le: août 28, 2021, 04:36:28 pm »
Bonjour

Je suis en train de concevoir un réseau étagère. Vu le manque de place, c'est un réseau en HOe de 3,5m sur 30cm divisé en 4 modules. (3x90cm, 1x 80cm).

C'est un réseau en "Z" : La partie droite comprend une gare terminus avec une plaque tournante, la partie gauche une coulisse à tiroir. De la gauche vers la droite la diagonale du Z mène à une voie de rebroussement permettant de parcourir la partie haute à gauche qui serait une exploitation forestière. La jonction entre la voie du bas et la diagonale permet des croisements de trains (mini gare) et il y a également au milieu de la partie haute une zone de croisement (qui sera camouflée). De ce fait, malgré que le réseau soit à voie unique, entre la gare terminus, la coulisse, l'exploitation forestière (qui est aussi un mini terminus) et les deux zones de croisements, il pourrait y avoir 3 ou 4 trains qui pourraient circuler sur le réseau en même temps ...
Ca c'est la partie visible de l'iceberg ...
Pour ce qui est de la partie invisible (cad le moyen de piloter tout ça), j'ai pensé à des cartes électroniques basées sur des nanos pour gérer le PWM et la présence sur les différentes sections de voies où un train pourrait se trouver, des relais pour piloter les aiguilles et éventuellement des dételeurs pour les gares, un montage spécifique pour la table tournante, et un dernier pour gérer les animations éventuelles (leds, servos ou autres)
Pour piloter tout ça: j'ai pensé à un méga ou à un due qui serait le chef d'orchestre et commuterait la PWM sur le parcours du train. Il assurerait la sécurité, évitant que 2 trains ne se retrouvent sur la même section
La partie "IHM' serait un TCO. J'hésite à faire un TCO unique ou un TCO par module. Suivant la complexité du module, si on a un TCO par module, certains de ceux ci peuvent avoir un TCO sur un nano, d'autres seraient sur du méga.
Pour le pilotage des trains, un nano gèrerait des potentiomètres (un par locomotive possible sur le trajet) permettant d'avoir un pilotage individuel de chaque train.

Pour relier tous les arduinos, j'ai pensé à un bus CAN. J'ai commencé à tracer des schémas des certains éléments (en me basant sur les articles de locoduino et en grappillant des exemples de sketchs présentés dans les articles ou sur le forum)

Mon principal problème actuellement est la compréhension du bus CAN. J'ai vu beaucoup d'exemples à 2 ou 3 nœuds, mais pas d'exemples ou il y aurait une multitude de nœuds. Et du coup j'ai du mal à visualiser comment positionner les masques et les filtres des différents types de modules.
Dans mon esprit, le masque représenterait le type de module (par exemple 0x78f pour les modules relais, 0x71f pour les modules pwm, ...) et le filtre représenterait l'identifiant du module ciblé (0x780 à 0x78F pour 16 modules relais maximum, 0x700 à 0x71F pour 32 modules pwm possibles). Les 8 octets de donnée serviraient à décrire la consigne à appliquer ou en retour vers le tco ou le master à renvoyer des infos (position d'aiguille, occupation, ...). Est ce que c'est la meilleure manière de procéder ?
Il y a aussi un endroit dans la trame CAN décrivant si c'est un "ordre" où une "lecture". Comment est positionné ce bits dans les librairies CAN ?
Ensuite, au point de vue faisabilité/complexité: Est ce que ça parait faisable ? J'ai du mal à concevoir la partie "master" qui doit gérer la sécurité et servir éventuellement d'interface vers un PC ou rpi pour avoir un TCO informatisé dans le futur.... (la modélisation n'est pas simple, les solutions qui sont décritent sur le site me semblent assez ardues à appréhender ...)

En PJ le plan du réseau (les couleurs indiquent les section prévues avec un PWM autonome)

Merci d'avance pour vos avis.
Pascal
Le train train quotidien ... Oui Merci !!!

palou

  • Newbie
  • *
  • Messages: 10
  • presque à la retraite ...
    • Voir le profil
Re : projet de reseau etagere analogique
« Réponse #1 le: août 28, 2021, 07:59:48 pm »
Pour compléter, un exemple concret (pas testé je précise) de carte pour gérer les solénoides d'aiguilles et de dételeurs (en PJ le schéma)
Si vous voyez des erreurs n'hésitez pas ... :)
C'est basé sur un nano + une carte 8 relais + une interface CAN 2515

Le sketch est le suivant:
Tout d'abord la configuration générale (config.h):
(les adresses CAN ne sont pas fixées pour l'instant)
#ifndef CONFIG_H
#define CONFIG_H

// debug
#define _dumpCan true

/* aiguille */
#define DELAI_AIGUILLE 150
#define DIRECT 0
#define DEVIE 1
#define MOVEDIRECT 2
#define MOVEDEVIE 4
#define ACTIF HIGH
#define INACTIF LOW

/* deteleur */
#define DELAI_DETELEUR 200
#define DETACTIF HIGH
#define DETINACTIF LOW

/* interface CAN */

static const uint8_t MCP2515_CS  = 10;
static const uint8_t MCP2515_INT = 2;

#define ID_MASTER 0x7a0
#define RELAI_MASK 0x7ef
#define RELAI_FILTER 0x7e1


#define CAN_ORDRE 0x10
#define CAN_LIT   0x20
#define CAN_LIT_TOUT 0x30

#endif

Ensuite la configuration des sorties : C'est dans un fichier séparé de manière à pouvoir le modifier facilement suivant le module qui est la cible (parametres.h)
#ifndef PARAMETRES_H
#define PARAMETRES_H

#include "config.h"
#include "aiguille.h"
#include "deteleur.h"


/* 8 sorties donc 4 aiguilles possibles ou 8 dételeurs ou une combinaison des 2 */
// 2 aiguilles
aiguille aig0 = aiguille(3,4,DIRECT);
aiguille aig1 = aiguille(5,6,DIRECT);
aiguille tabAiguille[2] = {aig0,aig1};
byte nombreAiguille = 2 ;

// 3 deteleurs
deteleur det0 = deteleur(7);
deteleur det1 = deteleur(8);
deteleur det2 = deteleur(9);
deteleur tabDeteleur[3] = {det0,det1,det2};
byte nombreDeteleur = 3;



#endif

L'objet aiguille décrit la commutation d'une aiguille (aiguille.h). La commutation se fait en mettant la variable posAttendue à la valeur souhaitée (DIRECT ou DEVIE). La methode  "loop" qui est appelée cycliquement scanne cette valeur et la compare à la position actuelle de l'aiguille. Si les valeurs sont différentes  et qu'un déplacement n'est pas en cours, elle active le relai et positionne la limite de temps pour l'activation (en millisecondes). Si un déplacement est en cours, la méthode "loop" vérifie que le délai n'est pas dépassé. Si il est dépassé, elle désactive la sortie.
#ifndef AIGUILLE_H
#define AIGUILLE_H

#include "config.h"

class aiguille {
  private:
    byte ports[2];
    byte posAiguille;
    byte posAttendue;
    boolean enCours;
    unsigned long limite;
  public:
    aiguille(byte p0, byte p1, byte posDepart);
    boolean etat();
    void positionne(byte attendu);
    byte pos_aiguille();
    void loop(); 
};

aiguille::aiguille(byte p0, byte p1, byte posDepart) {
  ports[DIRECT] = p0;
  ports[DEVIE] = p1;
  pinMode(ports[DIRECT], OUTPUT);
  pinMode(ports[DEVIE], OUTPUT);
  digitalWrite(ports[DIRECT],INACTIF);
  digitalWrite(ports[DEVIE],INACTIF);
  digitalWrite(ports[posDepart],ACTIF);
  delay(DELAI_AIGUILLE);
  digitalWrite(ports[posDepart],INACTIF);
  posAiguille = posDepart;
  posAttendue = posDepart;
  enCours = false;
  limite = 0;
}

boolean aiguille::etat() {
  return enCours;
}

byte aiguille::pos_aiguille() {
  if (enCours) {
    if (posAttendue == DIRECT) {
      return MOVEDIRECT;
    }
    else {
      return MOVEDEVIE;
    }
  }
  else {
    return posAiguille;
  }
}

void aiguille::positionne(byte attendu) {
  posAttendue = attendu;
}

void aiguille::loop() {
  if (enCours) {
    if (millis() > limite) {
      digitalWrite(ports[posAttendue],INACTIF);
      posAiguille = posAttendue;
      enCours = false;
      moveEnCours = false;
    }
  }
  else {
    if (posAttendue != posAiguille) {
      digitalWrite(ports[posAttendue],ACTIF);
      enCours = true;
      moveEnCours = true;
      limite = millis() + DELAI_AIGUILLE;
    }
  }
}

#endif

L'objet deteleur (deteleur.h) est plus simple (il n'y a qu'un seul relai à activer). Le principe est le même, la fonction "loop" remplit la même fonction que celle d'aiguille.h en étant plus simple.
#ifndef DETELEUR_H
#define DETELEUR_H

#include "config.h"
class deteleur {
  private:
    byte port;
    boolean enCours;
    unsigned long limite;
  public:
    deteleur(byte p0);
    byte etat();
    void detelle();
    void loop(); 
};

deteleur::deteleur(byte p0) {
  port = p0;
  pinMode(port, OUTPUT);
  digitalWrite(port,DETINACTIF);
  enCours = false;
  limite = 0;
}

byte deteleur::etat() {
  return enCours?1:0;
}

void deteleur::detelle() {
  enCours = true;
  moveEnCours = true;
  digitalWrite(port,DETACTIF);
  limite = millis() + DELAI_DETELEUR;
 
}
   
void deteleur::loop() {
  if (enCours) {
    if (millis() > limite) {
      digitalWrite(port,DETINACTIF);
      enCours = false;
      moveEnCours = false;
    }
  }
}

#endif

Pour éviter l'activation simultanée de plusieurs solénoïdes, les demandes son stockées dans une pile et lancées lorsque il n'y a plus de sortie activée. L'objet gestAttente (gestAttente.h) remplit ce rôle. Pareil une méthode "loop" vérifie qu'aucun relai n'est actif et lance une action en attente si il y en a.

#ifndef GESTATTENTE_H
#define GESTATTENTE_H

#include "config.h"
#include "aiguille.h"
#include "deteleur.h"
#include "parametres.h"

#define MAXLISTE 20

struct ordre {
  byte index;
  byte action;
};

class gestAttente {
  private:
    ordre liste[MAXLISTE];
    int indexListe;

  public:
    gestAttente();
    boolean ajouteOrdre(byte index, byte action);
    void loop();
};

gestAttente::gestAttente() {
  indexListe = -1;
}

boolean gestAttente::ajouteOrdre(byte index, byte action) {
  ordre o;
  if (indexListe < MAXLISTE -1) {
    indexListe++;
    o.index = index;
    o.action = action;
    liste[indexListe] = o;
    return true;
  }
  return false;
}

void gestAttente::loop() {
  ordre o;
  if (! moveEnCours) {
    if (indexListe > -1) {
      o = liste[indexListe];
      if (o.index < 10) {
        if (o.index < nombreAiguille) {
          tabAiguille[o.index].positionne(o.action);
        }
      }
      else {
        o.index = o.index - 10;
        if (o.index < nombreDeteleur) {
          tabDeteleur[o.index].detelle();
        }
      }
      indexListe--;
    }
  }
}


#endif

Enfin le sketch lui même: Il est basé sur les articles de Locoduino quand à la gestion du bus CAN
Le paquet de données de 8 octets est découpé de la façon suivante:
buf[0] = ordre (CAN_ORDRE, CAN_LIT, CAN_LIT_TOUT)
buf[1] = cible (0 -> 3 aiguille, 10 - 17 dételeur)
buf[2] = pour une aiguille, la position attendue (DIRECT/DEVIE)
En cas d'ordre CAN_LIT ou CAN_LIT_TOUT, une réponse est envoyée au demandeur
#include <SPI.h>

//  Déclaration de la bubliothèque CAN pour ATmega328
#include <mcp_can.h>
boolean moveEnCours;
#include "config.h"
#include "aiguille.h"
#include "deteleur.h"
#include "parametres.h"
#include "gestAttente.h"

gestAttente gest = gestAttente();

// variables globales pour l'interface CAN
MCP_CAN CAN(MCP2515_CS);   // Definition du CS (chip select) pin 53 (SS du bus SPI)
volatile byte Flag_Recv = 0;   // variable d'échange avec l'interruption IRQ

byte IdR;                       // Id pour la routine CAN_recup()
unsigned char lenR = 0;         // Longueur "    "       "
unsigned char bufR[8];          // tampon de reception      "
unsigned char bufS[8];          // tampon d'emission
 
// Variable globale Mémoire circulaire pour le stockage des messages reçus
unsigned char _Circule[256];    // récepteur circulaire des messages CAN sous IT
int _indexW, _indexR, _Ncan;    // index d'écriture et lecture, nb d'octets a lire
byte _CANoverflow = 0;          // flag overflow (buffer _Circule plein)

/*
 *  ISR CAN (Routine de Service d'Interruption)
 *  le flag IRQ monte quand au moins un message est reçu
 *  le flag IRQ ne retombe QUE si tous les messages sont lus
 */
     
void MCP2515_ISR() {
  Flag_Recv = 1;
}



void setup() {
  while(true) {
    if (CAN_OK == CAN.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ)) {     
    // initialisation du can bus : baudrate = 500k
      Serial.println(F("CAN BUS init ok!"));
      break; // on sort du while.
    }
    else {
      Serial.println(F("CAN BUS init echec !"));
      Serial.println(F("Init CAN BUS a nouveau"));
    }
    delay(200);
  } 
  attachInterrupt(digitalPinToInterrupt(MCP2515_INT), MCP2515_ISR, FALLING); // interrupt 0 (pin 2)
  /*
   * set mask & filter
  */
     
  CAN.init_Mask(0, 0, RELAI_MASK); 
  CAN.init_Mask(1, 0, RELAI_MASK); 
   
  CAN.init_Filt(0, 0, RELAI_FILTER);
  CAN.init_Filt(1, 0, RELAI_FILTER);
  CAN.init_Filt(2, 0, RELAI_FILTER);
  CAN.init_Filt(3, 0, RELAI_FILTER);
  CAN.init_Filt(4, 0, RELAI_FILTER);
  CAN.init_Filt(5, 0, RELAI_FILTER);

}

void loop() {
  if (Flag_Recv)  {
    Flag_Recv = 0;  // Flag MCP2515 prêt pour un nouvel IRQ
    CAN_recup();    // récupération du ou des messages CAN reçus
    CAN_depile();
  }
 
  gest.loop();
  for (int i = 0; i < nombreAiguille; i++) {
    tabAiguille[i].loop();
  }
  for (int i = 0; i < nombreDeteleur; i++) {
    tabDeteleur[i].loop();
  }
}

void CAN_recup() {
  unsigned char len = 0;  // nombre d'octets du message
  unsigned char buf[8];   // message
  unsigned long Id;   // Id (on devrait plutôt utiliser un int car il y a 11 bits)
 
  while (CAN_MSGAVAIL == CAN.checkReceive())  {
    CAN.readMsgBuf(&Id, &len, buf);        // read data, len: data length, buf: data buf
    if ((_Ncan+len+2) < sizeof(_Circule))  { // il reste de la place dans _Circule
      _Circule[_indexW] = Id;         // enregistrement de Id
      _indexW++;
      _Ncan++;
      if (_indexW == sizeof(_Circule))  {_indexW = 0;}
      _Circule[_indexW] = len;        // enregistrement de len
      _indexW++;
      _Ncan++;
      if (_indexW == sizeof(_Circule))  {_indexW = 0;}
      for (byte z = 0; z<len; z++)  {
        _Circule[_indexW] = buf[z];    // enregistrement du message
        _indexW++;
        _Ncan++;
        if (_indexW == sizeof(_Circule))  {_indexW = 0;}
      }
    }
    else {
      _CANoverflow = 1;  // dépassement de la capacite de Circule
                            // le message est perdu
    }
  }
}

void CAN_depile() {
  byte RId;  // variables pour le traitement des messages lus dans _Circule
  byte Rlen;
  byte Rbuf[8];
  byte Ebuf[8];
  byte element;
  byte action;
  byte i;
     
  // traitement des messages stockés dans la mémoire circulaire _Circule
     
  while (_Ncan > 2)  {    // chaque message dans _Circule occupe au moins 3 octets
    _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)  {        // _dumpCan est un boolean a déclarer en globale
      Serial.print("CAN id ");  // si on veut conditionner l'affichage des message
      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.print(Rbuf[k], HEX);
      }
    }
    if (_dumpCan) Serial.println();
    // le message est maintenant dans les globales RId, Rlen et Rbuf[..]
    switch (Rbuf[0]) {
      case CAN_ORDRE:
        if (Rlen > 1) {
          element = Rbuf[1];
          if (element < nombreAiguille && Rlen > 2) {
            action = Rbuf[2];
            gest.ajouteOrdre(element,action);
          }
          else {
            if (element >= 10 && element < nombreDeteleur) {
              gest.ajouteOrdre(element,0);
            }
          }
        }
        break;
      case CAN_LIT:
        if (Rlen > 1) {
          element = Rbuf[1];
          if (element < nombreAiguille) {
            Ebuf[0] = CAN_LIT;
            Ebuf[1] = element;
            Ebuf[2] = tabAiguille[element].pos_aiguille();
            CAN.sendMsgBuf(RId, 0, 3, Ebuf);
          }
        }
        break;
      case CAN_LIT_TOUT:
        Ebuf[0] = CAN_LIT_TOUT;
        Ebuf[1] = 0;
        Ebuf[2] = 0;
        for(i = 0 ; i < nombreAiguille ; i++) {
          Ebuf[1] |= tabAiguille[i].pos_aiguille()<<(i*2); 
        }
        for (i = 0 ; i < nombreDeteleur ; i++) {
          Ebuf[2] |= tabDeteleur[i].etat() << i;
        }
        CAN.sendMsgBuf(RId, 0, 3, Ebuf);
        break;
      default:
        break;
    }
  }
}

Voila

Je me pose surtout des questions coté CAN:
Est ce que ma gestion des ID est bonne ?
Est ce que l'ID reçu est celui de l'émetteur du message ou faut il envoyer la réponse à un id prédéfini ?
Est ce que la logique est bonne ?

Pascal
Le train train quotidien ... Oui Merci !!!

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 2616
  • 100% Arduino et N
    • Voir le profil
Re : projet de reseau etagere analogique
« Réponse #2 le: août 29, 2021, 11:21:38 am »
Bonjour Pascal,

Votre réseau est très intéressant car il compte pas mal de modules et quelques images, photos, plans du réseau permettrait de mieux voir le projet global.

Votre architecture de communication sur bus Can ressemble assez bien à celle de mon réseau ( https://forum.locoduino.org/index.php?topic=290.0 donc je pourrai vous aider.

Mais je sors d’un mariage dans la famille qui va me prendre encore un peu de temps avant de vous consacrer plus de temps.

Commencez par vous dire que c’est l’identifiant des messages qui est important : un identifiant par fonction, chaque module pouvant avoir plusieurs fonctions, certaines fonctions pouvant intéresser plusieurs modules.
Les masques et les filtres ne servent qu’à définir les seuls messages qui intéressent un module pour éviter de le déranger par ceux qui ne le concernent pas.

Donc il faut lister toutes les fonctions qui émettent un message, et quels modules émettent et reçoivent ces messages.

Je vous donnerai un exemple ou on regarde directement le cas de votre réseau.

Cordialement
Dominique
« Modifié: août 30, 2021, 07:23:31 am par Dominique »
Cordialement,
Dominique

palou

  • Newbie
  • *
  • Messages: 10
  • presque à la retraite ...
    • Voir le profil
Re : projet de reseau etagere analogique
« Réponse #3 le: août 29, 2021, 05:32:04 pm »
Bonjour Dominique

J'ai parcouru le post décrivant votre solution. Bravo pour la réalisation :)

En effet, il y a des ressemblances et aussi des différences. La principale différence est que je prévoie de rester en analogique (pas de DCC). Donc au niveau des sections,  celles ci comporteront leur propre source PWM qui sera pilotée depuis le "master" suivant le trajet des trains.

Sinon l'architecture de base est la même : Chaque module du réseau aura ses propres contrôleurs pour les aiguilles et dételeurs, les alimentations PWM, la détection de présence (que je prévoie d'intégrer aux modules gérant la PWM des sections) et les accessoires. La gare terminus aura un module spécifique pour piloter la plaque tournante (gros morceau :) ).
Pour l'instant je ne prévoie pas de signalisation sur le réseau, car le thème sera une ligne secondaire en voie étroite dans les années 50-60 (à l'époque où il y avait des trains dans nos campagnes ... :) ). Du coup si il devait y avoir une signalisation pour moi elle devrait être "mécanique" à base de sémaphores par exemple...
Les connections entre les modules comprendraient l'alimentation voie et électronique (12V), une alimentation "solénoides", le bus CAN et les raccords entre segments de voies faisant partie de sections communes.

Comme je ne suis pas riche ( :-[ ) je prévoie de construire module par module en étalant dans le temps la réalisation. La déco peut être faite en parallèle une fois les bases posées pour un module  (  cad définition des volumes et rampes + pose de la voie + l'électronique minimale du module (aiguilles/pwm/détection))

Donc je prévoie de commencer par le module de droite (la gare terminus) qui devrait d'après mes prévision nécessiter 2aiguilles, 3 dételeurs, 6 sections PWM et maximum 12 détecteurs de présence type "barrière infrarouge ou réfléchissants" (extrémités de sections) et 6 détecteurs de présence par conso de courant (1/ section PWM). Cela devrait faire 1 contrôleur aiguille et 3 contrôleurs PWM + éventuellement 2 ou 3 contrôleurs "présence" (si je n'arrive pas à les fusionner avec les contrôleurs PWM).
Ensuite j'avancerai vers la gauche module par module...

Le gros morceau au niveau électronique et logique pour ce module est la plaque tournante: Le contrôleur devra piloter un moteur pas à pas (rotation), six contacts "rupteurs" (six positions possibles), une sortie PWM (alimentation voie) et deux barrieres infrarouges d'extrémité (histoire de ne pas faire tourner la plaque si une locomotive est à cheval sur l'entrée). Avec un nano, impossible de rajouter une détection par conso de courant, il ne reste pas assez d'E/S.

Sinon globalement ce qu'il me reste à faire:
  • Prévoir un plan d'adressage des modules pour le CAN.
  • Mettre au point des modules génériques (solénoides, PWM, présence) et faire un banc test pour vérifier que ça marche
  • Mettre au point la plaque tournante (au niveau mécanique c'est pas évident, au niveau logique encore moins ... :) )
  • Le module "master" au niveau électronique est assez simple : 1 Méga ou Due avec un CAN + éventuellement une carte wifi pour dialoguer avec un pc ou autre et un écran pour voir ce qui se passe. Par contre, la partie logique est elle assez ardue ... (là je dois avouer que je tatonne beaucoup)
  • Le module TCO sera basé sur un méga pour gérer les différents boutons poussoirs ou inverseurs  + leds + les potentiomètres de pilotage des locos.

Au niveau logique, les points qui restent à déterminer sont (en dehors de la plaque tournante qui est un mini projet à elle seule):
  • Comment gérer la sécurité entre les sections. Est ce que le master s'en charge (il a une vue globale du réseau) ou est ce que ce sera décentralisé ? Dans ce cas les modules PWM devront demander à leurs voisins (en fonction des positions d'aiguille) si ils peuvent avancer ou non
  • J'ai vu en parcourant les forums des interrogations sur les "sauts de PWM" entre sections qui peuvent entrainer des comportements erratiques des locos lors du passage d'une section à l'autre. Vu l'architecture, il faut que je mette en place aussi une synchro entre les PWM...EDIT: J'ai trouvé la librairie syncPWM en parcourant les forums. Par contre du coup il faut aussi prévoir une base de temps pour générer le signal de synchro. A ce sujet: Quelle est la meilleure frequence de PWM pour animer une locomotive en analogique ?
  • Un truc trivial sans lequel cela ne marche pas: Le positionnement des trains et l'affectation des potentiomètres au train.
  • Prévoir un système d'itinéraires avec une gestion autonome du réseau ?
  • Au vue des points précédents, le script du "master"
  • Tous les trucs auquel je n'ai pas pensé pour l'instant ... :)
   

Enfin voilà l'état de mes réflexions...
En PJ une vue agrandie de la gare terminus (tracé des rails) (la voie en haut à gauche surplombe la gare et est en fait l'extrémité haute du "Z")
Et le synoptique de la carte de commande du pont tournant

Cordialement
Pascal
 
« Modifié: août 29, 2021, 09:12:35 pm par palou »
Le train train quotidien ... Oui Merci !!!

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 2616
  • 100% Arduino et N
    • Voir le profil
Re : projet de reseau etagere analogique
« Réponse #4 le: août 30, 2021, 10:06:43 am »
Il est évident que ton réseau analogique est très différent de mon réseau digital mais, du point de vue Can, nous mettons en œuvre un ensemble de sous-systemes interconnectés via Can et c’est là qu’il y a similitude.

En ce qui concerne la synchronisation des PWM, l’expert Locoduino est Jean-Luc.
Tu trouveras ici http://modelleisenbahn.triskell.org/ son site perso.

Cordialement,
Dominique

palou

  • Newbie
  • *
  • Messages: 10
  • presque à la retraite ...
    • Voir le profil
Re : projet de reseau etagere analogique
« Réponse #5 le: août 30, 2021, 03:07:16 pm »
Bonjour

Je viens de faire un tableau avec un plan d'adressage des ID CAN des modules :
type de contrôleurMasqueFiltre miniFiltre maxiNombre idsNotes
MASTER0x6fe0x6fe0x6fe1
TCO0x6ff0x6ff0x6ff1
spéciaux0x7f00x7f00x7ff16par exemple plaque tournante
aiguilles0x7c00x7c00x7df32commandes par relais (aiguilles et dételeurs)
pwm0x7a00x7a00x7bf32Alimentation des sections de voies
capteursOx7800x7800x79f32Capteurs "courant" + barrières IR
accessoires0x7600x7600x77f32sorties diverses (leds, servos, ...)

Pour préciser, les contrôleurs aiguilles peuvent piloter 8 solénoïdes, les contrôleurs pwm pourront alimenter 1 ou 2 sections chacun (voir la faisabilité avec la bibliothèque syncPwm), les contrôleurs capteurs devraient pouvoir surveiller 8 capteurs chacun, et il se peut que certains capteurs soient pris en charge par les contrôleurs PWM (à étudier) et enfin les contrôleurs accessoires seront à la demande vu la diversité des accessoires possibles (passage à niveau, éclairage, portes de remise, ...)

Est ce que ce plan d'adressage vous parait cohérent ?

cdt
Pascal
Le train train quotidien ... Oui Merci !!!

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 2616
  • 100% Arduino et N
    • Voir le profil
Re : projet de reseau etagere analogique
« Réponse #6 le: août 30, 2021, 09:43:58 pm »
Bonsoir,

Je ne suis pas à l'aise avec votre présentation par masque et filtre : ce qui compte c'est qu'il y ait un identifiant unique par émetteur (car il ne doit pas y avoir 2 messages émis avec le même identifiant par 2 émetteurs différents) et on ne voit pas qui est/sont les récepteurs et ce sont les récepteurs qui peuvent appliquer des masques et filtres pour ne voir que les messages qui les intéressent.
Evidemment je comprend que quand il y a un filtre min et max, cela veut dire qu'il y a une plage d'Id pour un ensemble de modules. Mais ce ne sont pas ces modules qui ont besoin de ces masques et filtres, ce sont les récepteurs qui sont d'autres modules (master, TCOs, etc..)
D'autre part, il faut faire jouer les priorités si nécessaire : les Id les plus petits seront prioritaires sur les plus grands. donc qui est prioritaire ?

Voici, comme exemple, les identifiants utilisés dans mon réseau :


J'ai listé tous les modules qui communiquent sur mon réseau et pour chacun, quel Id pour les messages émis, et quel(s) récepteur(s : souvent plusieurs) dont le gestionnaire de réseau qui reçoit tout en principe. Les masques et filtres c'est à voir après.
Pour les priorités :
- les détecteurs des zones d'arrêt (DP + feux) sont les plus prioritaires car les trains doivent stopper immédiatement.
- ensuite il y a les occupations de zones
- puis les demandes de commutation d'aiguilles
- puis les commandes de traction
- ...
- le RFID vient plus loin car c'est une confirmation de position, pas urgente
- puis viennent les commandes émises par le gestionnaire.

Je n'ai pas cherché de valeurs compliquées (il y aura plus tard des valeurs plus élevées pour les commandes du décor et des Id étendus pour les programmations)
« Modifié: août 30, 2021, 09:58:35 pm par Dominique »
Cordialement,
Dominique

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 2616
  • 100% Arduino et N
    • Voir le profil
Re : projet de reseau etagere analogique
« Réponse #7 le: août 30, 2021, 09:57:54 pm »
Au passage, vous pouvez supprimer le buffer de réception (Can_recup()) car la bibliothèque ACAN contient une file d'attente qui fait la même chose.
Regardez les articles sur la bibliothèque ACAN et mon article sur les détecteurs RFID (https://www.locoduino.org/spip.php?article271)
Cordialement,
Dominique

palou

  • Newbie
  • *
  • Messages: 10
  • presque à la retraite ...
    • Voir le profil
Re : projet de reseau etagere analogique
« Réponse #8 le: août 31, 2021, 01:09:37 am »
Bonsoir Dominique

Si je comprends bien ce que vous me dites, il faudrait prévoir pour chaque controleur au moins 2 ID : 1 d'action et 1 d'interrogation.

Dans mon cas la chaine de commandement serait TCO => Master (pour les ordres), Master => controleurs (actions), Master => controleurs (interrogations).
Dans le sens retour on aurait controleurs => Master (alertes (par exemple occupation/libération/fin d'action)), controleurs => Master (réponses aux interrogations), et enfin Master => TCO (affichage du résultat)

Donc le TCO devrait avoir au moins 1 ID (pour l'affichage), le Master au moins 3 ID (ordres TCO, alertes controleurs, réponses controleurs) et les controleurs au moins 2 ID (actions et interrogation), sachant qu'un controleur pourrait avoir plusieurs ID pour les actions si certaines actions sont plus prioritaires que d'autre (par exemple pour la plaque tournante, l'alimentation de la voie est plus importante que la rotation. Il faut qu'on coupe le courant avant de commencer à tourner et  donc il y aurait 2 ID d'actions pour le controleur de plaque...)

Et ensuite définir un ordre de priorite : Par exemple par ordre d'importance, les alertes (C => M), les ordres (T => M), les actions (M => C), les réponses (C => M), les interrogations (M => C) et enfin l'affichage (M => T), sachant que certaines actions d'un controleur sont plus prioritaires que d'autres: Par exemple , le controleur de section (qui alimente la voie) devrait etre plus prioritaire qu'un controleur d'aiguille donc ses actions et alertes devraient avoir une valeur d'id plus faibles par rapport aux équivalents "aiguilles".

J'ai tout compris ? :)

Pascal
Le train train quotidien ... Oui Merci !!!

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 2616
  • 100% Arduino et N
    • Voir le profil
Re : projet de reseau etagere analogique
« Réponse #9 le: août 31, 2021, 07:22:16 am »
Citer
J'ai tout compris ? :)

Pas tout à fait : chaque module a un Id et un seul pour les messages émis. Mais ces messages peuvent avoir plusieurs significations en jouant sur les octets de données.
Moi je positionne des bits dans l’octet 0 (le premier) pour cela (voir mon exemple RFID).
Les autres octets dépendent de la signification.

La bibliothèque ACAN permet de manipuler facilement ces octets grâce aux multiples définitions du tableau de 8 octets (union de bytes ou Int ou long).

N’oubliez pas que les messages émis sont reçus par tout le monde (d’où les masques et filtres pour limiter le dérangement). Et il faut au moins un récepteur sinon l’émetteur se bloque.

Cordialement,
Dominique

palou

  • Newbie
  • *
  • Messages: 10
  • presque à la retraite ...
    • Voir le profil
Re : projet de reseau etagere analogique
« Réponse #10 le: août 31, 2021, 11:14:17 am »
Bonjour

J'ai bien compris le coup de l'ID unique en réception. Par exemple 1 contrôleur de sections de voie est nécessaire pour chaque segment où un seul train peut se trouver à la fois. Concrètement, la gare terminus a besoin de 5 contrôleurs de sections si on ne compte pas le gril d'entrée (3 quais, la voie de débord derrière la plaque, le dépôt). Il faut donc que je prévoie pour chaque contrôleur de section un ID différent pour l'alimentation (vitesse et sens/brake/roue libre) + un ID pour les demandes d'infos (occupation). Coté master, pour gérer les contrôleurs, 1 seul ID unique suffira pour recevoir les infos des contrôleurs de sections + 1 ID unique pour les demandes urgentes des sections (par exemple le contrôleur peut demander "qu'est ce que je fais quand le train arrive dans la zone d’arrêt ? on continue (section suivante libre ?) ou on arrête (section suivante occupée ou gril non positionné) ?"

ce qui pourrait donner :
Contrôleur section Quai1 : ID commande = 0x10, ID demande = 0x50
Contrôleur section Quai 2: Id Commande = 0x11, ID Demande = 0x51
...
Master: ID alertes sections = 0x2F, ID réponse aux demandes sections = 0x6F (ces deux ID prenant les alertes et réponses provenant de tous les contrôleurs de sections du réseau)

Donc ce qu'il me reste à faire, c'est déterminer tous les contrôleurs nécessaires avec leur type (aiguille,section,accessoire, ...) pour le réseau, ensuite la nature des commandes recevables et leur priorité (action ou question non prioritaire) pour chaque type de contrôleur et pour l'ensemble du réseau, puis coté Master des ID pour la réception des alertes et infos, avec éventuellement un priorité entre les alertes et infos suivant le type de contrôleur émetteur.

Après tout ça, affecter des ID uniques pour chaque contrôleur avec la notion de priorité : Id faible = prioritaire)

En fait la liaison TCO <=> Master est la plus simple :
coté TCO un ID pour la maj des leds d’état (aiguille/occupation) +et éventuellement un autre Id pour les autres affichages (vitesse des trains ???)
coté Master un ID commande d'aiguillage +définition itinéraire et  éventuellement un ID "vitesse des trains et sens" (si tant est qu'on priorise la vitesse des trains)

Une fois les Id définis, pour chaque type de contrôleur, le master et le TCO, découper les 8 octets des messages échangés pour mettre dedans les infos (byte 0 = nature, byte 1 à 7 complément)

(à la limite, mettre tout ça dans un include permettrait de les partager entre les différents types de controleurs, le TCO et le master)

Cdt

Pascal

Le train train quotidien ... Oui Merci !!!

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 2616
  • 100% Arduino et N
    • Voir le profil
Re : projet de reseau etagere analogique
« Réponse #11 le: août 31, 2021, 05:46:06 pm »
Bien mieux qu’un texte : un diagramme d’échanges de messages comme par exemple ici :


Ceci permet de mettre les relations entre les fonctions de l’émetteur qui envoie un message avec les fonctions du récepteur qui va traiter ce message.

C’est un gros boulot de passer tous les cas possibles, mais c’est essentiel  :P

Mais l’avantage est que les développements se font petit à petit, modules par modules, éventuellement sur plusieurs années comme c’est le cas pour moi (5 ans déjà) où tout marche depuis le début et s’enrichit sans casser l’existant: j’arrive au gestionnaire global maintenant et ça fonctionne !
Cordialement,
Dominique

palou

  • Newbie
  • *
  • Messages: 10
  • presque à la retraite ...
    • Voir le profil
Re : projet de reseau etagere analogique
« Réponse #12 le: septembre 05, 2021, 12:47:41 am »
Bonsoir Dominique

J'ai suivi votre conseil et j'ai établi une priorité entre les ID suivant leur fonctionnalité:
Tout d'abord, la liste des types de composants prévus (j'ai prévu plus large que le nombre nécessaire au cas où ...). Comme le réseau sera modulaire, chaque module aura ses propres  composants gérant les sections, les aiguilles/dételeurs et accessoires du module d'installés.
ComposantNombre maxiutilisation
MASTER1centre névralgique: possède la typologie du réseau
TCO1Affichage état + prise de commandes manuelles (aiguilles/dételeurs/vitesse des trains,…)
SECTION32Alimentation des sections + géstion des zones d'arrêt + détections présence
SOLENOIDE16Commandes appareils de voie (aiguilles/dételeur) (4 aiguilles/8 dételeurs par composant)
PLAQUE2Plaque tournante (rotation + alimentation + détection présence)
COULISSE2Coulisse du réseau (position sortie + alimentation + détection présence)
ACCESSOIRE16Pilotage des accessoires (éclairage, animation, …)

Le masque CAN est simple : 0x7FF. Les filtres CAN sont positionnés de la manière suivante 0x700 | ID du composant. Il y a plusieurs ID par composants (7 maximum). En cas de composant multiple (solénoide, section, ...) l'ID est choisi en fonction de l'id de base de la fonction + le numéro d'ordre du composant. Par exemple pour la section n° 3 la commande de vitesse/sens aura comme ID 0x50 + 3 = 0x53 et le filtre correspondant ( 0x700 | 0x53 = 0x753) sera implanté dans le composant.

IDNomSourceCibleutilisation
0x01MASTER_ALERTEtousMASTERréception des alertes provenant des autres composants
0x03MASTER_ERREURtousMASTERréception des erreurs provenant des autres composants
0x05MASTER_ORDRETCOMASTERréception des ordres provenant du TCO
0x07MASTER_ACKtousMASTERréception acquittement des ordres transmis
0x09MASTER_ACCESSOIRETCOMASTERréception ordres accessoires provenant du TCO
0x0AMASTER_FINItousMASTERréception signal de fin d'action
0x0FMASTER_INFOtousMASTERréception infos provenant des autres composants
0x10-0x2FSECTION_ARRETMASTERSECTIONCommandes zones d’arrêt des sections
0x30-0x31PLAQUE_VITESSEMASTERPLAQUEcommande vitesse/sens sur plaque
0x32-0x33PLAQUE_POSITIONMASTERPLAQUEcommande position plaque
0x34-0x35COULISSE_VITESSEMASTERCOULISSECommande vitesse/sens dans coulisse
0x36-0x37COULISSE_POSITONMASTERCOULISSEcommande position coulisse
0x40-0x4FSOLENOIDE_ORDREMASTERSOLENOIDECommandes aiguilles + dételeurs
0x50-0x6FSECTION_VITESSEMASTERSECTIONcommandes vitesse/sens des sections
0x70-0x7FSOLENOIDE_INFOMASTERSOLENOIDEdemandes états aiguilles/dételeurs
0x80-0x9FSECTION_INFOMASTERSECTIONdemandes état des sections
0xA0-0xAfACCESSOIRE_ACTIONMASTERACCESSOIREmodification accessoire
0xD0-0xD1PLAQUE_INFOMASTERPLAQUEdemandes état des plaques tournantes
0xD2-0xD3COULISSE_INFOMASTERCOULISSEdemande état de la coulisse
0xE0-0xEFACCESSOIRE_INFOMASTER   ACCESSOIREdemande d'info accessoire
0xF0TCO_AFFICHAGEMASTERTCOAffichage sur le TCO (position aiguille, occupation,vitesse…)

Les ID sont implantés sous formes de filtres sur les composants cible
Les ordres prioritaires sont ceux destinés au MASTER, puis les commandes de vitesse, puis les commandes de positionnement, et enfin les ordres d'information.


Un échange type sera le suivant :
<action> TCO MASTER_ORDRE -> MASTER demande d'action (aiguille, dételeur,vitesse) <vérification possible> SOLENOIDE_ORDRE-> SOLENOIDE <verification possible et mise en pile commande>  MASTER_ACK -> MASTER  TCO_AFFICHAGE-> TCO (clignotement)
Une fois l'aiguille positionnée SOLENOIDE MASTER_FINI-> MASTER TCO_AFFICHAGE-> TCO (nouvelle position)
autre exemple
SECTION détection en fin de section MASTER_ALERTE-> MASTER (vérification suivant libre ou occupé) si occupé SECTION_ARRET-> SECTION vitesse nulle ou coupure section d'arrêt (arrêt)  MASTER_ACK-> MASTER  TCO_AFFICHAGE-> TCO affichage vitesse

Je n'ai pas fait de joli diagramme d'échange mais j'ai commencé de faire un .h décrivant l'architecture globale (IDs de base, dispatch des aiguilles/deteleurs sur les composants solenoides...)
De cette maniere la configuration se fera en fonction d'un numéra d'ordre positionné pour aller chercher les infos adhoc suivant le type de composant et son numéro. L'avantage, c'est que comme les infos sont centralisées, chaque module aura la même base que ses petits camarades et du coup le risque de coquille est limité dans les sources.

Cdt
Pascal
Le train train quotidien ... Oui Merci !!!

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 2616
  • 100% Arduino et N
    • Voir le profil
Re : Re : projet de reseau etagere analogique
« Réponse #13 le: septembre 05, 2021, 07:41:10 am »

Un échange type sera le suivant :
<action> TCO MASTER_ORDRE -> MASTER demande d'action (aiguille, dételeur,vitesse) <vérification possible> SOLENOIDE_ORDRE-> SOLENOIDE <verification possible et mise en pile commande>  MASTER_ACK -> MASTER  TCO_AFFICHAGE-> TCO (clignotement)
Une fois l'aiguille positionnée SOLENOIDE MASTER_FINI-> MASTER TCO_AFFICHAGE-> TCO (nouvelle position)
autre exemple
SECTION détection en fin de section MASTER_ALERTE-> MASTER (vérification suivant libre ou occupé) si occupé SECTION_ARRET-> SECTION vitesse nulle ou coupure section d'arrêt (arrêt)  MASTER_ACK-> MASTER  TCO_AFFICHAGE-> TCO affichage vitesse


Bonjour Pascal,

Pour que je comprennes bien, peux tu indiquer l’ID des messages dans ces deux exemples, pour bien voir celui qui l’émet et qui le reçoit (et filtre) ?

CDT
Dominique
Cordialement,
Dominique

palou

  • Newbie
  • *
  • Messages: 10
  • presque à la retraite ...
    • Voir le profil
Re : projet de reseau etagere analogique
« Réponse #14 le: septembre 05, 2021, 06:09:34 pm »
Bonjour Dominique

Pour reprendre les exemples :
tous types de modules : le masque est égal 0x7FF ce qui correspond à "aucun masque" si j'ai bien compris
Les filtres sont positionnés comme suivant suivant les types:
Master :
0x701 (MASTER_ALERTE)
0x703 (MASTER_ERREUR)
0x705 (MASTER_ORDRE)
0x707 (MASTER_ACK)
0x709 (MASTER_ACCESSOIRE)
0x70A (MASTER_FINI)
0x70F (MASTER_INFO)

TCO:
0x7F0 (TCO_AFFICHAGE)

SOLENOIDE (dans notre exemple, le contrôleur de solénoïdes n° 2)
0x742 (SOLENOIDE_ORDRE)
0x772 (SOLENOIDE_INFO)
Si c'était le controleur de solénoïdes n°4 les filtres seraient les suivants
0x744 (SOLENOIDE_ORDRE)
0x774 (SOLENOIDE_INFO)


SECTION (dans notre exemple, le contrôleur de section n° 3)
0x713 (SECTION_ARRET)
0x753 (SECTION_VITESSE)
0x783 (SECTION_INFO)
Si c'était le  contrôleur de section n° 17 (0x11):
0x721 (SECTION_ARRET)
0x761 (SECTION_VITESSE)
0x791 (SECTION_INFO)

Premier exemple, action sur aiguillage depuis le TCO
utilisateurTCOmessageMASTERmessageSOLENOIDE
modifie position aiguille n° 7envoi de l'information au MASTER==> MASTER_ORDREconvertit le numéro de l'aiguille en couple contrôleur/sortie et envoie l'info au contrôleur de solénoide==> SOLENOIDE_ORDREréception du message
Affichage clignotant de l'aiguille<== TCO_AFFICHAGEréception du ACK et demande d'affichage (aiguille en attente)<== MASTER_ACKMise en liste d'attente de la commande (pas d'activation simultanée de solénoïdes)
Affichage fixe de l'aiguille<== TCO_AFFICHAGEmodification aiguille terminée. On affiche le résultat final sur le TCO<== MASTER_FINIL'aiguille a été actionnée

Second exemple: Un train va sortir d'une section (section courante: section ou se trouve le train, section suivante: section en aval de la section courante)
utilisateurTCOmessageMASTERmessageSECTION
Vérification de l'état de la section suivante<== MASTER_ALERTE(section courante) Un train a atteint l'extrémité de la section et va sortir de la section
premier cas: La section suivante est libre et accessible
Alimentation section suivante (pas la même)et attente sortie de la section courante==> SECTION_VITESSE(section suivante) Alimentation des voies
<== MASTER_ACK(section suivante) acquittement
Affichage état des sections<== TCO_AFFICHAGEAffichage sur TCO: section courante libérée, section suivante occupée<== MASTER_FINI(section courante) Le train n'est plus dans la section
second cas: La section suivante est occupée ou inaccessible
Affichage état du train<== TCO_AFFICHAGEArrêt !!! et indication TCO que le train est arrêté==> SECTION_STOP(section courante) coupure alimentation des voies
<== MASTER_ACK(section courante) acquittement
un ange passe ... la section suivante devient accessible ...==> SECTION_VITESSE(section courante) réalimentation des voies
<== MASTER_ACK(section courante) acquittement
A partir de là, on repart depuis le premier cas

Une question au passage : Dans votre programme de gestion des RFID j'ai remarqué plusieurs choses:
Vous déclarez la fonction messageCmd. Pour moi cette fonction est appelée automatiquement dès qu'un filtre s'active (un peu comme une interruption)
void messageCmd (const CANMessage & inMessage) {
  uint32_t mId = inMessage.id;
  uint8_t cmdType = inMessage.data[0];
  if ((mId & 0x1F) == EstouOuest) { // test 1A (EST) ou 1B (OUEST)
   
  }
}

const ACAN2515Mask masque = extended2515Mask (0x1FFFFFFF) ; // For filter #0 and #1
  const ACAN2515AcceptanceFilter filtres [] = {
    {extended2515Filter (0x1FFFFF7A), messageCmd},
    {extended2515Filter (0x1FFFFF7B), messageCmd},
  } ;

Par contre ce que je ne comprends pas, c'est pourquoi elle est rappelée dans le "loop":
  // réception des messages CAN : voir messageCmd
  if (controleurCAN.receive(messageCANReception)) {
    Serial.print("->recu : ");
    Serial.print(messageCANReception.len);
    Serial.print(", data : ");
    Serial.print(messageCANReception.data[0], HEX);
    Serial.print(", ");
    Serial.print(messageCANReception.data[1], HEX);
    Serial.print(", ");
    Serial.println(messageCANReception.data[2], HEX);
    messageCmd (messageCANReception);
  }
Est ce que le driver CAN ne va pas l'appeler lui même ?

D'autre part: est il conseillé de déclarer plusieurs fonctions en fonction du filtrage genre
const ACAN2515Mask masque = extended2515Mask (0x1FFFFFFF) ; // For filter #0 and #1
  const ACAN2515AcceptanceFilter filtres [] = {
    {extended2515Filter (0x1FFFFF7A), messageAlert},
    {extended2515Filter (0x1FFFFF7B), messageTCO},
  } ;
ou traiter tous les filtrages dans la même fonction (comme dans votre programme) ?

Enfin A priori vous utilisez un adressage étendu (extended2515Mask et extended2515Filter). Quel est l'avantage en dehors d'étendre le nombre d'id disponibles ?

Cdt
Pascal


Le train train quotidien ... Oui Merci !!!