Utilisation du port série dans le framework Microsoft .NET 1.1 et en VB.NET

Vincent Caron

Cet article traite de façon pratique la manière de lire le port série avec le framework .NET 1.1
Télécharger les sources commentées

- Titre : Utilisation du port série dans le framework Microsoft .NET 1.1 et en VB.NET

- Auteur : Vincent Caron

- Parution : 14 mars 2006

- Mise à jour : 30 mars 2020

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2006 Vincent Caron. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Introduction

- I. Comment lire sur le port série?

- I-A. La classe RS232

- I-B. Lecture sur le port série

- II. L'importance des threads

- III. Paramètres linguistiques

- Conclusion

- IV. Annexe : la norme NMEA

- V. L'article au format PDF

Introduction

Ce tutoriel se base sur un programme concret permettant de recevoir les informations d'un GPS depuis le port série. (Les GPS actuels utilisent toujours le port série même s'ils passent par du Bluetooth ou de l'USB. Ceci est dû en partie à la norme NMEA).

Dans le prochain tutoriel, vous trouverez comment utiliser et créer des composants graphiques afin de représenter la vitesse du GPS sur un cadran analogique (composant graphique).

I - Comment lire sur le port série?

I-A - La classe RS232

Nous utiliserons pour arriver à nos fins une classe que publie Microsoft pour communiquer avec le port série.
Voici le lien qui permet de télécharger la classe avec un exemple d'utilisation pour un modem.
Nous aurions pu tout aussi bien utiliser MSCOMM32.ocx, mais les “ocx” sont plutôt une solution de la technologie précédente, alors mettons-nous au goût du jour.
Consulter un article sur la définition des ocx.

Nous reprendrons dans notre exemple la classe prénommée rs232.vb, que vous pouvez obtenir directement dans le paquetage des sources de ce tutoriel. Cette classe sert à dialoguer avec le port série. Elle utilise les dll de base de Windows afin d'établir la communication. On aura donc des difficultés à utiliser cette classe dans un programme qui devrait tourner sous une autre plate-forme que celle de Windows. Dans notre exemple, nous nous servirons uniquement des capacités de lecture du port série, même si cette classe gère également l'écriture sur le port série, ainsi que des événements (par exemple un événement qui indique que des données ont été reçues).

On crée une application Windows. Dans la fenêtre de l'application principale, on insère les composants qui vont nous être utiles.

Voici la liste de ces composants :

'Un bouton pour lancer l'ouverture et la lecture du port série.
Private ouvreCom As System.Windows.Forms.Button

Par défaut c'est le port 4 qui est utilisé, vous pouvez changer le champ privé de notre classe principale (MainForm), afin d'utiliser le port que vous voulez, par exemple : Private comPort As Integer = 3 . Dans une version de production de notre programme, il serait judicieux de placer cette variable dans un fichier de configuration, et de pouvoir la changer avec l'interface graphique.

'Un bouton pour arrêter la lecture du port série, et fermer ce dernier.
Private arreteAffiche As System.Windows.Forms.Button

'Une case à cocher pour que ce qu'on lit sur le port série soit affiché dans la zone de texte.
Private checkBox_afficheSortie As System.Windows.Forms.CheckBox

'Une zone de texte où on affiche ce qu'on lit du port série.
Private textBox1 As System.Windows.Forms.TextBox

'Une étiquette pour afficher la vitesse à laquelle on extrait des données GPS.
Private label_vitesse As System.Windows.Forms.Label

I-B - Lecture sur le port série

Pour utiliser le port série, nous avons donc le fichier rs232.vb dans notre projet. Ce fichier contient la classe Rs232. Nous travaillons directement avec la source de cette classe, mais on pourrait générer un ficher d'assemblée (dll), pour pouvoir s'en servir dans notre projet.

Pour l'utiliser, on déclare et on initialise un objet m_CommPort qui instancie notre classe Rs232 :

Private m_CommPort As New Rs232()

On peut régler les champs de la classe, avant d'ouvrir le port, par exemple :

m_CommPort.Timeout = 200

Certaines propriétés ne peuvent être réglées qu'avant l'ouverture du port, d'autres propriétés peuvent être modifiées « à chaud ». On ouvre le port avec les paramètres de la liaison série :

m_CommPort.Open(comPort, 4800, 8, Rs232.DataParity.Parity_None, Rs232.DataStopBit.StopBit_1, 4000)

Le premier argument est le numéro de port, 4800 est la vitesse de transmission du port série, 8 : le nombre de bits de données, ensuite on indique qu'il n'y pas de parité, puis qu'il faut un bit d'arrêt, et enfin un tampon de 4000 octets. À part la vitesse de connexion, qui pour un GPS peut être plus élevée (9400 bauds par exemple), ce sont généralement ces mêmes paramètres qui sont utilisés. Si les données que vous recevez vous apparaissent étant n'importe quoi, il est sans doute probable que la vitesse de votre port série est mal réglée.

Pour lire le port série, en argument on précise le nombre d'octets à lire dans le tampon :

m_CommPort.Read(1)

Ici, on ne lit qu'un octet dans le tampon. Nous lançons cette lecture en boucle, s'il n'y a pas de données dans le tampon, la classe Rs232 lance une exception, il faut donc entourer cette lecture par un Try-catch.

À la demande de lecture d'un certain nombre d'octets, notre objet m_CommPort enregistre ce qu'il a lu du tampon dans un de ses champs privés. Pour récupérer le tableau des caractères qu'on lui a demandé de lire, on utilise la propriété :

m_CommPort.InputStream

On place le premier caractère (et le seul dans notre cas, puisqu'on demande de ne lire qu'un octet) dans une variable de type caractère :

Dim temp As Byte = m_CommPort.InputStream(0)

Dans notre application, on lit le contenu du tampon caractère par caractère, quand ce dernier est vide, on attend qu'il se remplisse, puis on recommence.
Quand on arrive au caractère “fin de ligne” (code 13 en ASCII), la ligne est affichée et les traitements appropriés sont effectués. Dans notre exemple, quand la ligne commence par “GPRMC”, la vitesse est située après la 7e virgule, alors on récupère cette valeur.
Exemple :

"$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A"

Attention, cette vitesse est en nœuds nautiques, il faut donc multiplier cette valeur par 1,852 pour l'avoir en km/h.
Les explications sur la norme NMEA.

II - L'importance des threads

Pour lancer la lecture continue du port série, on clique sur le bouton “ouvreCom”. On pourrait penser que lancer une boucle infinie pour lire le port série et afficher en continu les données pourrait être une bonne solution. Mais (j'ai essayé), dans cette situation le programme perd complètement la main et le bouton “arrêter” ne stoppera pas l'exécution. La fermeture de votre application risque d'être tout aussi laborieuse. Seul le système d'exploitation peut arrêter la tâche dans ce cas.

En fait, dès que l'exécution entre dans cette boucle de lecture et d'affichage du port série, on tourne en boucle et on ne voit pas les actions de l'utilisateur, c'est pourquoi le programme se bloque. Pour pallier ce problème, on utilise les threads, un thread est une tâche qui va s'exécuter indépendamment de notre programme principal. Ainsi, l'application garde la main, peut lire les actions de l'utilisateur et aussi arrêter notre thread de lecture et d'affichage du port série. C'est donc notre programme principal qui se chargera d'arrêter notre thread avec l'appui sur le bouton “arrêter”. Quand il se ferme, il se charge d'éteindre automatiquement notre thread. On peut utiliser un thread si on veut lancer un long calcul, une longue lecture sur le disque et que l'on veut que l'utilisateur puisse garder la main sur son programme, voir la progression du calcul s'effectuer dans une barre d'avancement. Dès que l'on craint que le programme mette du temps à réaliser une opération ou risque de bloquer sur une boucle, il ne faut pas hésiter : il faut créer un thread.

Pour créer un thread, il faut en spécifier la bibliothèque :

Imports System.Threading

Déclarer une variable :

Private tache As Thread

Initialiser le thread :

tache = New Thread(New ThreadStart(AddressOf Affiche))

Dans l'initialisation du thread, on précise l'adresse de la méthode à lancer. Notre méthode contient une boucle sans fin. Quand le thread est lancé, il ne s'arrêtera que si on le lui demande.
« Affiche » est le nom de notre procédure à lancer. Dans notre cas, c'est la procédure contenue dans notre classe principale :

Private Sub Affiche()

En C#, la procédure est identique, sauf que l'on pas besoin du mot clé “AddressOf”, on écrira simplement :

tache = new Thread(new ThreadStart(Affiche));

Lancer le thread:

tache.Start();

On force la fermeture du thread (par exemple après l'appui du bouton arrêter) :

tache.Abort();

III - Paramètres linguistiques

Avec un système Windows réglé avec des paramètres linguistiques en français :

Pour transformer une chaîne de caractères en nombre décimal, on peut utiliser la fonction :

Dim temp As Single = CSng("19,6")

C'est bien pratique, mais il faut que l'utilisateur donne la chaîne avec une virgule comme séparateur ou que la chaîne soit codée en dur avec une virgule. Si la chaîne est codée en dur, on peut imaginer la catastrophe en passant le programme à Chris, votre copain londonien. Car si votre copain n'a pas les mêmes paramètres linguistiques (un point comme séparateur de décimales), l'instruction CSng(« 19,6 ») lancera une exception.
Dans notre cas, les données proviennent du GPS, le séparateur de décimales est un point. Cela tombe bien, c'est le séparateur utilisé dans les paramètres de la culture indifférente (InvariantCulture), qui est celle de l'anglais. On va donc spécifier au thread courant (celui de notre programme) d'utiliser cette culture indifférente :

Thread.CurrentThread.CurrentCulture = New CultureInfo("", false)

Le premier argument "" spécifie la culture indifférente, on aurait pu utiliser le code de l'anglais 0x0009. Le deuxième argument indique si les réglages de l'utilisateur peuvent remplacer des paramètres de cette culture : non, surtout pas ! C'est justement ce qu'on veut éviter, donc l'argument vaut false.
On aurait pu aussi créer notre propre culture. Une culture avec, par exemple un point-virgule comme séparateur de décimales, et l'appliquer au thread courant.
Désormais, sans erreur, on peut faire :

Dim temp As Single = CSng("19.6")

Il est donc important de changer la culture de notre programme afin de correspondre à la culture des données codées en dur ou afin de correspondre à la source de vos données. Il ne faut jamais se baser sur la culture du système par défaut pour traiter des données qui ne proviennent pas de l'utilisateur.

Conclusion

Nous avons créé une application qui permet de lire notre GPS, sachez qu'il est également possible d'écrire dans certains GPS. C'est notamment le cas des GPS équipés de la puce SiRF. On peut envoyer un message qui changera le comportement de notre GPS, par exemple choisir le type de message à envoyer. Il faut cependant faire attention à ce qu'on écrit sur le GPS, car il doit être possible de le rendre inopérationnel.

Dans le prochain tutoriel, nous verrons comment afficher notre vitesse sous la forme analogique un peu à la façon d'un compteur de voiture.

Je ne saurais trop vous conseiller de porter une extrême prudence à vos essais à bord de votre véhicule.

IV - Annexe : la norme NMEA

La norme NMEA (National Marine Electronics Association ) est très bien expliquée sur ce site Internet, vous trouverez une copie de ces explications dans le paquetage des sources.

Une traduction de cet article sur la norme NMEA est prévue prochainement. Juste un peu de patience !

V - L'article au format PDF

Vous pouvez télécharger l'article au format PDF (11 pages, 37 Ko)