LOCODUINO

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

Titre: Projet 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: Marc-Henri 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: Marc-Henri 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: Marc-Henri 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: Marc-Henri 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 ...