Voir les contributions

Cette section vous permet de consulter les contributions (messages, sujets et fichiers joints) d'un utilisateur. Vous ne pourrez voir que les contributions des zones auxquelles vous avez accès.


Messages - railyRabbit

Pages: [1] 2
1
3. Mouvement et alimentation des aiguilles
a) Matériel

Comme expliqué en introduction, les aiguilles du réseau sont mues par des petits servos 4G, fournis avec le matériel Tam Valley. La polarité des cœurs étaient à l'origine manœuvrées par les cartes-filles.

Celles-ci sont dotées d'un relai DPDT qui n'autorise qu'une gestion 'binaire' de la polarité du cœur (elle correspond à l'un OU l'autre des rails) sans pouvoir totalement l'isoler pendant le mouvement, d'où les fréquents court-circuits.

J'ai donc acheté des cartes relais 2 relais − solution court-terme − sur un site chinois bien connu. Elles me permettent de gérer la polarité des cœurs en toute liberté :
  • j'alimente le relai 1 pour polariser le cœur sur le rail 1
  • j'alimente le relai 2 pour polariser le cœur sur le rail 2
  • si aucun des deux relais n’est alimenté, le coeur est isolé



Pour gérer les servos, j'avais une carte driver 12 servos Adafruit sous le coude. Cette carte communique avec l'Arduino par le bus I2C. C'est surdimensionné pour 3 servos, mais je comptais m'en servir pour gérer des éclairages sur les ports inutilisés.

b) Logiciel
C'est là que j'ai commencé à me frotter aux Classes, en suivant la série d'articles "Le monde des objets" de Locoduino bien sûr ! Soyez indulgents sur la mise en œuvre :) Je suis preneur des conseils pour améliorer le code. J’ai aussi pas mal joué sur les manipulations de bits pour la gestion des registres d’état.

Le fichier .h déclare une classe turnoutDriver avec ses propriétés, fonctions privées et publiques.
Il déclare aussi quelques fonctions “générales” pour initialiser la librairie servo, initialiser les objets, mettre à jour l’ensemble des objets ou encore configurer les objets pendant l'exécution.

Le rôle de cette librairie est de mouvement des aiguilles et l’alimentation des cœurs. A terme, elle doit également communiquer avec le gestionnaire des façades de contrôle, le gestionnaire de détecteurs infrarouges (qui vont également donner des ordres selon le sens de marche et la position des aiguilles) et enfin avec un automate (qui va générer des itinéraires aléatoires).

Pour cela, la librairie crée plusieurs registres globaux :
Le registre d’état turnoutDriver_states : c’est un byte qui représente la position des aiguilles. Chaque bit représente une aiguille, un 1 si elle est déviée, un 0 si elle est fermée. Lorsqu’une aiguille est en cours de mouvement, son état représente sa position en fin de mouvement : si nécessaire (un train arrive sur une aiguille qui va être talonnée), on peut lui donner un contre-ordre même si la manœuvre n’est pas terminée.
Ce registre est utilisé en lecture / écriture par le gestionnaire d’aiguilles, et en lecture uniquement par les autres librairies qui ont besoin de connaître la position des aiguilles.
Le registre de progression turnoutDriver_progress : encore un byte, qui mémorise les aiguilles en cours de manœuvre. Chaque bit représente une aiguille, un 1 si elle est en cours de mouvement, un 0 si elle est immobile.
Ce registre est utilisé en lecture/écriture par le gestionnaire d’aiguilles, et en lecture uniquement par le gestionnaire des façades (pour gérer le clignotement des LEDs).
Le registre d’ordres turnoutController_buttons, toujours un byte, sert à transmettre les boutons de contrôle activés les ordres d’inversion des aiguilles, qu’ils viennent de la détection des boutons des façades de contrôle, de l’automate, des détecteurs IR… Là encore, chaque bit représente une aiguille. Un bit à 1 signifie qu’il faut inverser la position de l’aiguille correspondante.
Ce registre est utilisé en lecture/écriture par à peu près tout le monde ! Le gestionnaire d’aiguilles lit les bits et les réinitialise un fois les ordres traités. Les autres librairies positionnent les bits à 1 pour transmettre un ordre de manœuvre.

La taille de ces registres limitent l’utilisation à 8 aiguilles (ou 8 servos).

La librairie se charge d’instancier le gestionnaire de servos (ici c’est la librairie Adafruit_PWMServoDriver qui pilote le PCA9685 de la carte Adafruit) et la fonction turnoutDriverStart() va l’initialiser :

Adafruit_PWMServoDriver servos = Adafruit_PWMServoDriver();

void turnoutDriverStart()
{
    servos.begin();
    servos.setPWMFreq(60);
}

L’objet turnoutDriver possède les propriétés suivantes :
  • le numéro de l’aiguille (qui correspond également au numéro de port sur la carte driver des servos)
  • la valeur numérique des positions fermée/déviée
  • la vitesse du mouvement
  • s’il y a un cœur à alimenter ou non
  • les pins sur lesquels les relais sont connectés
  • le signal pour activer les relais (0 ou 5v : HIGH ou LOW)
  • la valeur numérique de la position courante du servo
  • le timestamp du dernier mouvement (également utilisé pour gérer le mouvement lent)

La fonction TurnoutDriverInit() va prendre en charge le processus d’instanciation et d’initialisation des aiguilles :

void turnoutDriverInit(int positionClosed, int positionThrown, byte pinFeederClosed, byte pinFeederThrown, bool feederOnLevel, int tempo)
{
    turnoutDriver_register[turnoutDriver_count] = new turnoutDriver (positionClosed, positionThrown, pinFeederClosed, pinFeederThrown, feederOnLevel, tempo);
    turnoutDriver_register[turnoutDriver_count]->begin();
    turnoutDriver_count++;
}

Elle instancie d’abord l’objet turnoutDriver, et s’assure que le courant est coupé dans le coeur :

turnoutDriver::turnoutDriver(int positionClosed, int positionThrown, byte pinFeederClosed, byte pinFeederThrown, bool feederOnLevel, int tempo)
{
    _driverId = turnoutDriver_count;
    _closed = positionClosed;
    _thrown = positionThrown;
 
    if (_closed > _thrown)
    {
        _closed *= -1;
        _thrown *= -1;
    }

    _tempo = tempo;
    _lastMove = 0;

    if (pinFeederClosed == 255)
        _hasFeeder = false;
    else
    {
        _hasFeeder = true;
        _pinFeederClosed = pinFeederClosed;
        _pinFeederThrown = pinFeederThrown;
        _feederOnLevel = feederOnLevel;
        pinMode(_pinFeederClosed, OUTPUT);
        pinMode(_pinFeederThrown, OUTPUT);
        power(0);
    }
}

La fonction récupère la référence de l’objet créé et le stocke dans le tableau turnoutDriver_register. Ainsi, il est possible de créer successivement les aiguilles depuis le programme principal et de sous-traiter la mise à jour de l’ensemble des objets. C’est ce que fait la fonction turnoutDriverUpdate() en parcourant ce tableau et invoquant successivement la fonction update() de chaque objet :

void turnoutDriverUpdate()
{
    for (byte i = 0; i < turnoutDriver_count; i++)
        turnoutDriver_register[i]->update();
}

Le programme principal peut donc ressembler donc à ça :

#include "turnoutDriver.h"

void setup()
{
    turnoutDriverStart();
    turnoutDriverInit(394, 334, 8, 12, HIGH, 40);
    turnoutDriverInit(347, 375, 8, 12, HIGH, 75);
    turnoutDriverInit(357, 329, 8, 12, HIGH, 95);
}

void loop()
{
    turnoutDriverUpdate();
}

La fonction d’initialisation va ensuite initialiser l’aiguille en position fermée :

void turnoutDriver::begin()
{
    _position = _closed;
    servos.setPWM(_driverId, 0, abs(_closed));
    turnoutDriver_states &= ~(1 << _driverId);
    turnoutDriver_progress &= ~(1 << _driverId);
    power(1);
}

A chaque tour de programme, la fonction update() regarde si un ordre a été transmis à l’aiguille via le registre turnoutController_buttons. Si c’est le cas, on appelle la fonction d’inversion (toggle()) puis on réinitialise le bit du registre d’ordre :

if (readButton())
{
    toggle();
    turnoutController_buttons &= ~(1 << _driverId);
}

Tous les mouvements (inversion, ouverture, fermeture) sont traités de la même façon.

D’abord on initialise le mouvement :
  • on renseigne le registre d’état avec la position de destination (ouverte ou fermée)
  • on renseigne le registre de progression, pour indiquer que l’aiguille est en mouvement
  • on coupe de courant dans le cœur

void turnoutDriver::initiateMove(bool _direction)
{
    if (_direction)
        turnoutDriver_states |= 1 << _driverId;
    else
        turnoutDriver_states &= ~(1 << _driverId);
    power(0);
    turnoutDriver_progress |= 1 << _driverId;
}

A chaque tour de programme, on met à jour la position des aiguilles en mouvement. Voici donc la fonction update() complète :

void turnoutDriver::update()
{
    if (readButton())
    {
        toggle();
        turnoutController_buttons &= ~(1 << _driverId);
    }
    if (watchProgress())
        move();
}

La fonction move() évalue la position réelle des aiguilles en mouvement par rapport à la position de destination.Si l’aiguille doit bouger et si l’intervalle de temps souhaité entre deux mouvements est atteint (pour gérer la vitesse), on incrémente/décrémente la position d’une unité. Et ainsi de suite.

void turnoutDriver::move()
{
    if (millis() - _lastMove > _tempo)
    {

        if (state())
            if (_position < _thrown)
            {
                _position++;
                servos.setPWM(_driverId, 0, abs(_position));
            }
            else
                terminateMove();

        else

            if (_position > _closed)
            {
                _position--;
                servos.setPWM(_driverId, 0, abs(_position));
            }
            else
                terminateMove();

        _lastMove = millis();
    }
}

Lorsque la position de destination est atteinte, on réinitialise le bit de progression et on remet le courant.

void turnoutDriver::terminateMove()
{
  turnoutDriver_progress &= ~(1 << _driverId);
  power(1);
}

Voilà pour l’essentiel.  ;)

2
Ça fait un moment que je bricole dans mon coin, évoquant par bribes ici ou là certains des aspects électroniques du projet auquel je participe. Je vais essayer de partager plus en détail les réalisations et projets en perspective.
 
Je participe à un réseau relativement simple à échelle N : 2 boucles, 1 voie de garage/belvédère, 3 aiguilles (voir le fil du réseau sur le Forum du N).

1. Matériel de départ
J’avais choisi un système au système complet, le Quad-Pic de Tam Valley Depot, pour piloter les 3 aiguilles du réseau : 3 servos pilotés par une carte à microprocesseur, des façades de contrôle et des cartes-filles équipées d’un relai pour inverser la polarité de la pointe de cœur à mi-parcours. La câblerie est entièrement basée sur des câbles servos, pratique pour une mise en oeuvre rapide. Quelques images pour les curieux :


Les façades de contrôle sont astucieusement conçues. Seuls 3 fils sont nécessaires pour les utiliser : 2 fils d’alimentation, 1 fil de signal. La couleur des LED bicolores (montées en opposition) est gérée via la polarité du fil de signal. En faisant varier rapidement cette polarité, on crée un clignotement alternatif des LED pendant le mouvement. Régulièrement on mesure si le fil de signal n’est pas relié à la masse, ce qui signifie que le bouton est appuyé.



C’est simple, économe en câblage. Je me suis construit un (tout) petit TCO avec 5 fils : 2 fils d’alim, 3 fils de signal, 1 pour chaque aiguille. Ça me permet de visualiser la position des aiguilles et de les commander.

Seulement voilà, entre l’effet de l’échelle N et sans doute la tringlerie maison de transmission entre le servo et l’aiguille, la polarité s’inversait souvent alors que l’aiguille était en contact avec le mauvais rail, causant des court-circuits.

2. Le cahier des charges
a) Mouvement et alimentation des aiguilles > lire
Je décide de remplacer l’électronique de pilotage des aiguilles et d’alimentation des cœurs, tout en gardant la motorisation par servo et les façades de contrôle Tam Valley.

b) Un peu d’automatisation
Je voulais aussi essayer de gérer le talonnage des aiguilles de façon automatique : si un train arrive et que l’aiguille n’est pas dans la bonne position, hop on corrige. Encore faut-il savoir dans quel sens circule le train.
Enfin, dernière piste qui s’est greffée récemment, gérer les itinéraires disponibles et pouvoir faire circuler un train sur un itinéraire aléatoire, quel que soit le sens de marche.

c) Yes we CAN
A l’heure actuelle, ayant fait le a) puis b), tout est géré en central sur un Arduino Uno où j’ai consommé toutes les entrées/sorties (et un peu plus de 50% de flash, rien que pour stocker le programme). J’envisage de décentraliser en mettant en œuvre 3 ou 4 satellites CAN autour d’un chef d’orchestre, plus 1 autre satellite dans le TCO.
Chaque satellite (sauf celui du TCO, un peu plus spécialisé) aura donc à gérer la détection infrarouge, les servos et l’alimentation des aiguilles, ainsi que très probablement des éclairages.

d) Radio Ga Ga
Étape suivante, rendre le TCO sans fil. Il faudra que je me repenche sur ce que Locoduino à démontré à Orléans. L’idée est que, en filaire, le TCO recharge sa batterie et communique en CAN. Sans fil, on passe en liaison radio. Wifi, Bluetooth, 2.4GHz ou 433MHz non licenciés, à voir. En revanche je n’ai pas nécessairement envie d’un TCO sur tablette ou smartphone, ce qui rend le choix du wifi moins évident (et plus de la consommation électrique).

e) Est-ce bien raisonnable ?
Non bien sûr, ça marche pas mal comme ça. Mais c’est aussi l’occasion de tester des nouvelles choses !

La suite très vite.

3
Vie du forum / Re : UP AND RUNNING !
« le: janvier 30, 2019, 01:25:15 pm »
welcome back!

4
Discussions ouvertes / Re : Locoduino à Orleans
« le: novembre 11, 2018, 03:47:48 pm »
J'ai impression que le stand a intéressé pas mal de monde, bravo :)
C'était sympa de vous voir hier !

5
C'est possible qu'ils ne soient pas les moins chers selon les configurations. Pour ma part, je n'ai fait que des panneaux de 5x5cm ou 10x10cm. Je garde fournisseurs sous le bras pour la prochaine fournée ! ;)

6
houlà mais c'est que vous êtes équipés ! :o

Nous c'est un petit réseau, pour le moment on a
- pour la conduite analogique, on a un Fleishmann 6735
- pour la conduite numérique, la Mobile Station 2 (alim 2A d'origine)
- pour tout le reste (alimentation des cartes, servos, et à voir pour les relais), j'ai l'alim chinoise de 6A

7
Bibliothèques / Re : Bibliothèque Commanders
« le: mai 21, 2018, 09:32:13 pm »
Hop, ça marche au poil ! ;D merci !
Je compte m'en servir pour sélectionner des itinéraires...

8
Donc si je résume, tu as :
- une alim pour l'électronique (9V, avec des régulateurs locaux)
- une autre alim pour les relais (24V, que tu dois abaisser qqpart j'imagine ? ou tes relais sont en 24V ?)

9
Bibliothèques / Re : Bibliothèque Commanders
« le: mai 20, 2018, 05:28:41 pm »
Je suis passé à l'implémentation d'un AnalogPushes, mais ça ne répond pas. :-[
Pourtant les valeurs attendues sont bonnes, vérifiées par 2 lignes de code :

/*************************************************************
project: <Commanders>
author: <Thierry PARIS>
description: <Demo sample with push buttons, event handler alternative>
*************************************************************/

#include "Commanders.h"

ButtonsCommanderAnalogPushes Pushes;
 
int values[] = { 0, 270, 518, 774 };
unsigned long ids[] = { 100, 101, 102, 103 };

void ReceiveEvent(unsigned long inId, COMMANDERS_EVENT_TYPE inEvent, int inData)
{
COMMANDERS_PRINT_EVENT(inId, inEvent, inData);
}

void setup()
{
Serial.begin(9600);
Commanders::begin(ReceiveEvent, LED_BUILTIN);

Pushes.begin(A0, 4, ids, values, 30);
}

void loop()
{
Commanders::loop();
if (analogRead(A0) < 900)
Serial.println(analogRead(A0));
 }

10
Bibliothèques / Re : Bibliothèque Commanders
« le: mai 20, 2018, 03:47:27 pm »
Ah super, j'essaye ça tout de suite ça marche nickel maintenant ! 8)
Merci Thierry !!

J'ai finalement réussi à avoir quelques infos sur le port série, en ajoutant un Serial.begin(9600) dans le setup.
Ça vaut peut-être le coup de le rajouter dans le sketch d'exemple ?

11
Bibliothèques / Re : Bibliothèque Commanders
« le: mai 20, 2018, 03:21:50 pm »
Hello,

Je suis en train d'essayer la lib Commanders, avec le sketch "SimpleButtons". Mais je n'ai aucune réaction, que ce soit sur la LED ou sur le port série, alors que j'ai bien activé le mode DEBUG dans Commanders.h

J'ai essayé avec l'IDE en ligne et local, en retéléchargeant la dernière version depuis Git..  :o
Ça compile bien, mais ça ne réagit pas :(

Une idée ?

(testé avec Uno, IDE local dernière version...)

12
C'est le type d'alim que j'avais regardé dans un 1er temps pour toute l'électronique, les servos. Mais j'ai eu peur que ça chauffe : il aurait été installé à l'envers, dans un caisson... je me suis dit que c'était peut-être pas top pour l'aération.

Du coup pour le court terme, j'ai pris une alimentation chinoise (genre ça). Ca marche pour ce que je lui demande mais le boitier fait un peu cheap : c'est *beaucoup* plus léger qu'une alim d'ordinateur (d'ici à ce qu'il n'y ait qu'un simple module dedans...)

13
Pour la petite histoire, les relais avaient souvent deux bobines :
On utilisait les deux bobines pour faire monter le contact et dès qu'on était dans la bonne position, l'un des contacts servait à couper l'une des deux bobine pour diminuer la consommation. On a en effet besoin de plus de courant pour faire bouger les lames que pour maintenir le contact.
Et quand on a 600 000 relais pour 10 000 abonnés, on regarde aussi la consommation.

Et vous faites comment pour gérer la conso électrique, quand vous avez pleins de relais sur votre réseau ?

14
@DDEFF : je crois qu'à part peut-être la désintégration du noyau atomique, tu as tout prévu :o

@CAT+ : toujours bon de rappeler les bonnes pratiques pour assurer la bonne alimentation des aiguilles. Un peu plus compliqué à faire une fois le réseau mis en place ! heureusement on avait lu un tuto du même genre avant de poser les voies.
La technique du fil recuit, je la garde au chaud... (c'est le cas de le dire)  ;D

15
Bus CAN / Re : Re : Réduire câblage
« le: mai 14, 2018, 10:58:18 am »
Bon, ce vœux va être exhaussé et avec un peu de patience nous allons décrire ce que nous présenterons à Orléans, notamment une telle carte et un bus Can (sans fichier texte !).
quel teasing ! 8)

Pages: [1] 2