Réalisation de centrales DCC avec le logiciel libre DCC++ (2)

Mise en œuvre d’un contrôleur pour BaseStation en HTML.

. Par : bobyAndCo. URL : http://www.locoduino.org/spip.php?article185

Dans le premier article sur DCC++ nous avons décrit les entrailles du logiciel "BaseStation" qui tourne sur Arduino.

Ce logiciel est prévu pour recevoir des commandes sur son port série, soit en direct, soit en Ethernet.

Nous aborderons dans cet article le coté "Contrôleur" qui, justement, envoie les commandes à BaseStation.

Plutôt qu’une version "Processing" dont un mini-exemple est montré sur le Forum, voici une description d’un Contrôleur Ethernet en HTML   qui vous donnera plein d’idées !!!

Le projet.

Qui n’a jamais rêvé de pouvoir piloter son réseau au travers de sa tablette ou de son téléphone portable ou de pouvoir partager avec ses enfants ou ses amis. Avec DCC  ++ de Gregg E. Berman et l’application web que nous vous proposons, c’est quelque chose que vous avez « à portée de doigts ».

Avouez tout de même que réaliser son TCO sans potentiomètre ni bouton-poussoir, sans soudure ni résistance (car en plus on n’a jamais celle qui faudrait sous la main), ça a quand même quelques attraits. Sans parler bien sûr de l’aspect économique puisque votre TCO virtuel ne vous coûtera que le temps que vous passerez à le réaliser.

L’objectif de notre projet consiste à créer une interface graphique « riche » et esthétique qui va, en appuyant sur des boutons ou en déplaçant des potentiomètres virtuels, transmettre les commandes, par Ethernet, à DCC  ++ BaseStation pour que celui-ci les exécute et nous rende compte de la bonne exécution. Nous allons réaliser ce que l’on appelle dans le jargon du web une « One-Page », ce n’est ni plus ni moins qu’une application de pilotage de locomotives.

Parmi les nombreuses qualités de DCC++, il y a celle de la simplicité de communication. En gros, il vous suffit d’envoyer un texte structuré de ce type <t 1 3 120 1> pour que DCC++ BaseStation (c’est le nom de l’application DCC++) exécute votre souhait. Le message ci-dessus par exemple demande (en liaison série et dans le moniteur de l’IDE) d’exécuter un ordre de traction le «  t  » sur le registre « 1 », destiné à la locomotive ayant l’adresse « 3 », à la vitesse de « 120 » vers l’avant « 1 ». Pour une traction en sens inverse, le dernier chiffre aurait été « 0 ». Difficile de faire plus simple. Ainsi, avec la seule zone de saisie du moniteur de l’IDE de l’Arduino, il est possible de piloter ses locomotives !!!

Avec ce controller réalisé en HTML   nous voulions vous proposer le côté « pilotage » de DCC++ BaseStation en espérant qu’il soit aussi simple à mettre en œuvre pour vous que ne l’est DCC++ BaseStation.

Nous avons intégré l’application sur le même Arduino que celui qui supporte DCC++ ce qui constitue une économie.

Normalement, en moins de 10 à 15 minutes, vous devriez pouvoir installer cette application et piloter vos propres locomotives, à condition toutefois que vous ayez déjà installé DCC++ BaseStation. Je précise bien que ce controller complète BaseStation, il en est l’interface.

Voici une petite vidéo qui montre l’utilisation :

Le voici en vrai ! Allez y n’ayez pas peur, vous pouvez toucher. Sélectionnez une locomotive. Les boutons s’activent, les potentiomètres fonctionnent. Pour peu, il ne manquerait que de le raccorder à votre réseau.

De quoi a t’on besoin ?

L’application est développée en HTML   et Javascript (JQuery). Elle fonctionne sur n’importe quel navigateur, Chrome, Firefox, Internet Explorer ou encore Safari et sur toutes plateformes (PC, Mac OS, IOS, Androïd…)

Matériel :

- Un Arduino MEGA sur lequel vous avez installé le logiciel DCC++ BaseStation.
- Un shield Ethernet qu’il suffit de placer directement sur la carte pour qu’il soit alimenté et que les différentes liaisons soient réalisées. (*)
- Une carte micro SD (moins d’1 Mo est nécessaire).
- Une connexion internet. Le meilleur moyen étant de relier le shield   Ethernet de votre Arduino à votre box en RJ 45, soit en direct, soit au travers d’un switch ou d’un routeur.
- Un ordinateur ou une tablette ou un téléphone portable (ou les trois !) reliés en Ethernet ou en WIFI à votre box et pouvant donc communiquer avec DCC++Base Station.

Ne figure pas sur ce schéma le matériel nécessaire au fonctionnement de DCC++ BaseStation, en particulier le Motor Shield   (LMD18200, POLOLU MC33926, L298) et la protection le cas échéant (MAX471). Vous reporter à l’article de Dominique : Réalisation de centrales DCC avec le logiciel libre DCC++ (1)

(*) Un shield est vendu environ 40 € sur le site Arduino : https://store.arduino.cc/category/83, mais il est possible d’en trouver des versions « officielles » à 20 € environ ou à beaucoup moins et qui fonctionnent très bien (sur eBay par exemple).

MEGA 2560 avec DCCBaseStation + shield Ethernet + Polulu MC33926

Logiciel :

Voici une version complète de DCC++ BaseStation à télécharger que nous avons renommée DCCpp_Ethernet. La plupart des réglages ont été faits. Nous détaillerons les réglages un peu plus bas. Vous trouverez dans ce zip :

- L’ensemble des fichiers pour Arduino. Il vous suffit d’ouvrir DCCpp_Ethernet.ino pour ouvrir tous les autres.

- Un fichier HTML control.htm de l’application à recopier au premier niveau de la carte micro SD. Dans ce fichier, vous n’aurez qu’à mettre à jour votre adresse IP si elle est différente de 192.168.1.200.

- Un fichier JSON locos.jso pour entrer les propriétés de vos propres locomotives à recopier également au premier niveau de la carte micro SD.

Téléchargement DCCpp_Ethernet

- DCCpp_Ethernet utilise trois bibliothèques <Ethernet>, <SPI> et <SD> qui sont installées avec l’IDE Arduino.cc. Vous n’avez rien à faire pour cela.

Les réglages

Le premier réglage que vous avez à faire, c’est de mettre à jour la base de donnée de vos propres locomotives. Pour cela il faut ouvrir le fichier locos.jso. Pour vous aider, il existe des éditeurs en ligne : http://www.jsoneditoronline.org/ qui vont vous permettre de charger votre fichier depuis votre disque et qui vont vous signaler toutes les erreurs de syntaxe. A utiliser absolument si vous n’êtes pas familiarisé avec le Json

Chaque élément a cette syntaxe :

  1. {
  2. "id": 0,
  3. "nom": "231 G",
  4. "address": 7,
  5. "register": 2,
  6. "direction": 1,
  7. "speed": 0,
  8. "fn": [0, 0, 0, 0, 0],
  9. "fnName": ["Bruit exploit.", "Sifflet 1", "Fumée", "Vapeur cylindres", "Feux"],
  10. "volumeSon": 100,
  11. "volumeSonMax": 192
  12. },

Télécharger

Vous pouvez enlever des « blocs » ou en ajouter selon le nombre de vos locos. Un bloc est toujours entre { et } . Tous les blocs sont séparés par une « , » mais il n’y en a pas sur le dernier bloc.

Respectez la numérotation des id, le premier « 0 », le second « 1 » et ainsi de suite. Ne sautez pas de numéros.

address c’est l’adresse DCC de vos locos, il n’y a que vous qui les connaissiez.
register c’est le registre interne de DCC++ pour chaque loco. Vous pouvez reprendre le même que l’ id » ce qui peut éviter des problèmes. Ne perdez pas de vue que DCC++ est programmé par défaut pour 12 registres (locos). Si vous en avez plus, il faut modifier <Config.h> ligne 23

23.
#define MAX_MAIN_REGISTERS 12

fn: [0, 0, 0, 0, 0], c’est en binaire l’état à l’initialisation de vos fonctions (ici tout est désactivé). Attention, on pense souvent que la lumière est F0 mais c’est F4 en réalité.

fnName : c’est le nom de fonction comme vous souhaitez qu’il apparaisse dans le controller. Là encore, vous remarquez que le dernier élément du tableau (car c’est bien un tableau) n’a pas de « , » après lui. Si vous n’avez pas de fonction F3 par exemple, laissez un espace vide mais ne le supprimez pas car F4 passerait en F3.

volumeSon : 100, c’est le volume que vous aurez à l’initialisation.

volumeSonMax : C’est la valeur au delà de laquelle le potentiomètre ne pourra pas aller. C’est important de mettre la bonne valeur.

Recopiez controller.htm et locos.jso sur la micro SD.

Voilà pour les paramètres. Téléversez DCCpp_Ethernet sur votre MEGA. N’oubliez pas d’ouvrir le moniteur de l’IDE d’Arduino pour vérifier que l’adresse IP indiquée est bien 192.168.1.200 et que la présence de la carte dans le lecteur est confirmée.

Les cartes micro SD sont assez sensibles. En cas de problème sortez la carte et nettoyez les contacts. Si l’IP n’est pas la bonne ou en cas de carte non reconnue, un reset sur la carte est souvent la meilleure réponse. Il se peut que vous ayez besoin d’une autre adresse IP. Nous allons voir cela en détail un peu plus loin.

Allez dans votre navigateur et entrez dans la barre d’url : http://192.168.1.200:2560/. Ne vous impatientez pas, il peut falloir 10 à 20 secondes pour charger le controller dans la page. N’oublier pas que les Arduino ne sont pas des foudres de guerre. Vous le constaterez avec l’affichage très progressif. Une fois le controller affiché, les communications seront beaucoup plus rapides.

TRES IMPORTANT : Vous devez être relié à internet car l’application à besoin d’y charger des bibliothèques.

Concernant l’adresse IP que je vous ai proposée, 192.168.1.200, il y a toutes chances qu’elle fonctionne du premier coup si vous vous branchez sur votre réseau domestique. Si toutefois vous rencontriez des problèmes, je vous renvoie sur le Forum où j’ai écrit et qui traite cela en détail.

Si vous deviez modifier cette adresse IP, n’oubliez pas que vous devrez le faire dans :
Config.h ligne 42 :

42.
#define IP_ADDRESS { 192, 168, 1, 200 }

Et dans control.htm ligne 45

45.

Voilà pour une mise en route rapide de DCC++ Ethernet. Vous avez maintenant tout ce qu’il faut pour faire fonctionner votre controller.

Si vous souhaitez aller plus loin et faire le tour du code pour l’application DCC++ Ethernet, je vous invite à lire la suite, nous allons ouvrir le capot et regarder la mécanique.

DCC++ Ethernet en détail

Intéressons nous tout d’abord à la configuration de DCC++ pour fonctionner en Ethernet : DCC++ BaseStation (que nous avons renommé DCC++ Ethernet) comporte plusieurs fichiers différents. Reportez-vous au besoin à l’article écrit par Dominique sur DCC++ qui reprend tous ces aspects.

On commence par le fichier Config.h. Pour fonctionner avec Ethernet, vous devrez apporter les modifications signalées ligne 35 et ligne 42. Le réglage de port ligne 49 ne doit pas être modifié et la ligne 56 non plus.

1.
/**********************************************************************
2.
Config.h
3.
COPYRIGHT © 2013-2016 Gregg E. Berman
4.
Part of DCC++ BASE STATION for the Arduino
5.
 
6.
**********************************************************************/
7.
 
8.
/////////////////////////////////////////////////////////////////////////////////////
9.
//
10.
// DEFINE MOTOR_SHIELD_TYPE ACCORDING TO THE FOLLOWING TABLE :
11.
//
12.
// 0 = LMD18200 MOTOR SHIELD (MAX 28V/3A PER CHANNEL)
13.
// 1 = POLOLU MC33926 MOTOR SHIELD (MAX 28V/3A PER CHANNEL)
14.
 
15.
#define MOTOR_SHIELD_TYPE 1
16.
 
17.
/////////////////////////////////////////////////////////////////////////////////////
18.
//
19.
// DEFINE NUMBER OF MAIN TRACK REGISTER
20.
 
21.
#define MAX_MAIN_REGISTERS 12
22.
 
23.
/////////////////////////////////////////////////////////////////////////////////////
24.
//
25.
// DEFINE COMMUNICATIONS INTERFACE
26.
//
27.
// 0 = Built-in Serial Port
28.
// 1 = Arduino.cc Ethernet/SD-Card Shield
29.
// 2 = Arduino.org Ethernet/SD-Card Shield
30.
// 3 = Seeed Studio Ethernet/SD-Card Shield W5200
31.
 
32.
#define COMM_INTERFACE 1 // A modifier pour ethernet
33.
 
34.
/////////////////////////////////////////////////////////////////////////////////////
35.
//
36.
// DEFINE STATIC IP ADDRESS *OR* COMMENT OUT TO USE DHCP
37.
//
38.
 
39.
 
40.
#define IP_ADDRESS { 192, 168, 1, 200 } // A modifier pour ethernet
41.
//#define GATEWAY { 192, 168, 1, 254 }
42.
//#define SUBNET{ 255, 255, 255, 0 }
43.
 
44.
/////////////////////////////////////////////////////////////////////////////////////
45.
//
46.
// DEFINE PORT TO USE FOR ETHERNET COMMUNICATIONS INTERFACE
47.
//
48.
 
49.
#define ETHERNET_PORT 2560 // Nécessaire pour ethernet
50.
 
51.
/////////////////////////////////////////////////////////////////////////////////////
52.
//
53.
// DEFINE MAC ADDRESS ARRAY FOR ETHERNET COMMUNICATIONS INTERFACE
54.
//
55.
 
56.
#define MAC_ADDRESS { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF } // Nécessaire pour ethernet
57.
 
58.
/////////////////////////////////////////////////////////////////////////////////////

Pour le GATEWAY et le SUBNET ils ne nous seront pas utiles ici, je les ai gardés pour savoir que ça existe pour vos développements futurs. Pour le port ETHERNET_PORT, laissez 2560 (petit clin d’œil au MEGA). Cela donne l’ordre au programme « d’écouter » les connexions qui lui seront adressées sur son adresse IP mais seulement celles qui seront adressées sur le port 2560.

Nous verrons par la suite que vous devrez entrer dans votre navigateur l’url http://192.168.1.200:2560. Le port est séparé de l’IP par «  : ». Alors vous me direz sans doute que dans la vraie vie on n’ajoute pas le port. C’est vrai quand on a demandé au programme d’écouter sur le port 80 qui est par principe le port d’écoute d’un serveur web. Et que donc, quand vous n’ajoutez rien, la requête est adressée par défaut sur le port 80 du serveur.

Ensuite, voici la version modifiée de SerialCommand.cpp.

61.
#elif COMM_TYPE == 1
62.
EthernetClient client = INTERFACE.available() ;
63.
if (client) {
64.
client.println("HTTP/1.1 200 OK") ;
65.
client.println("Content-Type : text/html") ;
66.
client.println("Access-Control-Allow-Origin : *") ;
67.
client.println("Connection : close") ;
68.
client.println() ;
69.
 
70.
bool html = true ;
71.
while (client.connected() && client.available()) { // while there is data on the network
72.
c = client.read() ;
73.
if (c == ’<’) { // start of new command
74.
html = false ;
75.
sprintf(commandString, "") ;
76.
}
77.
else if (c == ’>’) parse(commandString) ; // end of new command
78.
else if (strlen(commandString) < MAX_COMMAND_LENGTH) // if comandString still has space, append character just read from network
79.
sprintf(commandString, "%s%c", commandString, c) ; // otherwise, character is ignored (but continue to look for ’<’ or ’>’)
80.
} // while
81.
if (html == true) {
82.
client.print("\n<div id=’prev’>Loading…</div>\n") ;
83.
sprintf(commandString, "%s", "D")
84.
parse(commandString) ;
85.
}
86.
client.stop() ;
87.
}
88.
 
89.
#endif
90.
 
91.
} // SerialCommand:process

Une précision concernant Access-Control-Allow-Origin: * qui mérite que l’on s’y attarde un peu car il peut être la source de bien des déboires. Pour simplifier, sachez que votre navigateur a interdiction de se connecter en Ajax (pour des raisons de sécurité) à des serveurs qui sont hors de son domaine. Sauf si le serveur les y autorise expressément avec justement le sésame miracle Access-Control-Allow-Origin: * (toutes origines est spécifié par l’ « * »). Cela aurait pu être Access-Control-Allow-Origin:192.255.2.1 qui n’autoriserait alors que cette seule adresse IP. Pensez toujours bien à cela car vous pouvez chercher bien longtemps pourquoi la réponse attendue ne vient pas.

En principe, la question du Access-Control ne devrait pas se poser ici. Cependant, j’ai constaté des erreurs signalées comme telles. Alors, puisque le Allow-Origin les a résolu et que nous sommes en Intranet et pas sur l’Internet, il n’y a pas de risque à laisser comme celà !!!

Toujours dans le fichier SerialCommand.cpp, nous avons ajouté quelques lignes pour traiter cette nouvelle commande. Pour ces modifications, nous nous sommes conformés à la syntaxe de DCC++. Ainsi, tout ce qui est appel au serveur web s’écrit avec la lettre D entourée des caractères < >. L’appel du fichier Json qui incrémente les valeurs pour les locomotives est par exemple appelé sur le serveur web par la commande : <D 3>.

97.
void SerialCommand: :parse(char *com) {
98.
switch (com[0]) {
99.
/***** SERVEUR WEB ****/
100.
 
101.
case ’D’ :
102.
ServWeb: :parse(com + 1) ;
103.
break ;

Enfin, pour la partie serveur web, nous avons créé deux nouveaux fichiers :

1° - ServWeb.cpp

1.
//
2.
// ServWeb.cpp
3.
//
4.
//
5.
// Created by Christophe on 22/11/2016.
6.
// Modified 25/11/2016 09:10:52
7.
//
8.
#include "ServWeb.h"
9.
#include "SerialCommand.h"
10.
#include "DCCpp_Ethernet.h"
11.
#include "Comm.h"
12.
#include <SD.h>
13.
 
14.
 
15.
void ServWeb: :sendFile(String const *file) {
16.
File dataFile = SD.open(*file) ; // Ouverture du fichier
17.
if (dataFile) {
18.
while (dataFile.available()) {
19.
INTERFACE.write(dataFile.read()) ;
20.
}
21.
}
22.
else {
23.
INTERFACE.print("No file found : ") ;
24.
INTERFACE.println(*file) ;
25.
}
26.
dataFile.close() ;
27.
}
28.
 
29.
void ServWeb: :parse(char *c) {
30.
int y ;
31.
String file ;
32.
 
33.
switch (sscanf(c, "%d", &y)) {
34.
case 1 : // avec parametre
35.
switch (y) {
36.
case 3 : // valeur du parametre == 3
37.
file = LOCOS_JSON ;
38.
break ;
39.
}
40.
break ;
41.
case -1 : // sans param : envoyer control.htm
42.
file = CONTROL_HTM ;
43.
break ;
44.
}
45.
sendFile( &file ) ;
46.
}

2° - Et le fichier ServWeb.h qui lui correspond.

1.
//
2.
// ServWeb.h
3.
//
4.
//
5.
// Created by Christophe on 22/11/2016.
6.
//
7.
//
8.
#include "Arduino.h"
9.
 
10.
#ifndef _ServWeb_h
11.
#define _ServWeb_h
12.
 
13.
 
14.
#define CONTROL_HTM "control.htm"
15.
#define LOCOS_JSON "locos.jso"
16.
//#define CONTROL_CSS "control.css  " // For future upgrade
17.
//#define CONTROL_JS "control.js" // For future upgrade
18.
 
19.
struct ServWeb{
20.
static void parse(char *c) ;
21.
static void sendFile( String const *) ;
22.
} ;
23.
 
24.
#endif

Le fichier DCCpp_Ethernet.ino a lui aussi reçu un certain nombre de modifications principalement liées au lecteur de carte micro SD.

Voilà pour DCCpp_Ethernet, le sketch Arduino pour la connexion, la lecture des fichiers HTML et Json sur la carte et l’envoi au navigateur !

Au moment du téléversement de votre sketch sur l’Arduino, je vous recommande d’ouvrir le moniteur de l’IDE comme je l’ai précisé plus haut.

Tout d’abord, vérifiez l’adresse IP du serveur. Si vous avez autre chose que ce que vous avez demandé, ça ne va pas marcher. J’ai vu des adresses en 255.123.1.199 s’afficher par exemple et qui n’ont rien à voir. C’est souvent le reset sur la carte directement qui est le meilleur remède. Une adresse 0.0.0.0 indique qu’il y a probablement un problème de câblage. Enfin, avant de vous mettre dans une fureur terrible, le moniteur peut vous indiquer « pas de carte trouvée » car vous avez tout simplement oublié de l’insérer.

Concernant les adresses IP que je vous ai proposé, il y a toutes chances qu’elles fonctionnent du premier coup si vous vous branchez sur votre réseau domestique. En cas de problème avec l’adresse IP, voyez plus haut dans l’article où je traite de cette question.

Le controller HTML pour DCC++ Ethernet

Le fichier locos.jso : Je ne reviens pas non plus sur le paramétrage du fichier Json qui a été vu plus haut. Si vous ne connaissez pas json, je vous invite à vous y intéresser pour vos échanges de données. Il repose sur un principe de représentation "key" : "value". Il existe une bibliothèque ArduinoJson à inclure dans vos sketches.

Le fichier control.htm : Un fichier HTML est un simple fichier texte qui peut s’ouvrir et se modifier avec tous les éditeurs de texte, Notepad sur PC par exemple. Mais si vous vous lancez vraiment dans le développement de pages web pour votre Arduino, il vous faudra quelque chose d’un peu plus pratique. Pour vous aider, cherchez sur Google « choisir un éditeur html » et vous allez avoir des réponses à foison. Beaucoup sont gratuits, d’autres pas. Pour débuter, il est sans doute mieux d’avoir un logiciel qui vous présente à la fois le code et le résultat sur la même fenêtre.

Il existe pour le HTML aussi des éditeurs gratuits. Je trouve https://html-online.com/editor/ très agréable à utiliser. Il a recours à un très puissant éditeur (TinyMCE) qui vous permet de saisir comme avec un traitement de texte et qui retranscrit en temps réel en HTML. Son seul inconvenient est qu’il n’active pas les bibliothèques de style, ce qui ne vous donne pas tout à fait votre rendu final. Mais pourquoi pas alors ébaucher votre projet dessus et finaliser sur des sites comme http://editor.livegap.com/

A n’en pas douter, vous saurez trouver un excellent outil « à votre main », intéressons nous alors au contenu de control.htm. En préalable, il faut savoir qu’une application développée en HTML regroupe en fait trois grands sous-ensembles.

  1. <!doctype html>
  2. <html><!--Début de HTML-->
  3.  
  4. <!--Balises entre les quelles on place le code pour le style-->
  5. <!--Balises entre les quelles on place le code JavaScript-->
  6. </head>
  7.  
  8. <!--Balises entre les quelles on place la partie visible de la page-->
  9. </body>
  10. </html><!--Fin de HTML-->

Le code HTML en lui même dont la partie affichée sur la page de votre navigateur va de <body> à </body>. En haut de la page, de <head> à </head> sont contenus les utilitaires en quelque sorte. Voilà le premier sous ensemble HTML avec sa partie visible de <body> à </body>. Le second sous ensemble est appelé CSS   (pour Cascading Style Sheets) et permet de définir du style pour votre site entre les balises <style></style>. Enfin, le troisième sous ensemble est <script></script> qui contient le JavaScript, le langage de programmation du web (avec quelques autres cependant) et sans lequel vous serez bien incapable de construire un application même très simple comme notre contrôleur. Rassurez vous, avec de l’attention et en suivant pas à pas ce qui va vous être dit, vous devriez vous en sortir sans trop de difficultés. D’autant que si vous savez programmer dans d’autres langages, tout ça se ressemble beaucoup.

Commençons par le haut de la page pour nous intéresser aux lignes 5 et 6.

Elles servent à charger sur internet et de manière transparente deux bibliothèques. Une pour JavaScript, JQuery et une autre pour le CSS, Bootstrap. Allez sur le site de bootstrap et plus particulièrement sur la page des Glyphicons http://getbootstrap.com/components/ pour vous rendre compte de toute la matière disponible au travers de ces bibliothèques pour rendre nos pages plus agréables. Pour JQuery, je ne vous montre rien car c’est trop austère mais sachez seulement que cela va grandement faciliter l’écriture de votre code à l’instar de ce que sont les bibliothèques pour Arduino.

Allons maintenant au <body> observer le HTML qui sert à l’affichage dans la page.

290.
< !—Début de la page affichée—>
291.
292.
<div id="container">
293.
<p>
294.
<button type=’button’ id=’power’ class="glyphicon glyphicon-off btn btn-default redColor" onClick="setPower()"> </button>
295.
Power </p>
296.
<p>
297.
<button type="button" class="glyphicon glyphicon-alert btn btn-default yellowColor" id=’e-Stop’ onClick="setThrottle(1, -1)"></button>
298.
e-Stop </p>
299.
<p>
300.
<label for=’log’>Logs</label>
301.
<input name=’’ type=’text’ id=’log’ value="" size="30" />
302.
</p>
303.
<p>
304.
<select id="locos" name="locos" onchange="checkLoco($(this).val()) ;">
305.
<option value="">— Locomotives —</option>
306.
</select>
307.
</p>
308.
<p>
309.
<label for=’address’>Adresse</label>
310.
<input name=’’ type=’text’ id=’address’ value="" size="" readOnly />
311.
</p>
312.
< !—<p>
313.
<label for=’register’>Registre</label>
314.
<input name=’’ type=’text’ id=’register’ value="" size="" readOnly />
315.
</p>—>
316.
<p> Sens
317.
<button type=’button’ id=’rearward’ class="glyphicon glyphicon-chevron-left btn btn-default" onClick="setDirection(0)"></button>
318.
<button type=’button’ id=’forward’ class="glyphicon glyphicon-chevron-right btn btn-default" onClick="setDirection(1)"></button>
319.
<input name=’’ type=’hidden’ id=’direction’ value="1" size="1" />
320.
</p>
321.
<p> Stop
322.
<button type=’button’ id=’stop’ class="glyphicon glyphicon-stop btn btn-default" onClick="setThrottle(1, 0)"></button>
323.
</p>
324.
<div class="row">
325.
<div class="col-md-2">Vitesse</div>
326.
<div class="col-md-8">
327.
<input id="speedSlider" type="range" min="0" max="126" value="" class="" onChange="setThrottle()" />
328.
</div>
329.
<div class="col-md-2">
330.
<input id="speed" type="text" readOnly value="" size="3" />
331.
</div>
332.
</div>
333.
<p>< !—La fonction "lumières" est toujours F4 bien qu’on la présente en premier—>
334.
<input type=’checkbox’ id=’F4’ name=’’ onClick="setFunction(0, 1, 2, 3, 4)" />
335.
<input type=’texte’ id=’fnName4’ name=’’ readonly />
336.
</p>
337.
<p>
338.
<input type=’checkbox’ id=’F0’ name=’’ onClick="setFunction(0, 1, 2, 3, 4)" />
339.
<input type=’texte’ id=’fnName0’ name=’’ readonly />
340.
</p>
341.
<p>
342.
<input type=’checkbox’ id=’F1’ name=’’ onClick="setFunction(0, 1, 2, 3, 4)" />
343.
<input type=’texte’ id=’fnName1’ name=’’ readonly />
344.
</p>
345.
<p>
346.
<input type=’checkbox’ id=’F2’ name=’’ onClick="setFunction(0, 1, 2, 3, 4)" />
347.
<input type=’texte’ id=’fnName2’ name=’’ readonly />
348.
</p>
349.
<p>
350.
<input type=’checkbox’ id=’F3’ name=’’ onClick="setFunction(0, 1, 2, 3, 4)" />
351.
<input type=’texte’ id=’fnName3’ name=’’ readonly />
352.
</p>
353.
< !—
354.
\tExemple de ce que peut être la suite des boutons de fonctions <p>
355.
<input type=’checkbox’ id=’F5’ name=’’ onClick="setFunction(5, 6, 7, 8)" />
356.
<input type=’texte’ id=’fnName5’ name=’’ readonly />
357.
</p>
358.
<p>
359.
<input type=’checkbox’ id=’F6’ name=’’ onClick="setFunction(5, 6, 7, 8)" />
360.
<input type=’texte’ id=’fnName6’ name=’’ readonly />
361.
</p>
362.
etc…
363.
—>
364.
<div class="row">
365.
<div class="col-md-2">Volume</div>
366.
<div class="col-md-8">
367.
<input id="volumeSlider" type="range" min="0" max="0" value="" class="" onChange="setCv(63, this.value)" />
368.
</div>
369.
<div class="col-md-2">
370.
<input id="volume" type="text" readOnly value="" size="3" />
371.
</div>
372.
 
373.
</div>
374.
< !—fin de div container—>
375.
</body>
376.
</html>

Vous n’aurez pas trop de mal à comprendre que les <button> indiquent des boutons surtout si votre éditeur permet la visualisation simultanée. Notre page contient en effet un certain nombre de boutons qui vont provoquer des actions. Nous voyons également des <input type="text"> Ce sont toutes les zones où s’affichent des informations ou là où l’on peut saisir du texte. Nous voyons aussi des <input> de type=checkbox qui sont bien sûr les cases à cocher. Enfin, vous avez également deux champs <input type='range'> qui sont les deux sliders (potentiomètres) pour le réglage de la vitesse et le volume du son.

Le style : le CSS

Retour vers le début du <body> ligne 261 pour détailler certaines choses. Regardons le bouton <button type=’button’ id=’power’ puis class= qui sert à définir son style et a son apparence : Glyphicon, la page sur laquelle je vous ai envoyé tout à l’heure et glyphicon-off qui est le nom du bouton avec cet aspect. btn et btn-default sont d’autres propriétés de style de bootstap qui en s’additionnant permettent d’arriver au résultat souhaité. Les différentes classes séparées par un espace «  » se cumulent. Nous avons donc pour l’instant sur ce seul bouton quatre « class » qui s’additionnent.

261.
<button type=’button’ id=’power’ class="glyphicon glyphicon-off btn btn-default redColor" onClick="setPower()"> </button>

Et puis, une class supplémentaire, redColor qui ne vient pas de bootstrap mais que j’ai écrite dans le haut dans les styles.

28.
.redColor {
29.
color : #F00 ;
30.
}

Car je peux bien sûr écrire mes propres styles et donc au bouton proposé par bootstrap qui est de couleur grise, auquel j’ai ajouté un style pour qu’il devienne rouge à l’arrêt et vert en marche. Les styles sont « surchargeables », c’est le sens de Cascading Style Sheets. Tout ce qui est écrit plus bas dans votre page prend le dessus sur ce qui est écrit avant. C’est la feuille de style de bootstrap qui est appelée avant que n’apparaissent mes propres styles.

J’ai écrit ici quelques styles pour personnaliser un peu plus ma page.

8.
9.
input, button, select {
10.
margin : 6px ;
11.
}
12.
#container {
13.
border : 1px dotted ;
14.
width : 380px ;
15.
padding : 20px ;
16.
margin-left : auto ;
17.
margin-right : auto ;
18.
margin-top : 20px ;
19.
height : auto ;
20.
}
21.
body, td, th {
22.
font-family : Verdana, Geneva, sans-serif ;
23.
}
24.
.yellowColor {
25.
color : #F90 ;
26.
}
27.
.redColor {
28.
color : #F00 ;
29.
}
30.
.greenColor {
31.
color : #090 ;
32.
}
33.
input[type=range] {
34.
cursor : pointer ;
35.
width : 80% ;
36.
}
37.
#log {
38.
color : #ff0000 ;
39.
}
40.

Pour info, les feuilles de style, même personnelles comme ici, devraient être placées dans des fichiers externes à côté de votre page mais je les ai laissé ici pour faciliter l’apprentissage. Il en est de même pour le JavaScript qui est aussi écrit à l’intérieur de la page HTML mais qui devrait être dans un fichier externe.

Revenons à notre bouton Power. Reconnaissez qu’il a de la « tête » avec peu d’efforts. Restons sur ce bouton pour observer dans le code ligne 261 id="power".

261.
<button type=’button’ id=’power’ class="glyphicon glyphicon-off btn btn-default redColor" onClick="setPower()"> </button>

L’ID est une notion capitale en HTML. C’est ce qui sert à identifier de manière unique et certaine un élément du DOM (Document Object Model) qui n’est ni plus ni moins que la structure hiérarchisée de notre page. Vous n’êtes pas obligé de donner un « id » à tous vos éléments de la page sauf pour ceux sur lesquels vous voudrez intervenir par programmation (ou pour modifier le style mais c’est aussi de la programmation). Nous avons utilisé le style pour illustrer cela et nous aurons l’occasion d’y revenir aussi largement avec JavaScript.

Complétons cette notion importante de l’ID avec un autre style appliqué cette fois au champ <input id='log'  /> ligne 268 :

267.
<label for=’log’>Logs</label>
268.
<input name=’’ type=’text’ id=’log’ value="" size="30" />

Le style lui est écrit ici :

37.
#log {
38.
color : #ff0000 ;
39.
}

Le # devant le log indique que je veux m’adresser à cet élément de la page par son id qui est ici log. Ce style ne s’applique qu’à ce seul élément log contrairement à la classe qui a un point "." devant et qui peut s’appliquer à plusieurs éléments du DOM.

27.
.redColor
28.
color : #F00 ;
29.

Pour votre information, #log est la zone où vont s’écrire les messages en retour de DCC++ et comme c’est important, j’ai choisi d’afficher le texte en rouge.

A contrario, vous voyez au dessus des définitions de style commençant par un « . » qui indique qu’il s’agit d’une class et que je peux l’appliquer à plusieurs éléments du DOM. Pour les styles, je vous invite à vous réfèrer aux nombreux sites traitant de la matière où tout ou presque est possible jusqu’aux limites du mauvais gout !

Pour le reste du HTML, pas grand chose de plus à dire que vous ne puissiez comprendre par vous-même.

290.
<button type=’button’ id=’stop’ class="glyphicon glyphicon-stop btn btn-default" onClick="setThrottle(1, 0)"></button>

Quand on met onClick sous un bouton on peut s’attendre à ce que ça veuille appeler une fonction quand on clique dessus. On a ensuite le nom de la fonction appelée onClick="setThrottle(1, 0)" et des paramètres à la fonction (ou pas), c’est selon les besoins.

Petit arrêt sur le script du slider volume en bas de la page onChange="setCv(63, this.value)" :

336.
<input id="volumeSlider" type="range" min="0" max="0" value="" class="" onChange="setCv(63, this.value)" />

Tout d’abord pour constater que le choix des événements déclencheurs peut être vaste : click, dblclick, change, input, keypress, mouseOver au passage de la souris etc.. etc… Mais aussi et surtout pour observer le second paramètre this.value bien connu des développeurs « objets ». C’est une manière de passer en paramètre la valeur du slider à l’appel de la fonction. this.value fait référence à la valeur de cet l’objet, ici le champ input ayant pour id volumeSlider.

Un dernier point concernant le HTML pour vous signaler deux choses :

1 – Les textes entre <!—    --> sont des textes commentés donc que l’on a pas voulu rendre visible.
2 - C’est justement le cas des champs :

321.
< !—
322.
Exemple de ce que peut être la suite des boutons de fonctions <p>
323.
<input type=’checkbox’ id=’F5’ name=’’ onClick="setFunction(5, 6, 7, 8)" />
324.
<input type=’texte’ id=’fnName5’ name=’’ readonly />
325.
</p>
326.
<p>
327.
<input type=’checkbox’ id=’F6’ name=’’ onClick="setFunction(5, 6, 7, 8)" />
328.
<input type=’texte’ id=’fnName6’ name=’’ readonly />
329.
</p>
330.
etc…
331.
—>

…que j’ai placé là pour vous donner ce que devrait être la syntaxe pour les autres boutons de toutes les autres fonctions gérées par DCC++ qui sont tout de même au nombre non négligeable de 28. Si vous suivez bien tout ce qui va être dit dans cet article, vous ne devriez avoir aucun mal à finir la programmation de ces fonctions.

Voilà donc pour le survol de HTML et CSS (style). Vous devriez être à même d’adapter cette manette à vos propres souhaits sans trop de difficultés et au besoin un peu de recherches complémentaires.

Le Javascript

C’est surement le plus gros morceau mais c’est aussi ce qui est le plus intéressant. Heureusement, comme je vous le disais, nous allons largement recourir à la plus puissante bibliothèque JavaScript ou tout au moins la plus populaire.

C’est comme avec Arduino où les bibliothèques nous simplifient considérablement le travail.

Voici tout d’abord l’ensemble du code javascript, nous allons ensuite voir cela pas à pas :

42.
<script>
43.
// Variables globales
44.
var urlDccpp = "http://192.168.1.200:2560/" // IP de votre Arduino contenant le sketch DCC++
45.
var locomotives = {} ; // Objet qui va contenir la description de nos locomotives
46.
var powerStatus = 0 ; // Alimentation électrique sur "off" au début
47.
var curId = null ; // "currentId" L’identifiant de la loco qui est sélectionnée lors de l’utilisation
48.
// dont la valeur est pour l’instant mise à "null"
49.
document.addEventListener(’DOMContentLoaded’, setup, false) ; // Ecouteur sur chargement de la page -> appele la fonction "setup" ci dessous
50.
 
51.
 
52.
function setup () { // Initialise au chargement de la page les éléments du DOM, cases à cocher, champs de saisie etc…
53.
$(’#prev’).html("") ; // Là où s’écrit le message de loading
54.
$(’#speedSlider’).val(0) ;
55.
$(’#speed’).val(0) ;
56.
$(’#direction’).val(1) ;
57.
$(’#address’).val(0) ;
58.
$(’#register’).val(0) ;
59.
 
60.
// Requete sur le fichier json des locomotives
61.
var param = "<D 3>" ;
62.
sendReq(setLocos, urlDccpp, param) ;
63.
 
64.
// Installation d’un écouteur sur le slider son
65.
document.getElementById(’volumeSlider’).addEventListener(’change’, setVolume, false) ;
66.
}
67.
 
68.
function setLocos (data) {
69.
locomotives = jQuery.parseJSON(data) ; // objet "locomotives" contenant les informations sur les locomotives.
70.
menuLoco(locomotives) ; // Création du menu déroulant
71.
}
72.
 
73.
function setVolume () { // Executé lorsque l’écouteur installé ci-dessus capte un événement
74.
$(’#volume’).val($(’#volumeSlider’).val()) ;
75.
locomotives[curId].volumeSon = $(’#volume’).val() ;
76.
}
77.
 
78.
menuLoco = function (locomotives) {
79.
// Pour chaque élément de l’objet "locomotives", création d’une ligne de menu
80.
for( var i = 0 ; i < locomotives.length ; i++) {
81.
$(’#locos’).append(’<option value="’+ locomotives[i].id +’">’+ locomotives[i].nom +’</option>’) ;
82.
}
83.
}
84.
 
85.
checkLoco = function (x) { // Choix dans le menu déroulant
86.
//-> initialisation d’un certain nombre de variables et de champs correspondants à la loco sélectionnée
87.
curId = locomotives[x].id ; // curId est l’identifiant unique de la loco sélectionnée
88.
$(’#address’).val(locomotives[x].address) ; // Affectation de leur valeurs respectives aux éléments du DOM
89.
$(’#register’).val(locomotives[x].register) ;
90.
$(’#direction’).val(locomotives[x].direction) ;
91.
$(’#speed’).val(locomotives[x].speed) ;
92.
$(’#speedSlider’).val(locomotives[x].speed) ;
93.
for(var i = 0 ; i < locomotives[x].fnName.length ; i++) {
94.
if(locomotives[x].fn[i] == 1) $(’#F’+[i]).prop("checked", true) ;
95.
else $(’#F’+[i]).prop("checked", false) ;
96.
$(’#fnName’+i).val(locomotives[x].fnName[i]) ;
97.
}
98.
$(’#volumeSlider’).attr("max", locomotives[x].volumeSonMax) ;// Mise à jour du niveau max volume pour la loco sélectionnée
99.
$(’#volumeSlider’).val(locomotives[x].volumeSon) ;
100.
setVolume()
101.
changeBtnDirection(locomotives[x].direction) // Fonction qui change la couleur des boutons de direction
102.
}
103.
 
104.
changeBtnDirection = function (sens) {
105.
if(sens == 0) {
106.
$(’#direction’).val(0) ;
107.
$( "#rearward" ).addClass( "greenColor" ) ;
108.
$( "#forward" ).removeClass( "greenColor" ) ;
109.
}
110.
else {
111.
$(’#direction’).val(1) ;
112.
$( "#forward" ).addClass( "greenColor" ) ;
113.
$( "#rearward" ).removeClass( "greenColor" ) ;
114.
}
115.
$( "#forward" ).blur() ;
116.
$( "#rearward" ).blur() ;
117.
}
118.
 
119.
 
120.
setDirection = function (sens) { // Quand on a cliqué sur l’un ou l’autre des boutons de direction
121.
changeBtnDirection(sens) ; // On change la couleur, fonction ci-dessus
122.
locomotives[curId].direction = sens ; // On met le sens à jour dans l’objet "locomotives"
123.
setThrottle() ; // … et on appele la fonction qui va envoyer l’information à DCC++
124.
}
125.
 
126.
setPower = function () { // On / Off de DCC++
127.
var param = ""
128.
powerStatus =  !powerStatus ; // On inverse l’état
129.
if(powerStatus) {
130.
$( "#power" ).addClass( "greenColor" ) ;// Selon l’état du bouton, on change sa couleur
131.
$( "#power" ).removeClass( "redColor" ) ;
132.
}
133.
else {
134.
$( "#power" ).addClass( "redColor" ) ;
135.
$( "#power" ).removeClass( "greenColor" ) ;
136.
}
137.
$( "#power" ).blur() ; // Desactivation du blur sur le bouton
138.
param = "<"+Number(powerStatus)+">" ; // Formatage spécifique du message pour DCC++
139.
sendReq(showResponse, urlDccpp, param) ; //… et on appele la fonction qui va envoyer l’information à DCC++
140.
}
141.
 
142.
setThrottle = function (direction, speed) { // Commande DCC++ pour la traction
143.
 
144.
if(locomotives[curId].address > 0 && locomotives[curId].register > 0) { // address et register sont != de 0
145.
if(direction  != undefined) locomotives[curId].direction = direction ;
146.
else locomotives[curId].direction = $(’#direction’).val() ;
147.
if(speed  != undefined) locomotives[curId].speed = speed ;
148.
else locomotives[curId].speed = $(’#speedSlider’).val() ;
149.
var register = locomotives[curId].register ;
150.
var address = locomotives[curId].address ;
151.
var speed = locomotives[curId].speed ;
152.
var direction = locomotives[curId].direction ;
153.
$(’#speed’).val(locomotives[curId].speed) ;
154.
$(’#speedSlider’).val(locomotives[curId].speed) ;
155.
var param = " ?<t "+register+" "+address+" "+speed+" "+direction+">" ;
156.
sendReq(showResponse, urlDccpp, param) ; //… et on appele la fonction qui va envoyer l’information à DCC++
157.
}
158.
else {
159.
alert("Vous devez sélectionner une locomotive !") ;
160.
$(’#speed’).val(0) ;
161.
$(’#speedSlider’).val(0) ;
162.
}
163.
}
164.
 
165.
setFunction = function (a) { // Commande DCC++ pour les fonctions
166.
 
167.
if(locomotives[curId].address > 0 && locomotives[curId].register > 0) { // address et register sont != de 0
168.
var res = 0 ;
169.
var param = "" ;
170.
var param = "<f " ; // Formatage spécifique du message pour DCC++
171.
param += locomotives[curId].address ; // On met l’adresse de la loco dans le message
172.
param += " " ;
173.
 
174.
for( var i = a ; i < arguments.length ; i++) {
175.
res += $(’#F’+[i]).prop( "checked" )  ? Math.pow(2, i)  : 0 ;
176.
locomotives[curId].fn[i] = $(’#F’+[i]).prop( "checked" )  ? 1  : 0 ;
177.
}
178.
switch (a) {
179.
// Tous les cas pour les 28 fonctions disponibles dans DCC++ sont ici programmées
180.
// même si dans cet exemple nous n’avons utilisé que 5 fonctions (F0 à F4)
181.
case 0 :
182.
param += 128 + res ;
183.
break ;
184.
case 5 :
185.
param += 176 + res ;
186.
break ;
187.
case 9 :
188.
param += 160 + res ;
189.
break ;
190.
case 13 :
191.
param += 222 ;
192.
param += " " ;
193.
param += res ;
194.
break ;
195.
case 21 :
196.
param += 223 ;
197.
param += " " ;
198.
param += res ;
199.
break ;
200.
}
201.
param += ">" ;
202.
sendReq(showResponse, urlDccpp, param) ; //… et on appele la fonction qui va envoyer l’information à DCC++
203.
}
204.
else alert("Vous devez sélectionner une locomotive !") ;
205.
}
206.
 
207.
setCv = function (cv, value) {
208.
if(locomotives[curId].address > 0 && locomotives[curId].register > 0) {
209.
var param = "" ;
210.
var param = "<w " ; // Formatage spécifique du message pour DCC++
211.
param += locomotives[curId].address ; // On met l’adresse de la loco dans le message
212.
param += " " ;
213.
param += cv ;
214.
param += " " ;
215.
param += value ;
216.
param += ">" ;
217.
sendReq(showResponse, urlDccpp, param) ; //… et on appele la fonction qui va envoyer l’information à DCC++
218.
}
219.
else alert("Vous devez sélectionner une locomotive !") ;
220.
} ;
221.
 
222.
sendReq = function (callback, url, param) {
223.
// C’est la fonction d’envoi des données à à DCC++
224.
// le premier paramétre "callback" sert à désigner la fonction qui sera appelée en cas de succès
225.
// Ici pour tous les envois, c’est la fonction "showResponse()" qui sert à afficher le callback de DCC++
226.
$.ajax({
227.
type : "POST",
228.
url : url,
229.
data : param,
230.
dataType : "text",
231.
success : function (data) {
232.
callback(data) ;
233.
}
234.
}) ;
235.
}
236.
 
237.
// Quand les paquets ont étés envoyés, fonction qui permet l’affichage du callback…
238.
showResponse = function (response) {
239.
if(response.indexOf("<") == 0) {
240.
$(’#log’).val(response) ;
241.
setTimeout(clearShowResponse, 2000) ; // … pendant 2 secondes
242.
}
243.
} ;
244.
 
245.
clearShowResponse = function () {
246.
$(’#log’).val("") ;
247.
} ;
248.
 
249.
</script>

Lignes 44, vous devrez entrer l’adresse IP de votre Arduino qui contient DCC++.

Lignes 45, on crée un objet JavaScript qui va contenir toutes les informations de nos locomotives et qui pour l’instant est vide.

Ligne 46, on initialise une variable globale qui contient l’état de l’alimentation à 0.

Ligne 487, cette variable est importante. Elle va contenir l’ID de la locomotive « active ». Au départ, on l’initialise à null. Pas à 0 car c’est l’ID d’une loco.

Ligne 51, j’ai placé ici un écouteur (après chargement du DOM) qui appelle la fonction setup(). Vous l’aurez compris, cette fonction initialise les champs de saisie et place les sliders à 0.

document.addEventListener('DOMContentLoaded', setup, false); // Ecouteur sur chargement de la page -> appele la fonction "setup" ci dessous :

54.
function setup () { // Initialise au chargement de la page les éléments du DOM, cases à cocher, champs de saisie etc…
55.
$(’#prev’).html("") ; // Là où s’écrit le message de loading
56.
$(’#speedSlider’).val(0) ;
57.
$(’#speed’).val(0) ;
58.
$(’#direction’).val(1) ;
59.
$(’#address’).val(0) ;
60.
$(’#register’).val(0) ;
61.
// Requete sur le fichier json des locomotives
62.
var param = "<D 3>" ;
63.
sendReq(setLocos, urlDccpp, param) ;
64.
// Installation d’un écouteur sur le slider son
65.
document.getElementById(’volumeSlider’).addEventListener(’change’, setVolume, false) ;
66.
}

Ligne 63 et 64 : Nous préparons ici l’appel du fichier locos.jso. Ligne 63, j’ai adopté avec "" le même principe de syntaxe que DCC++ pour rester en cohérence. Ligne 64, c’est l’envoi de la requête au serveur DCC++ Ethernet. Ligne 70, c’est la fonction qui est appelée lorsque DCC++ Ethernet nous a retourné le contenu du fichier Json. On copie ces informations dans l’objet locomotives avec une fonction jQuery dont le nom est assez explicite.

locomotives [] est un objet et un tableau contenant lui même 4 objets. Chaque objet contient un certain nombre de propriétés dont la valeur peut être unique ou un tableau. Id est unique alors que les valeurs de la propriété fnName sont contenues dans un tableau. Notez que les tableaux fnName par exemple n’ont pas besoin d’avoir le même nombre d’élément à l’intérieur pour chacune des locos. Certaines locos disposent de 5 fonctions quand d’autres en ont 15.

Pour accéder par exemple à l’adresse de l’autorail « XBD-5830 » nous écrirons : var testAddress = locomotives[1].address et nous aurons comme réponse le chiffre 4. Pour le nom de la 3° fonction pour la « 231 G » on écrira var testfnName = locomotives[0].fnName[2] et nous obtiendrons la réponse « Sifflet 2 tons ».

Vous aurez sans doute remarqué que je nomme à chaque fois ma variable var alors qu’elle a un contenu alphanumérique une fois et entier une autre fois. Javascript est un langage non typé. La toute nouvelle version 6 introduit le typage mais vous verrez on s’y fait très bien.

J’en profite pour « effleurer » un autre sujet et l’abandonner ensuite très vite. Contrairement à ce que ses détracteurs véhiculent, Javascript est un langage objet à part entière. J’aurai aussi pu mettre dans mon objet ci-dessus des fonctions. L’héritage en Javascript se fait par prototypage et quand on a bien compris ce mécanisme on n’a pas plus de difficultés qu’en C++ par exemple.

J’ai utilisé ici un initialisateur pour mon objet, les { } mais j’aurai aussi pu utiliser un constructeur (plus lourd).

  1. function Locomotives(id, nom, address,) {
  2. this.id = id;
  3. this.name = name;
  4. this.address = address;
  5. }
  6.  
  7. // Puis créations d'instances...
  8.  
  9. var locomotive[0] = new Locomotives(0, "231 G", 7, ...);
  10. var locomotive[1] = new....
  11. var locomotive[2] = new....

Cette fonction setLocos réalise aussi ligne 72 une action très intéressante. On appelle ici la fonction menuLoco() avec pour paramètre l’objet locomotive dont nous venons de parler. Elle va ajouter dynamiquement du HTML dans le DOM. 

Voici la fonction menuLoco :

80.
menuLoco = function (locomotives) {
81.
// Pour chaque élément de l’objet "locomotives", création d’une ligne de menu
82.
for( var i = 0 ; i < locomotives.length ; i++) {
83.
$(’#locos’).append(’<option value="’+ locomotives[i].id +’">’+ locomotives[i].nom +’</option>’) ;
84.
}
85.
}

On voit que la fonction contient une boucle for et va faire autant de tours qu’il y a de locomotives. Ainsi que vous en ayez 3 ou 10, votre menu sera toujours à jour au moment du setup. On voit ligne 83, $(’#locos’), c’est comme cela que JQuery identifie l’élément unique du DOM dont l’ID est #locos et sur lequel j’ai beaucoup insisté.

269.
<select id="locos" name="locos" onchange="checkLoco($(this).val()) ;">
270.
<option value="">— Locomotives —</option>
271.

$('#locos').append ajoute des lignes de HTML <option value= c’est du HTML, auquel on ajoute l’id de la loco contenue dans locomotives[i].id et auquel on ajoute aussi son nom locomotives[i].name.

Javascript est donc capable de fabriquer du contenu HTML. On peut par exemple créer des tableaux complexes simplement à partir d’un objet Json. Il peut même fabriquer des scripts malvaillants et c’est pourquoi je vous ai parlé de problèmes de sécurité avec Domain-Allow plus haut.

Retour à la fonction setup ligne 67.

66.
// Installation d’un écouteur sur le slider son
67.
document.getElementById(’volumeSlider’).addEventListener(’change’, setVolume, false) ;

Ici j’installe un écouteur sur l’élément du DOM qui a comme ID volumeSlider le potentiomètre de volume du son en utilisant la "manière non JQuery". En JavaScript pur, cibler un élément dans le DOM par son ID se fait avec document.getElementById()

Et je précise à l’écouteur que je ne m’intéresse qu’à l’événement change, c’est à dire si l’on change le volume. L’écouteur va alors appeler la fonction setVolume(). Pas très dur à comprendre j’imagine pour vous.

75.
function setVolume () { // Executé lorsque l’écouteur installé ci-dessus capte un événement
76.
$(’#volume’).val($(’#volumeSlider’).val()) ;
77.
locomotives[curId].volumeSon = $(’#volume’).val() ;
78.
}

Dans la fonction setVolume() intéressons nous tout d’abord à [curId] en index du tableau locomotives : locomotives.[curId].. curId est la variable que nous avions initialisée à null au tout début. C’est en sélectionnant une locomotive avec le menu que curId à pris pour valeur l’index de la locomotive sélectionnée. Et que donc la fonction setVolume() peut affecter à la bonne loco la valeur du son modifié.

locomotives[curId].volumeSon = $(’#volume’).val(); veut dire : mettre dans l’objet locomotives à l’index [curId] la propriété volumeSon à ce qui est retourné par l’élément du DOM ayant comme id #volume et dont la valeur est retournée par la fonction .val(). Si vous avez un peu de mal, n’hésitez pas à revenir en arrière et surtout concentrez vous sur la manière dont on manipule les objets.

Allez, voici encore des objets et je pense que ça va vous aider à comprendre encore mieux.

87.
checkLoco = function (x) { // Choix dans le menu déroulant
88.
//-> initialisation d’un certain nombre de variables et de champs correspondants à la loco sélectionnée
89.
curId = locomotives[x].id ; // curId est l’identifiant unique de la loco sélectionnée
90.
$(’#address’).val(locomotives[x].address) ; // Affectation de leur valeurs respectives aux éléments du DOM
91.
$(’#register’).val(locomotives[x].register) ;
92.
$(’#direction’).val(locomotives[x].direction) ;
93.
$(’#speed’).val(locomotives[x].speed) ;
94.
$(’#speedSlider’).val(locomotives[x].speed) ;
95.
for(var i = 0 ; i < locomotives[x].fnName.length ; i++) {
96.
if(locomotives[x].fn[i] == 1) $(’#F’+[i]).prop("checked", true) ;
97.
else $(’#F’+[i]).prop("checked", false) ;
98.
$(’#fnName’+i).val(locomotives[x].fnName[i]) ;
99.
}
100.
$(’#volumeSlider’).attr("max", locomotives[x].volumeSonMax) ;// Mise à jour du niveau max volume pour la loco sélectionnée
101.
$(’#volumeSlider’).val(locomotives[x].volumeSon) ;
102.
setVolume()
103.
changeBtnDirection(locomotives[x].direction) // Fonction qui change la couleur des boutons de direction
104.
}

Ligne 87, la fonction checkLoco(). C’est la fonction qui est appelée quand on sélectionne une ligne du menu. On en a déjà parlé dans le HTML de la fonction checkLoco($(this).val()) ligne 271 du DOM. Elle envoie en paramètre le numéro de ligne du menu sélectionné, valeur qui a pris maintenant pour nom x en paramètre de la fonction : checkLoco = function (x).

Avec l’index de la loco sélectionnée, on va pouvoir procéder à un certain nombre d’affectations de valeurs.

Ligne 89, on a vu que curId prend comme valeur l’id de la loco sélectionnée locomotives[x].id

Puis tous les éléments du DOM, $('#address'), $('#register'), $('#direction'), $('#speed'), $('#speedSlider') ce sont les champs que vous voyez se remplir avec les bonnes valeurs.

De la même manière pour les cases à cocher qui, dans une boucle, vont prendre les valeurs des fonctions de locomotives[x].fn[i]

Contenues dans l’objet locomotives, toutes les valeurs modifiées sont conservées et peuvent ainsi être rappelées à chaque changement de loco dans le menu.

106.
changeBtnDirection = function (sens) {
107.
if(sens == 0) {
108.
$(’#direction’).val(0) ;
109.
$( "#rearward" ).addClass( "greenColor" ) ;
110.
$( "#forward" ).removeClass( "greenColor" ) ;
111.
}
112.
else {
113.
$(’#direction’).val(1) ;
114.
$( "#forward" ).addClass( "greenColor" ) ;
115.
$( "#rearward" ).removeClass( "greenColor" ) ;
116.
}
117.
$( "#forward" ).blur() ;
118.
$( "#rearward" ).blur() ;
119.
}

Une autre petite fonction pour illustrer à nouveau la manipulation du DOM par programmation, la fonction changeBtnDirection() à la ligne 106 qui est appelée ligne 103 juste au dessus. Elle a pour paramètre la valeur de locomotives[x].direction

En fonction du sens, on va donner à l’élément du DOM $('#direction') la valeur 0 ou 1 et surtout on va ajouter ou enlever la class (le style) greenColor. Ce qui fait que la flèche du bouton prend ou perd la couleur verte. Le blur ligne 117 et 118, c’est ce qui garde la sélection. Je l’enlève car sinon ça empêche de voir la changement de couleur. On retrouve le même principe pour le bouton setPower ligne 128.

Voilà pour l’entrée, passons maintenant au plat de résistance ! Les fonction setThrottle(), setFunction(), setCv() et sendReq().

Commençons par la fonction setThrottle() qui envoie les ordres de tractions à DCC++

144.
setThrottle = function (direction, speed) { // Commande DCC++ pour la traction
145.
if(locomotives[curId].address > 0 && locomotives[curId].register > 0) { // address et register sont != de 0
146.
if(direction  != undefined) locomotives[curId].direction = direction ;
147.
else locomotives[curId].direction = $(’#direction’).val() ;
148.
if(speed  != undefined) locomotives[curId].speed = speed ;
149.
else locomotives[curId].speed = $(’#speedSlider’).val() ;
150.
var register = locomotives[curId].register ;
151.
var address = locomotives[curId].address ;
152.
var speed = locomotives[curId].speed ;
153.
var direction = locomotives[curId].direction ;
154.
$(’#speed’).val(locomotives[curId].speed) ;
155.
$(’#speedSlider’).val(locomotives[curId].speed) ;
156.
var param = " ?<t "+register+" "+address+" "+speed+" "+direction+">" ;
157.
sendReq(showResponse, urlDccpp, param) ; //… et on appele la fonction qui va envoyer l’information à DCC++
158.
}
159.
else {
160.
alert("Vous devez sélectionner une locomotive !") ;
161.
$(’#speed’).val(0) ;
162.
$(’#speedSlider’).val(0) ;
163.
}
164.
}

Ligne 144 on voit que la fonction reçoit deux paramètres. Dans le DOM, la fonction est appelée à plusieurs endroits, soit par le bouton STOP :

288.
<button type=’button’ id=’stop’ class="glyphicon glyphicon-stop btn btn-default" onClick="setThrottle(1, 0)"></button>

soit par le bouton e_Stop (arrêt d’urgence) :

262.
<button type="button" class="glyphicon glyphicon-alert yellowColor" id=’e-Stop’ onClick="setThrottle(1, -1)"></button>

soit bien sûr avec le slider de vitesse :

293.
<input id="speedSlider" type="range" min="0" max="127" value="" class="" onChange="setThrottle()" />

Dans les deux premiers cas, il y a bien deux paramètres passés à la fonction car nous savons à l’avance pour stop et pour e_stop que les valeurs sont 0 dans le premier cas, -1 dans l’autre. Mais pour le slider, ces valeurs sont retournées par l’écouteur dont je vous ai parlé, donc pas besoin de paramètres.

En JavaScript, la fonction peut être « construite » avec des paramètres sans que vous soyez obligé de les renseigner au niveau de l’appel. Nous récupérerons ces valeurs de paramètres dont nous avons besoin un peu plus loin dans la fonction.

A la ligne 146, on vérifie que la locomotive « courrante » a une adresse et un registre. Sinon cela veut dire qu’il n’y a pas de locomotive sélectionnée dans le menu.

A partir de la ligne 147, le code est en version compressée. Le voici en étendu pour plus de lisibilité :

  1. if(direction != undefined) {
  2. locomotives[curId].direction = direction;
  3. }
  4. else {
  5. locomotives[curId].direction = $('#direction').val();
  6. }
  7. if(speed != undefined) {
  8. locomotives[curId].speed = speed;
  9. }
  10. else
  11. {
  12. locomotives[curId].speed = $('#speedSlider').val();
  13. }

Cela nous dit que si la direction n’est pas undefined (donc qu’elle était en paramètre) alors l’objet locomotives[curId].direction prend pour valeur le paramètre, sinon, la valeur de l’élément dans le DOM. Idem pour la vitesse.

Maintenant que les valeurs de notre objet locomotives[curId] sont à jour, on place dans des variables les valeurs de l’objet :

151.
var register = locomotives[curId].register ;
152.
var address = locomotives[curId].address ;
158.
var speed = locomotives[curId].speed ;
154.
var direction = locomotives[curId].direction ;

Puis on met à jour les éléments du DOM $('#speed') et $('#speedSlider') :

155.
$(’#speed’).val(locomotives[curId].speed) ;
156.
$(’#speedSlider’).val(locomotives[curId].speed) ;

Enfin, ligne 157, on construit le message qui sera envoyé à DCC++ et on le met dans une variable param :

157.
var param = " ?<t "+register+" "+address+" "+speed+" "+direction+">" ;

Reportez vous au premier sujet pour la syntaxe des messages pour DCC++ ou sur le gitHub de DCC++

Enfin, on envoie le message à l’aide de la fonction sendReq() à laquelle sont passés trois paramètres. Nous verrons cela quand nous aborderons cette fonction.

158.
sendReq(showResponse, urlDccpp, param) ;

La fonction setFunction() sert à envoyer à DCC++ les fonctions sons ou lumières que l’on souhaite activer. Pour bien comprendre, il nous faut revenir au DOM avec par exemple la case à cocher qui sert à l’activation :

301.
<p>< !—La fonction "lumières" est toujours F4 bien qu’on la présente en premier—>
302.
<input type=’checkbox’ id=’F4’ name=’’ onClick="setFunction(0, 1, 2, 3, 4)" />
303.
<input type=’texte’ id=’fnName4’ name=’’ readonly />
304.
</p>

En cliquant sur la case à cocher, nous appelons la fonction setFunction() avec cinq paramètres mais qui sont des chiffres, des constantes ! (0, 1, 2, 3, 4). Ca va nous servir à identifier que l’appel vient d’un bouton faisant partie du premier groupe de fonctions qui est normalisé pour le DCC (en générale et pas seulement pour DCC++).

La fonction qui reçoit l’appel a 1 paramètre ! Alors me direz-vous, ça ne peut pas marcher. Eh bien si, cette façon de passer des paramètres n’est pas spécifique à JavaScript. On la retrouve aussi en C++ par exemple. Je définis le premier paramètre (a) que je vais recevoir car j’en ai besoin en dessous dans ma fonction et la fonction « va s’adapter ». Par exemple arguments.length utilisé dans la fonction va parfaitement correspondre au nombre de paramètres effectivement reçus et non à 1.

167.
setFunction = function (a) { // Commande DCC++ pour les fonctions
168.
if(locomotives[curId].address > 0 && locomotives[curId].register > 0) { // address et register sont != de 0
169.
var res = 0 ;
170.
var param = "" ;
171.
var param = "<f " ; // Formatage spécifique du message pour DCC++
172.
param += locomotives[curId].address ; // On met l’adresse de la loco dans le message
173.
param += " " ;
174.
for( var i = a ; i < arguments.length ; i++) {
175.
res += $(’#F’+[i]).prop( "checked" )  ? Math.pow(2, i)  : 0 ;
176.
locomotives[curId].fn[i] = res ;
177.
}
178.
switch (a) {
179.
// Tous les cas pour les 28 fonctions disponibles dans DCC++ sont ici programmés
180.
// même si dans cet exemple nous n’avons utilisé que cinq fonctions (F0 à F4)
181.
case 0 :
182.
param += 128 + res ;
183.
break ;
184.
case 5 :
185.
param += 176 + res ;
186.
break ;
187.
case 9 :
188.
param += 160 + res ;
189.
break ;
190.
case 13 :
191.
param += 222 ;
192.
param += " " ;
193.
param += res ;
194.
break ;
195.
case 21 :
196.
param += 223 ;
197.
param += " " ;
198.
param += res ;
199.
break ;
200.
}
201.
param += ">" ;
202.
sendReq(showResponse, urlDccpp, param) ; //… et on appele la fonction qui va envoyer l’information à DCC++
203.
}
204.
else alert("Vous devez sélectionner une locomotive !") ;
205.
}

Ligne 168 si address et registre == 0, c’est qu’aucune loco n’a été choisie dans le menu ce qui conduit au message d’alerte et fin de la fonction ligne 204.

De la ligne 170 à la ligne 177, on commence à construire le message pour DCC++.

Les lignes de 174 et 176 sont très intéressantes mais aussi je le reconnais assez difficiles à comprendre pour un non initié au premier abord.

174.
for( var i = a ; i < arguments.length ; i++) {
175.
res += $(’#F’+[i]).prop( "checked" )  ? Math.pow(2, i)  : 0 ;
176.
locomotives[curId].fn[i] = res ;
177.
}

Ligne 174, il s’agit d’une boucle qui s’arrêtera quand on aura atteint le nombre de paramètres reçus (arguments.length), comme nous venons de le voir juste avant.

Ligne 175, nous allons regarder cela pas à pas :
- la variable res que nous avons initialisée ligne 169. Il suffit de lui affecter la valeur "0" pour qu’elle prenne le type Entier. C’est elle qui va nous servir à construire la valeur finale qui sera envoyée à DCC++
- la variable param de type String. Ce sera le message envoyé à DCC++
- $('#F'+[i]) désigne un élément du DOM, nous le savons par $('# , puis F + le compteur de la boucle [i]. Ainsi, à chaque tour de la boucle, nous savons que nous "pointerons" sur $('#F0'), puis $('#F1') et ainsi de suite. En JQuery $('#F0') est un objet et cet objet nous permet d’accéder à des fonctions qui lui appartiennent. Nous avons déjà vu $('#F0').val() qui retourne la valeur contenue dans l’objet ou $('#F0').val(x) qui permet de passer la valeur x à l’objet. Ici nous utilisons la fonction prop() de l’objet qui retourne la propriété de l’objet. En fait ici, $('#F'+[i]).prop( "checked" ) cherche à savoir si l’objet a la propriété "checked", s’il est coché ou non. Comprenez cette formulation comme : if($('#F'+[i]).prop( "checked" )). La réponse est true ou false. Comme nous sommes en présence d’une fonction dite "ternaire" ou conditionnelle : si la condition est vraie, donc ici si la propriété de $('#F'+[i]) est "checked", alors on applique ce qui est juste derrière le " ?", sinon, on applique ce qui est derrière les " :" ici 0.

Mais nous n’avons à priori pas fini car que veut dire Math.pow(2, i) ? Math.power c’est la fonction "puissance" du premier argument de la fonction, avec le second.

Alors en entier maintenant : Si la case $('#F'+[i]) est cochée, alors mettre 2 à la puissance i et retourner cette valeur à res, sinon retourner 0 à res.

Math.pow(2, i) nous permet d’écrire en DECIMAL le bit de chaque fonction.
Si F0 est décoché, le bit est 0 sinon le bit est 1. Eh oui vous faisiez quoi pendant vos cours de math ? 2 puissance 0 = 1 !
pour F1 2^1 = 2
pour F2 2^2 = 4
ainsi de suite. A supposer que F0 + F1 + F2 soient activées, la somme est alors 1+2+4 = 7. Le binaire de 7 est 00000111, on retrouve bien nos trois fonctions cochée égales à 1 (les bits se lisants de droite à gauche) CQFD !

La ligne suivante met à jour la valeur fn de l’objet courant locomotive à cette valeur.

Le code suivant nécessite qu’on s’y attarde aussi un peu. Nous nous rappelons que les fonctions appelantes étaient de type setFunction(0, 1, 2, 3, 4) si elles étaient situées dans la série des fonctions de 0 à 5 ou setFunction(5, 6, 7, 8) pour les fonctions de 5 à 8 etc…

L’instruction switch() vous la connaissez, c’est la même que pour l’Arduino. Elle va prendre pour condition (a) le premier paramètre passé à la fonction. Et alors, je vous le donne en mille, si (a) = 0 nous sommes dans la première série de fonction, si (a) = 5 dans la deuxième série etc…

178.
switch (a) {
179.
// Tous les cas pour les 28 fonctions disponibles dans DCC++ sont ici programmées
180.
// même si dans cet exemple nous n’avons utilisé que 5 fonctions (F0 à F4)
181.
case 0 :
182.
param += 128 + res ;
183.
break ;
184.
case 5 :
185.
param += 176 + res ;
186.
break ;
187.
case 9 :
188.
param += 160 + res ;
189.
break ;
190.
case 13 :
191.
param += 222 ;
192.
param += " " ;
193.
param += res ;
194.
break ;
195.
case 21 :
196.
param += 223 ;
197.
param += " " ;
198.
param += res ;
199.
break ;
200.
}

Et donc le message qui avait été commencé à construire dans la variable param va continuer à se construire ici. Je n’entre pas dans le détail de cette construction que vous retrouverez sur le gitHub de DCC++

Et puis comme tout à l’heure, on appele la fonction sendReq() qui va envoyer le message.

202.
sendReq(showResponse, urlDccpp, param) ; //… et on appele la fonction qui va envoyer l’information à DCC++

J’ai tenu à placer dans ce controller le slider de réglage du son qui va vous donner l’exemple de code nécessaire à la modification de cv’s.

Ligne 334

334.
<input id="volumeSlider" type="range" min="0" max="0" value="" class="" onChange="setCv(63, this.value)" />

Il n’y a rien ici que nous n’ayons déjà abordé.

this qui fait référence à l’objet volumeSlider pour nous en renvoyer la valeur volumeSlider.value. La constante 63 en premier paramètre est tout simplement la valeur de la cv pour le son, du moins pour la plupart des décodeurs. Je n’ai pas fait dans la complication.

Cependant, notez min="0" et max="0". Outre le fait qu’il est bien sûr possible de paramétrer le mini du slider et son maxi, vous vous étonnerez peut être que ces valeurs soient à 0. Rappelez vous la fonction checkLoco() sur le menu avec ce lignes :

100.
$(’#volumeSlider’).attr("max", locomotives[x].volumeSonMax) ;// Mise à jour du niveau max volume pour la loco sélectionnée
101.
$(’#volumeSlider’).val(locomotives[x].volumeSon) ;

Pour chaque loco, on affecte à la valeur maxi ce qui a été entré dans le fichier JSon des paramètres de locomotives.

Concernant la fonction setCV() aussi vous commencez à être familiarisé. Je ne m’y attarderai donc pas.

207.
setCv = function (cv, value) {
208.
if(locomotives[curId].address > 0 && locomotives[curId].register > 0) {
209.
var param = "" ;
210.
var param = "<w " ; // Formatage spécifique du message pour DCC++
211.
param += locomotives[curId].address ; // On met l’adresse de la loco dans le message
212.
param += " " ;
213.
param += cv ;
214.
param += " " ;
215.
param += value ;
216.
param += ">" ;
217.
sendReq(showResponse, urlDccpp, param) ; //… et on appele la fonction qui va envoyer l’information à DCC++
218.
}
219.
else alert("Vous devez sélectionner une locomotive !") ;
220.
} ;

Maintenant, avec la fonction sendReq(), nous arrivons à la fin des difficultés et à la fin aussi de notre exposé. C’est elle qui permet l’envoi, via Ethernet, à DCC++ des instructions. On l’a rencontrée à plusieurs reprises.

217.
sendReq(showResponse, urlDccpp, param) ; //… et on appele la fonction qui va envoyer l’information à DCC++

Petit mais costaud !

222.
sendReq = function (callback, url, param) {
223.
// C’est la fonction d’envoi des données à à DCC++
224.
// le premier paramétre "callback" sert à désigner la fonction qui sera appelée en cas de succès
225.
// Ici pour tous les envois, c’est la fonction "showResponse()" qui sert à afficher le callback de DCC++
226.
$.ajax({
227.
type : "POST",
228.
url : url,
229.
data : param,
230.
dataType : "text",
231.
success : function (data) {
232.
callback(data) ;
233.
}
234.
}) ;
235.
}

J’ai intitulé ce passage « petit mais costaud » car nous avons avec l’objet $.ajax probablement ce qui se fait de mieux en matière de bibliothèque et de concision. Une bibliothèque, vous savez, c’est une sorte de boite noire avec des lignes et des lignes de code à l’intérieur, des trucs très complexes qui s’appellent les uns les autres. Mais qui, pour l’utilisateur, sont finalement très simples à utiliser. DCC++ en est une démonstration qui d’ailleurs, plus qu’une bibliothèque, est selon moi une application à part entière.

Eh bien c’est pareil pour $.ajax. Imaginez qu’il y a dedans l’essentiel de la technique de ce qu’on appelle le Web 2.0. Voir la page Wikipedia

$.ajax contient XMLHttpRequest qui était déjà une grande avancée de programmation, mais les développeurs de JQuery ont encore concentré cet objet dans ces six caractères « $.ajax »

La suite de la fonction, ce ne sont que les paramètres. On précise que la méthode pour passer les paramètres sera POST, l’url qui est ici est celle de notre MEGA avec DCC++ (Ligne 46 : var urlDccpp = "http://192.168.1.200:2560/"), les datas à envoyer (les messages construits dans les fonctions respectives) le type de datas. $.ajax nous offre même en prime un callback "on succes" si tout c’est bien passé, rappelez vous OK 200. Le premier paramètre passé à la fonction sendReq, c’est la fonction que l’on veut executer quand tout c’est bien passé. En l’occurence ici, la fonction showResponse() qui va afficher dans le champ #log le message renvoyé par DCC++.

Ces messages sont de syntaxe :

"<" = Begin DCC++ command
"T" = (upper case T) DCC++ Cab command was sent from DCC++ BaseStation
"1" = register 1 was changed
"20" = set to speed 20
"1" = forward direction
">" = End DCC++ command

Vous les retrouvez sur la page déjà citée du gitHub de DCC++

En conclusion :

1 - Ethernet est selon moi une technologie d’échange d’information qui a toute sa place sur nos réseaux ferroviaires.

2 – Cela nous donne accès à des outils de développements riches comme le HTML 5 et le CSS en particulier pour ce qui est des interfaces.

3 – JavaScript qui est le langage de programmation pour le web est un langage puissant et très complet assez proche dans sa syntaxe de langages comme le C ou le C++. Les bibliothèques comme JQuery simplifient grandement les développements surtout pour les débutants.

4 – JavaScript est sans doute le langage qui bénéficie des plus gros investissements en R&D au monde. Pour s’en convaincre, il n’y a qu’a s’intéresser à ce que Google a fait avec Angular JS et de ce que fait Microsoft avec TypeScript. Regardez ce qu’est le Data Binding avec Angular JS, un régal.

Enfin, pour vous montrer « modestement » ce qu’il est possible de faire, j’avais déjà présenté il y a quelques mois une application plus complète « tournant » avec DCC++ et réalisée avec Angular JS que vous pouvez voir ici :

N’hésitez pas à me poser vos questions auxquelles j’essayerai de répondre au mieux.