Mise en oeuvre réseau du panneau lumineux

Il est conseillé de lire ou de s’aider de ce document et de ses cours réseaux avant de poursuive.

Présentation

Le panneau lumineux est un afficheur extérieur à Leds 16x96 (Ambre, pitch 16). Il a été facturé 2550 euros HT par la société DVI SIGNS.

Site : www.dvisigns.com

Les documentations sur ce panneau lumineux sont ici : doc/panneau-lumineux/

Lire le document sur l'installation du panneau lumineux et ses outils. 
Vous devez avoir noté son adresse MAC pour continuer.

Lire le document sur l’installation de l’afficheur lumineux LED Moving Sign.

Le panneau lumineux possède une liaison Ethernet via un module LTRX XPort de Lantronix. Ce module réalise un pont RS232-RS485/Ethernet.

Ici, l'adresse MAC du module LTRX XPort du panneau lumineux est 00:20:4A:BA:01:C7.

Identifications des paramètres réseau sous Linux

Pour cette partie, vous allez avoir besoin d'un analyseur de protocoles.
Lancer wireshark en mode root.
$ gksudo wireshark &

Ou :

Vérifier si l’adresse MAC du panneau est présente dans votre cache ARP :

$ arp -v
Address                  HWtype  HWaddress           Flags Mask            Iface

Démarrer une capture wireshark sur votre interface réseau et filtrer les trames qui contiennent seulement l’adresse MAC du panneau :

  1. Identifier l’adresse IP. Vous devez observer des paquets ARP émis périodiquement toutes les 180 s par le panneau :
Ici, l'adresse IP du panneau est 192.168.52.250. 

Remarque : il est aussi possible d’activer un ping en broadcast avec l’option -b.

Si vous devez modifier cette adresse : consulter le document Assigner une adresse IP au panneau lumineux.

  1. Tester la communication IP vers le panneau lumineux :
$ ping -c 1 192.168.52.250
PING 192.168.52.250 (192.168.52.250) 56(84) bytes of data.
64 bytes from 192.168.52.250: icmp_req=1 ttl=64 time=0.399 ms

--- 192.168.52.250 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.399/0.399/0.399/0.000 ms
  1. Identifier le port de communication. Scanner les ports ouverts sur le panneau lumineux :
$ nmap -A -T4 192.168.52.250

Starting Nmap 5.21 ( http://nmap.org ) at 2015-11-20 16:05 CET
Nmap scan report for 192.168.52.250
Host is up (0.0044s latency).
Not shown: 996 closed ports
PORT      STATE SERVICE VERSION
80/tcp    open  http    Lantronix XPort embedded ethernet http config
|_html-title: Site doesn't have a title (text/html).
9999/tcp  open  telnet  Lantronix XPort telnetd 6.6.0.2 080926 (MAC 00204ABA01C7)
10001/tcp open  unknown
30718/tcp open  unknown

Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 43.78 seconds

Il y a 4 ports ouverts, le panneau lumineux exécute donc 4 serveurs :

  • un serveur web (http) sur le port 80 pour la configuration
  • un serveur telnet sur le port 9999 pour la configuration
  • un serveur sur le port 10001 pour piloter l’affichage
  • un serveur sur le port 30718 utilisé lors de l’assignation de l’adresse IP
Ici, le port de communication TCP à joindre est 10001. 

Analyse de protocoles sous Windows

Pour cette partie, vous allez avoir besoin d'un analyseur de protocoles.
Vérifier si wireshark est installé, sinon installez le.

Lien : www.wireshark.org/download.html

La communication avec le panneau lumineux repose sur un protocole constructeur MovingSign. Le protocole MovingSign intervient au niveau la couche Application du modèle DoD et utilise les protocoles TCP et IP pour les couches Transport et Réseau. Les échanges avec l’afficheur se feront donc via un client TCP.

Vous devez avoir identifié l'adresse IP et le numero de port du panneau lumineux avant de continuer.

On se limitera à l’étude de deux types de trames :

  • les trames de commandes de contrôle
  • les trames de commande d’écriture de texte

Les trames de commandes de contrôle sont utilisées pour modifier les paramètres du panneau (luminosité, horodatage, etc …) et pour l’initialiser après chaque allumage.

  1. Exécuter le logiciel Multimedia led 2007.

  2. Démarrer une capture wireshark sur votre interface réseau et filtrer les trames qui contiennent seulement l’adresse IP de votre panneau :

  1. Créer un message et transférer le vers le panneau :
  1. Vous devez capturer un échange TCP :
  1. En utilisant les documentations sur le protocole MovingSign, décoder les données échangées lors de l’affichage d’un message afin d’en comprendre la structure.

Documentations : doc/panneau-lumineux/

Exemples de résultat attendu :

Diagramme d’échange

Diagramme d’échange

Modification de la luminosité

Modification de la luminosité

La trame d’initialisation

La trame d’initialisation

etc …

API Qt

Qt fournit un module QtNetwork qu’il faut activer dans son fichier de projet .pro pour pouvoir accéder aux classes :

...
QT += network
...

Pour inclure les déclarations des classes de ce module, il vous faut utiliser la directive suivante :

#include <QtNetwork>

Dans ce module, Qt fournit de nombreuses classes pour rendre la programmation réseau simple et portable :

En résumé, on disposera :

  • des classes comme QFtp pour les protocoles de la couche Application
  • des classes de plus bas niveau comme QTcpSocket, QTcpServer et QUdpSocket
  • des classes de plus haut niveau pour une gestion simplifiée du réseau comme QNetworkConfiguration, QNetworkConfigurationManager, …

Remarque : La classe QTcpSocket fournit une interface pour le protocole TCP. On peut donc utiliser QTcpSocket pour implémenter des protocoles réseau standard comme POP et SMTP, aussi bien que des protocoles personnalisés ou propriétaire.

Apparu dans les systèmes UNIX, un socket est un élément logiciel qui est aujourd’hui répandu dans la plupart des systèmes d’exploitation. Il s’agit d’une interface de communication logicielle avec les services du système d’exploitation, grâce à laquelle un développeur exploitera facilement et de manière uniforme les services d’un protocole réseau. Il s’agit d’un modèle permettant la communication bidirectionnelle inter processus IPC (Inter Process Communication) afin de permettre à divers processus de communiquer aussi bien sur une même machine qu’à travers un réseau TCP/IP. Les sockets se situent entre la couche Transport et la couche Application. En résumé, une socket est un point de communication par lequel un processus peut émettre et recevoir des informations. Ce point de communication devra être relié à une adresse IP et un numéro de port et associé à un mode de communication, le plus souvent : le mode connecté (TCP) ou le mode non connecté (UDP).

Ici, nous allons avoir besoin de la classe QTcpSocket pour réaliser un client TCP :

La classe QTcpSocket est asynchrone et émet des signaux pour reporter des changements de statuts et des erreurs. Elle repose sur une boucle d’événements pour détecter des données arrivantes et automatiquement envoyer les données partantes.

Les signaux qu’il faut au minimum gérer par connect() côté client sont :

  • readyRead() signale que des données ont été reçues et sont prêtes à être lues
  • bytesWritten() signale que des données ont été écrites
  • connected() signale que la socket est dans l’état connecté
  • disconnected() signale que la socket est dans l’état déconnecté
  • error() signale qu’une erreur s’est produite sur la socket

Vous pouvez écrire des données dans le socket avec QTcpSocket::write() et en lire avec QTcpSocket::read().

Remarque : QTcpSocket possède deux flux indépendants de données : un pour écrire et l’autre pour lire.

Il est possible d’attendre que des données aient été écrites (avec waitForBytesWritten()) ou reçues (avec waitForReadyRead()). En lisant depuis un QTcpSocket, vous devez être sûr qu’assez de données sont disponibles en appelant QTcpSocket::bytesAvailable() avant.

On appellera la méthode non bloquante QTcpSocket::connectToHost() pour se connecter à un serveur. Une connexion TCP doit être établie vers un hôte distant et un port avant que le transfert de données puisse commencer. Une fois la connexion établie, l’adresse IP et le port du point de communication distant sont disponibles avec les fonctions QTcpSocket::peerAddress() et QTcpSocket::peerPort(). À n’importe quel moment, le point de communication distant peut fermer la connexion et le transfert de données s’arrêtera immédiatement.

On se déconnecte du serveur en appelant disconnectFromHost().

Remarque : Qt fournit la classe QHostAddress pour manipuler des adresses IP.

Le principe de communication d’un client TCP est le suivant :

#include <QtNetwork>
#include "movingsign.h" // le protocole MovingSign

#define TIMEOUT_READ    2000
#define TIMEOUT_WRITE   100

QTcpSocket* socket; // un pointeur sur un QTcpSocket

// instancie la classe QTcpSocket
socket = new QTcpSocket;

// signaux/slots à faire
//connect(socket, SIGNAL(connected()),this, SLOT(connected()));
//connect(socket, SIGNAL(disconnected()),this, SLOT(disconnected()));
//connect(socket, SIGNAL(bytesWritten(qint64)),this, SLOT(bytesWritten(qint64)));
//connect(socket, SIGNAL(readyRead()),this, SLOT(readyRead()));

// connexion
socket->connectToHost(QHostAddress("192.168.52.250"), 10001, QTcpSocket::ReadWrite);
if( (socket->state() == QTcpSocket::ConnectedState) || (socket->waitForConnected(100) == true) )
{
    qDebug() << "Connexion ok";
}
else
{
    qDebug() << "Connexion impossible !";
}

// préparation de la trame à envoyer
QByteArray trame; // une trame
trame += NUL;
trame += NUL;
trame += NUL;
trame += NUL;
trame += NUL;
trame += SOH;
trame += SENDER_ADDRESS;
// ... cf. protocole MovingSign

// envoi de la trame
socket->write(trame);

// attente envoi
if(socket->waitForBytesWritten(TIMEOUT_WRITE) == true)
{
    // données reçues ?
    if(socket->waitForReadyRead(TIMEOUT_READ) == true)
    {
        int octetsRecus = socket->bytesAvailable();
        qDebug() << "Nb octets reçus : " << socket->bytesAvailable();

        QByteArray donneesRecues = socket->readAll();
        qDebug() << "Données reçues : " << donneesRecues;
    }
    else
    {
        qDebug() << "Aucune donnée reçue ! " << socket->errorString();
    }
}
else
{
    qDebug() << "Aucune donnée envoyée ! " << socket->errorString();
}

// déconnexion
socket->disconnectFromHost();
if( (socket->state() == QTcpSocket::UnconnectedState) || (socket->waitForDisconnected(100) == true) )
{
    qDebug() << "Déconnexion ok";
}
else
{
    qDebug() << "Déconnexion impossible !";
}

delete socket;

Pour faire simple ici, le protocole MovinSign se définira à base de constantes :

const char NUL = 0x00;
const char SOH = 0x01;
const char STX = 0x02;
const char ETX = 0x03;
const char EOT = 0x04;

const QString SENDER_ADDRESS = "FF";
// ...

Programmation Qt

Objectif : Être capable d’afficher un message sur le panneau lumineux via le réseau.

Code source fourni : activite-commande-panneau.zip

On vous fournit une classe PanneauLumineux que vous devez compléter et mettre en oeuvre. Pour cela, vous complèterez l’application GUI fournie qui permettra de saisir un message et l’envoyer au panneau lumineux. On pourra paramétrer la communication en saisissant l’adresse IP et le numéro de port du serveur embarqué de l’afficheur. L’application intègrera deux boutons pour se connecter ou se déconnecter.

Remarque : le panneau lumineux stocke dans sa mémoire le dernier message reçu et affiché. À chaque mise sous tension, le panneau réaffichera ce message. Il est intéressant d’avoir la possibilité de réinitialiser un message vierge.

L’application doit pouvoir gérer les erreurs de connexion :

Et d’envoi de message :

Par la suite, vous intégrerez la possibilité de modifier quelques paramètres d’affichage (le type, la vitesse, l’alignement, la taille de la police, …) à partir de l’IHM.

Retour au sommaire