Identification des trains par Arduino

Toutes les discussions sur l'Arduino !

Modérateur : MOD

Avatar du membre
Trusty
Bavard
Messages : 66
Enregistré le : lun. 03 déc. 2012, 11:04
Echelle pratiquée : N
Prénom : Thierry
Localisation : Melun
Âge : 57

Re: Identification des trains par Arduino

Message par Trusty » dim. 04 janv. 2015, 15:30

Si on veut pousser le bouchon, il y a des Mega à moins de 10€ ici http://www.banggood.com/Mega2560-R3-ATm ... 73020.html avec des frais de port gratuit, mais une réception de quatre à six semaines, c'est long... Si on veut plus de service et de rapidité, il a aussi ici http://www.electrodragon.com/product/ne ... mega-2560/ un peu plus cher, avec environ 4€ de frais de port, expédié par la poste belge (!) en 5 à 13 jours...
Rien à craindre du côté des douanes qui tolèrent les paquets de moins de 50€ de valeur... Je l'ai déjà fait plusieurs fois, y compris avec des paquets plus cher, mais là c'est la loterie...

Avatar du membre
Pierre59
Papotier
Messages : 147
Enregistré le : dim. 07 mars 2010, 09:17
Echelle pratiquée : HO
Club : Lille Modélisme
Localisation : Villeneuve d'Ascq (59650)
Âge : 76

Re: Identification des trains par Arduino

Message par Pierre59 » dim. 04 janv. 2015, 17:11

Bonjour

Pour gagner de la place dans la précieuse RAM, on peut allouer des bits plutot que des octets, Par exemple :

Code : Tout sélectionner

struct {
	unsigned char eff:1; // boolean     
	unsigned char lu:1; // boolean         
	unsigned char nb:4; // entier de 0 a 15
	unsigned char feux:1; // boolean       
	unsigned char nom:1; // boolean        
	
} infos={false,false,7,false,false};

void loop() { // utilisation

  if (infos.lu!=true) infos.lu=false;
  
  if (infos.nb!=7) infos.nb=9;
}
les 5 variables tiennent sur UN octet, au lieu des 5 qui seraient normalement nécessaires. Ceci au prix d'un programme un peut plus gros.

On aussi gagner beaucoup de mémoire RAM au prix d'une programmation plus technique et d'un programme encore plus gros, exemple :

Code : Tout sélectionner

// aiguille

struct Aiguille {
  boolean position; // true : directe   false : deviee

  Aiguille() { position=true; } // constructeur
  
  boolean directe() { return position==true; } // fonction utilitaire
  boolean deviee() { return position==false; } // fonction utilitaire
};

// canton

struct Canton { // classe virtuelle (on ne sait pas quel est le suivant ni le precedent)
  boolean etat; // true : occupe   false : libre

  Canton() { etat=false; } // constructeur
  
  virtual Canton* suivant()=0;   // pour avoir une erreur si on l'oublie dans les vrais cantons
  virtual Canton* precedent()=0; // pour avoir une erreur si on l'oublie dans les vrais cantons
  
  Canton* selon(Aiguille* a,Canton* c1,Canton* c2) { if (a->directe()) return c1; else return c2; } // fonction utilitaire
};

// les vrais cantons 

struct C1 : Canton { Canton* suivant(); Canton* precedent(); };
struct C2 : Canton { Canton* suivant(); Canton* precedent(); };
struct C3 : Canton { Canton* suivant(); Canton* precedent(); };
struct C4 : Canton { Canton* suivant(); Canton* precedent(); };

// declaration des aiguilles et des cantons

Aiguille a1=Aiguille(),a2=Aiguille(),a3=Aiguille();

C1 c1=C1(); C2 c2=C2(); C3 c3=C3(); C4 c4=C4();

// definitions des fonctions suivant et precedent ( ici car elles utilisent les declaration d'aiguilles et de cantons)

Canton* C1::suivant()   { return &c1; } 
Canton* C1::precedent() { return &c2; }

Canton* C2::suivant()   { return selon(&a1,&c1,&c3); } // selon aiguille a1
Canton* C2::precedent() { return &c3; }

Canton* C3::suivant()   { return &c2; } 
Canton* C3::precedent() { return selon(&a1,selon(&a2,&c1,&c2),&c4); } // selon aiguille a1 et a2

Canton* C4::suivant()   { return &c1; } 
Canton* C4::precedent() { return selon(&a1,selon(&a2,&c1,&c2),selon(&a3,&c3,&c4)); } // selon aiguille a1, a2 et a3

// le programme

void setup() {}

void loop() { // utilisation

  Canton* c=c2.suivant(); // on obtient le suivant du canton c2 (c1 ou c3) selon la position de l'aiguille a1 au moment de l'appel

}
On utilise ici la programmation orientée objet permise par le C++. Une structure (classe en fait) Aiguille est déclarée, puis une structure de base Canton (avec des fonctions suivant et précédent). Ensuite une structure (classe) est déclarée pour chaque canton, chacune de ces structures se base sur la structure Canton (héritage en fait) et définit les fonctions suivant() et precedent() propres à chaque canton (en fonction d'aiguilles si nécessaire).

Les définitions effectives des fonctions suivant() et precedent() doivent se trouver après les déclarations des cantons car elles les utilisent. Le C++ est un peu lourd dans l'écriture, de plus pour pouvoir mettre dans une variable de type Canton un canton quelconque (c1 c2 ...) il faut utiliser des pointeurs.

Voila ici il n'y a plus de table, tout est codé dans le programme.

J'ai bien conscience que c'est assez compliqué, mais c'est très facilement extensible et adaptable à n'importe quelle configuration.

Ne pas hésiter à poser des questions.

Pierre

Avatar du membre
groumfit
Papotier
Messages : 143
Enregistré le : sam. 25 oct. 2014, 11:25
Prénom : Denis
Âge : 63

Re: Identification des trains par Arduino

Message par groumfit » dim. 04 janv. 2015, 18:03

Merci à tous ceux qui s'intéressent à ce programme.
Merci donc à Pierre59 pour le dernier post.
J'avais déjà choisi des "byte" au lieu des "int" pour gagner de la place.
Pour quasiment tout, comme je n'ai pas plus de 128 cantons (quand même !), ça suffit.
J'ai 3 colonnes qui pourraient être booléennes (occupation du canton, sens de déplacement du train et position de l'aiguille).
Je ne savais pas qu'on pouvait mélanger les types dans la même array.
Ta structure me plait car au lieu de 3 octets (et donc 3*45 = 135 octets pour toute la table), je n'aurais plus que 3*45 bits < 17 octets.
Je vais creuser, c'est une excellente piste.
Ta deuxième piste est très certainement meilleure, mais, là, je décroche.
Pour ce que le programme doit faire, je pense que, au pire, j'utiliserai un mega.
Dans un premier temps, je vais essayer de finir ce programme.
Puis potasser les classes et les pointeurs qui ont l'air de faire peur à tout le monde :roll:
J'y vois pourtant une énorme puissance, quand on maîtrise.
Là, c'est mon tout premier programme Arduino... :D
J'y arriverai ... demain !

Avatar du membre
groumfit
Papotier
Messages : 143
Enregistré le : sam. 25 oct. 2014, 11:25
Prénom : Denis
Âge : 63

Re: Identification des trains par Arduino

Message par groumfit » dim. 04 janv. 2015, 20:22

Bon, voilà, les vacances sont finies. :cry:
J'ai revu mes commentaires, corrigé quelques bogues.
Maintenant, on a les feux sémaphore, carré et avertissement à jour dans les 2 sens pour tous les cantons.
Il reste les RR, RR+A, ...
J'ai simulé des occupations de canton pour les tests (colonne 4) Table_Cantons

Code : Tout sélectionner

// Denis_Deffunt_101

#include <Wire.h>              // La bibliothèque Wire.h gère le bus I2C
#include <Adafruit_MCP23017.h>  // La bibliothèque Adafruit_MCP23017 gère le circuit MCP23017 (16 sorties par boitier)

byte Bus_Occ=2;
byte Bus_Sens=3;
byte Bus_Position=4;
byte Occup=0;
byte Cmax=45;  //Nombre maximal de la Table_Cantons
byte Imax=5;  //Nombre maximal de la Table_Iti
byte Nb_Boitiers=2;
byte Cn;
byte Cn_Plus_1;
byte Canton;
byte Aiguille;
byte Position;
byte Sens_H;  //1 = Sens horaire, 0 = antihoraire
byte Ligne;
byte Ligne_Canton_1;
byte Ligne_Canton_2;
byte Ligne_Iti;


/*---------------------------------------- Table_Cantons récupère tous les résultats :
Cn+1 s'entend comme le canton suivant sens anti-horaire
Cn-1 s'entend comme le canton précedent sens anti-horaire
Pour un vrai canton, on remplit en tenant compte du sens
Pour une aiguille, on ne tient pas compte du sens : Cn-1 = côté pointe, Cn+1 = côté sorties
Colonne 0 = Canton Cn-1 si c'est un vrai canton, Cn-1 côté pointe si c'est une aiguille
Colonne 1 = Canton Cn
Colonne 2 = Canton Cn+1 si c'est un vrai canton, Cn+1 tout droit si c'est une aiguille
Colonne 3 = 0 si c'est un vrai canton, Cn+1 dévié si c'est une aiguille
Colonne 4 = occupation du canton Cn (0/1)
Colonne 5 = sens de déplacement (1= sens horaire/0 = sens anti-horaire
Colonne 6 = position de l'aiguille (1 = TD (tout droit)/2 = DV (dévié))
Colonne 7 = Canton suivant
Colonne 8 = numéro d'affichage du feu dans le sens anti-horaire
Colonne 9 = numéro d'affichage du feu dans le sens horaire

Dans le cas qui suit, on traite d'un circuit à 19 cantons et 26 aiguilles
J'ai simulé l'occupation des cantons 30,31 et 39
*/
byte Table_Cantons [45][10] =
{ { 25,1,2,3,0,0,1,0,0,0},
  { 7,2,1,4,0,0,1,0,0,0},
  { 4,3,26,1,0,0,0,0,0,0},
  { 3,4,2,5,0,0,0,0,0,0},
  { 6,5,27,4,0,0,2,0,0,0},
  { 5,6,7,11,0,0,2,0,0,0},
  { 8,7,6,2,0,0,2,0,0,0},
  { 31,8,7,9,0,0,1,0,0,0},
  { 10,9,37,8,0,0,1,0,0,0},
  { 9,10,11,12,0,0,1,0,0,0},
  { 10,11,28,6,0,0,2,0,0,0},
  { 10,12,30,29,0,0,0,0,0,0},
  { 30,13,18,14,0,0,1,0,0,0},
  { 15,14,29,13,0,0,1,0,0,0},
  { 14,15,18,16,0,0,2,0,0,0},
  { 17,16,28,15,0,0,2,0,0,0},
  { 16,17,19,24,0,0,2,0,0,0},
  { 19,18,13,15,0,0,1,0,0,0},
  { 20,19,17,18,0,0,2,0,0,0},
  { 42,20,19,21,0,0,1,0,0,0},
  { 22,21,36,20,0,0,1,0,0,0},
  { 21,22,24,23,0,0,1,0,0,0},
  { 22,23,45,26,0,0,0,0,0,0},
  { 22,24,27,17,0,0,2,0,0,0},
  { 45,25,1,0,0,0,0,0,0,0},
  { 23,26,3,0,0,0,0,0,0,0},
  { 24,27,5,0,0,0,0,0,0,0},
  { 16,28,11,0,0,0,0,0,0,0},
  { 14,29,12,0,0,0,0,0,0,0},
  { 13,30,12,0,1,0,0,0,0,0},
  { 8,31,32,0,1,0,0,0,0,0},
  { 31,32,33,0,0,0,0,0,0,0},
  { 32,33,43,0,0,0,0,0,0,0},
  { 43,34,35,0,0,0,0,0,0,0},
  { 34,35,36,0,0,0,0,0,0,0},
  { 35,36,21,0,0,0,0,0,0,0},
  { 9,37,38,0,0,0,0,0,0,0},
  { 37,38,39,0,0,0,0,0,0,0},
  { 38,39,40,0,1,0,0,0,0,0},
  { 39,40,41,0,0,0,0,0,0,0},
  { 40,41,42,0,0,0,0,0,0,0},
  { 41,42,20,0,0,0,0,0,0,0},
  { 33,43,34,44,0,0,2,0,0,0},
  { 43,44,45,0,0,0,0,0,0,0},
  { 23,45,44,25,0,0,0,0,0,0},
};

/*--------------------------------------Table des itinéraires----------------------------------
Décrit tous les itinéraires formés. C'est une table résultat.
Cette table est décrite ici "en dur", pour l'expérimentation, mais elle doit être calculée à partir de la Table_Cantons.
Cette partie du programmes est à venir ...
Table_Iti doit toujours être décrite dans le sens horaire, par définition

Colonne 0 = Canton départ
Colonne 1 = Aiguille 1
Colonne 2 = Aiguille 2
Colonne 3 = Aiguille 3
Colonne 4 = Aiguille 4
Colonne 5 = Aiguille 5
Colonne 6 = Aiguille 6
Colonne 7 = Aiguille 7
Colonne 8 = Canton arrivée
*/
byte Table_Iti [5][9] =
{ { 33,43,0,0,0,0,0,0,44},
  { 25,1,2,7,8,0,0,0,31},
  { 27,5,6,11,10,9,0,0,37},
  { 36,21,22,24,17,16,15,14,29},
  { 42,20,19,18,13,0,0,0,30},
};

/*
Basic pin reading and pullup test for the MCP23017 I/O expander
public domain!
Connect pin #12 of the expander to Analog 5 (i2c clock)
Connect pin #13 of the expander to Analog 4 (i2c data)
Connect pins #15, 16 and 17 of the expander to ground (address selection)
Connect pin #9 of the expander to 5V (power)
Connect pin #10 of the expander to ground (common ground)
Connect pin #18 through a ~10kohm resistor to 5V (reset pin, active low)

Un MCP23017 a une adresse = 0,1,0,0,A0,A1,A2
Pour le 1er MCP23017, A0 = 0, A1 = 0, A2 = 0
Pour le deuxième,     A0 = 0, A1 = 0, A2 = 1
....
Pour le dernier       A0 = 1, A1 = 1, A2 = 1
On adresse ainsi 8 boitiers MCP23017
*/

Adafruit_MCP23017 mcp[2];  // On déclare autant d'objets Adafruit_MCP23017 que de boitiers. Ici 2.

//--------------------------------------------Recherche d'un Canton dans la Table_Cantons-------------------------------
byte Rech_Canton_Table_Cantons (byte Canton)
{
  for (byte i=0; i<Cmax; i++)
  {
    if (Canton == Table_Cantons [i][1])
    {
      Ligne = i;
      return Ligne;
      break;
    }
  }
}
//-------------------------------------------Recherche d'un Canton Aiguille dans la Table_Iti----------------------------------
byte Rech_Canton_Aiguille_Table_Iti (byte Canton, byte Aiguille)
{
  Cn_Plus_1 = 0;
  for (byte i=0; i<Imax; i++)
  {
    if (Canton == Table_Iti [i][0])
    {
      for (byte j=1; j<8; j++)
      {
        if (Aiguille == Table_Iti [i][j])
        {
          Cn_Plus_1 = Table_Iti [i][8];
          break;
        }
      }
    }
    else
    {
      if (Canton == Table_Iti [i][8])
      {
        for (byte j=1; j<8; j++)
        {
          if (Aiguille == Table_Iti [i][j])
          {
            Cn_Plus_1 = Table_Iti [i][0];
            break;
          }
        }
      }
    }
  }
  return Cn_Plus_1;
}
//--------------------------------------------Recherche du canton suivant une aiguille dans la Table_Iti------------------------------------------------
byte Rech_Suiv_Aig_Table_Iti (byte Aiguille,byte Sens_H)
{
  Cn_Plus_1 = 0;
  for (byte i=0; i<Imax; i++)
  {
    for (byte j=1; j<8; j++)
    {
      if (Aiguille == Table_Iti [i][j])
      {
        Cn_Plus_1 = Table_Iti [i][j+1-2*Sens_H];
        if (Cn_Plus_1 == 0)
        {
          Cn_Plus_1 = Table_Iti [i][8];
        }
      }
    }
  }
  return Cn_Plus_1;
}
//--------------------------------------------Recherche du canton suivant en fonction du sens (horaire ou anti-horaire)---------------------------------------------------
byte Canton_Suivant (byte Canton,byte Sens_H)
{
  Rech_Canton_Table_Cantons (Canton);
  Ligne_Canton_1 = Ligne;
  // Quel est le type du canton ?
  if (Table_Cantons [Ligne_Canton_1][3] != 0)
  {
    // Le canton est une aiguille
    // La table_Iti va donner le canton suivant
    Rech_Suiv_Aig_Table_Iti (Canton,Sens_H);
    return Cn_Plus_1;     
  }
  else
  {
    // Le canton est un vrai canton. Quel est le type du canton suivant suivant ?
    Cn_Plus_1 = Table_Cantons [Ligne_Canton_1][2-2*Sens_H];
    Rech_Canton_Table_Cantons (Cn_Plus_1);
    Ligne_Canton_2 = Ligne;
    if (Table_Cantons [Ligne_Canton_2][3] != 0)
    {
      // Le canton suivant suivant est une aiguille
      // La table_Iti va donner le canton final
      Rech_Canton_Aiguille_Table_Iti(Canton,Cn_Plus_1);
      return Cn_Plus_1;
    }
    else
    {
      // Le canton suivant suivant est un vrai canton
      // On peut sortir, on a trouvé Cn_Plus_1
      return Cn_Plus_1;  
    }
  }
}

//******************************************* Programme principal ***********************
//-------------------------------------------setup-------------------------------------
void setup() 
{
  Serial.begin(9600);  
  for (byte i=0; i<2; i++)
  {
    mcp[i].begin(i); //On initialise chaque boitier
    for (byte j=0; j<16; j++)
    {
      mcp[i].pinMode(j, OUTPUT);  //On met toutes les broches en sortie
    }
  }
  pinMode (Bus_Occ,INPUT);  // broche Bus_Occ de l'Arduino pour le résultat
}

//-------------------------------------------loop---------------------------------------
void loop()
{
  /*
  La gestion des MCP 23017 fonctionne parfaitement, mais elle est ici en commentaire car ce n'est pas ça que je teste en ce moment
  ----------------------------------------------------------gestion du bus I2C---------------
  for (byte boitier=0; boitier<Nb_Boitiers; boitier++)
  {
    for (byte broche=0; broche<16; broche++)
    {
        Occup = 0;
        mcp[boitier].digitalWrite (broche, HIGH);
        Occup = digitalRead (Bus_Occ);
        Table_Cantons [boitier*16+broche][1] = Occup;
        mcp[boitier].digitalWrite (broche, LOW);
    }
  }  
  //La table est remplie avec les occupations des cantons (Colonne 4)
  for (byte canton=0; canton<32; canton++)
  {
    Serial.print(canton);
    Serial.print(" ");
    Serial.print(Table_Cantons [canton][0]);
    Serial.print(" ");
    Serial.println(Table_Cantons [canton][4]);
    delay(500);
  }
  */
  //---------------------------remplissage des colonnes 7 (=Cn+1), 8 (= feu sens anti horaire), 9 (= feu sens horaire) de la Table_Cantons--------------
  //Sont réalisés : carré, sémaphore et avertissement
  //
  for (byte Sens_H=0; Sens_H<2; Sens_H++)
  {
    //-----------------------------Canton suivant puis Carré (1) et Sémaphore (2)---------------------
    for (byte n=0; n<Cmax; n++)
    {
      Cn = Table_Cantons [n][1];
      Canton_Suivant (Cn,Sens_H);
      Table_Cantons [n][7] = Cn_Plus_1;
      if ((Table_Cantons [n][3] == 0) && (Cn_Plus_1 == 0))
      {
        // Cn est un vrai canton et Cn+1 = 0 : feu au carré
        Table_Cantons [n][8+Sens_H] = 1;
      }
      else
      {
        Rech_Canton_Table_Cantons (Cn_Plus_1);
        if ((Table_Cantons [n][3] == 0) && (Table_Cantons [Ligne][3] == 0))
        {
          // Cn est un vrai canton et Cn+1 aussi
          if (Table_Cantons [Ligne][4] == 1)
          {
            // Cn+1 est occupé : feu au sémaphore
            Table_Cantons [n][8+Sens_H] = 2;
          }
        }
      }
    }
  //-----------------------------------Avertissement (3)---------------------------------------
    for (byte n=0; n<Cmax; n++)
    {
      if (Table_Cantons [n][3] == 0)
      {
        // Cn est un vrai canton
        Cn = Table_Cantons [n][7];
        Rech_Canton_Table_Cantons (Cn);
        if ((Table_Cantons [Ligne][8+Sens_H] == 1) || (Table_Cantons [Ligne][8+Sens_H] == 2))
        {
          // Cn+2 est au carré ou au sémaphore
          if ((Table_Cantons [n][8+Sens_H] != 1) && (Table_Cantons [n][8+Sens_H] !=2))
          {
          // On n'efface pas sémaphore ou carré, prioritaires
          Table_Cantons [n][8+Sens_H] = 3;
          }
        }
      }
    }
  }
  //------------------------------------Affichage des résultats------------------
  for (byte n=0; n<Cmax; n++)
  {  
    Serial.print(Table_Cantons [n][1]);
    Serial.print("-->");
    Serial.print(Table_Cantons [n][7]);
    Serial.print(" ");
    Serial.print(Table_Cantons [n][8]);
    Serial.print(" ");
    Serial.println(Table_Cantons [n][9]);
    
    delay(100);
  } 
}
J'y arriverai ... demain !

Avatar du membre
groumfit
Papotier
Messages : 143
Enregistré le : sam. 25 oct. 2014, 11:25
Prénom : Denis
Âge : 63

Re: Identification des trains par Arduino

Message par groumfit » ven. 09 janv. 2015, 18:56

Bonjour à tous,
J'ai avancé sur plusieurs fronts.
1°) La table des itinéraires formés est maintenant au départ vide et je la construit complètement grâce à la position des aiguilles.
C'est important puisque cette table doit vivre en fonction de l'évolution des trains.
2°) Pour les feux, j'ai C, S, A, VL, R30, RR30 pour tous les cantons, dans tous les sens.
3°) La table décrivant les cantons a maintenant 100 lignes, celle décrivant les itinéraires formés est passée à 10 lignes, sans qu'on bloque en SRAM, même avec un Uno !
Remarquez que 10 itinéraires formés, c'est énorme.
Avec 54 itinéraires possibles pour mon réseau, je n'ai que 5 itinéraires formés maxi.
4°) Tous les commentaires sont à jour. Pour info, 648 lignes.

Code : Tout sélectionner

// Denis_Deffunt_101

#include <Wire.h>              // La bibliothèque Wire.h gère le bus I2C
#include <Adafruit_MCP23017.h>  // La bibliothèque Adafruit_MCP23017 gère le circuit MCP23017 (16 sorties par boitier)

byte Bus_Occ=2;            // Bus occupation broche 2
byte Bus_Sens=3;           // Bus sens de déplacement broche 3.
// Ne pas confondre ce sens de déplacement avec le sens horaire ou anti-horaire qui sert aux signaux
byte Bus_Position=6;       // Bus position des aiguilles en broche 6
byte Occup=0;
byte Cmax=45;              //  Nombre maximal de la Table_Cantons (Cmax est (pour l'instant) limité à 100
byte Imax=5;               //  Nombre maximal de la Table_Iti (Imax est inférieur à 10)
byte Nb_Boitiers=2;        // Nombre de boîtiers MCP23017 sur le bus I2C (broches 4 et 5)
byte Cn;                   // Canton Cn
byte Cn_Plus_1;            // Canton Cn+1
byte Canton;
byte Aiguille;
byte Position;
byte Sens_H;               //1 = Sens horaire, 0 = antihoraire
byte Ligne;
byte Ligne_Canton_1;
byte Ligne_Canton_2;
byte Ligne_Iti;


/*---------------------------------------- Table_Cantons récupère tous les résultats :
Cn+1 s'entend comme le canton suivant sens anti-horaire
Cn-1 s'entend comme le canton précedent sens anti-horaire
Pour un vrai canton, on remplit en tenant compte du sens
Pour une aiguille, on ne tient pas compte du sens : Cn-1 = côté pointe, Cn+1 = les 2 sorties
Colonne  0 = Canton Cn-1 si c'est un vrai canton, Cn-1 côté pointe si c'est une aiguille
Colonne  1 = Canton Cn
Colonne  2 = Canton Cn+1 si c'est un vrai canton, Cn+1 tout droit si c'est une aiguille
Colonne  3 = 0 si c'est un vrai canton, Cn+1 dévié si c'est une aiguille
Colonne  4 = occupation du canton Cn (0/1)
Colonne  5 = sens de déplacement (1= sens horaire/0 = sens anti-horaire
Colonne  6 = position de l'aiguille (1 = TD (tout droit)/2 = DV (dévié))
Colonne  7 = Canton suivant
Colonne  8 = numéro d'affichage du feu dans le sens anti-horaire
Colonne  9 = numéro d'affichage du feu dans le sens horaire
Colonne 10 = numéro du train qui est dans le canton Cn

Dans le cas qui suit, on traite d'un circuit à 19 cantons et 26 aiguilles
J'ai simulé l'occupation des cantons 30,31 et 39
*/
byte Table_Cantons [100][11] =
{ { 25,1,2,3,0,0,1,0,0,0,0},
  { 7,2,1,4,0,0,1,0,0,0,0},
  { 4,3,26,1,0,0,0,0,0,0,0},
  { 3,4,2,5,0,0,0,0,0,0,0},
  { 6,5,27,4,0,0,2,0,0,0,0},
  { 5,6,7,11,0,0,2,0,0,0,0},
  { 8,7,6,2,0,0,2,0,0,0,0},
  { 31,8,7,9,0,0,1,0,0,0,0},
  { 10,9,37,8,0,0,1,0,0,0,0},
  { 9,10,11,12,0,0,1,0,0,0,0},
  { 10,11,28,6,0,0,2,0,0,0,0},
  { 10,12,30,29,0,0,0,0,0,0,0},
  { 30,13,18,14,0,0,1,0,0,0,0},
  { 15,14,29,13,0,0,1,0,0,0,0},
  { 14,15,18,16,0,0,2,0,0,0,0},
  { 17,16,28,15,0,0,2,0,0,0,0},
  { 16,17,19,24,0,0,2,0,0,0,0},
  { 19,18,13,15,0,0,1,0,0,0,0},
  { 20,19,17,18,0,0,2,0,0,0,0},
  { 42,20,19,21,0,0,1,0,0,0,0},
  { 22,21,36,20,0,0,1,0,0,0,0},
  { 21,22,24,23,0,0,1,0,0,0,0},
  { 22,23,45,26,0,0,0,0,0,0,0},
  { 22,24,27,17,0,0,2,0,0,0,0},
  { 45,25,1,0,0,0,0,0,0,0,0},
  { 23,26,3,0,0,0,0,0,0,0,0},
  { 24,27,5,0,0,0,0,0,0,0,0},
  { 16,28,11,0,0,0,0,0,0,0,0},
  { 14,29,12,0,0,0,0,0,0,0,0},
  { 13,30,12,0,1,0,0,0,0,0,0},
  { 8,31,32,0,1,0,0,0,0,0,0},
  { 31,32,33,0,0,0,0,0,0,0,0},
  { 32,33,43,0,0,0,0,0,0,0,0},
  { 43,34,35,0,0,0,0,0,0,0,0},
  { 34,35,36,0,0,0,0,0,0,0,0},
  { 35,36,21,0,0,0,0,0,0,0,0},
  { 9,37,38,0,0,0,0,0,0,0,0},
  { 37,38,39,0,0,0,0,0,0,0,0},
  { 38,39,40,0,1,0,0,0,0,0,0},
  { 39,40,41,0,0,0,0,0,0,0,0},
  { 40,41,42,0,0,0,0,0,0,0,0},
  { 41,42,20,0,0,0,0,0,0,0,0},
  { 33,43,34,44,0,0,2,0,0,0,0},
  { 43,44,45,0,0,0,0,0,0,0,0},
  { 23,45,44,25,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
};

/*--------------------------------------Table des itinéraires----------------------------------
Décrit tous les itinéraires formés. C'est une table résultat.
Cette table est décrite ici "en dur", pour l'expérimentation, mais elle doit être calculée à partir de la Table_Cantons.
Cette partie du programmes est à venir ...
Table_Iti doit toujours être décrite dans le sens horaire, par définition

Colonne 0 = Canton départ
Colonne 1 = Aiguille 1
Colonne 2 = Aiguille 2
Colonne 3 = Aiguille 3
Colonne 4 = Aiguille 4
Colonne 5 = Aiguille 5
Colonne 6 = Aiguille 6
Colonne 7 = Aiguille 7
Colonne 8 = Canton arrivée
Colonne 9 = Ralenti : 1 = pas de ralenti, >1 = ralenti 30 km/h
*/
byte Table_Iti [10][11] =
{ { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
  { 0,0,0,0,0,0,0,0,0,0,0},
};

// Exemple de Table_Iti :
//byte Table_Iti [5][9] =
//{ { 33,43,0,0,0,0,0,0,44},
//  { 25,1,2,7,8,0,0,0,31},
//  { 27,5,6,11,10,9,0,0,37},
//  { 36,21,22,24,17,16,15,14,29},
//  { 42,20,19,18,13,0,0,0,30},
//};

/*
Basic pin reading and pullup test for the MCP23017 I/O expander
public domain!
Connect pin #12 of the expander to Analog 5 (i2c clock)
Connect pin #13 of the expander to Analog 4 (i2c data)
Connect pins #15, 16 and 17 of the expander to ground (address selection)
Connect pin #9 of the expander to 5V (power)
Connect pin #10 of the expander to ground (common ground)
Connect pin #18 through a ~10kohm resistor to 5V (reset pin, active low)

Un MCP23017 a une adresse = 0,1,0,0,A0,A1,A2
Pour le 1er MCP23017, A0 = 0, A1 = 0, A2 = 0
Pour le deuxième,     A0 = 0, A1 = 0, A2 = 1
....
Pour le dernier       A0 = 1, A1 = 1, A2 = 1
On adresse ainsi 8 boitiers MCP23017
*/

Adafruit_MCP23017 mcp[2];  // On déclare autant d'objets Adafruit_MCP23017 que de boitiers. Ici 2.

//--------------------------------------------Recherche d'un Canton dans la Table_Cantons-------------------------------
byte Rech_Canton_Table_Cantons (byte Canton)
{
  for (byte i=0; i<Cmax; i++)
  {
    if (Canton == Table_Cantons [i][1])
    {
      Ligne = i;
      return Ligne;
      break;
    }
  }
}
//-------------------------------------------Recherche d'un Canton Aiguille dans la Table_Iti----------------------------------
byte Rech_Canton_Aiguille_Table_Iti (byte Canton, byte Aiguille)
{
  Cn_Plus_1 = 0;
  for (byte i=0; i<Imax; i++)
  {
    if (Canton == Table_Iti [i][0])
    {
      for (byte j=1; j<9; j++)
      {
        if (Aiguille == Table_Iti [i][j])
        {
          Cn_Plus_1 = Table_Iti [i][9];
          break;
        }
      }
    }
    else
    {
      if (Canton == Table_Iti [i][9])
      {
        for (byte j=1; j<9; j++)
        {
          if (Aiguille == Table_Iti [i][j])
          {
            Cn_Plus_1 = Table_Iti [i][0];
            break;
          }
        }
      }
    }
  }
  return Cn_Plus_1;
}
//--------------------------------------------Recherche du canton suivant une aiguille dans la Table_Iti------------------------------------------------
byte Rech_Suiv_Aig_Table_Iti (byte Aiguille,byte Sens_H)
{
  Cn_Plus_1 = 0;
  for (byte i=0; i<Imax; i++)
  {
    for (byte j=1; j<9; j++)
    {
      if (Aiguille == Table_Iti [i][j])
      {
        Cn_Plus_1 = Table_Iti [i][j+1-2*Sens_H];
        if (Cn_Plus_1 == 0)
        {
          Cn_Plus_1 = Table_Iti [i][9];
        }
      }
    }
  }
  return Cn_Plus_1;
}
//--------------------------------------------Recherche du canton suivant en fonction du sens (horaire ou anti-horaire)---------------------------------------------------
byte Canton_Suivant (byte Canton,byte Sens_H)
{
  Rech_Canton_Table_Cantons (Canton);
  Ligne_Canton_1 = Ligne;
  // Quel est le type du canton ?
  if (Table_Cantons [Ligne_Canton_1][3] != 0)
  {
    // Le canton est une aiguille
    // La table_Iti va donner le canton suivant
    Rech_Suiv_Aig_Table_Iti (Canton,Sens_H);
    return Cn_Plus_1;     
  }
  else
  {
    // Le canton est un vrai canton. Quel est le type du canton suivant suivant ?
    Cn_Plus_1 = Table_Cantons [Ligne_Canton_1][2-2*Sens_H];
    Rech_Canton_Table_Cantons (Cn_Plus_1);
    Ligne_Canton_2 = Ligne;
    if (Table_Cantons [Ligne_Canton_2][3] != 0)
    {
      // Le canton suivant suivant est une aiguille
      // La table_Iti va donner le canton final
      Rech_Canton_Aiguille_Table_Iti(Canton,Cn_Plus_1);
      return Cn_Plus_1;
    }
    else
    {
      // Le canton suivant suivant est un vrai canton
      // On peut sortir, on a trouvé Cn_Plus_1
      return Cn_Plus_1;  
    }
  }
}
//-----------------------------------Itinéraire démarrant par canton-->aiguille------------------
byte Ajout_Aig_Canton_Init (byte Canton, byte Aiguille)
{
  for (byte i=0; i<5; i++)
  {
    if (Table_Iti [i][0] == 0)
    {
      // On peut écrire
      Table_Iti [i][0] = Canton;
      Table_Iti [i][1] = Aiguille;
      Rech_Canton_Table_Cantons (Aiguille);
      Ligne_Canton_1 = Ligne;
      Table_Iti [i][10] = Table_Cantons [Ligne_Canton_1][6];
      Ligne_Iti = i;
      return Ligne_Iti;
      break;      
    }
  }
}
//----------------------------------Ajout d'une aiguille-----------------------------
byte Ajout_Aiguille (byte Ligne_Iti)
{
  for (byte k=2; k<9; k++)
  {
    if (Table_Iti [Ligne_Iti][k] == 0)
    {
      Rech_Canton_Table_Cantons (Table_Iti [Ligne_Iti][k-1]);
      Ligne_Canton_1 = Ligne;
      if (Table_Cantons [Ligne_Canton_1][0] == Table_Iti [Ligne_Iti][k-2])
      {
        // Aiguille en pointe --|==
        Table_Iti [Ligne_Iti][k] = Table_Cantons [Ligne_Canton_1][1+Table_Cantons [Ligne_Canton_1][6]];
      }
      else
      {
        // Aiguille en talon  ==|--
        Table_Iti [Ligne_Iti][k] = Table_Cantons [Ligne_Canton_1][0];
      }
      Rech_Canton_Table_Cantons (Table_Iti [Ligne_Iti][k]);
      Ligne_Canton_2 = Ligne;
      if (Table_Cantons [Ligne_Canton_2][3] == 0)
      {
        // On est arrivé à un canton
        Table_Iti [Ligne_Iti][9] = Table_Cantons [Ligne_Canton_2][1];
        Table_Iti [Ligne_Iti][k] = 0;
        break;
      }
      else
      {
        if (Table_Cantons [Ligne_Canton_1][6] != 0)
        {
          Table_Iti [Ligne_Iti][10] = Table_Iti [Ligne_Iti][10]*Table_Cantons [Ligne_Canton_1][6];
        }        
      }
    }
  }
}
//******************************************* Programme principal ***********************
//-------------------------------------------setup-------------------------------------
void setup() 
{
  Serial.begin(9600);  
  for (byte i=0; i<2; i++)
  {
    mcp[i].begin(i); //On initialise chaque boitier
    for (byte j=0; j<16; j++)
    {
      mcp[i].pinMode(j, OUTPUT);  //On met toutes les broches en sortie
    }
  }
  pinMode (Bus_Occ,INPUT);  // broche Bus_Occ de l'Arduino pour le résultat
}

//-------------------------------------------loop---------------------------------------
void loop()
{
  /*
  
  La gestion des MCP 23017 fonctionne parfaitement, mais elle est ici en commentaire car ce n'est pas ça que je teste en ce moment
  
  ----------------------------------------------------------gestion du bus I2C---------------
  for (byte boitier=0; boitier<Nb_Boitiers; boitier++)
  {
    for (byte broche=0; broche<16; broche++)
    {
        Occup = 0;
        mcp[boitier].digitalWrite (broche, HIGH);
        Occup = digitalRead (Bus_Occ);
        Table_Cantons [boitier*16+broche][1] = Occup;
        mcp[boitier].digitalWrite (broche, LOW);
    }
  }  
  //La table est remplie avec les occupations des cantons (Colonne 4)
  for (byte canton=0; canton<45; canton++)
  {
    Serial.print(canton);
    Serial.print(" ");
    Serial.print(Table_Cantons [canton][0]);
    Serial.print(" ");
    Serial.println(Table_Cantons [canton][4]);
    delay(500);
  }
  */
 
  //---------------------------------------Effacement de la Table_Iti------------------------
  //  La Table_Iti est une table vivante, constamment recalculée en fonction de la position des aiguilles 
  for (byte i=0; i<5; i++)
  {
    for (byte j=0; j<9; j++)
    {
      Table_Iti [i][j] = 0;
    }
  }
  //-----------------------------------Remplissage de la Table_Iti-----------------------
  //  Les nombres indiqués correspondent au réseau traité. Ce sont ces nombres qu'on doit indiquer au programme pour qu'il puisse démarrer
  Ajout_Aig_Canton_Init (33,43);
  for (byte m=2; m<9; m++)
  {
    Ajout_Aiguille (0);
    if (Table_Iti [Ligne_Iti][10] !=0)
    {
      // La fin de l'itinéraire se voit par le fait qu'on a trouvé un canton. Il est toujours en colonne 10.
      break;
    }  
  }
  Ajout_Aig_Canton_Init (25,1);
  for (byte m=2; m<9; m++)
  {
    Ajout_Aiguille (1);
    if (Table_Iti [Ligne_Iti][10] !=0)
    {
      break;
    }  
  }
  Ajout_Aig_Canton_Init (27,5);
  for (byte m=2; m<9; m++)
  {
    Ajout_Aiguille (2);
    if (Table_Iti [Ligne_Iti][10] !=0)
    {
      break;
    }  
  }
  Ajout_Aig_Canton_Init (36,21);
  for (byte m=2; m<9; m++)
  {
    Ajout_Aiguille (3);
    if (Table_Iti [Ligne_Iti][10] !=0)
    {
      break;
    }  
  }
  Ajout_Aig_Canton_Init (42,20);
  for (byte m=2; m<9; m++)
  {
    Ajout_Aiguille (4);
    if (Table_Iti [Ligne_Iti][10] !=0)
    {
      break;
    }  
  }
  //---------------------------remplissage des colonnes 7 (=Cn+1), 8 (= feu sens anti horaire), 9 (= feu sens horaire) de la Table_Cantons--------------
  //Sont réalisés : Carré, Sémaphore, Avertissement, Ralentissement 30, Rappel de Ralentissement 30, Voie Libre
  //
  for (byte Sens_H=0; Sens_H<2; Sens_H++)
  {
    //-----------------------------Canton suivant puis Carré (1) et Sémaphore (2)---------------------
    for (byte n=0; n<Cmax; n++)
    {
      Cn = Table_Cantons [n][1];
      Canton_Suivant (Cn,Sens_H);
      Table_Cantons [n][7] = Cn_Plus_1;
      if ((Table_Cantons [n][3] == 0) && (Cn_Plus_1 == 0))
      {
        // Cn est un vrai canton et Cn+1 = 0 : feu au carré
        Table_Cantons [n][8+Sens_H] = 1;
      }
      else
      {
        Rech_Canton_Table_Cantons (Cn_Plus_1);
        if ((Table_Cantons [n][3] == 0) && (Table_Cantons [Ligne][3] == 0))
        {
          // Cn est un vrai canton et Cn+1 aussi
          if (Table_Cantons [Ligne][4] == 1)
          {
            // Cn+1 est occupé : feu au sémaphore
            Table_Cantons [n][8+Sens_H] = 2;
          }
        }
      }
    }
  //-----------------------------------Avertissement (3)---------------------------------------
    for (byte n=0; n<Cmax; n++)
    {
      if (Table_Cantons [n][3] == 0)
      {
        // Cn est un vrai canton
        Cn = Table_Cantons [n][7];
        Rech_Canton_Table_Cantons (Cn);
        if ((Table_Cantons [Ligne][8+Sens_H] == 1) || (Table_Cantons [Ligne][8+Sens_H] == 2))
        {
          // Cn+2 est au carré ou au sémaphore
          if ((Table_Cantons [n][8+Sens_H] != 1) && (Table_Cantons [n][8+Sens_H] !=2))
          {
          // On n'efface pas sémaphore ou carré, prioritaires
          Table_Cantons [n][8+Sens_H] = 3;
          }
        }
      }
    }
  }  
  //------------------------------------Ralentissement 30 (feu+10) et Rappel Ralentissement 30 (feu+20)-----------------------
  //  Le ralentissement est directement calculé dans la table_Iti
    for (byte n=0; n<Imax; n++)
    {
      if (Table_Iti [n][10] > 1)
      {
        // Recherche de l'info R 30 dans Table_Iti pour le sens_H = 0
        Rech_Canton_Table_Cantons(Table_Iti [n][0]);
        Ligne_Canton_1 = Ligne;
        if (Table_Cantons [Ligne_Canton_1][8] < 10)
        {
          Table_Cantons [Ligne_Canton_1][8] = Table_Cantons [Ligne_Canton_1][8]+10;
        }
        
          // Recherche du canton précédent pour lui mettre RR 30
          Canton_Suivant (Table_Iti [n][0],1);
          Ligne_Canton_2 = Ligne;
          if ((Table_Cantons [Ligne_Canton_2][3] == 0) && (Table_Cantons [Ligne_Canton_2][8] < 20))
          {
            Table_Cantons [Ligne_Canton_2][8] = Table_Cantons [Ligne_Canton_2][8]+20;
          }
        
        // Recherche de l'info R 30 dans Table_Iti pour le sens_H = 1
        Rech_Canton_Table_Cantons(Table_Iti [n][9]);
        Ligne_Canton_1 = Ligne;
        if (Table_Cantons [Ligne_Canton_1][9] < 10)
        {
          Table_Cantons [Ligne_Canton_1][9] = Table_Cantons [Ligne_Canton_1][9]+10;
        }
        
          // Recherche du canton précédent pour lui mettre RR 30        
          Canton_Suivant (Table_Iti [n][9],0);
          Ligne_Canton_2 = Ligne;
          if ((Table_Cantons [Ligne_Canton_2][3] == 0) && (Table_Cantons [Ligne_Canton_2][9] < 20))
          {
            Table_Cantons [Ligne_Canton_2][9] = Table_Cantons [Ligne_Canton_2][9]+20;
          }
      }
    }
  //-----------------------------Voie Libre (9)----------------------
  for (byte n=0; n<Cmax; n++)
  {
    if (Table_Cantons [n][3] == 0)
    {
      switch (Table_Cantons [n][8])
      {
        case 0 :
        Table_Cantons [n][8] = 9;
        break;
        case 10 :
        Table_Cantons [n][8] = 19;
        break;
        case 20 :
        Table_Cantons [n][8] = 29;
        break;
        default :;
      }
      switch (Table_Cantons [n][9])
      {
        case 0 :
        Table_Cantons [n][9] = 9;
        break;
        case 10 :
        Table_Cantons [n][9] = 19;
        break;
        case 20 :
        Table_Cantons [n][9] = 29;
        break;
        default :;
      }
    }    
  }
  //------------------------------------Affichage des résultats------------------
  for (byte m=0; m<5; m++)
  {
    Serial.print(Table_Iti [m][0]);
    Serial.print(" ");
    Serial.print(Table_Iti [m][1]);
    Serial.print(" ");
    Serial.print(Table_Iti [m][2]);
    Serial.print(" ");
    Serial.print(Table_Iti [m][3]);
    Serial.print(" ");
    Serial.print(Table_Iti [m][4]);
    Serial.print(" ");
    Serial.print(Table_Iti [m][5]);
    Serial.print(" ");
    Serial.print(Table_Iti [m][6]);
    Serial.print(" ");
    Serial.print(Table_Iti [m][7]);
    Serial.print(" ");
    Serial.print(Table_Iti [m][8]);
    Serial.print(" ");
    Serial.print(Table_Iti [m][9]);
    Serial.print(" ");
    Serial.println(Table_Iti [m][10]);


  }
 
 
  for (byte n=0; n<Cmax; n++)
  {  
    Serial.print(Table_Cantons [n][1]);
    Serial.print("-->");
    Serial.print(Table_Cantons [n][7]);
    Serial.print(" ");
    Serial.print(Table_Cantons [n][8]);
    Serial.print(" ");
    Serial.println(Table_Cantons [n][9]);
    
    delay(100);
  } 
}
J'y arriverai ... demain !

Avatar du membre
Barbadidoua
Bavard
Messages : 61
Enregistré le : lun. 31 juil. 2006, 19:11
Club : AMFBC73
Site Internet : http://fadiese.hd.free.fr/cms/
Localisation : Chambery

Re: Identification des trains par Arduino

Message par Barbadidoua » dim. 11 janv. 2015, 11:56

Bonjour,

C'est avec intérêt que je suis ce fil passionnant et prometteur.
Pour ma part, utilisateur de CDM-Rail, je programme un "client IPC" de CDM-Rail permettant de jouer des sons (annonces de gares, klaxons des trains, cloches de PN), de gérer la signalisation complexe SNCF et autres accessoires synchronisés avec les mouvements des trains (fermetures des PN, signalisation mécanique).
Je compte moi aussi utiliser un Arduino et le bus I2C pour les signaux et accessoires.

Mon soucis, au moment du design des modules I2C signalisation et accessoire, est de ne pas ré-inventer la roue... Avez vous déjà un schéma, un CI des modules à connecter sur le bus I2C pour ces éléments?

Merci,
Ech N, DCC LokMaus 2, CDM-Rail

Avatar du membre
groumfit
Papotier
Messages : 143
Enregistré le : sam. 25 oct. 2014, 11:25
Prénom : Denis
Âge : 63

Re: Identification des trains par Arduino

Message par groumfit » dim. 11 janv. 2015, 13:21

Je te remercie pour cet intérêt.
Concernant spécifiquement le bus I2C, je l'utilise pour fournir des infos à l'Arduino et pas l'inverse.
La commande des aiguilles est extérieure au système et je nourris l'Arduino de la position des aiguilles. Dans ce projet, je ne commande pas les aiguilles.
Évidemment, je meurs d'envie de faire un vrai PRS à base d'Arduino, mais je le ferai quand j'aurais fini ce projet. J'ai plein d'idées novatrices sur le sujet.
Un autre bus I2C gèrera la commande des aiguilles.
Concernant le programme actuel, je calcule ce que doivent afficher les 256 feux SNCF des 128 cantons possibles. Mais aujourd'hui, rien ne sort de l'Arduino. Je réfléchis à la façon de sortir cette info qui existe déjà dans l'Arduino et d'aller éclairer les feux.
J'y arriverai ... demain !

Avatar du membre
groumfit
Papotier
Messages : 143
Enregistré le : sam. 25 oct. 2014, 11:25
Prénom : Denis
Âge : 63

Re: Identification des trains par Arduino

Message par groumfit » jeu. 15 janv. 2015, 18:27

Et c'est reparti !
Voici le moment de faire un point d'étape qui permet d'intégrer tout ce que j'ai emmagasiné comme informations.
Voici d'abord le schéma de principe V2.0 :

Image

Que voit-on ?
1°) Grâce au multiplexeur de bus I2C (TCA9548A), on a maintenant 2 bus I2C
(On pourrait en avoir 8, mais on n'en est pas là :) )
2°) Le premier bus n'a que très peu évolué.
C'est toujours 8 x MCP23017 qui scrutent chacun 16 cartes, soit un maxi de 128 cartes. La différence par rapport aux précédents schémas, c'est que chaque carte n'a plus qu'une sortie vers le MCP23017, ce qui permet d'en avoir 128.
Mais il y a maintenant 3 bus (1fil chacun !) :
Le bus occupation, le bus sens et le bus position de l'aiguille.
3°) Le deuxième bus est, lui, entièrement nouveau : il commande les feux.
Parce que c'est bien d'avoir un Arduino parfaitement à jour de la couleur des feux, mais c'est mieux si le feu est sur le réseau, au bon endroit, avec la bonne couleur.
C'est là qu'intervient un CI ahurissant : le PCA9685 (merci Pierre59).
Figurez vous qu'on peut mettre sur un bus 2 fils (bus I2C) 62 x PCA9685 qui commandent chacun 16 LED.
Soit un maxi de 992 LED :applause:
Dans mon cas, 128 cartes, ça correspond environ à 64 cantons et 64 aiguilles. C'est déjà pas mal comme réseau :lol:
Et donc 128 feux, les aiguilles n'ayant pas de feux. On a largement de quoi, à moins d'avoir exclusivement des feux H, ce qui serait aberrant.
4°) Reste la dernière fonction :
Les trains sont lancés. On conduit l'un des trains en suivant l'allumage du feu que voit le conducteur du train. Feu qui peut être au TCO ou à proximité de la manette de conduite. Mais les autres trains continuent. Et même si l'Arduino voit arriver un feu Sémaphore ou Carré pour eux, il faut qu'il envoie l'information à la centrale pour que le train s'arrête.
J'opte POUR L'INSTANT pour une solution simple : le module XPA de Lenz auquel l'Arduino envoie les infos DTMF via un circuit HT9200 spécialisé pour ça.
Solution à 70 €.
Il faudra que je planche pour décoder une norme commune aux centrales DCC (Xpressnet ou Loconet). J'ai pour l'instant une préférence pour Xpressnet, déjà en partie décortiquée par d'autres.
5°) Puis une idée m'est venue hier soir :
Quand on regarde bien le schéma de principe, ce n'est que la fonction 4°) qui parle de DCC !
Et donc tout le reste peut marcher en analogique pur !
Mais comment faire pour arrêter les trains, alors ? :siffle:
Simple : comme on a trop de LED commandables, on remplace une LED par un transistor qui commande un relais, relais qui va couper le courant dans la zone d'arrêt d'un canton analogique. Ou relais qui va insérer une résistance dans l'alimentation d'un train (canton finissant par un Avertissement ou première partie d'un canton finissant par un Sémaphore ou un Carré).
Parce qu'en plus de donner la possibilité de gérer 992 LED, le PCA9685 peut les allumer chacune avec 4096 niveaux !!
L'Arduino sait quelle est la couleur du feu que voit le train et peut donc ajuster cette vitesse finement. Même en analogique pur !
Évidemment, tout ça est à tester, mais je pense que ça vaut le coup.
Sachant quand même que le bus I2C 1 est déjà testé, que tous les feux sont déjà calculés.
Il reste du code à faire (et à affiner celui existant), mais surtout tester le matériel.
J'y arriverai ... demain !

Avatar du membre
Francis8
Communicatif
Messages : 873
Enregistré le : jeu. 30 juil. 2009, 16:26
Echelle pratiquée : HO
Club : REV
Site Internet : http://www.association-rev.club
Localisation : Oise
Âge : 48
Contact :

Re: Identification des trains par Arduino

Message par Francis8 » lun. 19 janv. 2015, 14:37

groumfit a écrit :Et c'est reparti !
Voici le moment de faire un point d'étape qui permet d'intégrer tout ce que j'ai emmagasiné comme informations.
Voici d'abord le schéma de principe V2.0 :

Image

Que voit-on ?
Et bien en fait, on ne voit rien du tout justement :siffle: :wink:

Avatar du membre
groumfit
Papotier
Messages : 143
Enregistré le : sam. 25 oct. 2014, 11:25
Prénom : Denis
Âge : 63

Re: Identification des trains par Arduino

Message par groumfit » lun. 19 janv. 2015, 18:03

Bonjour Francis8,
Je ne comprends pas : moi, je vois un schéma et dans mon post et dans le tien. Un pb d'affichage ?
J'y arriverai ... demain !

Avatar du membre
likiki
Éloquent
Messages : 306
Enregistré le : dim. 29 avr. 2012, 15:21
Echelle pratiquée : H0 3R
Prénom : Christian
Site Internet : http://passionnement.forumactif.org
Localisation : Corbeil Essonne
Âge : 52

Re: Identification des trains par Arduino

Message par likiki » lun. 19 janv. 2015, 19:13

Je n'ai pas le schéma non plus. :gne:


Image

:gne:
Fichiers joints
page.png
page.png (101.29 Kio) Vu 5605 fois
Cordialement,

Christian.

Avatar du membre
Francis8
Communicatif
Messages : 873
Enregistré le : jeu. 30 juil. 2009, 16:26
Echelle pratiquée : HO
Club : REV
Site Internet : http://www.association-rev.club
Localisation : Oise
Âge : 48
Contact :

Re: Identification des trains par Arduino

Message par Francis8 » lun. 19 janv. 2015, 19:29

Lorsque je demande à afficher l'image dans une nouvelle fenêtre (ou onglet), j'ai ça comme réponse :
Information

La pièce jointe sélectionnée n’existe plus.
Et ceci chez moi (en ce moment) comme au boulot (tout à l'heure).

SixtySix
Bavard
Messages : 51
Enregistré le : dim. 05 janv. 2014, 18:53
Echelle pratiquée : N

Re: Identification des trains par Arduino

Message par SixtySix » mar. 20 janv. 2015, 14:37

Bonjour Denis,

Je viens de relire la totalité de ton fil : très intéressant !

Aurais-tu des détails, schéma compris de la partie détecteur de voie (émetteur IR et récepteur sur la voie, avec un code spécifique par train, si j'ai bien compris) ?

Merci d'avance

Avatar du membre
groumfit
Papotier
Messages : 143
Enregistré le : sam. 25 oct. 2014, 11:25
Prénom : Denis
Âge : 63

Re: Identification des trains par Arduino

Message par groumfit » mer. 21 janv. 2015, 20:42

Merci SixtySix, il y a au moins quelqu'un qui a tout lu :mdr2:
Le but de ce fil est de fournir des pistes de réflexion.
Il est donc possible qu'il y ait des retours en arrière quand une idée qui paraissait pourtant superbe, en fait, ne l'est pas tant que ça. C'est vivant.
Par exemple, je me suis rendu compte que l'Arduino pouvait savoir quel train était où, si tant est qu'on lui donne le canton de départ du train. Je pense mettre en pratique cette programmation d'ici 15 jours.
Et donc, avoir un détecteur qui identifie le train ne sert à rien.
Par ailleurs, il n'est pas évident qu'on ait besoin d'IR.
Le problème principal des détecteurs optiques est qu'ils sont perturbés par la lumière ambiante. D'où l'IR. Et il faut 2 parties : un émetteur et un récepteur qu'on essaie de planquer tous les 2 comme on peut.
Un peu comme au judo, il faut profiter de la force de l'autre pour le battre.
Puisqu'elle la lumière est présente, prenons là comme émetteur !
Et on se dit que quand un train passe sur le détecteur, il fait de l'ombre.
Il y a 30 ans (!), j'avais essayé avec des LDR qui, à l'époque étaient nulles. Pratiquement pas de différence de résistance entre la LDR à la lumière et la LDR à l'ombre. Mais les choses ont évolué :
http://www.electrodragon.com/product/2p ... photocell/
Là, on passe de 2,4k à 200 Mohm !!
Mon idée est donc simplement de faire un trou de 3mm (donc compatible avec l'échelle N) qui traverse le plateau et de mettre un tel détecteur au fond.
Quand la loco va passer, elle va forcément faire baisser la lumière. Moyennant un réglage de seuil (ampli-op et contre réaction) pour que le déclenchement ne se fasse pas en passant la main devant l'ampoule, ça me paraît jouable.
Mais c'est comme tout, il faut tester.
Évidemment, j'en entend déjà qui vont me dire "et dans un tunnel ?" "Et dans ma gare cachée ?" Là, c'est le moment d'éclairer. Je suis sûr que ça marche avec très peu de lumière. Et là, par contre, pas de problème pour cacher la LED. Si ça se trouve, cette photorésistance réagit aussi avec de l'IR ?
ça vaut le coup de risquer 1 €. Non ? Pour 10 !!
J'y arriverai ... demain !

Avatar du membre
groumfit
Papotier
Messages : 143
Enregistré le : sam. 25 oct. 2014, 11:25
Prénom : Denis
Âge : 63

Re: Identification des trains par Arduino

Message par groumfit » jeu. 22 janv. 2015, 20:53

Bonsoir,
Question : y-a-t'il encore des personnes qui n'ont pas pu lire le schéma de principe ?
Je peux le transmettre à qui me le demandera par mail, en attendant de comprendre ce qui bloque chez certaines personnes.

Autre problème : la gestion du temps dans ce projet.

J'ai eu la curiosité de chronométrer les différentes parties du programme.
Les calculs sur la table ne consomment que 5 ms (milli-secondes), ce qui est raisonnable. par contre, la récupération des infos d'occupation, gérée de manière "bestiale" et systématique dure 193 ms, ce qui est énorme.
En effet, un TGV qui roule à 270 km/h fait 86,2 cm par seconde au 1/87è.
En 193+5 = 198 ms, il fait 17 cm !! Au secours ! :mort:
Jean-Luc (jlb), que je remercie, a bien amélioré ce temps en utilisant la librairie I2C autrement, mieux, mais on reste à 65 ms, soit 5,6 cm, ce qui est encore trop.
Donc, pour que ce temps diminue, il faut gérer de manière plus intelligente la récupération d'informations.
Jean-Luc a une autre solution radicalement différente, basée sur le bus CAN au lieu d'I2C, mais j'aimerais aller jusqu'au bout de l'I2C. Comme ça, on en connaitra les limites. C'est aussi l'intérêt.
Par exemple, essayer d'être à 400 kHz au lieu de 100 kHz et de voir si on n'a pas une portée ridicule et une trop forte sensibilité aux parasites.
J'y arriverai ... demain !

Répondre