LOCODUINO

Discussions Générales => Les réseaux => Discussion démarrée par: Dominique le février 17, 2017, 12:03:02 pm

Titre: Réseau Dominique
Posté par: Dominique le février 17, 2017, 12:03:02 pm
Bonjour,

Enfin, je me jette à l'eau !
Cela fait un moment que j'envisageais de décrire mon projet, mais je ne le sentais pas assez mûr.

C'est seulement le 2ème réseau de ma vie, car je n'ai commencé qu'il y a peu de temps (quand même en 2012, à la retraite), après une reprise du réseau de mon fils qui m'a amené à découvrir l'Arduino et surtout après l'inscription au club CMFA d'Arpajon.

(http://www.locoduino.org/IMG/jpg/circuit_n1.jpg)

Ce réseau était trop petit mais j'avais réussi à y installer un bloc système en Arduino.
Donc j'ai décidé un jour de voir plus grand !

Il fallait surtout dégager de la place : l'aménagement du grenier du garage m'a apporté cette opportunité qu'il ne fallait pas laisser passer.

Après consultation de centaines de plans de réseau chez les amis et sur le web, j'ai choisi ce tracé :

(http://www.locoduino.org/IMG/png/projetdb1.png)

Il comprend une grande boucle en double voie avec 2 gares, dont une sera cachée sous le village au milieu.
J'ai ajouté une ligne à voie unique, en vert clair, qui parcoure le L du bas à gauche au haut à droite : ce sera un tramway ou métro qui reliera le centre-ville au parc de loisirs.

Au total il y a environ 50 m de voies en format N.

Il y a 3 niveaux et les pentes ne dépassent pas 2% :

Il se pose sur un chassis en L de 300 cm par 215 cm réalisé avec des vieux placards que j'ai débités en planches avec la scie circulaire.
La profondeur de la grande longueur fait 90 cm, c'est le maximum pour accéder au fond du réseau.
La profondeur de le partie à gauche fait 70 cm. Je l'ai allongée récemment pour construire un parc d'attractions dans l'espace au milieu de la boucle.

(http://www.locoduino.org/IMG/jpg/img_1c.jpg)

Actuellement, les trains peuvent circuler par conduite manuelle, et mon réseau ressemble à ça maintenant.

(http://www.locoduino.org/IMG/png/img_40c.png)

Petite précision importante : j'ai voulu que le pupitre de commande occupe toute la grande longueur du L (sur la photo il n'y a que la moitié), avec des interfaces pour tous les Arduino qui constitueront mon système sans PC  :D, j'y tiens  :)

Le décor est planté (si on peut dire), je vais progressivement entrer dans les détails.
Titre: Re : Projet Dominique
Posté par: DDEFF le février 17, 2017, 08:51:51 pm
Bonsoir Dominique,

C'est certainement le réseau que je connais le plus ...  ;D
Mais je suis très content que tu le présentes. On va apprendre plein de choses.

Denis
Titre: Re : Projet Dominique
Posté par: Dominique le février 17, 2017, 10:39:59 pm
Merci pour tes encouragements Denis  :D

En regardant la date de la photo du chassis (octobre 2014), ça fait donc plus de 2 ans que j'ai commencé ce réseau !
Je ne sais pas si je suis lent (sans doute) ou pas, en tout cas je n'avance pas vite  :-[

C'est aussi à peu près le moment où Locoduino a été créé par Jean-Luc : je n'ai pas hésité à le rejoindre et je pense que c'était ma meilleure décision : j'ai appris à me documenter tous azimuts, à tester des tas de choses, à réfléchir à diverses architectures et donc à construire mon réseau avec la certitude d'arriver à un résultat qui ira au delà de mes espérances de l'époque. Et j'ai aussi gagné une vraie bande d'amis   ;D

J'avais acquis une certaine expérience de l'Arduino avec la construction de mon premier réseau qui contenait 2 Arduino Mega, l'un dédié à la traction (avec quelques potentiomètres à glissière) et les automatismes de conduite (un mini block système), l'autre s'occupant de la rétrosignalisation (barrières infrarouge et capteurs RFID) et de la signalisation (quelques feux tricolores), une liaison I2C leur permettant d'échanger des messages d'événements et d'états.

En démarrant ce nouveau projet, je savais que je pouvais répartir les fonctions entre plusieurs Arduino mais je n'avais pas encore une idée précise de toute les fonctions qu'il faut développer et assembler.

Une seule certitude : vouloir développer 100% des circuits et des programmes moi-même dans la plus pure tradition du DIY (= Tout Faire Moi-même... sauf les locos, wagons, voitures et les décodeurs qui vont dedans, car, en N, c'est trop petit).

Fort de cette confiance, j'attaque la menuiserie.

(http://www.locoduino.org/IMG/jpg/img_2c.jpg)

Sur le chassis de 15 cm d'épaisseur, des tasseaux sont vissés pour réaliser les supports des plans de roulement. Ceux-ci sont découpés dans du contre-plaqué de 5mm.

(http://www.locoduino.org/IMG/jpg/img_6c.jpg)

Je pose les rails en provisoire et je fais rouler une loco analogique pour tester la continuité électrique.
La pente des rampes est soigneusement vérifiée au niveau à bulle.

Les tiges filetées servent à régler l'espacement facilement dans les courbes en pente.

(http://www.locoduino.org/IMG/jpg/img_8c.jpg)

(http://www.locoduino.org/IMG/jpg/img_12c.jpg)

Les rails et aiguilles sont de marque Fleischmann Piccolo avec ballast. Les moteurs d'aiguilles sont retournés et encastrés dans le CP du plan de roulement après avoir découpé l'emplacement du moteur. Cela condamne la commande manuelle (il faut utiliser un cure-dents pour les manoeuvrer dans un trou minuscule, donc à ce stade, il faut commencer à réaliser l'électronique de détection et de commande.
Titre: Re : Projet Dominique
Posté par: Jean-Luc le février 17, 2017, 10:57:25 pm
Tu me fais envie avec tes 310cm x 190cm  :P

Tu as pas mal de distance entre 2 voies parallèles ? non ?

15cm c'est la hauteur des planches de ton chassis ? Pour ma part je pense utiliser des planches de 13mm x 97mm (http://www.castorama.fr/store/Tasseau-en-sapin-rabote---Longueur-200-m-prod11360045.html?navCount=0&navAction=push&skuId=Casto626698) mais j'ai moins de longueur.
Titre: Re : Re : Projet Dominique
Posté par: Dominique le février 18, 2017, 07:31:52 am
Tu as pas mal de distance entre 2 voies parallèles ? non ?

J'ai respecteé la norme MOROP nem112 (entraxes minimum 25mm en pleine voie et 28 mm en gare) et, tu vois, les deux voies passent pile poil dans les entrées de tunnel :

(http://www.locoduino.org/IMG/jpg/img_11c.jpg)

Citer
15cm c'est la hauteur des planches de ton chassis ? Pour ma part je pense utiliser des planches de 13mm x 97mm (http://www.castorama.fr/store/Tasseau-en-sapin-rabote---Longueur-200-m-prod11360045.html?navCount=0&navAction=push&skuId=Casto626698) mais j'ai moins de longueur.

Je pense que ça va aller avec ces planches surtout si tu les assembles avec des emboitements, vissé et collé.
Moi j'ai récupéré des panneaux en latté de 16 mm qui avait au moins 30 ans donc définitivement stable ! J'ai assemblé 2 cadres comme celui-ci avec des vis et boulons de 10. Les pieds sont vissés dans les angles pour renforcer l'ensemble.

(http://forum.locoduino.org/index.php?action=dlattach;topic=290.0;attach=693;image)
Titre: Re : Projet Dominique
Posté par: Dominique le février 18, 2017, 10:09:29 am
Je reviens un instant sur le choix du tracé :

Je sais aujourd'hui qu'il aurait fallu éviter les voies parallèles aux bords. Mais ça aurait compliqué les virages, car j'ai voulu éviter les courbes R1et R2 les plus serrées. Il y en a néanmoins un peu, à gauche de la gare. C'est moche quand une voiture longue se retrouve presque à côté des rails. Je les cacherai dans un tunnel.

Je voulais à tout prix un long parcours fermé permettant à 2 trains de circuler automatiquement avec des horaires pendant que d'autres peuvent être en conduite manuelle. Ça corse les plaisirs et ça permet de mettre le réseau en demo. Là c'est réussi, c'est un grand tour.

Si, en plus, chaque train s'arrête dans la gare cachée et si un autre part juste après, cela fait 4 trains automatiques.

La ligne de métro est arrivée plus tard. J'en avais besoin pour mettre au point le va et vient du réseau de demo du club. On en parlera en détail. Comme il y plus de la moitié du trajet en dessous la voie de parade arrière, je mettrai une caméra dans le sous terrain.

Enfin, ce tracé continue à évoluer un peu. Je vais supprimer les TJdS qui font des court-circuits avec certaines locos et modifier la voie de manœuvre de devant la gare qui relie la voie de programmation au dépôt et au reste du réseau.
Cela veut dire que la voie de programmation a vocation à participer aux manœuvres. J'aurai une commutation des centrales DCC !
Titre: Re : Projet Dominique
Posté par: DDEFF le février 18, 2017, 07:34:58 pm
Bonsoir Dominique,

Je ne me permettrais pas de critiquer un réseau, n'en ayant moi-même aucun... :-X

Le mien sera forcément un "plat de spaghettis", fortement décrié dans toutes les revues.  >:(
Je ne pourrais m'en sortir qu'en en planquant une bonne partie, sauf la gare qui sort vraiment de l'ordinaire.

Mais je ne pouvais m'empêcher de remarquer que le "new Jean-Luc" correspondait aux canons actuels (je lis énormément de revues).

Pour le tien, je pense que quand le décor aura évolué, on en reparlera, je suis sûr que ça sera bien.
Titre: Re : Re : Re : Projet Dominique
Posté par: Jean-Luc le février 18, 2017, 09:32:50 pm
J'ai respecteé la norme MOROP nem112 (entraxes minimum 25mm en pleine voie et 28 mm en gare)

Effectivement, mon impression visuelle était fausse :) On a toujours l'impression que les voies sont trop écartées
Titre: Re : Re : Projet Dominique
Posté par: Dominique le février 20, 2017, 05:31:25 pm
Pour le tien, je pense que quand le décor aura évolué, on en reparlera, je suis sûr que ça sera bien.

D'autant qu'il n'y a pas encore de décor, car je tiens à installer tous les équipements électriques (détecteurs, signaux, alimentations DCC (au moins 3, une pour le va et vient, une pour la voie de programmation et au moins une pour le réseau principal, avec de l'électronique et des relais pour les isoler ou raccorder les zones ensemble), ainsi que les branchements du décor.

Pour le moment, j'ai 2 centrales DCC (modules de traction), un TCO physique (plaque représentant le réseau avec leds d'occupation et de position d'aiguilles et inverseurs d'aiguilles), un module de commande d'aiguilles et un système de gestion simplifié.

Chaque module est connecté sur un bus CAN et échange des messages avec les autres modules.

Le module de traction du va et vient est le plus récent. Il tourne sur Nano avec une face avant sympa :

(http://forum.locoduino.org/index.php?action=dlattach;topic=203.0;attach=524;image)

C'est un projet basé sur DCC++ (http://forum.locoduino.org/index.php?topic=203.60 (http://forum.locoduino.org/index.php?topic=203.60)) que j'ai modifié pour intégrer une reconnaissance automatique de l'adresse DCC de la loco posée sur la voie, l'automate de gestion complète du va et vient avec asservissement de vitesse selon une consigne affichée en km/h, et d'autres asservissements pour réaliser le ralentissement et l'arrêt en gare en souplesse, comme en vrai.

Le module de traction principal a été réalisé il y a plus d'un an et est basé sur CmdrArduino.
(http://www.locoduino.org/IMG/jpg/img_20c.jpg)

Il gère les vitesses, directions et feux de 4 trains par potentiomètre à glissière et inverseurs et envoie les changements par message CAN. Il peut aussi recevoir des messages pour ralentir ou stopper un train, ce qui est nécessaire pour faire du cantonnement et un block système.

Je vais complètement refaire ce module sur la base de DCC++ avec diverses manettes de commande. J'y reviendrai plus loin.

Le module de commande d'aiguilles est un Arduino Mega qui pilote 40 relais (2 par aiguille, afin de n'alimenter la bobine qu'une seconde et jamais 2 bobines en même temps).

Le module TCO est aussi un Arduino Mega qui gère 20 inverseurs et 80 leds. Il fallu recourir à un paquet de 74HC595 pour démultiplier les commandes des leds. Il reçoit aussi la rétrosignalisation (les détecteurs de consommation). Je le détaillerai plus loin.
(http://www.locoduino.org/IMG/jpg/montco-petit.jpg)


Enfin le module gestionnaire est un Arduino Due qui doit se charger de tout gérer en communiquant avec les autres modules via le bus CAN.

En fait j'ai construit ces modules ne sachant pas exactement où aller au moment où j'ai commencé. Mais les échanges très instructifs sur Locoduino m'ont amené à définir toutes les fonctionnalités que je souhaite avec des possibilités d'évolutions.

Ainsi, le gestionnaire devra piloter le réseau conformément aux règles de la SNCF : le train doit obéir aux signaux. Il doit aussi piloter le TCO, les aiguilles, les signaux et les éléments de décor en relation avec les circulations (passage à niveau par exemple). Il doit aussi assurer les circulations automatiques.

Je sais depuis longtemps qu'il serait trop compliqué de concentrer la totalité des traitements dans un seul Arduino, même un Due qui en est certainement capable.

Deux exemples :
1) Le module de traction peut connaitre les trains dans le détail (nom, correspondance entre crans DCC et vitesse réelle par étalonnage des vitesses, pilotage par la vitesse réelle plutôt que par le cran DCC, etc..). Il peut gérer seul le ralentissement avec la seule connaissance de la distance. Il peut donc présenter une interface simplifiée avec le gestionnaire qui peut voir des trains "virtuels" et ne pas s'encombrer des caractéristiques spécifiques à chaque train.

2) Le module TCO qui reçoit la rétrosignalisation peut filtrer les signaux reçus avant de transmettre des occupations et libérations "propres".

Sur tout cela je reviendrai plus loin en détail.

Titre: Re : Projet Dominique
Posté par: Dominique le mai 14, 2017, 10:47:02 pm
Retour sur la description de mon réseau : entre temps j'ai testé avec succès le Gestionnaire de Pierre (voir sur le Forum http://forum.locoduino.org/index.php?topic=166.msg2920#msg2920 (http://forum.locoduino.org/index.php?topic=166.msg2920#msg2920)).

Je reviendrai en détail sur les réflexions qui m'ont conduites à ce point, mais les principale conclusions qui vont guider toute ma réalisation sont les suivantes :


Tout cela renforce donc l'intérêt du bus CAN pour faire communiquer les modules Arduino entre eux.
C'est l'objet de l'article http://www.locoduino.org/spip.php?article148 (http://www.locoduino.org/spip.php?article148)

(http://www.locoduino.org/IMG/jpg/archi.018.jpg)

Dans mon projet, l'élément clé est la définition des messages CAN qui circulent sur le bus. Grâce à cela, chaque module peut évoluer indépendamment des autres. ;D

Et comme le projet est devenu modulaire grâce à cette architecture, je vais pouvoir en décrire la réalisation en plusieurs parties indépendantes.

Je vais commencer par le câblage de l'alimentation DCC qui vient en même temps que la pose des rails, avec la réalisation des détecteurs d'occupation par consommation de courant.

Logiquement je devrais poursuivre par la centrale DCC qui alimente le réseau. Mais la première version réalisée à base de la bibliothèque CmdrArduino ne me satisfait plus depuis que j'ai testé DCC++.  Je vais donc passer son tour en attendant d'avoir réalisé et testé la nouvelle centrale avant de la publier, car j'ai décidé de ne publier que ce qui marche !.

En attendant je vais décrire le TCO manuel dont a vu les images plus haut.

Ensuite viendra le module de commande des aiguilles.

Il restera ensuite :

... et plein d'autres modules de gestion du décor pour animer, éclairer, sonoriser, etc...

Soyez patients  8)
Titre: Re : Projet Dominique
Posté par: savignyexpress le mai 15, 2017, 07:45:45 am
Bravo Dominique pour ce super projet et merci pour toutes ces explications détaillées. Je me réjouis de voir la suite.

Tu mentionnes l'utilisation de registres à décalage 74HC595 et j'aimerais savoir si tu as rencontré des problèmes avec ce composant ?

Sur un projet de commande de gares (cf mon blog https://savignyexpress.wordpress.com/2014/01/20/gare-du-reseau-dun-ami-toute-la-doc/ (https://savignyexpress.wordpress.com/2014/01/20/gare-du-reseau-dun-ami-toute-la-doc/)), j'ai eu des problèmes d'interférences électromagnétiques. Le système comporte 6 registres à décalages commandant 48 relais. Certains relais sont utilisés pour commander des aiguillages Minitrix, d'autres pour alimenter des zones d'arrêt. Les aiguillages perturbent les registres à décalage ce qui active des bits non sollicités. Le problème a été en grande partie résolu par l'emploi de câbles blindés (RJ 45) pour acheminer les signaux des registres entre les cartes.

Bon début de semaine et meilleures salutations.

Marc-Henri
Titre: Re : Projet Dominique
Posté par: Dominique le mai 15, 2017, 12:31:03 pm
Bonjour Marc-Henri,

Je n'ai eu aucun souci avec les 74HC595 qui ne servent, en fait, qu'à allumer ou éteindre des leds basse consommation, donc qui ne doivent pas générer de parasites.

Dans la description de mon projet je vais continuer par la carte TCO/Rétrosignalisation qui, justement embarque 10 de ces circuits. Elle est présentée un peu ici :
http://forum.locoduino.org/index.php?topic=329.msg3102#msg3102 (http://forum.locoduino.org/index.php?topic=329.msg3102#msg3102)

et le câblage fouillis avec le Mega est ici :
(http://www.locoduino.org/IMG/jpg/reseau_2db.022.jpg)

Par contre, j'ai aussi une carte de commande de 40 relais (à laquelle de dois en ajouter 4) qui est constituée d'un Mega et de 5 plaques de 8 relais 5V avec optocoupleurs. Les relais commutent de l'alternatif pour des moteurs Fleischmann, je n'avais pas d'autre choix.

Là non plus, je n'ai pas de parasites.

Chaque carte est reliée au bus CAN et je suis certain de ne pas voir passer d'événements parasites !

C'est pour cela que je commence à décrire tout mon bazard  ;)

Bien amicalement
Dominique
Titre: Re : Projet Dominique
Posté par: Dominique le mai 20, 2017, 06:32:10 pm
Les détecteurs d'occupation et l'alimentation des rails

Après la pose des voies, il faut se poser tout de suite la question de l'alimentation des rails, les découpages en zones et cantons (pour l'exploitation des circulations) et la détection des trains dans toutes ces zones.

Le principe de base est que la centrale DCC de traction va fournir l'alimentation des rails sur 2 fils (rouge et noir). Comme je n'ai pas de boucle de retournement, j'ai identifié un rail "rouge" et un rail "noir" partout dans le réseau.

Les découpages en zones se font en isolant un deux rails (toujours le même) aux points de coupure. Fleischmann fournit de jolies éclisses isolantes en plastique gris. Parfois j'ai eu recours à la mini-disqueuse pour couper un rail.

Les points d'alimentation des zones sont faits en soudant un fil (noir ou rouge selon le rail) soit sur les éclisses métalliques entre 2 éléments de rail, soit en dégageant le rail sous le ballast et en le soudant à cet endroit.

Il faut avouer que ces opérations de découpage et d'alimentation sont fastidieuses surtout quand le nombre de zones est assez élevé (j'ai ai plus de 40). Le passage des fils dans le support doit être invisible et cela demande parfois des ajustements car la perceuse n'est pas toujours passée au bon endroit.

Les rails ne sont fixés pour le moment que par des petits clous enfoncés dans les trous prévus des traverses. Je n'ai pas utilisé de marteau mais un chasse-clou de 2 à 3 mm de diamètre avec lequel j'appuie sur la tête des clous à la main. Cela facilite le démontage ultérieur car il y a toujours des modifications à faire ultérieurement.

Le choix du détecteur d'occupation par consommation de courant s'est fait par recherche sur le web. Le sujet est abondamment traité dans les divers forums.

J'ai finalement choisi ce schéma :

(http://www.locoduino.org/IMG/jpg/im19.jpg)

Ensuite, comme il fallait en faire au moins 50, j'ai cherché à faire des circuits imprimés "professionnels". Jean-Luc et son ami Pierre m'ont bien aidé en me fournissant le logiciel Canari (pour Mac) et un peu d'aide au démarrage.

Voilà donc "mon premier circuit imprimé" réalisé en CAO :

(http://www.locoduino.org/IMG/jpg/im16.jpg)

Suivi d'une commande chez Electrodragon, puis de la livraison des circuits :

(http://www.locoduino.org/IMG/jpg/im17.jpg)

Et l'achat des composants chez TME.

Je rappelle au passage que ces fournisseurs sont présentés sur le site Locoduino :

http://www.locoduino.org/spip.php?article13 (http://www.locoduino.org/spip.php?article13)

Le circuit obtenu contient 2 détecteurs par plaque de 5x5 cm, j'ai omis les potentiomètres pour avoir le maximum de sensibilité (en N les courant sont faibles) et j'ai réalisé l'installation de ces circuits sous le réseau :

(http://www.locoduino.org/IMG/jpg/im18.jpg)

Comme on peut le voir sur cette image, à ce stade je n'ai raccordé que le coté alimentation DCC : Un fil noir + un fil rouge arrivent sur le détecteur en provenance de la traction DCC (via un bus réalisé en fil de forte section qui parcoure le centre du réseau) et un fil noir + un fil rouge partent vers les rails.

On verra plus loin le raccordement des détecteurs au TCO.

C'est à ce moment qu'on se rend compte que la hauteur du réseau a beaucoup d'importance pour rendre cette opération plus ou moins confortable  :-[

Titre: Re : Projet Dominique
Posté par: Dominique le mai 22, 2017, 03:45:41 pm
Une alternative aux détecteurs de consommation :

Une autre discussion sur la rétrosignalisation, ici -> http://forum.locoduino.org/index.php?topic=329.0 (http://forum.locoduino.org/index.php?topic=329.0)
présente une alternative à mes détecteurs.

Pierre montre un circuit qui en regroupe 8 avec un circuit de multiplexage pour la lecture des signaux, qui réduit beaucoup le nombre de fils de liaison avec l'Arduino qui gère ces signaux.

Personnellement j'ai fait le choix des détecteurs présentés dans ce fil avant de connaitre cette alternative. Par ailleurs, j'ai choisi un Arduino Mega pour traiter ces signaux (et plein d'autres) car il dispose de 80 ports et d'une mémoire Flash et Ram confortable (256K de flash et 8K de ram).

Ce n'est pas le prix du Mega2560 qui m'importait le plus (un peu plus de 30€ chez les revendeurs français ou moins de 10€ chez les clones), mais ses capacités techniques.
Titre: Re : Projet Dominique
Posté par: savignyexpress le mai 23, 2017, 09:54:53 am
Bonjour Dominique,

Merci pour ton partage. Pour mon nouveau projet de réseau, j'ai plutôt choisi le schéma décrit ici: http://www.1-zu-220.de/besetztmelder.htm (http://www.1-zu-220.de/besetztmelder.htm). Je n'utiliserai que la partie gauche, optocoupleur inclus avec un condensateur de filtrage en sortie pour ternir compte du fait qu'il ne détecte la consommation que durant les demi alternances positives du signal DCC.

Le principal avantage de ce montage est qu'il ne comporte que 2 diodes au lieu de 4, réduisant la chute de tension à 0.7 V plutôt que 1.4 V. Cet avantage est important pour mon réseau car des diodes de freinage pour le système ABC de Lenz seront en série avec les détecteurs. Testé avec succès en N.

Je développe des cartes regroupant freinage et détection et ce seront mes premiers circuits imprimés. Jusque là je travaillais en cartes à bandes.  ;)

Meilleures salutations helvético-ferroviaires.
Titre: Re : Projet Dominique
Posté par: DDEFF le mai 23, 2017, 04:18:15 pm
Bonjour Marc-Henri,

Je rebondis sur
Citer
des cartes regroupant freinage et détection
Freinage ?
J'imagine que c'est lié au système ABC de Lenz ? (la Brems-diode, le contact et le relai)

Sur le schéma que tu indiques, je vois bien 2 diodes tête bêche, d'où le 0,7 V. OK
Mais, par ailleurs, il faut aussi 0,7 V pour déclencher le transistor.
On doit être tangents...
Tu prends quoi, comme transistor ? un "TUP" comme ils disaient sur Elektor ?

Je suis content que tu l'aies testé et qu'il fonctionne. C'est une bonne nouvelle.
D'autant que le système ABC de Lenz fonctionne aussi sur des chutes de potentiel de diodes.

PS : c'est un des avantages d'être Helvète : tu saisis certainement plus vite que moi le sens des textes en allemand !  ;)
Et pourtant, j'ai été nourri au Märklin-Magazin  ::)
Titre: Re : Projet Dominique
Posté par: Dominique le mai 23, 2017, 10:09:09 pm
Bonjour Marc-Henri,

Je crois que je vais tester ce circuit, ainsi que celui de Pierre et comparer tout cela avec le mien.
Merci pour cette information.

Dans mon schéma, j'avais prévu des diodes Schottky SB260 qui passent facilement 2A. J'avais vu un peu grand car ces diodes ne perdent que 0,3 à 0,5V (voir les courbes ci-dessous) quand le courant est inférieur à 1A, ce qui est le cas d'une loco en N.

Même en en mettant 2 en série, il arrive que le transistor ne conduise pas assez pour l'optocoupleur.

J'ai été obligé de remplacer 2 des diodes SB260 par des 1N5819 qui sont limitées à 1A mais dont la chute de tension démarre à 0,75V (donc 1,5V avec 2 en série).
Pour le moment aucune diode ne m'a fait faux bond ;)

J'ai voulu utiliser des diodes rapides (schottky) car c'est recommandé pour ne pas dégrader le signal DCC.

Je n'ai pas essayé avec des bêtes 1N400x mais ça marche peut-être bien (en tout cas la chute de tension est bien supérieure).

Quelqu'un a-t-il un avis sur les diodes classiques (Pierre a utilisé de simples ponts redresseurs) ?

Amicalement
Dominique
Titre: Re : Projet Dominique
Posté par: Dominique le mai 23, 2017, 10:31:09 pm
En ce qui concerne le freinage, c'est le gestionnaire qui enverra un ordre de ralentissement au train. Il doit donc connaitre le N° de train présent devant un signal de ralentissement, car c'est du DCC.

C'est un challenge pour moi, très excitant   8)

J'ai déjà fait des tests concluants et j'ai plusieurs façon d'y arriver. J'en ai parlé un peu dans ce fil -> http://forum.locoduino.org/index.php?topic=245 (http://forum.locoduino.org/index.php?topic=245)

Ce sera décrit plus loin dans mon projet, quand j'aurai trouvé la ou les meilleures solutions.

Patience ...
Titre: Re : Projet Dominique
Posté par: DDEFF le mai 23, 2017, 11:06:53 pm
Bonsoir Dominique,

Dans le schéma proposé par Marc-Henri, comme le transistor n'est pas Shottky, il lui faut ses 0,7 V pour se déclencher et allumer la LED de l'optocoupleur...
Effectivement, 2 diodes Shottky en série, ça peut ne pas suffire.

J'ai une question bête :
L'ABC de Lenz déclenche le freinage s'il y a 0,6 V de différence entre une alternance et l'autre (relatif) ou à 0,6 V (absolu) ?
Parce que si c'est relatif, on peut mettre 2 diodes normales en série... dans tous le détecteurs.

Denis
Titre: Re : Projet Dominique
Posté par: savignyexpress le mai 24, 2017, 07:47:51 am
Bonjour à tous,

Je prévois effectivement un freinage ABC avec 4 diodes dans un sens et 1 dans l'autre, le tout commuté par un relais. Je confirme que c'est bien l'écart relatif entre l'alternance positive et l'alternance négative du signal DCC qui déclenche le freinage ABC. Et donc, comme Denis le mentionne, cela devrait aussi fonctionner avec une chute de tension de 1.4 V pour la détection. Mais j'aime bien l'idée de ce circuit par rapport à celui à 4 diodes. Le transistor PNP est un BC 327.

Il faut que je vérifie avec quel type de diode j'ai fait mon test ! S'il s'agit de diodes de redressement 1N400x, le temps de recouvrement de 30 us limite la fréquence du signal à 15-20 KHz, le signal DCC peut être assimilé à un signal carré de 8-9 KHz (période = 2 * 58 us, durée du 1). J'ai peut être eu de la chance en étant proche des limites.

ESU préconise l'utilisation de diodes UF5404 pour réaliser le freinage ABC. Cette diode a un temps de recouvrement de 75 ns et une tension de seuil du même ordre de grandeur que celle des 1N400x.

Bonne journée à tous.
Titre: Re : Projet Dominique
Posté par: CATPLUS le mai 24, 2017, 07:25:46 pm
Bonjour

Pour rebondir, j'utilise le montage cité en amont. Les diodes de type 1N5400 ou BY.. sont parfaites pour le DCC
J'ai fait 64 décodeurs et aucun problème à ce jour.

Lorsque je pratiquai l'analogique, j'avais fait également des détecteurs à diodes 1N400x avec alim 24 volts et bon nombre de ces diodes ont claquées
je pense que ces incidents étaient dues aux tensions trop justes pour les modèle 1N400x

Amicalement
Marcel
Titre: Re : Projet Dominique
Posté par: savignyexpress le mai 25, 2017, 11:28:13 am
Bonjour à tous,

Je viens de procéder à de nouveaux tests avec les diodes dont je dispose.

1N4002G
Tension inverse max 100 V. Temps de récupération 2 us.

1N4007H
Tension inverse max 1000 V, Temps de récupération 2 us.

J'ai comparé l'allure du signal DCC à l'oscillo avant, après le détecteur, après le freinage ABC, sans constater aucune différence visible. La loco réagit toujours aux commandes. Je pense qu'un temps de 2 us reste compatible avec les durées des demi-périodes DCC de 58 us (1) et 100 us (0).

Bon week-end prolongé.
Titre: Re : Projet Dominique
Posté par: Dominique le mai 25, 2017, 05:03:11 pm
Merci Marc-Henri,

C'est convainquant ! Les diodes Schottky ne sont pas nécessaires.

Bon week-end aussi si la Suisse a le même pont.
Pour moi c'est le pont tous les jours  :D

Bien cordialement
Dominique

PS: j'adore le train en métrique qui va de Martigny à St Gervais. Chaque fois je visite la fondation Pierre Gianadda. Parfois j'ai la chance de cueillir de délicieux abricots du Valais !
Titre: Re : Projet Dominique
Posté par: Dominique le juillet 21, 2017, 07:46:50 pm
Je reprends doucement la description de mon réseau !

Nous en étions aux détecteurs d'occupation qui sont reliés, chez moi, au TCO physique. J'avais aussi parlé du module de commandes d'aiguilles, du module de traction et du gestionnaire.

Au départ je n'avais pas idée de la manière de gérer l'ensemble du réseau par un gestionnaire, je ne soupçonnais pas l'étendue de la complexité et le nombre de choses à relier ensemble.

C'est pour cela que le découpage en modules reliés par un bus CAN m'a semblé idéal.

Pour mettre les idées en place, les rôles du module TCO physique (avec des leds et des clés) devait :

Il a donc fallu définir un certain nombre de messages CAN comme ceci :

Id CAN en Reception :
Id CAN en  Emission :

Je reviendrai plus loin sur les valeurs des octets de message qui suivent l'ID (maximum 8 octets, je n'utilise qu'un seul octet en général).

Le choix des messages CAN est très structurant pour le projet. Mon choix n'est pas le seul possible, il faut bien choisir un jour. Car cela définit les filtres CAN, les fonctions d'émission et réception et le traitement des messages reçus (parsing en anglais, mot qui revient souvent sur Locoduino), que l'on retrouvera partout dans les modules du réseau.

J'ai donc :

Une routine d'interruption qui se charge de récupérer tous les messages CAN acceptés dans une mémoire tampon :
/*
 *  ISR CAN
 */
 
void MCP2515_ISR()
{
     Flag_Recv = 1;
}

/*
 * Routine de récuperation des messages CAN dans la memoire circulaire _Circule
 * appelee par LOOP lorsque Flag_Recv = 1;
 */
 
void CAN_recup()
{
  unsigned char len = 0;                // nombre d'octets du message
  unsigned char buf[8];                 // message
  unsigned char Id;                     // Id

  while (CAN_MSGAVAIL == CAN.checkReceive())  {
    CAN.readMsgBuf(&len, buf);          // read data, len: data length, buf: data buf
    Id = CAN.getCanId();
    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;   // depassement de la capacite de Circule
                          // message perdu (pas enregistré dans Circule)
    }
  }
}


Et le traitement des messages CAN dans la loop :
  /*
   *  traitement des messages dans le buffer _Circule
   */
   
  while (_Ncan > 2)  {                // messages dans _Circule : au moins 3 bytes
    //digitalWrite(LedV, HIGH);       // led activité CAN reste allumée jusqu'à loop suivante
    _Ncan--;
    RId = _Circule[_indexR];          // recup Id
    _indexR++;
    if (_indexR == sizeof(_Circule))  {_indexR = 0;}
    _Ncan--;
    Rlen = _Circule[_indexR];         // recup longueur
    _indexR++;
    if (_indexR == sizeof(_Circule))  {_indexR = 0;}
    if (_dumpCan)  {     
      Serial.print("CAN id ");
      Serial.print(RId);
      Serial.print(", data ");
    }
    for (int k = 0; k < Rlen; k++)  {
      _Ncan--;
      Rbuf[k] = _Circule[_indexR];  // recup octets message
      _indexR++;
      if (_indexR == sizeof(_Circule))  {_indexR = 0;}
      if (_dumpCan)  { 
      Serial.print("0x");
      Serial.println(Rbuf[k], HEX);
      }
    } // le message est dans les globales RId, Rlen et Rbuf[..]
    curCLEF = Rbuf[0] & 0x3F;       // garder que les bits 0..5 = index aiguille
   
    if (RId == RId_TCO_Clef)  {
      // 1 octet donnees:  bit 7,6 -> 1,1 et 5..0 -> 3F = envoi etats toutes clefs               
      if (curCLEF == 0x3F) {          // commande de lecture etats
        if (bitRead(Rbuf[0], 7)) {
          if (bitRead(Rbuf[0], 6)) {  // demande etat toutes clefs (data = 0xFF)
            EnvoiCAN_toutesClefs();
          }
        }
      }
    }
    if (RId == RId_TCO_Led)  {
      // 1 octet donnees:  bit 7,6 -> 0,0 = devie ou 0,1 = droit, bits 5..0 = numero clef
      if (!bitRead(Rbuf[0], 7)) {    // bit 7=0, commande de positionnement de Led sur une Clef d'aiguille
        if (curCLEF < MaxCLES) {     // numero de clef
          ListeClef[curCLEF].LedUpdate(bitRead(Rbuf[0], 6)); // bit 6 = position
          //affiche_vertes();      // affichage vertes + rouges plus loin
          if (_dumpCan) {
            Serial.print(" CAN : curCLEF ");Serial.print(curCLEF);
            Serial.print(" Aiguille "); Serial.print(ListeClef[curCLEF].NomClef());
            if (bitRead(Rbuf[0], 6)) {
              Serial.println(" DROIT");
            } else {
              Serial.println(" DEVIE");
            }
          }
        }                             
      }   
    }                                // fin changement leds aiguilles 
  }                                  // fin traitement messages CAN (while)

Les clés et les zones sont décrites sous forme d'objets comme on peut le voir dans le traitement ci-dessus.

Ma premiere réalisation (toujours en place sur le réseau) se limitait à 19 aiguilles (dont 2 TJD) donc avec 19 clés. Mon plan de découpage en zones se limitait à 42 zones.

Il se trouve que la prise en compte des signaux, des itinéraires, les possibilités de manoeuvres offertes par le Gestionnaire que j'avais un peu de mal à appréhender par manque d'expérience, sont devenus maintenant beaucoup plus clairs grâce à l'aide de Pierre59.

Cela a entrainé un certain nombre de modifications qui ne remettent pas en cause les développements matériels et logiciels que j'avais fait, grâce au découpage en modules et aux communications CAN.

Je reviendrai donc sur ces évolutions après la description du TCO.
Titre: Re : Projet Dominique
Posté par: Dominique le juillet 22, 2017, 06:12:51 pm
Description du TCO "en dur"

Je précise "en dur" car réalisé sur une plaque de plexi avec de vraies Leds et de vrais inverseurs (clés). En réalité il y aura d'autres TCO tels qu'un écran graphique piloté par le Gestionnaire et un écran HDMI piloté par un pcDuino8 sous Ubuntu, avec Processing qui sera décrit plus tard.

Première opération, la plaque représentative du réseau :

(http://www.locoduino.org/IMG/png/montcoendur.png)

Le TCO est constitué d'une plaque de plexiglass blanc sur lequel est collée une représentation simplifiée du réseau, dans laquelle les cantons des tracés haut, bas et la boucle sont réduits sur la partie droite.
J'ai réalisé le dessin avec RailModeller Express (https://itunes.apple.com/us/app/railmodeller-express/id1008811516?mt=12 (https://itunes.apple.com/us/app/railmodeller-express/id1008811516?mt=12)), imprimé sur papier avec l'imprimante jet d'encre, plastifié et collé sur une plaque de plexi blanc avec de la colle en bombe Cléopâtre.

Cette plaque est percée au milieu de chaque canton pour loger une Led rouge indiquant l'occupation du canton. Elle est aussi percée au niveau de chaque aiguille pour loger une clé de sélection de position et 2 Leds vertes représentant la position des aiguilles.

Pour réaliser les perçages proprement après le collage du dessin, j'ai utilisé un emporte-pièce et j'ai marqué le plexi en faisant sauter un confetti de papier ce qui a permis ensuite de percer les trous au bon diamètre, sans risque de dérapage de la perceuse.

(http://www.locoduino.org/IMG/jpg/img51.jpg)

Après montage des inverseurs et des Leds (collées), la vue "en coulisse" du TCO est édifiante :

(http://www.locoduino.org/IMG/jpg/img57.jpg)

Le TCO est animé par un Arduino MEGA 2560 qui gère :
- 20 inverseurs d’aiguilles = 20 pattes : AG1 à AG20 sur 21 bornes avec Gnd
- 36 Leds vertes (positions aiguilles) : 5 x 74HC595 chainés = 3 pattes
- 36 Leds rouges (occupations) : 5 x 74HC595 chainés = +1 patte
- 35 Détecteurs occupation = 35 pattes
- 36 bornes avec Gnd (y.c. va-et-vient)
- Bus CAN = 5 pattes (port SPI)
- Console = 2 pattes (port USB)
AU TOTAL = 68 pattes sur 70 !

Schéma de principe des interface avec 74HC595 (il y a en tout 2 bancs de 5 74HC595):

(http://www.locoduino.org/IMG/jpg/img52.jpg)

La réalisation de cette carte à 74HC595 est faite sur une platine 100 x 160 mm que j'avais en stock depuis très longtemps :


(http://www.locoduino.org/IMG/jpg/img53.jpg)

Pour les raccordements, surtout pour avoir la plus grande fiabilité possible et permettre le montage et les modifications, j'ai ajouté sur le MEGA une carte Prototype de ce type (il en existe plein) :

(http://www.locoduino.org/IMG/jpg/img54.jpg)

J'y ai soudé des nappes au bout desquelles j'ai soudé des borniers à vis déportées de chaque coté du MEGA.

(http://www.locoduino.org/IMG/jpg/img55.jpg)

Cela fait un peu fouillis car je n'ai pas cherché à ranger les fils mais c'est très fiable et toujours accessible en soulevant la plaque du TCO montée sur charnière. C'est promis, mon prochain TCO sera plus professionnel.

On voit sur la droite la carte CAN Locoduino et au fond au milieu la carte 74HC595.

Des trous dans le fond assurent le passage des fils avec le dessous du réseau.

Pour le bornier de raccordement des occupations de zones, j'ai fait un tableau avec les relations entre les Pins du Mega raccordées aux détecteurs d’occupation, les Leds Rouges (coordonnées de la led dans la matrice de 74HC595) et les Zones.

(http://www.locoduino.org/IMG/jpg/img56.jpg)

Bien entendu il y a quelques bornes disponibles pour ajouter des détections d'occupation supplémentaires, ce qui va arriver, on le verra plus loin.

Depuis la construction de ce TCO (ça fait bien 2 ans minimum), je n'ai jamais eu de panne !
Titre: Re : Projet Dominique
Posté par: Dominique le juillet 22, 2017, 06:43:36 pm
Construction du contrôleur d’aiguilles

Le contrôleur d’aiguilles est construit sur une base Arduino Mega 2560 associé à une carte bus CAN et un ensemble de 40 relais qui commandent les 18 aiguilles (2 relais par aiguille) et 2 dételeurs (1 relai par dételeur).

(http://www.locoduino.org/IMG/jpg/img58.jpg)

L’Arduino est disposé coté tableau de bord tandis que les relais, disposés de chaque coté d’une planche de CP monté sur charnière, sont placés à l’intérieur, sous le circuit. Des câbles en nappes relient l’Arduino aux relais, comme expliqué pour le TCO, avec une carte prototype permettant la soudure des nappes pour un maximum de fiabilité.

(http://www.locoduino.org/IMG/jpg/img59.jpg)

Comme les aiguillages choisis sont des modèles Fleischmann à moteur à courant alternatif, j'ai utilisé 2 relais par aiguille, avec le schéma suivant :

(http://www.locoduino.org/IMG/jpg/img60.jpg)

Le relai Impulsion est activé 150 millisecondes après que le relai Sens est positionné selon le sens direct/droit ou dévié.

Les relais en couples sont affectés aux pins de l’Arduino (tout est noté dans un tableau):
Relais d'aiguilles 4,5(A),6,7(B),8,9(C),10,11(D) - 22,23(G),24,25(H),26,27(I),28,29(J) - 30,31(K),32,33(L),34,35(M),36,37(N) - 38,39(O),40,41(P),42,43(Q),44,45(R) - 12,13(E),14,15(F),46,47(S),48(T1),49(T2).
La carte CAN est raccordée par les pins : 50 (SO), 51 (SI), 52 (SCK), 53 (CS), 2 (INT).
Les pins A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15 restent libres pour le moment. Elles sont réservées pour des relais supplémentaires (ça va arriver, il y en a 4 maintenant installés) et des détecteurs complémentaires (RFID, Hall, IR).

Comme pour le TCO, le logiciel utilise les routines CAN décrites ci-dessus, avec des messages dont les Ids sont :

Les aiguilles sont déclarées sous forme d'objets :
class Aiguille
{
  private:   
    byte index;             // 0, .. MaxAIG-1
    char nom;               // 'A' ou 'B' ...
    byte pin_sens;          // sens droit (HIGH) ou devie (LOW)
    byte pin_pulse;         // impulsion de commande : actif LOW
    bool etat;              // true = droit, false =  devie
    bool change;            // rafraichir la position en fonction de etat avec envoi CAN
    bool echoCan;           // rafraichir la position en fonction de etat sans envoi CAN si false
    bool curstep;           // true = pulse en cours, false = cycle fini
    unsigned long milli;    // temps systeme au debut de l'impulsion
   
  public:
    // ----------- constructeur complet ------------
  Aiguille(byte a_index, char a_nom, byte a_pin_sens, byte a_pin_pulse);

  void Setup();
  void Armer();
  void Rafraichir();
  void Caner();
  void Preparer(bool a_sens);     // sur reception message CAN: mise en liste
  void Bouger();                  // execute suivant sens avec envoi CAN
  void Positionner(bool a_sens);  // si changement - cas recurent
  void Forcer(bool a_sens);       // cas setup lecture EEPROM
  void MiseAJour();               // din de la periode : desarmement relais
  byte NomAig();
  bool EtatAig();
};

Une des astuces que j'aime bien est le rafraichissement des positions de chaque aiguille, une toutes les 10 secondes. Cela permet d'éviter quelques déraillements en cas de changement de position provoqué par exemple par un convoi entrant par un talon positionné differemment.

/*
   * Toutes les 10 secondes : rafraichissement d'une position d'aiguille
   */
   
  if (_Minute + 10000 < millis()) {                        // toutes les 10 secondes
    _Minute = _Minute + 10000;
    ListeAig[RafraichAiguille].Rafraichir();               // rafraichissment d'une position d'aiguille sans envoi CAN
    if (_debug) {
      Serial.print(" 10SEC : rafraich: ");Serial.print(RafraichAiguille);
      Serial.print(ListeAig[RafraichAiguille].NomAig());
      Serial.print(" ram ");Serial.println(_RamFree());
    }
    RafraichAiguille++;
    if (RafraichAiguille > MaxAIG-1) RafraichAiguille = 0; // pas de rafraichisement des Deteleurs, evidemment !
  }

Voici le traitement des messages CAN:

/*
   *  Récupération d'un seul message par loop dans la memoire circulaire _Circule
   */
   
  while (_Ncan > 2)  {                 // messages dans _Circule : au moins 3 bytes
    //digitalWrite(LedV, HIGH);       // led activité CAN reste allumée jusqu'à loop suivante
    _Ncan--;
    RId = _Circule[_indexR];        // recup Id
    _indexR++;
    if (_indexR == sizeof(_Circule))  {_indexR = 0;}
    _Ncan--;
    Rlen = _Circule[_indexR];       // recup longueur
    _indexR++;
    if (_indexR == sizeof(_Circule))  {_indexR = 0;}
    if (_dumpCan)  {     
      Serial.print("CAN id ");
      Serial.print(RId);
      Serial.print(", data ");
    }
    for (int k = 0; k < Rlen; k++)  {
      _Ncan--;
      Rbuf[k] = _Circule[_indexR];  // recup octets message
      _indexR++;
      if (_indexR == sizeof(_Circule))  {_indexR = 0;}
      if (_dumpCan)  { 
      Serial.print("0x");
      Serial.println(Rbuf[k], HEX);
      }
    } // le message est dans les globales RId, Rlen et Rbuf[..]
    curAIG = Rbuf[0] & 0x3F;      // garder que les bits 0..5 = index aiguille

    /*
     * commandes aiguilles CAN sur ID 0x40
     */
     
    if (RId == RId_AIGUILLE)  {
      // commandes relatives aux aiguilles
      // 1 octet donnees:  bit 7,6 -> 1,0 = devie ou 1,1 = droit, bits 5..0 = numero aiguille

      if (bitRead(Rbuf[0], 7)) {  // commande de changement d'aiguille
        if (curAIG < MaxAIG) {    // aiguille
          if (bitRead(Rbuf[0], 6) != ListeAig[curAIG].EtatAig())  {
            ListeAig[curAIG].Positionner(bitRead(Rbuf[0], 6)); // bit 6 = position
            BougeAIG[curAIG] = true;
            if (_debug) {
              Serial.print(" CAN : curAIG ");Serial.print(curAIG);
              Serial.print(" Aiguille "); Serial.print(ListeAig[curAIG].NomAig());
              if (bitRead(Rbuf[0], 6)) {
                Serial.println(" DROIT");
              } else {
                Serial.println(" DEVIE");
              }
            }
          }
        }
      }
    }
  } 

Les commandes d'aiguilles ne sont pas directement émises par le TCO (les clés) mais par le Gestionnaire.

(http://www.locoduino.org/local/cache-vignettes/L610xH425/genese4.1-9855b.png?1456854952)

Imaginons qu’une aiguille soit protégée pour laisser passer un convoi prioritaire : cette aiguille ne pourra être manœuvrée qu’après le passage du convoi. C’est ce que fera le gestionnaire : si je bascule la clé de l’aiguille sur le TCO, rien ne se passe ! Dès que le convoi est passé, l’aiguille change automatiquement et la Led sur le TCO, à coté de la clé, répercute ce changement.

Je vais donc bientôt décrire le Gestionnaire !
Titre: Re : Projet Dominique
Posté par: Dominique le septembre 14, 2017, 04:15:47 pm
Je fume, donc je suie (sic) !

(http://forum.locoduino.org/index.php?action=dlattach;topic=290.0;attach=1044;image)

La réalisation de mon réseau prend un peu de retard à cause d'un ennui mécanique à la hanche droite qui va nécessiter un échange standard dans quelques jours. Désolé mais ça fait mal et monter l'escalier du grenier qui conduit à mon chantier ferroviaire devient pénible. Les travaux reprendront en octobre.

En attendant je travaille sur la centrale de programmation des locos, à base de DCC++ bien entendu, avec reconnaissance automatique de l'adresse DCC de la loco posée sur les rails et la lecture de la loco de même adresse en base de donnée (EEPROM).

En parallèle, j'attends 2 aiguilles à coeur métallique pour remplacer 2 aiguilles endommagées sur mon réseau, j'ai encore quelques détections de consommation à câbler, et installer de nouvelles versions des logiciels sur mes modules :

- module de traction (le prochain à décrire) : remplacer CmdrArduino par DCC++ et ajouter les commandes de pilotage (avec signal  en cabine possible) pour forcer les arrêts, ralentissements 30 et 60 à partir du gestionnaire et un mode de reconnaissance automatique de la position des trains au démarrage du système (en plus des sauvegardes en EEPROM). Je veux garder la base de donnée complète (CVs) des locos dans le gestionnaire de programmation pour décharger au maximum la traction.

- module TCO : le tracé ayant légèrement bougé, je dois refaire la platine et intégrer de nouvelles détections. A coté du TCO "boutons-leds", il y aura un TCO sur écran graphique tactile 7 pouces, histoire de programmer du graphique pour se changer les idées !

- module aiguilles et dételeur : à mettre à jour.

- module gestionnaire à base du gestionnaire de Pierre59 : intégrer plus de messages, les signaux, les itinéraires, ...

- module de signalisation : à faire

- modules décors : passer des protos à quelque chose de plus définitifs (chantier d'expérimentations extraordinaire)

Et le tout, bien-sûr, relié  en bus CAN

A suivre...
Titre: Re : Projet Dominique
Posté par: bobyAndCo le septembre 14, 2017, 05:58:07 pm
Ouuuuuuh, là, les projets permettent de rester jeune. Tu en as encore pour au moins 50 ans alors.

Et plein de bonnes choses pour ton opération.

Christophe.
Titre: Re : Projet Dominique
Posté par: CATPLUS le septembre 14, 2017, 08:20:37 pm
Dominique
Ci-joint la photo du 3,5" au 7" cela va être sympa

Bon courage et bonne réparation
Amicalement
Marcel
Titre: Re : Projet Dominique
Posté par: Dominique le septembre 15, 2017, 07:53:32 pm
Whaaaa !
Tu as fait des frais  8)

Amicalement
Dominique
Titre: Re : Projet Dominique
Posté par: Dominique le novembre 25, 2017, 11:25:47 am
Le temps passe et le projet continue :

- Le modéliste est réparé (la hanche est refaite à neuf, pour 30 ans au moins).
- Sur les conseils de Pierre, j'ai ajouté 2 aiguilles pour permettre des manoeuvres entre la voie de programmation (qui fait partie intégrante de la zone de jeu) et la zone de dépôt.
- Du coup j'ai ajouté 4 relais sur le module de commande d'aiguilles.
- Du coup j'ai intégré ces 2 aiguilles dans le Gestionnaire avec 2 boutons tactiles, ce qui permet de les commander en l'absence de boutons réels sur le TCO qui, d'ailleurs, devra subir une rénovation drastique comme son modéliste.
- Je dois encore ajouter des connexions entre des capteurs de consommation récemment installés et le TCO pour permettre encore plus d'itinéraires que ce qui était possible au départ.

Mais, surtout, je dois maintenant refaire le module de traction, simplement récupéré sur mon précédent réseau, dont il manque une fonction essentielle : le pilotage par le gestionnaire et bien d'autres fonctions que je vais détailler.

Une remarque quand même au passage : toutes ces modifications n'interrompent à aucun moment la disponibilité du réseau pour jouer (sauf le temps d'une mise à jour logicielle d'un module ou le branchement de quelque fil). Cela est dû en grande partie à l'architecture choisie, découpée en modules dédiés à un seul type de fonction.

C'est pour cela que :
- j'ai pu ajouter 2 aiguilles supplémentaires sans remettre en cause la disponibilité du réseau;
- j'ai changé d'écran sur le gestionnaire pour passer de 2 écrans 2,8' à un écran 5' en attendant de passer au 7' grâce à Jean-Luc (on verra cela prochainement);
- je vais maintenant refaire complètement le module de traction sur la base de DCC++, mais avec la bibliothèque de Thierry et une ergonomie plus adaptée au jeu à plusieurs et à la circulation de nombreuses machines.

Vous voyez, nous travaillons tous ensemble et chacun profite des compétences des autres, pour le meilleur du modélisme ferroviaire.

Amicalement
A bientôt
Dominique
Titre: Re : Projet Dominique
Posté par: Dominique le novembre 26, 2017, 01:44:34 pm
Voici maintenant le module de traction dernière version.

Comme on s'en doutait il est équipé du logiciel DCC++ et en particulier de sa version sous forme de bibliothèque concoctée par Thierry.

Comme j'aime bien les systèmes à l'ancienne, j'ai récupéré un bloc de 12 potentiomètres de 10K professionnels issus d'une table de mixage.

(http://www.locoduino.org/IMG/jpg/ac5da181-b91b-47fe-81c5-dd73a5207ed0.jpg)

Ensuite, il faut une électronique que j'ai voulue totalement sans PC, donc un panneau avec des boutons et afficheur :

(http://www.locoduino.org/IMG/jpg/03bddc0a-1f77-4b62-8377-b1d961c7decb.jpg)

Ce panneau est couplé à un Mega2560 qui va gérer :

Il y aura aussi 12 Leds rouges et 12 Leds verte en face de chaque potentiomètre.

Je vais expliquer ensuite comment je compte utiliser ce module qui doit assurer la traction jusqu'à 12 locos, la configuration de tout ce qu'il faut configurer et le pilotage à partir du Gestionnaire via l'interface CAN.

La réalisation n'est pas terminée donc je distillerai les détails au fur et à mesure des mises au point lorsqu'elles seront couronnés de succès  8).

Mais en attendant, je viens de faire un essai sur le réseau et, sans les potentiomètres ni le Gestionnaire, j'ai bien piloté 4 machines simultanément, rien qu'avec le claver tactile pour les sélectionner et l'encodeur rotatif (gros bouton à droite) pour la vitesse et la direction.

L'intensité affichée en mA se situait autour de 500 mA, ça dépendait si la voie était à plat ou en côtes, soit environ 100 à 150 mA par loco et plus s'il y a des wagons et voitures derrière. Sur la photo ci-dessus, ma loco dite "Cabane" consomme 140 mA ;)

A suivre...
Titre: Re : Projet Dominique
Posté par: Dominique le novembre 26, 2017, 05:45:37 pm
Voilà comment j'ai conçu l'ergonomie du module, avec les fonctions de chaque élément sur cette photo de la face avant :
(http://www.locoduino.org/IMG/jpg/faceavant2.jpg).

A gauche les fonctions DCC :

Avec ces quelques éléments on peut faire beaucoup de choses !

Bien entendu tout fonctionne en même temps : les locos continuent de tourner même en mode configuration, le gestionnaire peut piloter les locos, ainsi que les 12 potentiomètres à coté de ce panneau.

Cet aspect "multi-tâche" sera décrit en détail pour vous donner un exemple à réutiliser dans votre projet.

Ajout

Je reviendrai en détail plus loin sur l'afficheur LCD 4x20, mais je précise tout de suite les contenus des 4 lignes :
- ligne 0 (en haut) : nom de fa fonction de configuration
- ligne 1 : question/saisie pour la configuration ou message hors configuration (par exemple le contenu d'un message CAN reçu ou la vitesse telle d’une loco mesurée sur la voie de parade juste devant)
- ligne 2 : nom de la loco sélectionnée
- ligne 3 : paramètres de conduite de cette loco (N°, adresse DCC, vitesse, direction et lumières) et le courant consommé total en mA.
Titre: Re : Projet Dominique
Posté par: Dominique le novembre 26, 2017, 07:51:37 pm
Juste pour voir l'envers du décor, j'ai travaillé sur un petit chassis en bois qui permet de déplacer le projet facilement :

(http://www.locoduino.org/IMG/jpg/chassistravail.jpg)

A l'interieur il y a le Mega2560 sur lequel est monté une plaque d’extension pour y raccorder les fils de liaison vers ses accessoires tout autour :

(http://www.locoduino.org/IMG/jpg/megatraction.jpg)

Et même l'envers de la face avant, dont le chassis m'a bien aidé pour faire les soudures.

(http://www.locoduino.org/IMG/jpg/faceenvers.jpg)

C'est bidouille mais c'est solide, modifiable et fait pour durer.

A suivre...
Titre: Re : Projet Dominique
Posté par: Dominique le décembre 01, 2017, 12:51:28 pm
Voici les schémas :

L'Arduino Mega 2560 et ses connexions
(http://www.locoduino.org/IMG/jpg/mega2560.jpg)
La broche A0 (Max471) est reliée à la sortie "courant" du Max471 (figure suivante).

La puissance DCC et l'alimentation 9V de l'Arduino (la tension d'alim du DCC étant trop élevée pour l'Arduino)
(http://www.locoduino.org/IMG/jpg/puissance.jpg)

Les potentiomètres, leds et boutons de direction (sur les même pins que les leds vertes)
(http://www.locoduino.org/IMG/jpg/potars.jpg)

Les éléments de la face avant
(http://www.locoduino.org/IMG/jpg/ui.jpg)

Les télécom CAN et NRF24L01+ vers la manette sans fil
(http://www.locoduino.org/IMG/jpg/telecom.jpg)

Pour le moment j’ai mis de côté le NRF24 pour bien mettre au point le reste car chaque loco doit pouvoir être commandée par le bouton rotatif, un potentiomètre, un message en provenance du CAN (ça fait 3 sources), puis en radio via le NRF24 (ça fait 4 sources), en enfin, plus tard, par une interface WiFi et mon iPhone (ça fera 5 sources).

Rassurez-vous, DCCpp supporte tout ça, la complexité réside dans l’ecriture multitâche du programme.

Titre: Re : Projet Dominique
Posté par: Dominique le décembre 09, 2017, 04:54:28 pm
Premier assemblages des éléments : ça marche du 1er coup, avec 4 trains : la conduite est douce et réaliste, du fait de la grande longueur des potentiomètres et de leur haute qualité.

(http://www.locoduino.org/IMG/jpg/face_avant-traction.jpg)
Titre: Re : Projet Dominique
Posté par: Dominique le décembre 15, 2017, 11:26:42 pm
Voici une petite photo d'étape, un panoramique (pas terrible) avec presque tous les éléments côte à côte :

(http://www.locoduino.org/IMG/jpg/pano.jpg)

De gauche à droite :
- la centrale de programmation
- le gestionnaire avec son écran graphique tactile (version 2, pas la dernière !) et le controlleur d'aiguilles (face blanche)
- le TCO à boutons et leds
- la centrale va-et-vient pour le métro
- le module de traction avec ses 12 potars

Il manque le contrôleur des signaux et le contrôleur du décor, ainsi qu'un écran vidéo pour les caméras.

De quoi s'amuser encore un certain temps ....
C'est tout en plexi et Arduino (il y en a 6 pour le moment, un Uno, un Due et 4 Megas)
Titre: Re : Projet Dominique
Posté par: Dominique le décembre 21, 2017, 07:16:10 pm
Le logiciel du module de traction

Je ne vais pas recopier tout le logiciel d'un coup, 1700 lignes, ce serait imbuvable !

Mais je vais distiller des morceaux que vous pourrez récupérer et re-utiliser dans vos projets.

D'abord un numéro et une date de version : ça permet d'éviter de se mélanger les pinceaux quand le projet s'étale dans le temps :

const char VersTRACTION[20] =  "DCCppTract08 141217"; // 19 caractères + \0
Les bibliothèques utilisées :

#include "DCCpp.h"              // La bibliothèque de Thierry

#include <Wire.h>               // bus I2C
#include <Streaming.h>          // http://arduiniana.org/libraries/streaming/
#include <LiquidCrystal_I2C.h>  // I2C adresse 0x27
#include <SPI.h>                // bus SPI
#include <mcp_can.h>            // bus CAN
#include "Adafruit_MPR121.h"    // I2C adresse 0x5A
#include <Encoder.h>            // encodeur
#include <Bounce2.h>            // boutons poussoir
#include <EEPROM.h>             // Eeprom integree
#include "Classes.h"            // Ma classe CmdLoco pour commander les locos

Pour utiliser la bibliothèque DCCpp de Therry, rien de plus simple :

#define MOTOR_SHIELD_TYPE   0           // LMD18200
#define COMM_INTERFACE   0

#define LMD_DIR 12                      // DCC_MAIN = Arduino Mega - uses OC1B - DIR LMD18200
#define DCC_MAIN 12
#define LMD_PWM 3                       // DCC_ENABLE = PWM LMD18200
#define DCC_ENABLE 3
#define Max471  A0                      // current sensor

Je redéfini dans mon programme les valeurs qui sont dans config.h et dans DCCpp.h et il ne me reste plus qu'à démarrer DCCpp dans le Setup :

DCCpp::begin();
DCCpp::beginMain(UNDEFINED_PIN, LMD_DIR, LMD_PWM, Max471);        // Dc: Dir, Pwm, current sensor

Recherche des adresses des terminaux I2C

J'installe le sketch I2C_Scanner et j'obtient les adresses (je recopie le résultat dans mon programme, mais en commentaire :

//////////// I2C ///////////
// I2C device found at address 0x23  = NRF24L01
// I2C device found at address 0x27  = lcd
// I2C device found at address 0x5A  = clavier tactile

Cela me permet de déclarer l'écran LCD et le clavier tactile (qui fonctionne en 3,3V, attention, il ne faut pas oublier d'ajouter un circuit convertisseur de niveau)

///////////// LCD a l'adresse I2C = 0x27 ////////////
LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 20 chars and 4 line display

///////////// RETRO-ECLAIRAGE LCD ///////////////////
#define eclairage  180000   // extinction auto du retro-eclairage au bout de 3 minutes
unsigned long retrotime;
unsigned long lblinktime;

///////////// Touchpad à l'adresse I2C = 0x5A ///////
Adafruit_MPR121 cap = Adafruit_MPR121();
uint16_t lasttouched = 0;
uint16_t currtouched = 0;
int Key;
// Attach the MPR121's IRQ pin to digital pin 4
const int PIN_TOUCH_IRQ = 4;

Puis on installe ces périphériques dans le setup :

  //--- init Serial et LCD
  pinMode(PIN_TOUCH_IRQ, INPUT);// pour l'interruption du clavier tactile
Serial.begin(115200);
  Serial.flush();
  Serial.println(VersTRACTION); // affichage du N° de version sur le Terminal
  lcd.init();                   // initialize the lcd
  lcd.clear();
  lcd .backlight();
  lcd.noBlink();

Le bus CAN

Je fais carrément un copier-coller depuis mes autres programmes (ceux des autres modules) :
- Constructeur de l'objet CAN
- Définitions et variables de la memoire tampon circulaire pour y stocker immédiatement tout message reçu
- routine d'interruption du CAN, qui ne fait que monter un flag
- liste des identifiants utilisés dans mon réseau, qui concernent ce module de traction

///////////// CAN BUS //////////////
MCP_CAN CAN(53);                    // Set CS to pin 53 (MEGA)
volatile byte Flag_Recv = 0;        // flag pour l'interruption IRQ

//--- Message recu
byte IdR;                           // Id pour la routine CAN_recup()
unsigned char lenR = 0;             // Longueur "    "       "
unsigned char bufR[8];              // buffer reception      "

//--- Message emis
unsigned char bufS[8];              // buffer emission

//--- Memoire circulaire pour le stockage rapide des messages recus
unsigned char _Circule[512];          // recepteur circulaire des messages CAN sous IT
unsigned int _indexW, _indexR, _Ncan; // index d'ecriture et lecture, nb d'octets a lire
byte _CANoverflow = 0;                // flag overflow (buffer _Circule plein)

int curIndex;                         // loco désignée dans un message CAN
bool DCC_OK_Central = false;

//--- Interruption (ISR) CAN
void MCP2515_ISR()
{
     Flag_Recv = 1;
}

//--- Ids des messages
#define RId_LOCO        0x30        // commande central pour un train (Index, Vit, Dir, F0)
#define RId_F0          0x31        // commande central F0
#define RId_URGENT      0x32        // arrêt urgence (0x8F) et reprise (0x0F)
#define RId_PARAM       0x33        // configuration (index, @dcc, Vmin, Vmax, cran30, cran60)
#define RId_POSTE       0x38        // affichage sur poste de conduite (0x00..0x3F)
#define TId_LOCO        0x13        // envoi des paramêtres de conduite (Index, Vit, Dir, F0)
#define TId_F0          0x14        // envoi F0 d'une loco (index, F0)
#define TId_DCC         0x15        // envoi état DCC (0x80 = on; 0x00 = off)
#define TId_ICC         0X16        // envoi intensité (mA/16) et alarme CC (bit 7)
#define TId_VV          0x17        // envoi consignes V&V (vitesses aller et retour)
#define TId_VITESSE     0x18        // envoi mesure de vitesse d'un train
#define TId_TRACT_CAN   0x1E        // envoi alarme CAN overflow

Et puis la routine appelée chaque fois que le Flag_Recv est monté :

//--- Routine de récuperation des messages CAN dans la memoire circulaire _Circule
//--- appelee par LOOP lorsque Flag_Recv = 1;
 
void CAN_recup()
{
  unsigned char len = 0;                // nombre d'octets du message
  unsigned char buf[8];                 // message
  unsigned char Id;                     // Id

  while (CAN_MSGAVAIL == CAN.checkReceive())  {
    CAN.readMsgBuf(&len, buf);          // read data, len: data length, buf: data buf
    Id = CAN.getCanId();
    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;  // depassement de la capacite de Circule
    }
  }
}

On remarque que cette routine assure la gestion des débordements (overflow) éventuels de cette mémoire tampon, ce qui ne m'est jamais arrivé.

On trouvera plus d'explications sur le CAN dans l'article ici : http://www.locoduino.org/spip.php?article148 (http://www.locoduino.org/spip.php?article148) et un peu plus loin dans ce sujet.

Petite pause pour se détendre, ensuite on verra un certain nombre de routines utilitaires écrites pour décharger la loop et la rendre plus lisible.

A suivre ...
Titre: Re : Projet Dominique
Posté par: bricoleau le décembre 21, 2017, 10:14:38 pm
Super intéressant

Je te soumets une petite analyse personnelle, en ce qui concerne le LCD texte à interface I2C :

Au travers de la bibliothèque <LiquidCrystal_I2C.h>, l'arduino envoie des données via le bus I2C, à un io expander PCF8574, lui même connecté aux entrées du contrôleur HD44780 de l'écran LCD.
Le PCF8574 a 8 sorties parallèles :
Le principe de fonctionnement du PCF8574 est très simple : l'arduino lui transmet un octet, dont les 8 bits correspondent aux 8 sorties du PCF8574.

Le bus I2C fonctionne au max à 400 kb/s sur arduino (100 kb/s en standard)
Ce qui, déduction faite des bits de contrôle START, ACK etc. permet de faire passer grosso modo un débit de 40 octets par milliseconde, adresse incluse (elle prend aussi un octet).

Pour chaque octet utile à transmettre depuis l'arduino vers le LCD (= un caractère à afficher, ou une commande d'effacement écran, ou une commande de positionnement de curseur), il y a 2x6=12 octets qui passent sur le bus I2C :
et pareil pour le second demi-octet

Au niveau du programme, l'instruction de base LCD.print(chaine) ne rend la main qu'à la fin des émissions sur le bus.
Dans le pire de cas, la chaîne fait 20 caractères, soit 20x12 = 240 octets de trafic sur le bus I2C, ce qui va durer 6 ms.

En supposant que je ne me suis pas trompé dans le calcul (de toute manière facile à vérifier par mesure de temps dans le programme), cela fait 6 millisecondes pendant lesquelles l'arduino ne fait aucune lecture des buffers de réception du MCP2515.

Si l'écosystème fait qu'il n'y a aucune chance de recevoir plusieurs messages CAN pendant ce laps de temps, alors pas de problème.
Sinon, il y a un risque de perte de message au niveau du mcp2515, pour cause de buffers de réception pleins.


Pour réduire le risque :
N'afficher que les caractères qui ont changé
Effectuer des LCD.print caractère par caractère, en testant la réception MCP2515 entre chaque, cela réduit le tunnel à 300 µs.

On peut faire mieux, mais il faut réécrire <LiquidCrystal_I2C.h>
Titre: Re : Projet Dominique
Posté par: Dominique le décembre 21, 2017, 11:32:17 pm
Je n’ai pas fait ce calcul du temps de blocage du cpu pendant un lcd.print, mais je m’en doutais.

Il y a normalement tout ce qu’il faut dans le MCP2515 pour ne pas perdre de message : le protocole contient un accusé de réception qui force l’emetteur à répéter une transmission quand il reçoit un  «acknowledge error». Le MCP2515 contient 3 buffers de réception, un avant les filtres et 2 après les filtres. Un message est d’abord reçu dans le 1er buffer et sera transmis dans l’un des 2 autres si le filtre l'autorise et si l’un des buffers est disponible. Sinon il y aura une «acknowledge error» et la transmission sera répétée.

Pour qu’un message soit répété il faut qu’il ne soit pas reçu, ni par le récepteur prévu pour cela, ni par un espion placé sur le bus Can. J’ai réalisé un tel outil pour voir le trafic sur mon bus et là j’ai commencé à perdre des messages parce qu’ils étaient réputés reçus.

C’est pour ça aussi que le choix des identifiants et des filtres est si important !

Donc, pour revenir à notre problème d’ecran LCD/i2c, je ne m’inquiète pas trop car le bus CAN peut attendre. Mais ce serait mieux d’améliorer l’afficheur, théoriquement.

Pratiquement, comme j’ai choisi le DCC, le bus CAN a relativement peu de messages à transmettre et il est toujours possible d’ajouter un peu de contrôle de flux applicatif, et encore ce n’est pas certain.

Personnellement je suis confiant pour avoir un réseau qui tourne depuis un bon moment.

En cas de problème il y a des solutions ,
- doubler le bus CAN pour alléger le trafic (mais ça fait 2 fils de plus a brancher)
- remplacer l’interface I2C du LCD par un Arduino mini qui servira de buffet d’affichage. C’est pas plus cher !
- remplacer l’i2c par du SPI.

Jean-Luc a plus d’expérience que moi sur le CAN et un bus CAN plus chargé. Il pourra éventuellement compléter ces explications.


Titre: Re : Projet Dominique
Posté par: Dominique le décembre 30, 2017, 07:32:35 pm
Puisqu'on aborde les communications par le bus CAN, je vais décrire ce que j'ai réalisé pour le moment.

D'abord un petit rappel de ce que j'ai compris du CAN :

Le bus CAN possède plusieurs caractéristiques intéressantes. Il est :
• Temps-réel. C’est à dire que les temps de transmission sont déterministes et peuvent être calculés à priori.
• Peu coûteux. Les contrôleurs CAN sont soit disponibles en tant que composant destiné à être connecté à un micro-contrôleur, soit intégrés directement dans un micro-contrôleur. La quantité de logiciel nécessaire pour le faire fonctionner est relativement succincte. Les composants CAN sont fabriqués en grand nombre, ce qui abaisse leur coût.
• Multi-maître asynchrone. pas d’arbitre centralisé, pas d’horloge globale, une station peut émettre dès que le bus est libre.
• Priorité entre les messages. Les collisions, quand plusieurs contrôleurs CAN émettent sur le bus simultanément, sont résolues sans destruction du message le plus prioritaire.
• Messages de taille limitée. 8 octets de données au plus.
Le bus CAN est fiable :
• Une détection d’erreur est incluse. La probabilité qu’une erreur reste non détectée est inférieure à 4,7 10-11.
• Le protocole définit une surveillance très stricte du bus.
• La cohérence des données est garantie. Un message est soit accepté par tous les contrôleurs CAN du réseau, soit rejeté par tous.
• Acquittement des messages par toutes les stations, le signalement d’erreur émis par une station est reçu par toutes les stations.
• Retransmission automatique des messages après signalement d’erreur.
• Le confinement des erreurs. Un contrôleur CAN gère de façon autonome les erreurs temporaires qu’il détecte.
Les performances
Le bus CAN propose des débits allant de 100kbits/s à 1Mbits/s.
La distance maximale entre 2 noeuds dépend de cette vitesse : 100 m à 500kb/s, ou 500 m à 125 kb/s, ce qui ne pose aucun problème pour nos réseaux, même excessivement grands !

Qu’est-ce qu’un message ?
Quand on construit un système avec un bus CAN, on définit une messagerie : l’ensemble des messages qui vont transiter et leur identifiant.
Les messages CAN sont courts, 94 bits maximum. Il n’y a pas d’adresse explicite donc aucun système de routage de messages entre un émetteur et un récepteur. Tout le monde reçoit tout et doit filtrer pour ne traiter que ce qui le concerne.
La partie "données" d’un message contient un identifiant de 11 bits (mode standard) ou 29 bits (mode étendu que je n'utilise pas dans cette application) et des données de 0 à 8 octets. L’identifiant est indispensable, les données non.
Dans mon application, je n’utilise pratiquement qu’un seul octet de données, en plus de l’identifiant. Seuls quelques cas spécifiques comme les messages de configuration utilisent plusieurs octets.
Comme dans tout système de transmission, il y a d’autres bits de contrôle et de calcul d’erreur qui servent uniquement au circuit d’interface MCP2515, donc il n’est pas besoin d’en parler, « ça se débrouille tout seul » !

L’identifiant joue un rôle particulièrement important
Il sert à plusieurs choses :
• l’arbitrage en cas de contention, c’est à dire quand 2 ou plusieurs messages sont émis sur le bus simultanément,
• le filtrage, c’est à dire la possibilité pour le récepteur de ne recevoir que ce qui l’intéresse,
• l’adressage qui n’est pas un adressage classique de messagerie, mais qui est utilisé dans ce but, d’abord au moyen du filtrage, puis par le récepteur pour déterminer à quel fonction le message est destiné.

En cas de contention, chaque émetteur regarde si un identifiant émis est plus prioritaire que le sien. Si oui, il stoppe d’émettre et attend son tour. Celui qui a l’identifiant le plus prioritaire continue d’émettre comme si de rien n’était. Il n’y a donc pas de perte de temps !
La règle de priorité est simple : le plus prioritaire est le plus petit !
Une autre règle en découle, qui est que 2 émetteurs différents ne doivent pas émettre de messages avec un même identifiant.

Par exemple, supposons que le Central du réseau veuille envoyer des ordres à des moteurs d’aiguillage (donc à la carte de commande d’aiguilles) et d’autres ordres à des signaux (donc à la carte de commande des signaux) et d’autres encore à la traction (donc la carte générateur du DCC ou du PWM ).
On va donc définir une série d’identifiants pour les messages destinés exclusivement aux commandes d’aiguille, par exemple la série de 16 identifiants de Id = 0x40 à 0x4F, une autre série d’identifiants pour les commandes de signaux, par exemple Id = 0x50 à 0x5F, et une autre pour la traction, par exemple Id 0x30 à 0x3F.
On remarque au passage qu’on ne s’intéresse qu’aux messages reçus par la carte CAN dont il faut définir les masques et les filtres.

On va rester en trame standard, donc les identifiants font 11 bits. Les valeurs de 1 à 2047 sont largement suffisantes pour tous les types de messages possibles. Et même en gardant uniquement les 8 bits de poids faible, 256 types de messages me semblent suffisants.

Supposons qu’au maximum il y ait 22 moteurs d’aiguillage et quelques dételeurs : 5 bits suffiront dans un unique octet de message pour désigner un moteur d’aiguilles ou un dételeur, les 3 bits supplémentaires permettant de définir les types de commande qui s’appliquent aux aiguilles.

Les réponses de la carte d’aiguilles vers le gestionnaire de réseau et/ou d’autres cartes étant filtrées par leur destinataire, les Id des messages émis seront donc pris dans la liste des Id qu’elles acceptent de recevoir.

Par exemple, pour gérer mes aiguilles, les commandes reçues seront décodées en fonction de l’octet de données qui a la forme suivante :
• les bits 0 à 5 définissent le numéro de l’aiguille ;
• les bits 6 et 7 portent la consigne à appliquer.
- bit 7 = 1, bit 6 = 1 : sens « droit »
- bit 7 = 1, bit 6 = 0 : sens « dévié »


Comme la carte de commande d’aiguilles ne doit réagir qu’aux ordres émis par le central (avec l’Id = 0x40) et comme le TCO permet de positionner les aiguilles "à la main" avec des clés, chaque fois qu’un opérateur tournera une clé d’aiguille sur le TCO, la demande du TCO sera d’abord adressée au Central. Ce dernier devra la valider avant de l’envoyer à l’aiguille si l’opération est possible.
Dans ce schéma, le TCO enverra les même données que dans les commandes ci-dessus mais avec un Id = 0x11 (dans la plage des Id acceptés par le Central). Dès que le Central les aura validées, ce dernier les renverra alors avec l’Id = 0x40 et elles seront exécutées par la carte de commande d’aiguilles. Ensuite les réponses de la carte d’aiguilles avec l’Id = 0x12 seront reçues par le central (pour la mise à jour des tables de positions ou des objets aiguilles) et ce dernier enverra un message vers le TCO avec l’Id 0x20 pour l’allumage de la diode verte qui représente la position d’aiguille (et l’extinction de l’autre Led) à coté de la clé de commande.

Le schéma des échanges est le suivant pour une commande d’aiguille :
Demande TCO -> Id=0x11-> Central -> Commande -> Id=0x40 -> Aiguilles
Réponse état Aiguilles -> Id=0x12 -> Central -> Etat -> Id=0x20 -> TCO

Dans le projet de réseau, il faut donc étudier tous les échanges possibles et dresser le tableau des Id et des données de tous les messages.
Rappel : ce qui compte, c’est de rassembler les identifiants des messages reçus dans une plage qui permet de définir les masques et filtres de façon pratique.

Et les filtres alors ?
On l’a entrevu dans la routine de setup : il y a 2 masques et 6 filtres.
Les masques définissent les bits de l’identifiant qui sont significatifs (sinon ils sont ignorés par le filtre).
Les filtres font que seuls les identifiants présentés sont acceptés Les bits du masque travaille avec les bits des filtres de même position.
Un bit « 0 » du masque permet d’accepter n’importe quel bit de l’identifiant (pas de filtre). Cela permet d’éviter de filtrer tous les bits et donc d’accepter des plages entières d’identifiants.
Un bit « 1 » du masque autorise le filtrage : pour qu’un bit de l’identifiant soit accepté, il doit être identique à celui du filtre. Tous les bits de l’identifiant doivent être acceptés pour que l’identifiant soit accepté.

Exemple :
Filtrage total pour accepter seulement l’Id = 0x317 dans un buffer
•   - Masque = 111 1111 1111 •   - Filtre = 011 0001 0111
Filtrage partiel pour accepter une plage d’Id de 0x310 à 0x317
•   - Masque = 111 1111 1000 •   - Filtre = 011 0001 0xxx
Pas de filtrage du tout
•   - Masque = 000 0000 0000 •   - Filtre = xxx xxxx xxxx

Dans mon cas, je n’ai qu’une carte de commande d’aiguilles, le masque est fixé à 0x7F0 et les filtres définissent les identifiants des seuls messages attendus : 0x40 à 0x4F.
Titre: Re : Projet Dominique
Posté par: Dominique le décembre 30, 2017, 07:54:29 pm
Voici la liste des identifiants que j'ai prévu : une plage de 16 identifiants est réservée à chaque module (32 pour le Gestionnaire). Les valeurs sont données en hexadécimal.

Gestionnaire : 00..1F
Gestionnaire global du réseau, il reçoit toutes les demandes du TCO (cles d’aiguilles, occupations/libérations de zones), de la traction (commandes des pilotes, état des générateurs DCC), des Aiguilles (positions), tous les autres capteurs.
Il assure l’IHM, l’interface avec le WiFI, les commandes sans fil, la configuration, la gestion du temps, etc..

TCO, occupations de zones : 20..2F
Affiche (leds rouges) et transmet au Gestionnaire les occupations/libérations de zones et les positions des clés d’aiguilles (actions opérateurs). Reçoit des Aiguilles les positions réelles d’aiguilles affichées par led vertes.

Traction DCC - réseau principal - va-et-vient : 30..3F
Transmet au Gestionnaire les consignes de conduite (potentiomètre de vitesse, clé direction et lumière FL et fonctions Fx), reçoit du gestionnaire les commandes de vitesse/direction/Fonction pour chaque train. Gère la génération DCC et l’interface au Booster, ainsi que les relais de protection des zones Va-et-Vient et Programmation.

Aiguilles, dételeurs : 40..4F
Reçoit les ordres de positionnement des aiguilles et dételeurs du Gestionnaire, la sauvegarde en EEPROM des positions à l’arrêt du réseau pour les retrouver au démarrage. Transmet au Gestionnaire et au TCO les positions d’aiguilles.

Signalisation : 50..5F
Reçoit du Gestionnaire les états des signaux et commande les signaux en conséquence.

Autres capteurs : 60..6F
Gèrent les capteurs complémentaires comme les zones d’arrêt spécifiques (quai), les capteurs RFID, etc.. Transmet les événements au Gestionnaire.

Autres actionneurs : 70..7F
Reçoit les ordres du Gestionnaire pour actionner des organes comme le passage à niveau

Animation décor : 80..FF
Ensemble de capteurs et d’actionneurs liés à l’animation du décor

Pour le moment je n'ai pas réalisé le module de commande du décor, et je lui ai attribué la moitié des identifiants.
Je pense évoluer en créant un second bus CAN dédié aux éléments du décor. Ainsi je pourrai étendre les capacités du bus principal et disposer d'un bus décor très étendu, tout en gardant seulement 8 bits significatifs pour les identifiants.

Titre: Re : Projet Dominique
Posté par: Dominique le décembre 31, 2017, 10:57:01 am
Ce qui m'amène à traiter les messages CAN de la manière suivante :

En entrée, j'ai donc des messages possédant un identifiant RId et un ou deux octets de données (mais potentiellement jusqu'à huit) dans la table Rbuf[], avec sa longueur Rlen.

Je commence donc à récupérer chaque message dans le buffer circulaire :
Un message existe si le nombre d'octets présent dans le buffer est au moins égal à 3.


  while (_Ncan > 2)  {                // messages dans _Circule : au moins 3 bytes
    _Ncan--;
    RId = _Circule[_indexR];          // recuperation de l' Identifiant
    _indexR++;
    if (_indexR == sizeof(_Circule))  {_indexR = 0;}
    _Ncan--;
    Rlen = _Circule[_indexR];         // recup longueur
    _indexR++;
    if (_indexR == sizeof(_Circule))  {_indexR = 0;}
    for (int k = 0; k < Rlen; k++)  {
      _Ncan--;
      Rbuf[k] = _Circule[_indexR];    // recuperation des octets du message
      _indexR++;
      if (_indexR == sizeof(_Circule))  {_indexR = 0;}
    } // le message est dans les globales RId, Rlen et Rbuf[..]

    //-- Traitement des messages CAN (commandes du gestionnaire)

  } // fin traitement messages CAN

Ensuite le traitement de chaque message (à ajouter sous le commentaire dans le code ci-dessus) se résume à un switch - case :

    //-- Traitement des messages CAN (commandes du gestionnaire)
 
    curIndex = Rbuf[0] & 0x3F;       // ne garder que les bits 0..5 = index loco
    if (curIndex < MaxLocos) {
    switch (RId)  {
      case RId_LOCO:                 // 0x30 commande central pour un train (vitesse et direction)
      CanSpeed = Rbuf[1] & 0x7F;
      switch (CanSpeed) {
        case 30:
        gLocoSpeed[curIndex] = gConfigLocos[curIndex].cran30;
        break;
        case 60:
        gLocoSpeed[curIndex] = gConfigLocos[curIndex].cran60;
        break;
        case 90:
        gLocoSpeed[curIndex] = gConfigLocos[curIndex].cran90;
        break;
        default:
        gLocoSpeed[curIndex] = CanSpeed;
        break;
      }
      DccDirection = bitRead(Rbuf[1], 7);
      gLocoDirection[curIndex] = DccDirection;
      if (gConfigLocos[curIndex].inverse) {DccDirection = !DccDirection;}
      DCCpp::setSpeedMain(gConfigLocos[curIndex].registre, gConfigLocos[curIndex].dccAddress, gLocoStepsNumber, gLocoSpeed[curIndex], DccDirection);
      gChange = true;
      break;
     
      case RId_F0:                  // 0x31 commande central F0
      if (bitRead(Rbuf[0], 7)) {gLocoFunctions[curIndex].activate(0);} else {gLocoFunctions[curIndex].inactivate(0);}
      DCCpp::setFunctionsMain(gConfigLocos[curIndex].registre, gConfigLocos[curIndex].dccAddress, gLocoFunctions[curIndex]);
      gChange = true;
      break;
     
      case RId_URGENT:              // 0x32 arrêt urgence et reprise
      if (Rbuf[0] == 0x8F) {                  // arret immediat
        stop_DCC();
        DCC_OK_Central = false;
      }
      if (Rbuf[0] == 0x0F) {                  // reprise
        start_DCC();
        DCC_OK_Central = true;
      }
      break;
     
      case RId_PARAM:                 // 0x33 configuration
      //curManette = Rbuf[0] >> 4;    // manette = quartet poids fort,
      break;
     
      case RId_POSTE:                 // 0x38 affichage du central vers poste de conduite (ligne 1)
      // afficher l'adresse DCC sur la manette, l'indication F0, ...
      break; 
      } 
    } 

Je n'ai pas mis tous les cas à traiter car je ne les ai pas encore étudiés mais l'ajout ultérieur de ces cas sera simple : il suffit d'ajouter le traitement présenté en commentaire.

Quelques explications sur les variables apparaissant dans le code :

curIndex désigne une loco parmi 12 (MaxLocos).
gLocoSpeed[curIndex] désigne la vitesse de cette loco dans une table de 12 valeurs.
gConfigLocos[curIndex] désigne une structure contenant les paramètres permanents d'une loco, dans ue table de 12 structures.
Cette structure est :

struct LocoCONFIG {
  byte registre;                      // numero de registre = pot+1
  byte active;                        // loco active (1=oui)
  int  dccAddress;                    // adresse DCC loco courte ou longue
  byte cran30;                        // cran DCC pour 30 km/h
  byte cran60;                        // cran DCC pour 60 km/h
  byte cran90;                        // cran DCC pour 90 km/h
  byte inverse;                       // inversion avant (0) /arriere (1=oui)
  byte lumiere;                       // lumiere FL (1=oui)
  byte MaxSpeed;                      // vitesse maxi souhaitable en cran DCC
  byte MinSpeed;                      // vitesse mini possible
};

L'analyse de la valeur de vitesse transmise dans le message CAN par un autre switch montre que je m'intéresse particulièrement aux vitesses 30, 60 et 90 (km/h à l'échelle 1/160 pour le N).
L'interface utilisateur du module de traction permet de configurer les crans DCC qui correspondent aux vitesses réelles 30, 60 et 90 km/h.
Quand une de ces vitesses est demandée par le Gestionnaire, c'est cette valeur qui est envoyée à la commande DCC, sinon, c'est la valeur contenue dans le message (qui le Gestionnaire n'a aucune raison d'envoyer).

DccDirection est la direction indiquée dans le message CAN (bit 7 du premier octet).
Ensuite on teste la valeur inverse configurable, dans la structure, pour tenir compte du sens réel de la machine.

Et enfin, le commande DCC est envoyée grâce à la bibliothèque DCCpp :
DCCpp::setSpeedMain(gConfigLocos[curIndex].registre, gConfigLocos[curIndex].dccAddress, gLocoStepsNumber, gLocoSpeed[curIndex], DccDirection);
On continue ensuite par le traitement des fonctions de la loco, l'arrêt d'urgence et la reprise (en cas de court-circuit) et d'autres fonctions que nous verrons plus tard.
Titre: Re : Projet Dominique
Posté par: Dominique le décembre 31, 2017, 11:20:19 am
Maintenant que nous avons une commande des trains par le bus CAN, je vais décrire la commande des trains par les potentiomètres.
Nous avons ce schéma :
(http://www.locoduino.org/IMG/jpg/potars.jpg)

Chaque train est piloté par un potentiomètre et un bouton poussoir, avec une led verte indiquant la marche avant et une led rouge indiquant la marche arrière.

Le challenge qui se présentait était multiple :
- écrire une seule fois le code pour les 12 commandes;
- assurer la gestion des anti-rebonds pour les 12 boutons (l'utilisation de la bibliothèque Bounce2 n'étant pas facile dans ce cas);
- gérer un ensemble de variables indépendantes pour chaque commande.

J'ai donc créé une classe pour gérer ces commandes, la classe permettant d'encapsuler les variables et les fonctions :
class CmdLoco {
private :
  int pinLedVerte; // et bouton
  int pinLedRouge;
  int pinPotar;
  unsigned long previousTime ;
  unsigned int intervalTime;
  int etat; // 0 : attente, 1 : 1er changement, 2 : confirmation
  bool buttonVState;
  bool marcheAvant;
  int potValue;
 
public :
  CmdLoco(int pLV, int pLR, int pPot, int pIt) {           // constructeur
    pinLedVerte = pLV;
    pinLedRouge = pLR;
    pinPotar = pPot;
    intervalTime = pIt;
  }

  void CmdInit() {
    marcheAvant=true;
    pinMode(pinLedVerte, OUTPUT);
    pinMode(pinLedRouge, OUTPUT);
    digitalWrite(pinLedVerte, !marcheAvant);        // allume si false
    digitalWrite(pinLedRouge, marcheAvant);         // eteinte
    previousTime = millis();
    this->etat = 0;
    buttonVState = true;   
  }

  void MajLed(bool d) {
    digitalWrite(pinLedRouge, d);
    digitalWrite(pinLedVerte, !d);      // allume si false
  }

  bool CmdMaj( bool d) {
    bool dir = d;
    bool change = false;
    bool buttonValue;
    if (millis()-previousTime >= intervalTime) {   
      previousTime = millis();   
      pinMode(pinLedVerte, INPUT);
      buttonValue = digitalRead(pinLedVerte);
      if (buttonValue != buttonVState) {    // changement d'etat de la pin
        switch (this->etat) {
          case 0:
          if (!buttonValue) {               // 1er appui
          this->etat = 1;                   // attente confirmation
          }
          break;
          case 1:
          if (!buttonValue) {               // confirmation d'appui
            buttonVState = buttonValue;     // nouvel ancien etat de la pin
            dir = !dir;                     // inversion sens de marche
            this->etat = 2;                 // appui en cours, do nothing
            change = true;                  // job done !
          }
          break;
          case 2:
          if (buttonValue) {                // relaché
            this->etat = 3;                 // attente confirmation
          }
          break;
          case 3:
          if (buttonValue) {                // relaché
            buttonVState = buttonValue;     // nouvel etat
            this->etat = 0;                 // fin process
          }
        }
      }
      pinMode(pinLedVerte, OUTPUT);
      digitalWrite(pinLedRouge, dir);
      digitalWrite(pinLedVerte, !dir);      // allume si false
    }
    return (change);
  }
 
}; // fin classe CmdLoco

Ainsi nous aurons regroupé :
- la fonction CmdInit() qui doit initialiser chaque objet dans le setup();
- la fonction MajLed(bool d) qui allume l'une des 2 leds verte et rouge;
- la fonction CmdMaj( bool d) qui va tester l'état du bouton-poussoir et retourner un true si le bouton est enfoncé, tout en gérant l'anti-rebond  :D :D

Pour mettre en place ces objets, il faut créer une table en variables globales :
//--- TABLE DES LOCOS
CmdLoco * gTableCloco[MaxLocos];          // Table des locos

et l'initialiser dans le setup() :

  //--- creation des objets Cloco et de la table
  for (int L=0; L<MaxLocos; L++) {
    gTableCloco[L] = new CmdLoco(ledVerte[L],ledRouge[L],gPotPin[L], 25); // ledVerte, ledRouge, pot, intervalTime)
  }
  //--- initialisation des objets Cloco
  for (int L=0;L<MaxLocos;L++) {
    gTableCloco[L]->CmdInit();   
  }

Pour gérer les potentiomètres j'ai quelques variables globales que j'aurais très bien pu intégrer à la classe CmdLoco :

int gSensorValue = 0;           // value read from the pot
int gOutputValue;
int gPot[MaxLocos];
bool gPotChange = false;

Enfin, la gestion de toute cette interface, dans la loop(), se résume à ces quelques lignes de code :

  // potentiometres

  for (int i = 0; i < 12; i++) {
    gSensorValue = analogRead(gPotPin[i]);
    gOutputValue = map(gSensorValue, 0, 1023, 0, gConfigLocos[i].MaxSpeed);
    if (gOutputValue != gPot[i]) {
      gPot[i] = gOutputValue;
      gLocoSpeed[i] = gPot[i];
      gPotChange = true;
      DccDirection = gLocoDirection[i];
      if (gConfigLocos[i].inverse) {DccDirection = !DccDirection;}
      DCCpp::setSpeedMain(gConfigLocos[i].registre, gConfigLocos[i].dccAddress, gLocoStepsNumber, gLocoSpeed[i], DccDirection);     
    }
  }
 
  // boutons et leds de direction

  for (int u=0;u<MaxLocos;u++) {
    if (gLocoSpeed[u] == 0) {
      if (gTableCloco[u]->CmdMaj(gLocoDirection[u])) {     
        gLocoDirection[u] = !gLocoDirection[u];
      }
    }     
  }

Titre: Re : Projet Dominique
Posté par: Dominique le décembre 31, 2017, 11:34:58 am
Pour un 31 décembre, c'est déjà pas mal : Nous avons deux exemples de pilotage par la bibliothèque DCCpp :


Je vous souhaites à tous un excellent réveillon de la Saint Sylvestre et une bonne année 2018  ;)

(http://www.locoduino.org/IMG/jpg/3112.jpg)

Amicalement

Dominique
Titre: Re : Projet Dominique
Posté par: DDEFF le décembre 31, 2017, 12:14:06 pm
Bonne Année à toi aussi. ;D

Magnifique programme, testé, qui contient plein d'astuces bien utiles.
A lire à tête reposée... ;)

Denis
Titre: Re : Projet Dominique
Posté par: Dominique le décembre 31, 2017, 03:39:42 pm
Merci Denis,

C'est justement pour t'aider à construire ton réseau que je publies tous ces détails, car tu as choisis aussi le bus CAN et tu vas aussi partir sur DCCpp.

Une des pièces maitresses d'un réseau est justement la grosse fonction qu'il faut mettre entre la capture des informations de rétrosignalisation et le pilotage des trains = le Gestionnaire de réseau. J'explique d'abord tout ce que j'ai mis autour.

Evidemment tu es en plein là dedans !
http://forum.locoduino.org/index.php?topic=211 (http://forum.locoduino.org/index.php?topic=211)

La grande majorité des modélistes ne voient pas d'autre solution que d'acheter une centrale du commerce (et hop : 500€ !) et d'y ajouter un logiciel sur PC comme RRTC (et hop, encore : 500€ !).

Tu sais bien que nous, à Locoduino, on souhaite expliquer comment faire autrement, en retroussant les manches et avec peu d'huile de coude ? ... si, un peu quand même ! Avec un poil de maitrise de l'environnement Arduino et quelques séances de lecture de Locoduino où on commence à pouvoir trouver l'essentiel pour faire tout soi-même (DIY). Un bon fer à souder et quelques composants et hop !

(http://p9.storage.canalblog.com/91/49/412142/33679453.jpg)

Mon exemple n'est pas le seul, loin s'en faut. J'ai choisi cette architecture, maintenant je continue sur la lancée, mais je sais qu'elle reste évolutive (module par module), ce dont je ne vais pas me priver.

Donc il y aura plein d'autres épisodes à suivre ...

Dominique
Titre: Re : Projet Dominique
Posté par: Dominique le décembre 31, 2017, 05:15:47 pm
Voici maintenant la gestion du clavier tactile 12 touches.

J'ai utilisé un composant comme celui-ci :
https://www.ebay.fr/itm/1Pcs-Mpr121-Capacitive-Touch-Keypad-Shield-Module-Sensitive-Key-Keyboard-New-I-P/252906299459?hash=item3ae263e043:g:JrAAAOSwx2dYIPLs (https://www.ebay.fr/itm/1Pcs-Mpr121-Capacitive-Touch-Keypad-Shield-Module-Sensitive-Key-Keyboard-New-I-P/252906299459?hash=item3ae263e043:g:JrAAAOSwx2dYIPLs)

Il a l'avantage d'avoir sa puce MPR121 du coté opposé aux touches tactiles et 4 trous de fixation, ce qui permet de l'installer avec discrétion et élégance. J'ai réalisé un cache plastifié qui est posé dessus et tient par les même vis de fixation. Ce cache me permet de reorganiser les touches autrement.

Dans les globales j'ai ajouté :
///////////// Touchpad à l'adresse I2C = 0x5A ///////
Adafruit_MPR121 cap = Adafruit_MPR121();
uint16_t lasttouched = 0;
uint16_t currtouched = 0;
int Key;
// Attach the MPR121's IRQ pin to digital pin 4
const int PIN_TOUCH_IRQ = 4;

Pour lire ce clavier tactile, j'ai écrit une fonction Read_Keypad qui retourne un numéro de 0 à 11, ou 12 si rien (car le 0 est déjà affecté à une touche).

///////////////////////////////////////////////////////////////////////
// LECTURE CLAVIER TACTILE
///////////////////////////////////////////////////////////////////////

byte Read_Keypad(void)
{
  uint8_t i;
  bool keydown = false;
  // Get the currently touched pads
  currtouched = cap.touched();
  for (i=0; i<12; i++) {
    // if it *is* touched and *wasnt* touched before => key down
    if ((currtouched & _BV(i)) && !(lasttouched & _BV(i)) ) {
      keydown = true;
      //Serial.print(i); Serial.println(" touched");
      break;
    }
    // if it *was* touched and now *isnt* => key up (ignored)
    if (!(currtouched & _BV(i)) && (lasttouched & _BV(i)) ) {
      keydown = false;
      //Serial.print(i); Serial.println(" released");
      break;
    }
  } 
  lasttouched = currtouched;
  switch (i) {
    case 0:  i=9;  break;
    case 1:  i=6;  break;
    case 2:  i=3;  break;
    case 3:  i=11; break;
    case 4:  i=8;  break;
    case 5:  i=5;  break;
    case 6:  i=2;  break;
    case 7:  i=0;  break;
    case 8:  i=7;  break;
    case 9:  i=4;  break;
    case 10: i=1;  break;
    case 11: i=10; break;   
  }
  if (keydown) {
    return (i);
  } else {
    return (i+12);
  }
}

On remarque le switch qui permet de changer les numéros de touches, car l'ordre des touches du produit de base n'est pas bien adapté à ce que je recherche.

Dans la loop, la lecture des appuis sur le clavier tactile est très simple :

   // gestion du clavier tactile
   if (digitalRead(PIN_TOUCH_IRQ) == LOW) {
    int Key = Read_Keypad();
   }

Dans mon programme, les entrées sur ce clavier tactile sont très nombreuses et dépendent de plusieurs indicateurs, par exemple :

- Hors configuration, l'appui sur une touche sélectionne une loco dans la table et affiche ses variables courantes (nom, vitesse, direction, ..)

    // choix registre/loco hors configuration et saisie
    if ((!gConfig)&&(!gSaisie)&&(Key < 12)) {
      gId = Key;
      gCurrentLoco = gConfigLocos[gId];
      gChange = true;
      MajLeds();
    }

- En configuration, le clavier sert à saisir des valeurs sur 1, 2 ou 3 chiffres significatifs, avec affichage sur l'écran LCD, possibilité de correction avec la touche 'c' (numéro 10)  et validation avec la touche 11.

Par exemple, pour la configuration de l'adresse DCC :

        //--- saisie adresse DCC
        if ((Key >= 0)&&(Key <=9)) {
          gSaisieValeur[gSaisieValeurIndex] = Key;
          if (gSaisieValeurIndex < 3) {
            gSaisieValeurIndex++;
            lcd.setCursor(14,1);
            for (int c=0;c<gSaisieValeurIndex;c++) {
              lcd.print(gSaisieValeur[c]);
            }
            lcd.print("  ");
          }
        }
        if (Key == 10) { // back
          if (gSaisieValeurIndex > 0) {
            gSaisieValeurIndex--;
            lcd.setCursor(14,1);
            for (int c=0;c<gSaisieValeurIndex;c++) {
              lcd.print(gSaisieValeur[c]);
            }
            lcd.print("  ");
          }
        }
        if (Key == 11) { // enter
          gChange = true;
          switch (gSaisieValeurIndex) {
            case 0:
            gChange = false; // saisie vide
            break;
            case 1:
            gCurrentLoco.dccAddress = gSaisieValeur[0];
            break;
            case 2:
            gCurrentLoco.dccAddress = (gSaisieValeur[0] * 10) + gSaisieValeur[1];
            break;
            case 3:
            gCurrentLoco.dccAddress = (gSaisieValeur[0] * 100) + (gSaisieValeur[1] * 10) + gSaisieValeur[2];
            break;           
          }
          lcd.setCursor(14,1);lcd.print(gCurrentLoco.dccAddress);lcd.print("  ");
          if (gChange) {
            gConfigLocos[gId] = gCurrentLoco;   // sauvegarde dans la table des locos
            EnregistreLocoEEProm(gId);          // enregistre gCurrentLoco dans l'EEPROM
          }
          gSaisie=false; gConfig=false; gChange = true;
        }

Titre: Re : Projet Dominique
Posté par: Dominique le décembre 31, 2017, 05:24:57 pm
Et maintenant le mesure de vitesse des trains !

Très simplement j'ai tiré 2 fils (signal et masse) entre 1 capteur de consommation d'une zone droite et horizontale d'une longueur de 172 cm sur mon réseau et une entrée capt1 de l'Arduino.

Quand une loco entre dans cette zone, capt1 passe à l'état bas. Quand elle sort de la zone, capt1 passe à l'état haut.

Le code pour gérer cela est :

void mesureV1() {
  int v1 = digitalRead(capt1);
  if (v1 != gOldcapt1) {    // changement d'état capt1
    gOldcapt1 = v1;
    if (v1 == 0) {          // zone occupée
      if (gModeMV1 == 0) {  // entree dans zone de mesure 1
        gModeMV1 = 1;
        gTempsMV1 = millis();
        lcd.setCursor(0,1);
        lcd.print("Vitesse 1          ");
      }
    } else {                // v1 = 1 : zone libérée
      if (gModeMV1 == 1) {  // sortie et calcul vitesse
        gModeMV1 = 0;
        gTempsMV1 = millis() - gTempsMV1;
        gVitesse1 = calcul(gTempsMV1);
        lcd.setCursor(0,1);
        lcd.print("Vitesse 1 = ");
        lcd.print(gVitesse1);lcd.print(" km/h ");
        lcd.backlight();
        retrotime = millis();
      }
    }
  }
}

Le principe est simple : On enregistre le temps système (millis()) à l'entrée dans la zone, puis à la sortie de la zone. Ensuite on utilise la différence de temps et la longueur de la zone pour calculer la vitesse à l'échelle N et on l'affiche sur le LCD. On pourrait configurer cette longueur dans le configurateur.

Le calcul de la vitesse est :

int calcul(unsigned long temps) {         // exemple 100 cm en 10 s => 57,6 km/h
  unsigned long VKM;
  if (temps>0) { //VKM=(gDistanceMV*5760)/temps; // 172*5760 / 10000 = 57,60 km/h
    VKM=990720/temps; // ici 172*5760 / 10000 = 57,60 km/h
    Serial.println(VKM);
  }
  return ((int)VKM);
}
Titre: Re : Projet Dominique
Posté par: Dominique le janvier 03, 2018, 03:22:44 pm
Maintenant il faut assembler tous les morceaux pour que l'Arduino fasse toutes ces tâches séquentiellement (à l'échelle humaine on ne s'en rend pas compte et cela semble simultané) et sans blocage. Ce qui compte c'est que toutes les commandes de l'interface utilisateur soit les plus instantanées possibles.

On va donc placer dans le loop() des séquences de code, les unes après les autres, chacune se chargeant d'une tâche simple. Aucune de ces séquence ne doit bloquer l'Arduino :

1- récupérer un message CAN et le traiter (on l'a vu ici http://forum.locoduino.org/index.php?topic=290.msg4319#msg4319 (http://forum.locoduino.org/index.php?topic=290.msg4319#msg4319))
2- détecter un appui sur le bouton poussoir de l'encodeur quadratique
---> le 1er appui démarre le choix d'une séquence de configuration
---> le 2ème appui démarre la modification d'un paramètre
3- lire le clavier tactile (vu ici http://forum.locoduino.org/index.php?topic=290.msg4328#msg4328 (http://forum.locoduino.org/index.php?topic=290.msg4328#msg4328))
---> en mode configuration c'est une saisie de paramètre
---> hors mode configuration, c'est une sélection de loco
4- lire la position de l'encodeur quadratique
---> en configuration, c'est une sélection de paramètre
---> hors configuration, c'est une variation de vitesse de la loco sélectionnée
5- lire les potentiomètres de vitesse (vu ici http://forum.locoduino.org/index.php?topic=290.msg4320#msg4320 (http://forum.locoduino.org/index.php?topic=290.msg4320#msg4320))
6- gérer les boutons et leds de direction (idem)
7- gérer les boutons "panic", F0 (FL), F1 et F2
8- mesurer la vitesse des trains dans 2 zones distinctes (vu ici http://forum.locoduino.org/index.php?topic=290.msg4328#msg4328 (http://forum.locoduino.org/index.php?topic=290.msg4328#msg4328))
9- gérer les clignotements des leds
10- gérer le rétro-éclairage de l'écran LCD
11- gérer la mise à jour des affichages de l'écran LCD (état de la loco sélectionnée)
12- surveiller le courant pour couper le DCC en cas de court-circuits
13- gérer l'interface avec un petit moniteur de debugging sur l'écran terminal

OUF !!!!

Il en fait des choses cet Arduino  (MEGA). Mais certaines des fonctions listées ci-dessus sont conditionnées par des variables qui permettent d'exécuter certains parties du code et pas d'autres.

C'est ce que je vais appeler pompeusement ici "la logique de décision".

Dans un tel projet, c'est très important. Définir les variables globales d'état de ces séquences conditionne la réussite du projet.
Cela demande un long moment de réflexion avant d'écrire le code. Tout au plus, on peut écrire des petits programmes pour tester séparément les différentes parties du code, puis on assemble le tout à la fin. C'est ce que j'ai fait et je pense que je modifierai encore le code pour l'optimiser si cela est nécessaire.

Titre: Re : Projet Dominique
Posté par: Dominique le janvier 03, 2018, 04:50:57 pm
Maintenant abordons la logique de décision, c'est à dire l'ordonnancement des tâches dans la loop() en fonction d'un certain nombre de variables d'état globales.

1- Traitement des messages CAN
C'est le compteur d'octets non lus dans le buffer circulaire Ncan qui conditionne cette tâche. Si Ncan > 2 il y a forcément un message (la taille minimum est 3) et dans ce cas on le traite.

2- Détection d'appui sur le bouton de l'encodeur.
C'est un bouton que l'on teste à chaque tour de loop
//--- Detection d'appui sur SWenc :
  SWenc.update();
  if ( SWenc.fell() ) {     
    gSWState = !gSWState;
    gSW = true;     // appui effectif
    gConfig = true; // mode global configuration
  }

Ce bouton poussoir est géré par la bibliothèque Bounce2 comme n'importe quel bouton poussoir.
Chaque appui met à true une variable gConfig (mode configuration) et gSW (appui détecté) et inverse la variable gSWState pour différencier les cas du 1er et du 2ème appui.

Le premier appui sert à afficher le choix de la variable à configurer en fonction de la position de l'encodeur qui permet, en tournant le bouton dans un sens ou un autre, de choisir parmi 10 variables de configuration, avec une variable gMode :
  // premier appui : mode choix parametre de configuration
  if (gSWState && gConfig ) { // passage en choix de mode
    if (gSW) {
      afficheTitre();
      gSW=false;
    }
    long newPosition = myEnc.read()/4;
    if (newPosition != oldPosition) { 
      if ( newPosition > oldPosition) {
        if (gMode < 9) gMode ++;
      } else {
        if (gMode > 0) gMode--;
      }
      oldPosition = newPosition;   
      afficheTitre();
      MajLeds();
    }
   }


La fonction afficheTitre est ici :
    // affichage titre
    void afficheTitre() {
      lcd.clear();
      lcd.print(gMode); 
      if (gMode < 10) lcd.print(' ');
      switch (gMode) {
      case 0:
        lcd.print("Inverser sens");
      break;
      case 1:
        lcd.print("Choix adresse");
      break;
      case 2:
        lcd.print("Mesure vitesse");
      break;
      case 3:
        lcd.print("Choix cran 30");
      break;
      case 4:
        lcd.print("Choix cran 60");
      break;
      case 5:
        lcd.print("Choix cran 90");
      break;
      case 6:
        lcd.print("Choix Vit Max");
      break;
      case 7:
        lcd.print("Choix Vit Min");
      break;
      case 8:
        lcd.print("Distance M/V cm");
      break;
      case 9:
        lcd.print("Erase EEProm ");
      break;
      default:
      break;   
      }
    }

Le deuxième appui sert à dérouler le code de configuration en fonction de la variable gMode :
  // Deuxieme appui : passage en modification de parametre
  if (!gSWState && gConfig && gSW) { // sortie du choix de mode
    gSW=false;
    switch (gMode) {
      case 0: // inverser sens
        lcd.setCursor(0,1);
        lcd.print("Normal  : taper 0 ");
        lcd.setCursor(0,2);
        lcd.print("Inverse : taper 1 ");
        gSaisie = true;
      break;
      case 1: // choix @ DCC
        lcd.setCursor(0,1);
        lcd.print("adresse DCC : ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 2:
        lcd.setCursor(0,1);
        lcd.print("Attente... ");
        gMesureVitesse = true;
        gSaisie = true;
        //gSaisieValeurIndex = 0;
      break;
      case 3:
        lcd.setCursor(0,1);
        lcd.print("Cran vit 30 : ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 4:
        lcd.setCursor(0,1);
        lcd.print("Cran vit 60 : ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 5:
        lcd.setCursor(0,1);
        lcd.print("Cran vit 90 : ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 6:
        lcd.setCursor(0,1);
        lcd.print("Cran vit max: ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 7:
        lcd.setCursor(0,1);
        lcd.print("Cran vit min: ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 8:
        lcd.setCursor(0,1);
        lcd.print("Distance M/V: ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 9:
        lcd.setCursor(0,2);
        lcd.print("Erase & Reset");
        EraseEEProm();
        reset();
      break;
      default:
        gConfig=false;
      break;   
    }
    gChange = true;
  }

Ca a l'air simple comme cela, mais ce switch sert à autoriser la saisie de valeurs sur le clavier tactile.

3- lecture du clavier tactile
Donc, en plus de gMode, d'autres variables viennent autoriser la gestion du clavier tactile dont la bonne exécution se terminera par la modification d'un paramètre de configuration qui sera sauvegardé en EEPROM.

Voici la séquence d'entrée dans ce code : elle réactive le rétro-éclairage du LCD
   // gestion du clavier tactile
   if (digitalRead(PIN_TOUCH_IRQ) == LOW) {
    int Key = Read_Keypad();
    lcd.backlight();
    retrotime = millis();

Deux cas se présentent :

A- Hors configuration on ne fait que choisir une des 12 locos
    // choix registre/loco hors configuration et saisie
    if ((!gConfig)&&(!gSaisie)&&(Key < 12)) {
      gId = Key;
      gCurrentLoco = gConfigLocos[gId];
      gChange = true;
      MajLeds();
    }

gId est le numéro de loco en cours, qui est utilisé partout pour paramètrer cette loco.

B- En configuration, on modifie un paramêtre, fonction de gId et il y a autant de programme que de paramêtre.

Cas de gMode = 0 (inversion de sens de la loco)
    // mode saisie en configuration
    if ((gSaisie)&&(Key < 12)) {
      switch (gMode) {
       
        //--- INVERSION de SENS
        case 0:
        lcd.setCursor(0,1);
        gChange = false;
        if (gCurrentLoco.inverse) {
          lcd.print("Sens Inverse       ");
        } else {
          lcd.print("Sens Normal        ");
        }
        if (Key==0) {
          gCurrentLoco.inverse = 0; 
          gChange = true;       
        }
        if (Key==1) {
          gCurrentLoco.inverse = 1;
          gChange = true;         
          }
        // toute autre touche ne change pas le sens
        if (gChange) {
          lcd.setCursor(0,1);
          if (gCurrentLoco.inverse) {
            lcd.print("Sens Inverse       ");
          } else {
            lcd.print("Sens Normal        ");
          }
          gConfigLocos[gId] = gCurrentLoco;   // sauvegarde dans la table des locos
          EnregistreLocoEEProm(gId);          // enregistre gCurrentLoco dans l'EEPROM
        }
        gSaisie=false; gConfig=false; gChange = true;
        break;

Ce code affiche des choses sur l'écran LCD, modifie le paramètre CurrentLoco.inverse dans l'objet loco et sauvegarde cet objet dans l'EEPROM, puis re-initialise les variables globales d'état pour terminer le processus.

Je donne le cas gMode = 1 : saisie de l'adresse DCC de la loco sélectionnée car il peut être intéressant à réutiliser dans votre code :
        //--- saisie adresse DCC
        case 1:
        if ((Key >= 0)&&(Key <=9)) {
          gSaisieValeur[gSaisieValeurIndex] = Key;
          if (gSaisieValeurIndex < 3) {
            gSaisieValeurIndex++;
            lcd.setCursor(14,1);
            for (int c=0;c<gSaisieValeurIndex;c++) {
              lcd.print(gSaisieValeur[c]);
            }
            lcd.print("  ");
          }
        }
        if (Key == 10) { // back
          if (gSaisieValeurIndex > 0) {
            gSaisieValeurIndex--;
            lcd.setCursor(14,1);
            for (int c=0;c<gSaisieValeurIndex;c++) {
              lcd.print(gSaisieValeur[c]);
            }
            lcd.print("  ");
          }
        }
        if (Key == 11) { // enter
          gChange = true;
          switch (gSaisieValeurIndex) {
            case 0:
            gChange = false; // saisie vide
            break;
            case 1:
            gCurrentLoco.dccAddress = gSaisieValeur[0];
            break;
            case 2:
            gCurrentLoco.dccAddress = (gSaisieValeur[0] * 10) + gSaisieValeur[1];
            break;
            case 3:
            gCurrentLoco.dccAddress = (gSaisieValeur[0] * 100) + (gSaisieValeur[1] * 10) + gSaisieValeur[2];
            break;           
          }
          lcd.setCursor(14,1);lcd.print(gCurrentLoco.dccAddress);lcd.print("  ");
          if (gChange) {
            gConfigLocos[gId] = gCurrentLoco;   // sauvegarde dans la table des locos
            EnregistreLocoEEProm(gId);          // enregistre gCurrentLoco dans l'EEPROM
          }
          gSaisie=false; gConfig=false; gChange = true;
        }
        break;

Là le but est d'afficher la valeur initiale, de pouvoir taper sur les touches du clavier, corriger une erreur avec la touche "c" et de valider la saisie puis l'enregistrer.
J'ai mis du temps à la mettre au point donc si cela peut vous éviter d'en perdre, j'en serais ravi !

On retrouvera le même principe pour toutes les autres saisies (cran des vitesses 30, 60, 90, choix des vitesses Mini et Maxi, de la distance de mesure de vitesse).

Et pour terminer, le plus simple, c'est la gestion du temps système avec millis() pour gérer les cligotements, le rétro-éclairage, la surveillance du courant et la mise à jour des lignes d'affichage de l'écran LCD :
  //clignotements Led Rouge
  if (gblinkR) {
    if ((millis() - gClignoteR) > 250) {
      gClignoteR = millis();
      digitalWrite(LedModeR,!digitalRead(LedModeR));
    }
  }
  //clignotements Led Jaune
  if (gblinkJ) {
    if ((millis() - gClignoteJ) > 500) {
      gClignoteJ = millis();
      digitalWrite(LedModeJ,!digitalRead(LedModeJ));
    }
  }

  // retro-eclairage
  if ((millis() - retrotime) > eclairage) {
    lcd.noBacklight();     
  }

  // mise à jour de la ligne d'etat en L3
  if ((gChange)&&(!gSaisie)) {
    StatutL3();
  }

  // surveillance courant MAXIMUM 2 A
  // affichage courant = la valeur maxi pendant 500 ms
  if ((millis() - gCurrentSampleTime) > 500) {
    gCurrentSampleTime = millis();
    lcd.setCursor(16,3);lcd.print(gCurrent*5);lcd.print(' ');
    gCurrent = 0;
  }
  int iCurrent = analogRead(A0);
  if (iCurrent > gCurrent) {
    gCurrent = iCurrent;
    if (gCurrent > CurrentMax) { // 400 * 5 = 2000 mA
    stop_DCC();
    }
  }

Je passe l'écran terminal pour le debugging et j'aurais aimé faire quelques mesures des temps passés dans chaque taches pour connaitre l'état de saturation du Mega2560, cela viendra plus tard. En tout cas je suis satisfait de ce code qui mériterait quand même d'être amélioré car on peut mtoujours faire mieux, avec du temps..

A suivre...
Titre: Re : Projet Dominique
Posté par: francisch le janvier 06, 2019, 09:42:21 pm
Tout d'abord je vous adresse tous mes meilleurs vœux de santé et de réussite dans vos projets.

Je lu et essayé de suivre le Projet de Dominique, mais ma culture de la programmation est trop légère beaucoup de choses m'ont échappé.

Etant sur un la réalisation d'une centrale pour 5 locos sans PC, je cherchais dans les programme de Dominique la commande pour l'arrêt d'urgence d'une seule loco (pour toutes pas de problème).
En TextCommand on envoi -1 pour la vitesse est ça marche, mais avec  DCCpp::setSpeedMain comme la vitesse est mappée dans DCCpp.cpp pas question de lui envoyé -1.
Je pensais utilisé TextCommand avec un message créé par le programme mais je ne vois pas comment faire sans PC.

Cordialement.
Francis
Titre: Re : Projet Dominique
Posté par: Dominique le janvier 07, 2019, 09:11:11 am
La doocumentation de DCCpp dit qu'il faut mettre "1" dans le parametre inNewSpeed :

Voir le dossier de la bibliothèque / extras / doc / index.html

static bool DCCpp::setSpeedMain ( int nReg,
int inLocoId,
int inStepsNumber,
int inNewSpeed,
bool inForward
)
inlinestatic
For the given decoder id, set the speed and the direction on the main track.

Parameters
nReg Register number. Avoid register 0, used for one shot commands like accessories or CV programming.
inLocoId Decoder address in short or long format.
inStepsNumber According to the decoder configuration, set it to 14, 28 or 128 .
inNewSpeed Speed of the loco, between 2 and the steps number - 1 (13, 27 or 127). 0 means normal complete stop. 1 means emergency stop.
inForward True means forward move, false means backward.
Titre: Re : Projet Dominique
Posté par: francisch le janvier 07, 2019, 01:51:50 pm
Effectivement j'avais lu (trop vite) mais pas compris.
J'applique et cela va marcher.
Merci
Titre: Re : Projet Dominique
Posté par: francisch le janvier 10, 2019, 02:23:23 pm
Bon, j'applique et cela ne marche
j’envoie
 DCCpp::setSpeedMain(1, 3, 28, 0, 1);
en retour j'ai
DCCpp SetSpeed 0/28 (in Dcc 0 )
 cela va tres bien
mais si j'envoie
DCCpp::setSpeedMain(1, 3, 28, 0, 1);
en retour j'ai
DCCpp SetSpeed 1/28 (in Dcc 6 )

La valeur 1 est transformée en 6 par la fonction map  dans DCCpp.cpp
 /***************************** Driving functions */
 
 bool DCCpp::setThrottle(volatile RegisterList *inpRegs, int nReg,  int inLocoId, int inStepsNumber, int inNewSpeed, bool inForward)
 {
   int val = 0;
 
   if (panicStopped)
     val = 1;
   else
     if (inNewSpeed > 0)
       val = map(inNewSpeed, 0, inStepsNumber, 2, 127);
 
 #ifdef DCCPP_DEBUG_MODE
   Serial.print(F("DCCpp SetSpeed "));
   Serial.print(inForward?inNewSpeed:-inNewSpeed);
   Serial.print(F("/"));
   Serial.print(inStepsNumber);
   Serial.print(F(" (in Dcc "));
   Serial.print(val);
   Serial.println(F(" )"));
 #endif


D'autre part je n'ai pas retrouve les lignes de code citées dans la reponse de Dominique.
Est ce que j'ai sauté une etape ?
Titre: Re : Projet Dominique
Posté par: Thierry le janvier 11, 2019, 11:34:26 am
C'est une incohérence de DCCpp. Vu le code, le Map est là pour permettre à l'utilisateur de setSpeedMain de ne pas se préoccuper des valeurs spéciales : il peut utiliser des valeurs de vitesse de 0 à 127, ce sera transformé pour exclure 0 et 1. La doc explique bien la norme pour la vitesse mais ne dit pas que setSpeedMain n'aura pas ce comportement... On peut contourner ça en appelant directement la bonne fonction de Greeg via les registres :

DCCpp::mainRegs.setThrottle(1, 3, 0, 1);  // fourni sans test !

sachant que cette fonction impose une valeur de vitesse réglée pour 128 pas pour une vitesse normale. Les vitesses spéciales 0 et 1 ne sont pas concernées par le nombre de pas.
Titre: Re : Projet Dominique
Posté par: francisch le janvier 11, 2019, 01:18:01 pm
Merci de cette réponse, elle m'amène une autre question. Est ce que cela veut dire que DCCpp n'envoie que des ordres de vitesse en 128crans ?
Titre: Re�: Projet Dominique
Posté par: Tony04 le janvier 11, 2019, 01:41:04 pm
Bonjour francish,

non, DCCpp gere tres bien aussi les 28 crans mais je n'ai trouve aucune doc la dessus alors que le 28 crans est tres utile pour certaines locos.

Voici comment je procede dans ma souris/centrale sans fil.

Pour le 128 crans registre=1 par defaut mais peut etre 1 chiffre defini pour chaque loco, adresse_loco=adresse DCC, vitesse 0 a 127, sens=0 ou 1  :
DCCpp::setSpeedMain(registre, adresse_loco, 128, vitesse, sens); 
Pour le 28 crans idem sauf le 28 et vitesse de 0 a 27 :
DCCpp::setSpeedMain(registre, adresse_loco, 28, vitesse, sens); 

Cordialement
Antoine

PS: J'ai enleve les accents
Titre: Re : Projet Dominique
Posté par: Thierry le janvier 11, 2019, 04:10:02 pm
DCC++ en interne ne gère que 128 crans, mais le Map de DCCpp.cpp est là pour permettre de gérer n'importe quel nombre de crans. L'utilisateur de setSpeedMain met une valeur entre 0 et 27 pour une loco 28 crans, et c'est le setThrottle avec son Map qui va donner une vitesse connue en 128 crans à DCC++ . Par contre effectivement, c'est un ordre 128 crans qui sera envoyé au décodeur de la loco... Je n'ai pas l'impression qu'autre chose est possible avec DCC++. Je ne suis pas rentré dans le détail, je suis au boulot...
Titre: Re : Projet Dominique
Posté par: Tony04 le janvier 11, 2019, 05:20:28 pm
Bonsoir Thierry,

de toute facon sur les decodeurs tu ne peux pas choisir entre 28 et 128 (a ma connaissance), c'est toujours 28/128 ou autre chose, donc l'ordre de DCCpp doit effectivement etre en 128 crans mais c'est transparent pour l'utilisateur qui peux lui utiliser une plage de 28 crans.

Titre: Re : Projet Dominique
Posté par: francisch le janvier 11, 2019, 06:08:01 pm
Je viens de vérifier mes docs de mes décodeurs : le codage de la CV29 ne différencie pas le 28 et 128 crans.
Merci de vos réponses
Francis
Titre: Re : Projet Dominique
Posté par: Tony04 le janvier 11, 2019, 06:24:10 pm
Non, c'est ce que je disais, mais tu as l'avantage de pouvoir passer de l'arret a la vitesse maximum avec beaucoup moins de crans et pour certaines petites locos c'est bien pratique
Titre: Re : Projet Dominique
Posté par: Dominique le janvier 11, 2019, 07:17:59 pm
Le potentiometre lineaire est ce qu’il y a de mieux pour aller vite.
Et puis, accelerer ou ralentir vite n’est pas realiste  ::)
Titre: Re : Projet Dominique
Posté par: Tony04 le janvier 11, 2019, 08:50:45 pm
Bonsoir Dominique,

je transmets juste ce que plusieurs membres du club qui utilisent mes souris sans fil m'ont demande, a savoir pouvoir travailler avec les 2 types de crans et aussi a la question de francisch. Apres chacun est libre de faire ce qu'il veut.

Desole pour les accents qui me choquent autant que toi je suppose.

Amicalement
Antoine
Titre: Re : Projet Dominique
Posté par: Dominique le janvier 11, 2019, 09:37:50 pm
Bonsoir Antoine,

Rassures-toi je comprends bien la demande dans ton club, car ta manette commande la vitesse avec un encodeur quadratique. C'est aussi le cas de mes 2 dernieres centrales et j'envisage de realiser un accelerateur d'increments c'est a dire une variation qui depend de la vitesse de rotation du bouton : a la vitesse actuelle, chaque cran de l'encodeur se traduit par +1 ou -1 selon le sens. En mesurant le temps entre 2 changements de crans successifs, je devrais pouvoir traduire ca par + ou - 2 ou 3 ou 5 ou 10 comme le reglage de la minuterie sur mon four.

Mais ce n'est pas pour tout de suite ...
Titre: Re : Re�: Projet Dominique
Posté par: Tony04 le janvier 11, 2019, 09:55:56 pm
j'envisage de realiser un accelerateur d'increments c'est a dire une variation qui depend de la vitesse de rotation du bouton : a la vitesse actuelle, chaque cran de l'encodeur se traduit par +1 ou -1 selon le sens. En mesurant le temps entre 2 changements de crans successifs, je devrais pouvoir traduire ca par + ou - 2 ou 3 ou 5 ou 10 comme le reglage de la minuterie sur mon four.

C'est exactement ce que j'ai fait au debut de mon developpement et il s'est avere que personne n'a aime et du coup j'incremente plus rapidement en fonction de la position des crans avec des constantes modifiables facilement en début de programme et la ca plait a tout le monde. Parfois on se creuse la tete pour rien.

Mais je veux bien tester ta version le jour ou tu la mettre en ligne, je me suis peut etre mal debrouille.
Titre: Re : Projet Dominique
Posté par: DDEFF le janvier 11, 2019, 10:09:32 pm
Bonsoir,

Le problème n'est-il pas que tout cela est linéaire ? ::)

Je pense qu'il faut une courbe log ou exp (suivant le sens où on la regarde).
Deux possibilités :
1°) ceux qui veulent peaufiner les ralentis (zone de manœuvre) où 70% de la course du potar sert de 0 à 10% de la vitesse maxi et 30% de la course du potar pour aller de 10% à 100% de la vitesse maxi
2°) ceux qui sont hors zone de manœuvre et qui veulent que 30% de la course du potar pour aller de 0 à 70% de la vitesse maxi et les 70 % restants de la course du potar pour aller de 70 à 100% de la vitesse maxi.
Dans un gestionnaire bien fait, on sait où est le train et on peut "changer de potar" suivant les zones.
Problème intéressant.

Denis
Titre: Re : Projet Dominique
Posté par: Rob1 le janvier 13, 2019, 02:51:11 pm
Je vote pour le RIC ( Rotation Incrémentale Contrôlée) telle que proposée par Dominique mais qui à mon avis supposerait de disposer d'un coefficient d'incrémentation personnalisable pour que chaque utilisateur y trouve un confort d'utilisation car certains sont très véloces à la souris d'autre le sont moins.
Titre: Re : Projet Dominique
Posté par: Dominique le janvier 13, 2019, 03:18:51 pm
Quelqu'un se propose-t-il d'écrire une bibliothèque liées aux codeurs quadratiques, si elle n'existe pas ?
Titre: Re : Projet Dominique
Posté par: Rob1 le janvier 13, 2019, 08:39:24 pm
Je veux bien m'y atteler mais je ne dispose pas de ce type de codeur.
Je vais en commander un. Avez-vous une référence courante ?
Cependant il me faudrait un tuteur pour valider ma production car je n'ai jamais complétement créé de bibliothèque, d'où ma proposition de m'y coller.
Je partirais sur un traitement sous interruption avec une sortie vitesse dont le signe indiquerait le sens de rotation.


Titre: Re : Projet Dominique
Posté par: Dominique le janvier 13, 2019, 09:07:05 pm
Je pense que le mieux est de partir d'une bibliothèque existante qui utilise les interruptions.

Je viens de voir que PJRC vient d'en pousser une ces derniers jours :
https://github.com/PaulStoffregen/Encoder (https://github.com/PaulStoffregen/Encoder)

Il me semble aussi que la question a probablement déjà des réponses. Je vais chercher avant de se lancer dans un boulot déjà  fait.

Tu as le temps de commander un encoder de ce type :
https://howtomechatronics.com/tutorials/arduino/rotary-encoder-works-use-arduino/ (https://howtomechatronics.com/tutorials/arduino/rotary-encoder-works-use-arduino/)
Ou
https://playground.arduino.cc/Main/RotaryEncoders (https://playground.arduino.cc/Main/RotaryEncoders)
Ou
https://www.amazon.fr/Rotary-Encoder-Decoder-Module-Arduino/dp/B00Q6WP196 (https://www.amazon.fr/Rotary-Encoder-Decoder-Module-Arduino/dp/B00Q6WP196)
Titre: Re : Projet Dominique
Posté par: ponponmvo le janvier 14, 2019, 06:03:11 pm
Bonsoir à tous,

Dans la lignée de ce qu'ecrit Denis, on peut, la plupart du temps, modifier l'increment en fonction de la vitesse (ou du cran) active par programmation.

Donc au lieu d'augmenter ou diminuer la vitesse cran par cran sur toute l'étendue des 128 crans, se limiter aux crans 0 à 20 et en dehors de cette zone progresser par pas de 5.

Daniel
Titre: Re : Re : Projet Dominique
Posté par: Tony04 le janvier 14, 2019, 07:19:13 pm
Donc au lieu d'augmenter ou diminuer la vitesse cran par cran sur toute l'étendue des 128 crans, se limiter aux crans 0 à 20 et en dehors de cette zone progresser par pas de 5.

Bonsoir tout le monde,

c'est la solution utilisée dans la souris sans fil et elle convient à tout le monde visiblement. Il y a même 3 niveaux différents d'incrément.

Bonne soirée à tous
Titre: Re : Projet Dominique
Posté par: francisch le janvier 14, 2019, 08:40:08 pm
Pour ceux qui restent aux potars, l'emploi d'un potetiometre logarithmique repond a la progressivite de l'acceleration.
Avec 28 crans il faut la moitie de la course pour faire 6 crans et l'autre moitie pour les 22 restants, le tout suivant une courbe pseudo-logarithmique.
L'inconveniant du potar c'est qu'il faut obliger de repasser par 0 apres un Stop d'urgence, sinon :'( :'( :'( :'(
Francis
Titre: Re : Projet Dominique
Posté par: Tony04 le janvier 14, 2019, 08:55:25 pm
Et ne pas gérer plusieurs locos avec ta souris!
Titre: Re : Projet Dominique
Posté par: Dominique le janvier 14, 2019, 09:02:20 pm
Avec les evolutions de mon va-et-vient automatique, j’ai ajoute une commande manuelle avec l’encodeur, de telle sorte que le passage par zero entraine un changement de sens.

Je me rends compte qu’il faudrait un retour rapide a zero sans le depasser pour eviter un arret d’urgence qui n’est pas realiste.
Titre: Re : Projet Dominique
Posté par: Tony04 le janvier 15, 2019, 12:26:10 am
Bonsoir Dominique,

Pourquoi ne pas passer à 0 par un simple appui sur l'encodeur, c'est très pratique, je l'ai intégré dans ma souris et cela a été adopté à l'unanimité ?

Amitiés
Antoine
Titre: Re : Projet Dominique
Posté par: Dominique le janvier 15, 2019, 08:49:39 am
Oui mais c’est déjà mon arrêt d’urgence (vitesse à 0 et arrêt DCC).
Titre: Re : Projet Dominique
Posté par: msport le janvier 15, 2019, 09:44:51 am
Peut-être discriminer par un appui long ?
Titre: Re : Projet Dominique
Posté par: Tony04 le janvier 15, 2019, 10:27:03 am
Avec la librairie avdweb_Switch par exemple (clic, double-clic et appui long)
Titre: Re : Projet Dominique
Posté par: Dominique le janvier 15, 2019, 05:35:49 pm
Voilà plein de bonnes idées pour un futur article sur les encodeurs ...
Titre: Re : Projet Dominique
Posté par: Dominique le août 12, 2020, 03:42:33 pm
Bon, je me disperse tellement que des bouts de description de mon réseau se retrouvent un peu partout dans ce forum  :o >:(

Je vais essayer de rassembler les morceaux !
Titre: Re : Projet Dominique
Posté par: Dominique le août 12, 2020, 03:46:49 pm
Pour la modélisation du réseau là où on peut implémenter un gestionnaire des circulations, voila ceci :

 http://forum.locoduino.org/index.php?topic=166.msg2920#msg2920 (http://forum.locoduino.org/index.php?topic=166.msg2920#msg2920)

Et ici :
 http://forum.locoduino.org/index.php?topic=166.msg2922#msg2922 (http://forum.locoduino.org/index.php?topic=166.msg2922#msg2922)

Puis
 http://forum.locoduino.org/index.php?topic=166.msg2923#msg2923 (http://forum.locoduino.org/index.php?topic=166.msg2923#msg2923)

Et la suite :
 http://forum.locoduino.org/index.php?topic=166.msg2925#msg2925 (http://forum.locoduino.org/index.php?topic=166.msg2925#msg2925)

 http://forum.locoduino.org/index.php?topic=166.msg2926#msg2926 (http://forum.locoduino.org/index.php?topic=166.msg2926#msg2926)




Titre: Re : Projet Dominique
Posté par: Pyk35 le août 15, 2020, 12:01:58 pm
Bonjour Dominique,

Je n'ai pas encore tout lu mais je suis très impressionné par cette réalisation !
Un grand bravo.
Titre: Re : Projet Dominique
Posté par: Dominique le août 18, 2020, 02:35:19 pm
Pour continuer la description de mon réseau, j'aborde maintenant le TCO graphique que j'ai intégré au gestionnaire des circulations. Ce gestionnaire tourne sur un DUE avec une interface CAN. Il reçoit donc tous les messages de rétrosignalisation, ainsi que ceux de la traction. Je n'aborde pas ici le fonctionnement du gestionnaire (encore en chantier) mais seulement l'onglet Graphiques.h du programme gestionnaire graphique-tactile sur DUE que je donne en PJ : (Graphiques.h).

Il y a 1360 lignes !
C'est normal, vu le nombre de zones (47) et d'aiguilles (21).

Mais cela nécessite des explications que je vais donner progressivement :

Le tracé réel de mon réseau est à peu près ceci (RéseauDBReel.jpg)
(https://forum.locoduino.org/index.php?action=dlattach;topic=1045.0;attach=3314;image)
Les couleurs correspondent à l'altitude (bleu en bas, vert au milieu et rouge en haut).

Mais pour caser tout cela dans un écran 5 pouces, j'ai replié les grandes longueurs dans le milieu entre les 2 gares, afin de permettre la visualisation plus confortable des occupations (MonResauTypeTCO.png).
(https://forum.locoduino.org/index.php?action=dlattach;topic=1045.0;attach=3316;image)

Maintenant, pour programmer tous les segments et arcs qui constituent les 21 aiguilles et les 47 zones (qui incluent parfois une ou plusieurs aiguilles), il faut determiner les coordonnées graphiques de tous les éléments graaphiques, ce qui est fastidieux et donne ceci (tracéDB0.png)
(https://forum.locoduino.org/index.php?action=dlattach;topic=1045.0;attach=3318;image)

Je vais expliquer le code dans une réponse suivante ...

En ce qui concerne le matériel utilisé : j'ai combiné un Arduino DUE avec un écran 5 pouces et la carte d'interface Sainsmart comme présenté dans la page suivante :
https://www.ebay.com/itm/5-inch-800x480-TFT-LCD-Resistive-Touch-Shield-for-Arduino-Due-MEGA-2560-Library/291874076554? (https://www.ebay.com/itm/5-inch-800x480-TFT-LCD-Resistive-Touch-Shield-for-Arduino-Due-MEGA-2560-Library/291874076554?hash=item43f50cf78a:g:7rwAAOSwCGVX2RWd)
Titre: Re : Projet Dominique
Posté par: Dominique le août 18, 2020, 02:52:03 pm
Vu le nombre d'objets aiguilles (21) et zones (47), autant passer en C++.

Leurs classes sont définies au début du fichier :
// classe de base AIGUILLE Graphique

class GAiguille {
public:
  boolean etat=true; // directe (true) ou deviee (false)
  int no;       // numéro de l'aiguille
  int nz;       // le numéro de Zone ou est l'aiguille
 
  GAiguille(int n, int z) { no=n; nz=z; } // constructeur utilisé

  boolean directe() { return etat; }  // méthode de test
  boolean deviee()  { return !etat; } // méthode de test
  void directer() {etat=true;}   // mettre l'aiguille en position directe
  void devier()   {etat=false;}  // mettre l'aiguille en position deviee
  virtual void tracer(int c); // TCO c= couleur K_ouvert ou K_iti ou Kocc selon etat, K_ferme selon !etat
};
 
// classe de base ZONE Graphique

class GZone {
public:
   boolean etat=false; // libre (false) ou occupé (true)
   int no; // le numero de la zone pour commander l'electronique ( TCO, relais, ... )
   int na=-1; // le numéro de l'aiguille dans la zone, sinon -1
   int nb=-1; // le numéro de l'aiguille dans la zone, sinon -1

   GZone(int n) { no=n; } // constructeur mini
   GZone(int n,int a) { no=n; na=a; } // constructeur   OPTION
   GZone(int n,int a,int b) {no=n; na=a; nb=b;} // constructeur   OPTION
 
   void occuper() {etat=true;} // appelée par la rétrosignalisation lors de l'occupation d'une zone
   void liberer() {etat=false;} // appelée par la rétrosignalisation lors de la liberation d'une zone
   virtual void tracer(int c, boolean s); // TCO c= couleur K_ouvert ou K_iti ou Kocc selon etat, K_ferme selon !etat
};


Ces classes définissent les fonctions qu'il faudra appeler par le gestionnaire en fonction des occupations, libérations, manœuvres d'aiguilles, tracés graphiques.
Ces classes de base servent de modèle aux classes spécifiques de chaque zone et chaque aiguille.

Les classes aiguille sont dénommées GA0 à GA20.
par exemple :
class GA0: public GAiguille {
  public:
  GA0(int n,int z):GAiguille(n,z) { no=n; nz=z; } // constructeur utilisé
  void tracer(int c);   
};
void GA0::tracer(int c) {
  if (etat) {//droit
    myGLCD.setColor(K_ferme);
    geo.drawArc(90, 358, 38, 0, 45, 5);
    myGLCD.setColor(c);
    drawbarreH(90, 320, 27);
  }
  else {     // devié
    myGLCD.setColor(K_ferme);
    drawbarreH(90, 320, 27);
    myGLCD.setColor(c);
    geo.drawArc(90, 358, 38, 0, 45, 5);
  }
}
GAiguille* Ga0 = new GA0(0,26); // a0  est dans la zone z26

On voit que le dessin d'une aiguille est la combinaison de segments de droite et d'arc avec 2 cas différents selon que l'aiguile est droite ou déviée.

De même, les classes zones sont dénommées de GZ0 à GZ46.
Par exemple :
class GZ2 : public GZone {
  public:
  //GZone(int n) { no=n; } // constructeur mini
  GZ2(int n,int a):GZone(n,a) { no=n; na=a; } // constructeur   OPTION
  //GZone(int n,int a,int b) {no=n; na=a; nb=b;} // constructeur   OPTION
  void tracer(int c,boolean s); // TCO c= couleur K_ouvert ou K_iti ou Kocc selon etat, K_ferme selon !etat
};
void GZ2::tracer(int c, boolean s) {
  myGLCD.setColor(c);
  Ga15->tracer(c);
  if (Ga15->directe()) {drawbarreH(547, 72, 26);}
  myGLCD.setColor(K_trace);
  if (Ga15->deviee()) {drawbarreH(547, 72, 26);} // Z2 contient a15
}
GZone* Gz2 = new GZ2(2,15); 

On remarque ici, dans la zone 2 qu'il y a une aiguille 15, d'ou les appels aux fonctions graphiques de l'aiguille 15.

A la fin, pour manupuler les objets, les tables d'objets indexées sont créees :
////////////////////////// table des aiguilles

GAiguille* tableGA[MaxAig]= {
  Ga0, Ga1, Ga2, Ga3, Ga4, Ga5, Ga6, Ga7, Ga8, Ga9, Ga10, Ga11,
  Ga12, Ga13, Ga14, Ga15, Ga16, Ga17, Ga18, Ga19, Ga20 };
 
/////////////////// TABLE DES ZONES /////////////////

GZone* tableGZ[MaxZone]=  { 
    Gz0, Gz1, Gz2, Gz3, Gz4, Gz5, Gz6, Gz7, Gz8, Gz9, Gz10, Gz11, Gz12, Gz13, Gz14, Gz15,
    Gz16, Gz17, Gz18, Gz19, Gz20, Gz21, Gz22, Gz23, Gz24, Gz25, Gz26, Gz27, Gz28, Gz29, Gz30,
    Gz31, Gz32, Gz33, Gz34, Gz35, Gz36, Gz37, Gz38, Gz39, Gz40, Gz41, Gz42, Gz43, Gz44, Gz45, Gz46  };
   
   
void drawallzones(int k) {
  for (int i=0; i<MaxZone; i++)
 {
  tableGZ[i]->tracer(k, true);
 }
}
////////////////////////////////////////////////////////////////////////////////////

On remarque la fonction drawallzones() qui sert à aficher l'ensemble du circuit au démarrage et lors des rafraichissements d'écran.

Titre: Re : Projet Dominique
Posté par: Dominique le août 18, 2020, 02:59:44 pm
Donc le tracé de mon réseau sur un écran graphique est constitué de multiples segments de droite et d'arcs.

Les bibliothèques UTFT nous donnent tout ce qu'il faut :
#include <UTFT.h>
#include <UTFT_Geometry.h>
#include <UTouch.h>
#include <UTFT_Buttons.h>
#include <UTFT_SdRaw.h>
#include <SdFat.h>

// Declare which fonts we will be using
extern uint8_t SmallFont[];
extern uint8_t BigFont[];

// CTE TFT LCD/SD Shield for Arduino Due       : <display model>,25,26,27,28
UTFT           myGLCD(CTE50,25,26,27,28);
UTFT_Geometry  geo(&myGLCD);
UTouch         myTouch(6,5,32,3,2);
UTFT_Buttons   myButtons(&myGLCD, &myTouch);


int max_x, max_y;
int kolor[16] = {VGA_WHITE,VGA_RED,VGA_GREEN,VGA_BLUE,VGA_SILVER,VGA_GRAY,VGA_MAROON,
VGA_YELLOW,VGA_OLIVE,VGA_LIME,VGA_AQUA,VGA_TEAL,VGA_NAVY,VGA_FUCHSIA,VGA_PURPLE,VGA_BLACK};
int kolori[4] = {VGA_WHITE,VGA_RED,VGA_GREEN,VGA_BLUE};

int K_fond = VGA_SILVER; // indice 4
int K_trace = VGA_GRAY;
int K_ferme = VGA_BLACK;
int K_ouvert = K_trace;
int K_iti = VGA_WHITE;
int K_occ = VGA_RED;

Donc il faut comencer par des routines graphiques de segment en d'arc :
///////////////////////// ROUTINES GRAPHIQUES ////////////////////////
// barre horizontale long=+/-, vers droite s1 l>0 ou gauche si l<0
void drawbarreH(int x, int y, int l) {
  myGLCD.fillRect(x,y-2,x+l,y+2);
}
// barre verticale long=+/-, vers droite s1 l>0 ou gauche si l<0
void drawbarreV(int x, int y, int l) {
  myGLCD.fillRect(x-2,y,x+2,y+l);
}

Maintenant interviennent le papier, le crayon et la calculatrice pour déterminer les paramêtres de ces fonctions appelées dans chaque aiguille et chaque zone.

C'est bien le but de ce dessin renseigné soigneusement :
(https://forum.locoduino.org/index.php?action=dlattach;topic=1045.0;attach=3318;image)
Titre: Re : Projet Dominique
Posté par: Dominique le août 18, 2020, 03:09:24 pm
A ce stade, je pense que chacun pourra adapter cette méthode à le représentation de son propre réseau.

J'aborderai plus tard l'intégration du TCO graphique avec le reste du gestionnaire.

A suivre...
Titre: Re : Projet Dominique
Posté par: Dominique le octobre 20, 2020, 07:26:16 pm
Un gros "petit détail" à respecter : le choix de la carte interface de votre écran à "décommenter" dans le fichier "HW_ARM_defines.h" du dossier "arm" comme le montre l'arborescence en image pour un Due et la carte CTE_DUE que j'utilise (CT50 avec un écran de 5 pouces).

Sinon, rien ne s'affiche sur l'écran et on peut croire qu'il est en panne.
Titre: Re : Projet Dominique
Posté par: Dominique le novembre 04, 2020, 10:18:04 pm
Voici un exemple de circulation sur mon réseau, dans son état actuel :
Pour rappel, j'ai décidé de matérialiser un TCO (un Arduino Mega) pour visualiser les occupations et positionner les aiguilles à la main , puis un module de commande des aiguilles (un Mega caché sous la plaque à coté de l'écran 5 pouces) puis une centrale (DCCpp bien-sur, encore un Mega) capable de piloter 12 trains avec des potars linéaires (j'adore), on les voit à droite sur la photo (le bloc noir) :

(https://www.locoduino.org/IMG/jpg/img_4291.jpg)

puis un module de programmation des locos (à gauche, pas aussi sophistiqué que ce que fait Denis, mais ça me suffit) et enfin un gestionnaire basé sur le gestionnaire en C++ de Pierre qui occupe un Due avec un écran 5 pouces tactile.
Il y a en plus des détecteurs de consommation, des détecteurs RFID pour déterminer les trains à coup sur et des détecteurs ponctuels pour les zones d'arrêt. Un jour il y aura des barrières infrarouges pour compter les wagons et vérifier que les trains n'en perdent pas (ça se passera dans les tunnels).

Tout ce petit monde est relié par un bus Can (c'est d'ailleurs le truc qui pose le moins de problème dans l'ensemble du réseau !).

L'écran 5 pouces fait donc partie du gestionnaire (le Due fait la gestion et l'affichage graphique) et permet de suivre son propre fonctionnement par les affichages et aussi de piloter les aiguilles (faire des itinéraires), de piloter les trains (suivre les trains, varier les vitesses, assurer la sécurité, les arrêts, les pannes, etc..).

Voici un exemple avec 4 trains stockés en gare cachée (sous le village) et un itinéraire pour un premier train qui démarre pour aller dans la gare visible devant le village (en faisant le grand tour) :

(https://www.locoduino.org/IMG/jpg/img_4303.jpg)

Puis un 2eme train avec un autre itinéraire (plus court, mais dans l'autre sens) pendant que le premier se déplace (il démarre) :

(https://www.locoduino.org/IMG/jpg/img_4304.jpg)

A noter : j'ai supprimé le segment noir qui est remplacé par rien sur les positions d'aiguilles non réalisées : on voit mieux le chemin à parcourir sur un petit écran.

Les boutons tactiles permettent à mon petit fils (4 ans) de faire marcher tout ça sans moi (enfin je reste pas loin, mais en cas de déraillement sous la montagne, c'est lui le meilleur !). De plus je garde le contrôle manuel à tout moment ! Tout n'est pas automatique. Les potars restent actifs en double commande.
Et je n'ai pas fini et je passerai à 7 pouces prochainement .
Titre: Re : Projet Dominique
Posté par: Dominique le novembre 30, 2020, 11:38:42 pm
Mise en oeuvre du type de détecteur décrit par Geoff Bunza sur le blog MRH :
SMA23 – A New DCC & DC Car & Loco Detector – Differential Absolute Position Detector (DAPD) (https://model-railroad-hobbyist.com/node/26133)

Il s'agit d'utiliser 2 capteurs de lumière (des photo-transistors pour la lumière naturelle). L'un est placé à coté de la voie (donc éclairé en permanence) et l'autre au milieu de la voie (donc occulté au passage d'un train). Un comparateur (un ampli OP) des sorties de ces 2 détecteurs va donc délivrer un signal binaire qui représente les états occulté et libre d'un point précis du réseau.

Jean-Luc m'avait confectionné quelques circuits imprimés que je décide de mettre en oeuvre sur mon réseau pour détecter les trains en bout de quai dans chaque gare devant les signaux.

Je viens de monter une paire de ces détecteurs sur mon réseau et je suis surpris par les performances et la simplicité de ces détecteurs qui ont moultes qualités :

Voià ce que ça donne sur 2 voies en bout de quai :

(https://www.locoduino.org/IMG/jpg/dpad1.jpg)

Et le schéma du détecteur double :

(https://www.locoduino.org/IMG/jpg/pd6l_schematic.jpg)

Le cicruit imprimé (4,8 x 2,2 cm). En voici 4 vus recto et verso :

(https://www.locoduino.org/IMG/jpg/img_4406s.jpg)

J'ai utilisé des photo-transistors PT 204-6C

(https://www.locoduino.org/IMG/jpg/2_photo_detectors_showing_flag-small__66987.1519752866.500.659.jpg)

et un double ampli op MCP6002, 5 résistances 10K et 2 résistances 4,7K.

Les photo-transistors sont enfoncés dans les trous préparés sous le plan de voie.

(https://www.locoduino.org/IMG/jpg/img_4385.jpg)

Les deux sorties, le +5V et le Gnd sont connectés sur les entrées détecteurs ponctuels d'un satellite V1.

J'ai ajouté la gestion de 2 signaux (carrés ou sémaphores) comme le décrit l'article La carte Satellite V1 (https://www.locoduino.org/spip.php?article239)

Et ce satellite envoie de beaux messages Can lorsqu'un engin passe au dessus du détecteur situé sur la voie : occupation ou libération, le sens Pair ou Impair qui situe ce détecteur dans une zone et le numéro de zone. Ces données sont exploitées par le gestionnaire pour arrêter le train en gare, exactement en bout de quai devant le signal carré ou stop.

Titre: Re : Projet Dominique
Posté par: DDEFF le décembre 01, 2020, 08:50:48 am
AAAAh !
Enfin un système simple et minuscule !

Denis  :P
Titre: Re : Projet Dominique
Posté par: Tony04 le décembre 01, 2020, 10:53:18 am
Qui vient malheureusement 1 mois trop tard pour mon projet sur lequel j'ai choisi cette solution (en PJ), car avec les barrières à réflexion il y avait trop de problèmes liés au soleil à certaines heures de la journée.
Mais je vais garder précieusement cette superbe idée.

Merci une fois encore Dominique.

Cordialement
Antoine
Titre: Re : Projet Dominique
Posté par: DDEFF le décembre 08, 2020, 09:31:35 am
Bonjour,

Je suis tombé sur une vidéo qui explique qu'une LED peut être utilisée comme ... phototransistor !

https://www.youtube.com/watch?v=ta7nxlY4Xek (https://www.youtube.com/watch?v=ta7nxlY4Xek)

Cela peut permettre d'utiliser une LED encore plus minuscule avec un montage hyper simple.
A mon avis, il est même un peu trop simple : il faudrait y ajouter une bascule de Schmidt pour avoir des  flancs bien raides.
Voire utiliser la platine de Jean-Luc, mais avec une LED SMD récupérée sur une guirlande.

Autre remarque : le module MB102 pour alimenter les breadbord en 5V ou 3.3V (7 € les 3)

https://www.amazon.fr/AZDelivery-Breadboard-Adaptateur-Dalimentation-Arduino/dp/B07VCB2JQT/ref=sr_1_5?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&dchild=1&keywords=mb102&qid=1607415926&sr=8-5 (https://www.amazon.fr/AZDelivery-Breadboard-Adaptateur-Dalimentation-Arduino/dp/B07VCB2JQT/ref=sr_1_5?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&dchild=1&keywords=mb102&qid=1607415926&sr=8-5)

Denis  :P
Titre: Re : Projet Dominique
Posté par: chris_bzg le décembre 08, 2020, 09:52:56 am
Bonjour Denis,

C'est juste ce qu'on appelle la réversibilité des phénomènes quantiques : si un photon peut être émis, il peut être absorbé !
Par exemple, c'est ce phénomène dans la transmission par onde radio : l'oscillation d'un circuit électrique produit une onde électromagnétique qui pourra faire osciller un autre circuit électrique à distance. Il y a bien d'autres exemples...
Mais comme tu le dis, il faut aussi examiner la fonction de transfert du composant en réversibilité : avec une LED, le signal obtenu n'aura pas les mêmes caractéristiques qu'avec un phototransistor, sinon pourquoi construire ces derniers? Néanmoins, on niveau d'une expérience amusante, ça peut le faire...

Bonne journée à toi.
Titre: Re : Projet Dominique
Posté par: msport le décembre 08, 2020, 10:29:11 am
Bonjour à tous,
si on cherche minuscule, avec 2mmx1,25mm on a ce qu'il faut :
https://www.ebay.fr/itm/0805-SMD-Infrared-Receiver-PT17-21C-L41-TR8-940nm-Phototransistor-10-20-50Pcs/153453116772

C'est un phototransistor infrarouge mais sa sensibilité est encore de 50% dans le visible.
Titre: Re : Réseau Dominique
Posté par: Dominique le août 18, 2021, 04:15:55 pm
Le gros morceau qui reste à faire (mais bien commencé quand même) est le gestionnaire des circulations qui assure le suivi des trains, la sécurité, les itinéraires, les horaires, etc..

Tout ça dans le contexte décrit depuis le début de ce sujet : toute une série de modules ou « sous-systèmes » indépendants qui communiquent entre eux par le bus Can. Évidemment sans PC, je préfère inventer mon propre système simple que d’utiliser une usine à gaz faite par d’autres (contribution aux réductions des gaz à effet de serre oblige  :D)

Ainsi je conçois un autre sous-système appelé « gestionnaire » qui peut :
- commander la centrale DCC++ (jusqu’à 12 trains qui peuvent être commandés manuellement avec les 12 potentiomètres d’écrits ci-avant)
- recevoir les commandes manuelles de la centrale DCC pour connaître a tout moment les comportements des trains
- recevoir toute la rétrosignalisation qui est installée sur le bus Can : les détections d’occupation de chaque zone, les détecteurs RFID et les détecteurs des points d’arrêt en gare, devant les signaux.
- commander les aiguilles lors de la création des itinéraires et lors des commandes manuelles à partir du premier TCO que j’avais fait, sur lequel s’affichent aussi les occupations de zones et les états des aiguilles.
- commander les signaux.

Et aussi s’interfacer avec d’autres tableaux de commande, voir un smart phone ou une petite tablette (en mode html) via une passerelle can-WiFi pour décupler les possibilités d’interface humaine.
Sans oublier les animations du décor, lumineuses et sonores.

Étant donné que la réalisation de mon réseau passe par les réalisations successives de ces sous-systèmes, toute la complexité est divisée en parties indépendantes qui communiquent par échanges de messages (voir les articles sur le bus Can). La première heureuse conséquence de cette architecture est que j’ai pu réaliser les différents morceaux sur plusieurs années sans me priver des joies de la conduite manuelle et sans remettre en cause les modules réalisés précédemment (sauf quelques ajustements logiciels quand nécessaire)

Même si la réalisation de mon réseau est personnelle, chacun pourra s’en inspirer pour l’adapter à son propre réseau.

Donc, comme c’est une période de vacances, loin de mon réseau, je commence donc à présenter ce gestionnaire dont il y aura plusieurs niveaux d’automaticité et de complexité. Mais, vacances obliges, les descriptions seront diffusées lentement, à commencer par les messages échangés entre les sous-systèmes.

Ce gestionnaire tourne, pour le moment sur un Due avec écran graphique 5 pouces décrit précédemment. L’écran graphique me permet de voir ce qui se passe sur le réseau, commander quelques scenarii via les boutons tactile.
L’ensemble du programme y compris la partie graphique n’occupe pas plus de 50% de la mémoire du Due. Donc pas d’urgence à changer de processeur  ???.
Le moniteur de l’IDE me permet de suivre toutes les actions de gestionnaire, puis, a posteriori, de comprendre les anomalies pour les corriger. J’ai ajouté sur le bus Can un sniffer pour enregistrer tout ce qui circule.
Et je joue au train !

A suivre…
Titre: Re : Réseau Dominique
Posté par: Dominique le décembre 01, 2021, 03:44:17 pm
Mais avant...

Juste pour le fun, une photo prise du haut d'un escabeau :

(https://forum.locoduino.org/index.php?action=dlattach;topic=290.0;attach=4320;image)
Titre: Re : Réseau Dominique
Posté par: DDEFF le décembre 01, 2021, 03:50:48 pm
Ouaou ! :D :D
Titre: Re : Réseau Dominique
Posté par: fcot2002 le décembre 02, 2021, 10:27:18 am
Salut

Nickel ! ! ! 8) 8) 8)
Titre: Re : Réseau Dominique
Posté par: Tony04 le décembre 02, 2021, 11:58:16 am
Bonjour Dominique,

quelle belle évolution quand on compare avec les premières images, et je ne parle même pas de la partie Arduino...  :)

Un grand bravo à toi

Amicalement
Antoine
Titre: Re : Réseau Dominique
Posté par: Dominique le décembre 02, 2021, 01:45:01 pm
Merci les Amis !
J’ai encore plein de choses à faire donc il y aura des suites  :P
Titre: Re : Réseau Dominique
Posté par: pascine le décembre 02, 2021, 06:31:46 pm
Superbe projet, quel travail, j'ai bien envie d'essayer d'en faire autant, mais il va me falloir reprendre des cours car je risque de sécher ;)
Titre: Re : Réseau Dominique
Posté par: savignyexpress le janvier 13, 2022, 08:18:58 pm
Bravo Dominique pour ce superbe réseau.

La structure et le décor sont superbes. Je suis aussi très impressionné par le TCO et l'alignement des autres modules de commande.

Je profite de te poser une question au sujet des moniteurs vidéo à droite. J'imagine que c'est pour surveiller la gare cachée. Quelle technologie as-tu employé ? webcam en USB ou un autre système ?

Bonne suite de réalisation.
Titre: Re : Réseau Dominique
Posté par: Dominique le janvier 14, 2022, 05:01:15 pm
Merci Marc-Henri pour ces compliments venant de ta part  ;D

Peut-être ne sont-ils pas si mérités que ça. Déjà la photo n’est pas très nette et j’ai encore beaucoup de choses à finir (le balaste, planter les arbres qui attendent dans leurs sachets, mettre l’éclairage dans les maisons et, entre autres, équiper les 2 extrémités du réseau) sans oublier le gestionnaire des circulations et de la signalisation.

Mais ça va venir.

En ce qui concerne les moniteurs vidéo, ce sont des écrans de visiophones que j’ai récupérés, avec les caméras. Tout fonctionne en Pal sous 12v. Des inverseurs permettront de choisir les points de vue grâce à plusieurs cameras.

Oui cela permettra de voir dans la gare cachée et sous la montagne où il y a un virage serré, afin de savoir exactement où intervenir en cas de problème : récupérer un wagon détaché avec une loco de dépannage ou passer la main dans l’espace prévu sous le réseau, ou suivre les trajets du train de nettoyage.

Je posterai une vidéo prochainement.
Titre: Re : Réseau Dominique
Posté par: Juan le avril 05, 2022, 12:52:22 pm
Bonjour Dominique,
Je suis très intéressé par votre  module de programmation des locos.
Pourriez-vous m'envoyer plus d'informations ? Pourrait-il être envoyé directement à mon courrier électronique ?
Merci beaucoup.
Juan.



Titre: Re : Réseau Dominique
Posté par: Dominique le avril 05, 2022, 01:21:37 pm
Oui, bien-sur.
Envoyez moi votre e-mail en MP.

Ce module a été fait il y a longtemps et je dois regarder s’il y a des mises à jour à faire.
Ça va prendre un peu de temps
Titre: Re : Réseau Dominique
Posté par: Juan le avril 05, 2022, 05:30:01 pm
Bonjour Dominique, je pense vous avoir envoyé un message à votre MP, mais je ne suis pas sûr de l'avoir fait correctement. Pouvez-vous confirmer si vous l'avez reçu ou non ?
Merci encore.
Titre: Re : Réseau Dominique
Posté par: christian maccaire le juin 14, 2022, 01:17:41 pm
Bonjour Dominique
Je suis a la recherche d'un spécialiste des boucles, j'ai conçu mon circuit un peu avec ce que j'avais sous main je me retrouve avec un passage surélevé et une boucle, je n'arrive pas a voir  l'entré et la sortie afin d'intégrer un module de gestion.
Ci joint la vue du circuit avec des repères numérique.
Titre: Re : Réseau Dominique
Posté par: jeanmi67 le juin 14, 2022, 01:41:21 pm
 
Bonjour,

A priori, un module de gestion de boucle (si c'est ça la question) avec les coupures de rails qui vont bien doivent être positionnés entre les aiguilles repérées 1 et 2.

Jean-Michel
Titre: Re : Réseau Dominique
Posté par: christian maccaire le juin 14, 2022, 08:43:17 pm
Merci de votre éclaircissement mais ne faut il pas inclure un aiguillage dans le dispositif avec une gestion des polarités sur cœur de l'aiguille
Titre: Re : Réseau Dominique
Posté par: jeanmi67 le juin 14, 2022, 10:44:13 pm
 
Bonsoir,

Pourquoi voulez-vous compliquer les choses ? La gestion de la polarité du cœur d'une aiguille est une chose, la gestion d'une boucle de retournement est autre chose. Un module de gestion de boucle de retournement détectera une éventuelle inversion de polarité (à l'intérieur de la boucle) quand il le faudra, c'est à dire lors du passage d'une circulation, indépendamment de la position des aiguilles menant à cette boucle, et donc indépendamment de la polarité du cœur de ces aiguilles.

Jean-Michel
Titre: Re : Réseau Dominique
Posté par: CATPLUS le juin 15, 2022, 05:48:32 am
Bonjour

https://rudysmodelrailway.wordpress.com/2018/11/01/reversing-loop-arduino-controlled/
Titre: Re : Réseau Dominique
Posté par: msport le juin 15, 2022, 10:18:06 am
Bonjour,

à votre place, je ferais les coupures (les deux rails) au niveau des flèches jaunes. Ou n'importe où entre ces deux points.
Cette partie du circuit doit être alimentée directement et c'est le reste du circuit qui est inversé par le module à détection de court-circuit.

Titre: Re : Réseau Dominique
Posté par: christian maccaire le juin 16, 2022, 07:39:21 pm
Bonjour
Je retiens le montage
rudysmodelrailway mais peut on utiliser sur un même arduino UNO le montage de boucle  plus mes commandes d'aiguillage le tout piloté par JMRI ?

Titre: Re : Réseau Christian Maccaire
Posté par: msport le juin 28, 2022, 04:02:54 pm
https://forum.locoduino.org/index.php?topic=1432.0
Titre: Re : Re : Réseau Dominique
Posté par: Dominique le janvier 25, 2023, 06:02:16 pm
Merci les Amis !
J’ai encore plein de choses à faire donc il y aura des suites  :P

Que le temps passe vite  ::)
Dans ce sujet, il n'est question que de mon réseau.
Merci de ne pas poser de questions relatives à votre propre projet, il est simple de créer votre propre sujet.

Bon, je vais essayer de décrire la partie GESTIONNAIRE du réseau : gros morceau qui se fera par petits bouts.

Mais auparavant il faut se rappeler l'architecture globale du réseau :
(http://www.locoduino.org/IMG/jpg/archi.018.jpg)

L'idée simple que j'ai suivi depuis le début et qui marche toujours est que tous les modules communiquent entre eux sur un bus CAN.

Ainsi, chaque module est développé indépendamment des autres et sa mise à jour éventuelle est aussi indépendante.
A cette époque je ne connaissais pas l'ESP32 et le wifi : je n'ai donc pas hésité. Toutefois le bus CAN est tellement pratique (il y en a dans toutes les voitures et les avions, ce n'est pas par Hazard). Je ne remercierai jamais assez Jean-Luc qui m'a présenté cette technologie.

Pour que tous ces modules échangent entre eux des messages qui signifient des événements sur le réseau, j'ai conçu un protocole personnel, le plus simple possible avec comme intention de ne rien changer pour éviter de modifier tous les modules en cas de changement. Ce qui ne m'empêche pas d'ajouter des nouveaux modules avec de nouveaux messages.

Je vais donc présenter les messages CAN qui concernent chaque module, ce qui permettra de comprendre facilement comment ça marche.

Petit rappel sur les messages CAN :
On relira si nécessaire l'article de base : Mise en oeuvre du Bus CAN entre modules Arduino (1)
https://www.locoduino.org/spip.php?article130 (https://www.locoduino.org/spip.php?article130)

Chaque message est contient un identifiant Id et jusqu'à 8 octets de données

L'identifiant Id est unique. Il définit qui l'envoie (un seul émetteur car pas de doublons qui se promènent sur le réseau) et ce qu'il contient (ce qui est décrit dans le protocole CAN exposé par la suite.
Les octets suivants contiennent les détails du message. Parfois je n'en utilise qu'un seul, parfois plus. C'est aussi décrit dans le protocole.

Titre: Re : Réseau Dominique
Posté par: Dominique le janvier 25, 2023, 06:30:38 pm
Le premier module que j'ai réalisé après la découpe des plans de voies et la pose des rails avec les coupures des zones et la pose des détecteurs d'occupation est le TCO "manuel" décrit ici :
https://forum.locoduino.org/index.php?topic=290.msg3405#msg3405 (https://forum.locoduino.org/index.php?topic=290.msg3405#msg3405)

Les messages CAN du TCO sont :
TCO capteurs :
- occupations et libérations des zones z0 à z39 (40 zones) : emis par le TCO
- Id = 0x10
 Les états d'occupation des zones sont émises vers le CENTRAL sur 1 octet avec l'Id = 10H :
 * ->  bit 7,6 -> 0,0 = libre ou 0,1 = occupe, bits 5..0 = numéro de zone 0.. MaxZONES (42)

- demande de commutation d’aiguilles par clé manuelle : emis par le TCO
- Id = 0x11

TCO actionneurs :
- affichage de la Led verte correspondant à la position d’aiguille réelle (indépendant de la clé)
- Id = 0x20
- Id = 0x21 (demande d'état de toutes les clefs)
* La demande d’états de toutes les clés par le central (MaxAIG réponses) Id=21H, Data=0xFF

Principe des commandes d’aiguilles
 TCO clefs -> Id=11H -> Central -> Id=40H -> Aiguilles cmde
 Aiguilles etat -> Id=12H -> Central -> Id=20H -> TCO
* ->  bit 7,6 -> 1,0 = dévié ou 1,1 = droit, bits 5..0 = numéro aiguille 0.. MaxAIG (20)
 * Les réponses d’états des aiguilles vers le central et le TCO Id = 20H:
 * ->  bit 7,6 -> 0,0 = dévié ou 0,1 = droit, bits 5..0 = numéro aiguille 0.. MaxAIG (20)


On comprend donc que :
- tous les détecteurs d'occupation par consommation de courant sont connectés au TCO (ce n'est pas ce qui réduit la masse de câbles sous le réseau). Chaque état détecteur est visualisé sur le TCO par une led rouge (allumée = occupé) installée dans le dessin du réseau.
- chaque aiguille est agrémentée d'une clé inverseur et 2 leds vertes qui représentent la position droite ou déviée de l'aiguille.
A ce stade da la réalisation, il fallait que je puisse commander les éléments du réseau manuellement.
(http://www.locoduino.org/IMG/png/montcoendur.png)

... à suivre.. ;)
Titre: Re : Réseau Dominique
Posté par: Dominique le janvier 25, 2023, 06:37:30 pm
Le deuxième module réalisé après le TCO est le module de commande des aiguilles décrit ici :
https://forum.locoduino.org/index.php?topic=290.msg3412#msg3412 (https://forum.locoduino.org/index.php?topic=290.msg3412#msg3412)

(http://www.locoduino.org/IMG/jpg/img58.jpg)

Il reçoit des ordres venant du TCO en commande manuelle (en fait la commande manuelle reste toujours possible quoi qu'il arrive ☺️) et aussi du gestionnaire quand il forme des itinéraires.
Après avoir reçu une commande il répond que le boulot est bien fait.

AIGUILLES (en tant que capteurs) : emis par module Aiguilles
- état aiguille après commande pour positionner la Led verte du TCO
- Id = 0x12
- confirmation d’enregistrement des états courant d’aiguille en EEPROM
- Id = 0x19
- AIGUILLES (en tant qu'actionneurs) : reçu par periphérique Aiguilles
- commandes d’aiguilles a0 à a18 (19 aiguilles)
- Id = 0x40
->  bit 7,6 -> 1,0 = devie ou 1,1 = droit, bits 5..0 = numero aiguille 0.. MaxAIG (20)
- réponse Id = 0x20 vers central ET TCO
*     COMMANDE RId = 40H          REPONSE TId = 12H et 20H               
 * 0xC0 à 0xD2 position DROIT     0x40 à 0x52
 * 0x80 à 0x92 position DEVIE      0x00 à 0x12
 * 0xD3 = deteleur T1              pas de reponse
 * 0x93 = deteleur T2              pas de reponse
- Id = 0x43
La demande d'etats de toutes les aiguilles par le central (MaxAIG reponses) RId =0x43
 * ->  bit 7,6 -> 1,1 , bits 5..0 = 1,1,1,1,1,1 (numero d'aiguille = 63 / 0x3F) = 0xFF
 * 0xFF = demande d'etat de toutes les aiguilles
- Id = 0x42
 * La  demande de sauvegarde des etats des aiguilles dans l'EEPROM avant extinction du reseau RId = 0x42
 * ->  bit 7,6 -> 1,0 , bits 5..0 = 1,1,1,1,1,1 (numero d'aiguille = 63 / 0x3F) = 0xBF
 * 0xBF = demande de sauvegarde EEPROM de l'etat de toutes les aiguilles
- Id = 0x19
 * La confirmation de mémorisation EEPROM vers le central TiD = 0x19
 * ->  bit 7,6 -> 0,0 , bits 5..0 = 1,1,1,1,1,1 (numero d'aiguille = 63 / 0x3F) = 0x3F
 * 0x3F = confirmation de sauvegarde EEPROM

Il peut donc revevoir des commandes venant du TCO mais aussi du gestionnaire.
Ce dernier en fait reçoit les commandes d'aiguilles du TCO et les retransmet au module de commande d'aiguilles selon le principe suivant :

(http://www.locoduino.org/local/cache-vignettes/L610xH425/genese4.1-9855b.png?1456854952)


Le TCO envoie une demande de commande d'aiguille avec l'ID 0x11. Si le gestionnaire accepte il transmet cette commande avec l'ID 0x40.

Quand le gestionnaire n'était pas là, le programme du module d'aiguille acceptait les commandes avec l'ID 0x11 pour un fonctionnement totalement manuel.
Titre: Re : Réseau Dominique
Posté par: Dominique le janvier 25, 2023, 07:18:17 pm
Naturellement vient ensuite le module de traction, il fallait bien jouer aux trains !

https://forum.locoduino.org/index.php?topic=290.msg4005#msg4005 (https://forum.locoduino.org/index.php?topic=290.msg4005#msg4005)
(http://www.locoduino.org/IMG/jpg/face_avant-traction.jpg)

et les messages CAN qui le concernent :

TRACTION
Adresses Id CAN = Reception (en tant qu'actionneurs, reçoit des ordres) :
* Id = 30H commande d'un train (Index, Vit, Dir, F0): 2 octets
data[0] = bits 0..5 = index loco
data[1] = bits 0..6 = vitesse, bit 7 = direction
* Id = 31H commande de lumière F0
data[0] = bits 7 = lumière
* Id = 32H arrêt d'urgence DCCet reprise
data[0] = 0x8F = arrêt urgence : stop DCC
data[0] = 0x0F = reprise : start DCC
* Id = 33H paramètres de configuration
* Id = 38H commande poste de conduite

Adresses Id CAN = Emission (en tant que capteurs, envoie des messages d'évènements) :
* Id = 13H envoi des paramètres de conduite (Index, Vit, Dir, F0) : 2 octets  (ni)
* Id = 14H envoi de F0 seul
* Id = 15H envoi état DCC on/off (0x80 = on; 0x00 = off)
data[0] = 0x80 ON
data[0] = 0c00 OFF
* Id = 16H envoi Intensité et CC (mA/16) et alarme CC (bit 7)
* Id = 17H consignes de vitesse V&V
* Id = 18H mesure de vitesse d'un train

Il y a là une plus grande richesse de messages CAN :
Au démarrage, après les initialisations de la centrale, celle-ci envoie un message de l'état DCC (Id 0x15), On en principe qui permet au gestionnaire de savoir qu'il est prêt à commander des trains.
A chaque changement manuel de la position d'un potentiomètre, la centrale envoie un message de conduite (Id 0x13) qui permet au gestionnaire de savoir quels trains sont en mouvement avec leurs paramètres de conduite (vitesse, direction, feux).
Il y a d'autres informations utiles qui sont envoyées comme la mesure de vitesse d'un train au passage dans les 2 zones prévues à cette effet et l'intensité de traction DCC fournie par le module de traction aux rails.

En échange, le module de traction doit obéir aux ordres tels que la commande d'un train (0x30 et 0x31), l'arrêt d'urgence (stop DCC) et la reprise (0x32), et des commandes de configuration (0x33) et de poste de conduite (0x38) pas encore utilisé  actuellement.

Il y a encore de la place pour ajouter d'autres commandes !

Titre: Re : Réseau Dominique
Posté par: Dominique le janvier 25, 2023, 07:56:44 pm
Pour compléter la batterie de capteurs nécessaires à la sécurité des trains, j'ai ajouté 4 détecteurs RFID/NFC et 8 points d'arrêt dans les 2 gares (2 pour chaque sens)

Les points d'arrêt sont décrits ici :
https://forum.locoduino.org/index.php?topic=290.msg12026#msg12026 (https://forum.locoduino.org/index.php?topic=290.msg12026#msg12026)

Et les capteurs RFID ici :
https://www.locoduino.org/spip.php?article271 (https://www.locoduino.org/spip.php?article271)

Pour les points d'arrêt, les messages CAN sont :
Point d’arrêt - satellite capteurs de positions et signaux carrés/sémaphore
- détections et libération d’un point d’arrêt : émis par le satellite
Id = 0x07 + N° de satellite = 0x09 et 0x0A (gare cachée) ou 0x07 et 0x08 (gare principale)
Les messages de détection ponctuelles sont envoyés vers le CENTRAL sur 1 octet de donnée avec l’Id :
-> data[0] :
bit 7 = 1 occ ou 0 libre;
bit 6 = sens Pair =1, ou sens Impair = 0;
bits 5..0 = numéro de zone
pas de répétition 500 ms

- Signaux :  reçu par le satellite
- 0x57 + Id sat = 0x57, 0x58, 0x59, 0x5A
data[0] :
bit 7 = signal 0 ou signal 1 et
bits 0..2 = signaux (0= VL =vert, 1 = A=jaune, 2 = S=rouge, >=3 = test = tous allumés)


Pour les capteurs RFID les messages CAN sont :
- RFID Capteurs : emis par le satellite RFID
- RFID OUEST : Id = 0x1A
messages 1 Can émis Id (court) = 0x1A (détecteur EST)
  octet 0 : bit 7 = 1, bit 6 = 0,            -> numéro de train = bits 0 à 5 (0 à 63) zone 25 (intérieur)
  octet 0 : but 7 = 0, bit 6 = 1             -> numéro de train = bits 0 à 5 (0 à 63) zone 16 (extérieur)
        octets 1..7 = nom du train

- RFID EST : Id = 0x1B
  messages 1 Can émis Id (court) = 0x1B (détecteur OUEST)
  octet 0 : bit 7 = 1, bit 6 = 0,            -> numéro de train = bits 0 à 5 (0 à 63) zone 30 (intérieur)
  octet 0 : bit 7 = 0, bit 6 = 1             -> numero de train = bits 0 à 5 (0 à 63) zone 11 (exterieur)
  octets 1..7 = nom du train

- ou message 2
  octet 0 : 0xC0 + index train créé ,  puis octets 1 à 7 = NUID

- ou message 3
  octet 0 : 0xFF              -> inconnu   (erreur, table pleine ou UID hors table)

- commandes RFID capteurs :
- commande 1 vers capteur RFID :  Id (long) = Id 0x1FFFFF60 (hex) + ID court (1A ou 1B) donc 0x1FFFFF7A (EST) ou 0x1FFFFF7B (OUEST)
  data[0] = 0x80 + index table des trains : demande numero train + NUID
  reponse = message 2
  - commande de changement de numéro de train
  data[0] = 0x10 demande de changement de numéro de train
  data[1] = prev numéro de train
  data[2] = nouveau numéro de train
pas de réponse mais sauvegarde eeprom
  - commande d’enregistrement EEPROM (après modifications)
data[0] = 0x20 sauvegarde eeprom
pas de réponse
Titre: Re : Réseau Dominique
Posté par: Dominique le janvier 25, 2023, 08:24:03 pm
Au total, un résumé des messages CAN utilisés dans mon réseau sont dans ce tableau :

(https://forum.locoduino.org/index.php?action=dlattach;topic=290.0;attach=5104;image)

On voit que le gestionnaire doit recevoir et traiter environ la moitié des messages qui circulent sur le bus et envoyer les autres 😅.

Ce qui donne un gros switch dans le programme :
//---------------  TRAITEMENT DES MESSAGES CAN  ----------------------//

void parseCANFrame(CAN_FRAME &frame) {
 
  switch (frame.id)  {

  //-------------- POINT D'ARRET EN GARE ---------------------//
  //--            pas de reponse à envoyer                  --//
 
  /*Detecteurs DAPD - data[0] :
    bit 7 = 1 occ ou 0 libre;
    bit 6 = coté Pair =1, ou coté Impair = 0; (il faudrait correspondre à : SENS_PAIR=0,SENS_IMPAIR=1)
    bits 5..0 = numéro de zone
    */
  //-------------------------------------------------// 
  case 0x07:        // detecteur DAPD gare principale sens impair : fin de z27 et z28 SENS IMPAIR avant z29
  case 0x08:        // detecteur DAPD gare principale sens pair: fin de z13 et z14 SENS PAIR avant z15
  case 0x09:        // detecteur DAPD gare cachée sens impair : fin de z22 et z23 SENS IMPAIR avant z24
  case 0x0A:        // detecteur DAPD gare cachée sens pair : fin de z4 et z5 SENS PAIR avant z6
  //-------------------------------------------------//
    //doPointDarret();
   
  break;

  //-------------------------------------------------//
  case 0x10:          // OCCUPATION ou LIBERATION de zone, venant de TCO
                      //  pas de réponse à envoyer
  //-------------------------------------------

  //doOccupationLiberation();
  break;    // sortie du case 0x10
       
   
  //-------------------------------------------------//
  case 0x11:          // demande clé aiguille venant de TCO
  //-------------------------------------------------//

    //doDemandeAiguille();
break;
   
  //-------------------------------------------------//
  case 0x12:          // réponse etat d'une aiguille, venant du controleur d'aiguilles
  //-------------------------------------------------//

    //doReponseAiguille();
    break;
   
  //-------------------------------------------------//
  case 0x13:          // message traction = curseur vitesse et direction d'une manette venant de la Traction
  //-------------------------------------------------//

    //doTraction();
    break;
   
  //-------------------------------------------------//
  case 0x14:          // lumiere d'une manette
  //-------------------------------------------------//

    //doLumiere();
    break;
   
  //-------------------------------------------------//
  case 0x15:          // DCC enabled de la traction
  //-------------------------------------------------//

    //doDCC();
    break;
   
  //-------------------------------------------------//
  case 0x16:         // intensité mA du booster DCC et alarme court-circuit éventuelle
  //-------------------------------------------------//
 
    //doCourant();
    break;
   
  //-------------------------------------------------//
  case 0x18:          // Mesure de vitesse d’un train
  //-------------------------------------------------//

    //doMesureVitesse();
    break;
   
  //-------------------------------------------------//
  case 0x19:          // confirmation d’enregistrement des états d’aiguilles en EEPROM
  //-------------------------------------------------//

    //doEnregistre();
    break;

  //-------------------------------------------------//
  case 0x1A:          // RFID EST gare cachée et gare principale
  case 0x1B:          // RFID OUEST gare principale
  case 0x1C:          // RFID ouest gare cachee - a faire
  //-------------------------------------------------//

    //doRFID();
    break;

  //-------------------------------------------------//
  case 0x1D:          // Evenement secteur (coupure d'alimentation)
  //-------------------------------------------------//

    //doSecteur();
    break;

  //-------------------------------------------------//
  case 0x1F:          // Alarme
  //-------------------------------------------------//

    //doAlarme();
    break;   

   //-------------------------------------------------//
  case 0x20:          // Reponse Aiguille -> TCO
  //-------------------------------------------------//

    //doReponseAiguille();
    break; 

  //-------------------------------------------------//
  default:
  //-------------------------------------------------//
    Serial.println();
    Serial.print(F("??? ID:"));
    Serial.print(frame.id, HEX);
    Serial.print(F(" Len: "));
    Serial.print(frame.length);
    Serial.print(F(" Data: 0x"));
    for (int count = 0; count < frame.length; count++) {
      Serial.print(frame.data.bytes[count], HEX);
      Serial.print(" ");
    }
  break;
       
  } // switch FrameId
} // fin parseCANFrame

Avant de rentrer dans les traitements du gestionnaire qui est presque totalement dédié au traitement des messages reçus, mais aussi à la présentation graphique du réseau et des évènements, il faut présenter de quoi est fait ce gestionnaire (pour le moment car il va évoluer au gré de mon temps disponible et des fonctions nouvelles que je vais ajouter).

Titre: Re : Réseau Dominique
Posté par: Dominique le février 18, 2023, 01:13:43 pm
Une liste des fichiers constituant le programme Gestionnaire permet de se faire une idée sur ce qu'il contient :


CAN_Gestionnaire_154DUE.ino
C'est le programme principal qui articule toutes les ressources situées dans les autres fichiers, la version (date et numéro), les appels de bibliothèques, les variables globales, les fonctions Setup et Loop.

Constantes.h
C'est l'ensemble des constantes communes à tous les modules

Can.ino
C'est l'ensemble des traitements des messages Can qui sont reçus par le gestionnaire (on en a parlé juste avant ce post), ainsi que les utilitaires de commande CAN des trains, accessoires et TCO.

ClassesDeBase.h
C'est la définition des classes de base des objets manipulés dans ce programma. C'est complètement inspiré des articles de Pierre59 (https://www.locoduino.org/spip.php?article154 et toute la série), avec de nombreuses additions propres à mon réseau.
On va y trouver les Aiguilles, les Zones, les Signaux, les Itinéraires ou enclenchements, les Trains. Environ 300 lignes.
Exemple :
class Zone {
protected:
   boolean etat=ZONE_LIBRE; // libre (false =0) ou occupé (true =1) // l'entrée dans une zone occupée provoque un arrêt immediat
   boolean r60 = false; // true = ralentir 60
   boolean r30 = false; // true = ralentir 30
   boolean wAig = false;
   byte trace=0; // pour tco   OPTION
public:
   int no; // le numero de la zone pour commander l'electronique ( TCO, relais, ... )
   char* nom; // le nom de la zone   OPTION
   byte type = LIBRE;  // etat de la zone  (LIBRE=0, RALENTI=1, OCCUPANT=2, ENTRANT=3)
   int circule; // sens de circulation
   Signal* signalPair; Signal* signalImpair; // les signaux eventuels de la zone   OPTION
   Train* train=NULL; // le train actuel dans la zone
   boolean zar=true; // indicateur de presence de zone d'arret pour detecteur ponctuel optique
protected:   
   //Zone(int n) { no=n; } // constructeur mini
   //Zone(char* nm,int t,int n) { nom=nm, type=t; no=n; } // constructeur maxi
   Zone(int n,Signal* sp,Signal* si,int s);  // constructeur utilisé
public:   
  boolean occupee() { return etat; }  // méthode de test
  boolean libre()   { return !etat; } // méthode de test
  boolean testr60() { return r60; }
  boolean testr30() { return r30; }
  boolean retwAig() { return wAig; } // test presence aiguille
  void setwAig()    { wAig = true; } // declare contenir une aiguille
  void ralentir() { if (!r60 && !r30) r60 = true; if (r60 && !r30) r30 = true; }
  void repartir() { if (r60) {r30 = false;} else {r60 = false;} }
  void ral30() { r30 = true;}
  void ral60() { r60 = true;}
 
   
  virtual Zone* suivantePaire()   { return NULL; } // la zone suivante paire (éventuellement vide)   OPTION
  virtual Zone* suivanteImpaire() { return NULL; } // la zone suivante impaire (éventuellement vide)   OPTION

  virtual void actions() {} // les actions spécifiques à faire en cas d'occupation
  virtual void desactions() {} // les actions spécifiques à faire en cas de libération

  void occuper(); // appelée par la rétrosignalisation lors de l'occupation d'une zone
  void liberer(); // appelée par la rétrosignalisation lors de la liberation d'une zone
  int longueur(); // pas utilisé pour l'instant
   
  void init(boolean e, boolean r3, boolean r6) {
    etat=e; r30=r3; r60=r6; type = 0; zar=false;
    if (etat) {
      type=2; // OCCUPANT
    } else {
      type=0; // LIBRE
    }
  }

Composants.h
Ces composants sont les objets réels hérités des classes de base, afin de leur ajouter les propriétés et les fonctions dont ils ont besoin pour décrire mon réseau. Cela occupe 800 lignes environ que je vais simplifier car je n'ai pas besoin de tous les objets (notamment les signaux dans les tunnels et la gare cachée ainsi qu'une grande partie des itinéraires).
Exemple :
// 2 liaison VV-Hor (pair/droite=3siPdroite, impair/gauche=1+aiguillePdevié=4)
class Z2 : public Zone {
public:
  Z2(int n,Signal* sp,Signal* si,int s):Zone(n,sp,si,s) {circule=s;}// constructeur
  virtual Zone* suivantePaire();
  virtual Zone* suivanteImpaire();
  //virtual void actions();
  //virtual void desactions();
};

Description.h
C'est là dedans que la topographie et les comportements du réseau se trouvent décrits sur 600 lignes : quelles zones sont reliées aux 3 cotés des aiguilles, dans quelles zones sont situées les aiguilles et les signaux, avec les propriétés très importantes : suivantePaire() et suivanteImpaire() comme pour les zones consécutives :
Zone* Z0::suivantePaire() { return z1; }
Zone* Z0::suivanteImpaire() { return z40; }
ou autour des aiguilles :
Zone* Z2::suivantePaire() { return z3; }
Zone* Z2::suivanteImpaire() { return selonAiguille(a15,z1,selonAiguille(a16,NULL,z4)); }
ou pour chaque signal :
Signal* C1::suivant() { return selonAiguille(a9,selonAiguille(a11,NULL,NULL), NULL); }
Signal* C1::precedent() { return selonAiguille(a1,selonAiguille(a0, crr2,NULL),NULL); }
our pour les itinéraires :
boolean X4a7::formable() { return libres(x4a7,x3a4,x4a3,x5a7) && libres(z6,z7,z39); }
void X4a7::former() { a16->directer(); a13->devier(); c7->ouvrir(); }
boolean X4a7::deformable() { return libres(z4,z6,z39); }
void X4a7::deformer() { c7->fermer(); }
void X4a7::tracer(boolean b) { trace(b,z4,z6,z39);z7->tracer(b); }

Evidemment il faut avoir préparé un dessin du réseau avec la représentation de tous les éléments comme celui-ci :
(https://www.locoduino.org/IMG/png/reseaudb.png)

Graphiques.h
C'est un gros morceau ! C'est la représentation graphique du réseau sur un écran graphite tactile :
(https://www.locoduino.org/IMG/jpg/img_8402.jpg)

La représentation des positions d'aiguilles doit correspondre aux leds vertes sur le TCO manuel (ce qui n'est pas le cas ici, mais bon...):
(https://www.locoduino.org/IMG/jpg/img_8403.jpg)

A l'image des objets réels ci-dessus, j'ai décrit les même objets en graphique, avec les classes de base, les classes réelles et lees propriétés particulières. Par exemple la zone avec ses fonctions de tracé "libre" ou "occupé" ou "itinéraire":
class GZone {
public:
   boolean etat=false; // libre (false) ou occupé (true)
   int no; // le numero de la zone pour commander l'electronique ( TCO, relais, ... )
   int na=-1; // le numéro de l'aiguille dans la zone, sinon -1
   int nb=-1; // le numéro de l'aiguille dans la zone, sinon -1

   GZone(int n) { no=n; } // constructeur mini
   GZone(int n,int a) { no=n; na=a; } // constructeur   OPTION
   GZone(int n,int a,int b) {no=n; na=a; nb=b;} // constructeur   OPTION
 
   void occuper() {etat=true;} // appelée par la rétrosignalisation lors de l'occupation d'une zone
   void liberer() {etat=false;} // appelée par la rétrosignalisation lors de la liberation d'une zone
   virtual void tracer(int c, boolean s); // TCO c= couleur K_ouvert ou K_iti ou Kocc selon etat, K_ferme selon !etat
};
ou l'exemple d'une aiguille :
class GA0: public GAiguille {
  public:
  GA0(int n,int z):GAiguille(n,z) { no=n; nz=z; } // constructeur utilisé
  void tracer(int c);   
};
void GA0::tracer(int c) {
  if (etat) {//droit
    myGLCD.setColor(K_ferme);
    geo.drawArc(90, 358, 38, 0, 45, 5);
    myGLCD.setColor(c);
    drawbarreH(90, 320, 27);
  }
  else {     // devié
    myGLCD.setColor(K_ferme);
    drawbarreH(90, 320, 27);
    myGLCD.setColor(c);
    geo.drawArc(90, 358, 38, 0, 45, 5);
  }
}
GAiguille* Ga0 = new GA0(0,26); // a0  est dans la zone z26

Pour déterminer toutes les coordonnées graphiques indiquées dans le code, un dessin détaillé a été nécessaire :

(https://forum.locoduino.org/index.php?action=dlattach;topic=1045.0;attach=3318;image)

On notera que la manipulation de tous les objets se fait à l'aide de tableaux de pointeurs construit à la création des objets, tout comme les objets réels.


Methodes.h
Cet onglet contient toutes les méthodes utilitaires des objets de base et réels.

Par exemple cette fonction "provenance" qui permet de savoir quelle est la zone située à l'arrière d'une zone selon le sens de déplacement, ce qui permettra de trouver le train qui s'y trouve afin d'assurer le suivi des trains au fur et à mesure de leur déplacement:
Zone* Zone::provenance() {
  Zone* zp;
  Zone* zi;
  if (circule==SENS_IMPAIR_SEUL) return suivantePaire();   // optimisation (si type)
  if (circule==SENS_PAIR_SEUL)   return suivanteImpaire(); // optimisation (si type)
  zp=suivantePaire();   if (zp!=NULL && zp->libre()) zp=NULL; // candidate si non nulle et occupee
  zi=suivanteImpaire(); if (zi!=NULL && zi->libre()) zi=NULL; // candidate si non nulle et occupee
  if ((zp!=NULL) && (zi==NULL)) return zp;
  if ((zp==NULL) && (zi!=NULL)) return zi;
  if ((zp==NULL) && (zi==NULL)) {erreur(202); return NULL;}  // pas de provenance
  if ((zp!=NULL) && (zi!=NULL)) { // recherche avec les trains sur zp et zi
    Train* tp;
    Train* ti;
    tp=zp->train; // a condition que tp != NULL
    ti=zi->train; // a condition que ti != NULL
    if ((tp==NULL) || (ti==NULL)) {return NULL;} // pas de provenance
    if (tp->sens==SENS_IMPAIR && ti->sens==SENS_IMPAIR) return zp;
    if (tp->sens==SENS_PAIR   && ti->sens==SENS_PAIR)   return zi;
    return(ambiguite()); // sinon deux candidates (ambiguite)     
   }
   return NULL;
}

Le principe est donc qu’un train qui entre dans une zone (détecté par son capteur) se trouvait dans la zone de provenance qui contient d’ailleurs la queue du train. C’est là qu’il faut récupérer le train pour marquer la zone suivante.

Signaux.h
Ce fichier contient les traitements des signaux, commandés à partir des autres objets.

Tactile.h
Cette partie regroupe les actions utilisateur sur l'écran tactile. Pour le moment la bibliothèque textile "UTouch" étant très insuffisante voire problématique, je me suis limité à 2 rangées de 12 boutons qui me permettent de sélectionner des itinéraires, commander des trains et lancer des tests pour debugger le programme. Par exemple pour visualiser le tracé d'un itinéraire avec une couleur différence des états libre et occupé.

Fonctions de mise au point
L'avantage de disposer du graphique dans le programme est la facilité de mise au point.
Bien entendu j'ai passé pas mal de temps à trouver les bonnes coordonnées graphiques de éléments d'objets constituant chaque objet pour qu'ils se raccordent parfaitement.

J'ai aussi utilisé intensivement le moniteur série pour afficher des informations sur le déroulement du programme ou sur ce qu'il a prévu de faire.

Par exemple, je peux demander à voir la suite de zones accessibles vers l'avant d'une zone de départ et aussi vers l'arrière, en fonction des positions des aiguilles qui sont sur son passage. Ces fonctions affichent en même temps l'état "libre" ou "occupé" ainsi que le numéro de trains occupant.

////////// TRACE AVANT ///////////////////////////////

void traceAvant(Zone* zd) {         // zone depart
  Zone* zt = NULL;                  // zone de travail
  Zone* zno = zd;                   // zone courante iterative
  Serial.println(); 
  for (int i=0; i<47; i++)  { // MaxZone = 47
    Serial.print(zno->no);Serial.print(zno->occupee()?"O":"L");Serial.print(zno->type);Serial.print(':');
    if (zno->occupee()) {Serial.print(zno->train->no);}       
    Serial.print('-');
    zt = zno->circule? zno->suivanteImpaire() : zno->suivantePaire(); //Paire 0, Impaire 1
    if ((zt==NULL)||(zt==zd)) {
      break;
    } else {
      zno = zt;
    }         
  }
}

////////// TRACE ARRIERE ///////////////////////////////

void traceArriere(Zone* zd) {         // zone depart
  Zone* zt = NULL;                  // zone de travail
  Zone* zno = zd;                   // zone courante iterative
  Serial.println(); 
  for (int i=0; i<47; i++)  { // MaxZone = 47
    Serial.print(zno->no);Serial.print(zno->occupee()?"O":"L");Serial.print(zno->type);Serial.print(':');
    if (zno->occupee()) {Serial.print(zno->train->no);}       
    Serial.print('-');
    zt = zno->provenance();
    if ((zt==NULL)||(zt==zd)) {
      break;
    } else {
      zno = zt;
    }         
  }
}

Mise au point (suite)

Une grande partie de la mise au point est faite avec l'analyse des messages CAN dans l'onglet CAN.
Les traitements de ces messages sont ponctués de "Serial.print" pour suivre le décodage des événements, donc pour suivre les trains et contrôler le bon déroulement du programme.

Par exemple cet extrait montre la progression du train N° 0 (BB20158) qui parcoure successivement les zones 8, 9, 10, 11 (avec détection RFID dans cette z11), puis 12, 13, 15 et 16 avec détection de ralenti à 30km/h, et une autre détection RFID :
z8 LIBRE  zp7 T0 OK  VOIE LIBRE DEVANT 
z9 LIBRE  zp8 T0 OK  VOIE LIBRE DEVANT 
VLoco 0>20
z10 LIBRE  zp9 T0 OK  VOIE LIBRE DEVANT 
z11 LIBRE  zp10 T0 OK  VOIE LIBRE DEVANT 
OUEST-ext z11 T0 (BB20158)  Train ok
z12 LIBRE  zp11 T0 OK  VOIE LIBRE DEVANT 
z13 LIBRE  zp12 T0 OK  VOIE LIBRE DEVANT 
z15 LIBRE  zp13 T0 OK  VOIE LIBRE DEVANT  0A30  0>30
z16 RALENTI  zp15 T0 OK  VOIE LIBRE DEVANT  0V34 0>34
EST-ext z16 T0 (BB20158)  Train ok
z5 RALENTI  zp17 train perdu
Train 0 detecte sur z5

Dans cette exemple le train 0 est perdu en arrivant sur z5 à cause d'une mauvaise détection sur z17. Une procédure de découverte est lancée et le train est retrouvé sur z5 (Magique ?).

C’est un peu plus compliqué à lire quand plusieurs trains circulent simultanément mais c’est intéressant de suivre ce qui se passe sur le réseau. Comme il peut y avoir de nombreuses anomalies, il faut donc développer du code pour les détecter et les corriger ! C’est la vie normale de tous les programmeurs et il faut aimer ça  :D

Vous me suivez ???

Titre: Re : Réseau Dominique
Posté par: Dominique le février 18, 2023, 05:18:34 pm
A ce stade de la présentation du gestionnaire, je ne vois pas l'interêt de diffuser le logiciel qui est, comme vous l'avez compris, très adapté à mon réseau donc inutilisable sur un autre réseau.

Par ailleurs, mon chantier est en perpétuel travaux et évolue chaque fois que je le peux.
Par exemple j'hésite encore dans la mise en oeuvre de la conduite des trains : manuelle ou automatique ou les deux sans doute. Mais priorité au manuel de toute façon.
La signalisation n'est pas encore branchée, donc probablement de la mise au point à faire.
Et puis encore l'animation du décor synchronisée avec les trains (passages à niveau, annonces en gare, etc..)
Le scripting de scenarii de conduite a été testée avec support sur carte SD. Je le garde pour un hiver prochain.

Mais si certains souhaitent comprendre et s'inspirer de parties qu'ils souhaiteraient réutiliser, je peux entrer dans les détails de ces parties. Il faudra donc me contacter pour cela en posant des questions ou en m'envoyant des MPs.