J’ai hacké mon onduleur ou le reverse engineering de protocoles de communication (part 4)

Me voici donc au quatrième épisode. Pour suivre les épisodes précédents, c’est ici pour le premier, ici pour le second et ici pour le troisième. Pour rappel, j’ai donc réussi à obtenir les informations nécessaires à l’ouverture du port série. J’en ai profité pour donner quelques explications qui permettent avec différentes méthode d’obtenir ces informations. Même si l’exemple est basé sur un port série, cela fonctionne avec à peu près n’importe quel type de protocole. Le principe de base reste le même.

Je tiens quand même à apporter quelques précisions si vous utilisez un procédé d’acquisition discret tel que la carte son. Dans l’exemple que j’ai donné, je suis parti sur une acquisition à 44,1 KHz (ce qui correspond à ce que la plupart des cartes d’acquisition audio fond en standard). Il ne faut pas oublier le théorème de Nyquist – Shanon qui stipule que la fréquence maximale que l’on peut acquérir est égale à la moitié de la fréquence d’acquisition. Donc si l’on acquière à 44,1 KHz, cela donne donc une fréquence maximale de 22,05 KHz. En effet, il faut au moins 2 points dans une période pour déterminer la fréquence d’un sinus pur. Si l’on utilise une carte son, avec cette fréquence d’acquisition, cela permet de calculer jusqu’à 19 600 baud. Au-delà, il faut donc acquérir avec une fréquence plus importante (si la carte le permet) ou vraiment utiliser un oscilloscope.

Aller, je gardais une autre solution pour ce post : utiliser un port parallèle qui permet d’acquérir avec une fréquence suffisamment importante pour aller jusqu’à 115 KHz /2 soit 57,5 KHz. Le montage n’est pas plus compliqué et il faut écrire à la main le bout de soft qui permet de faire cela. Quand j’étais étudiant (il y a bien longtemps maintenant…), nous nous amusions à faire des chenillards et autres affichages loufoques à base de port parallèle. Un port très sympathique qui a inspiré la plupart des affichages LCD de façade…

Bon, revenons-en au code. J’ai promis à Benjamin (qui est dans mon équipe) que je publierais un peu code. Alors, Benj, chose promise, chose due :-) Je vais donc publier un morceau de code qui permet de se connecter à l’onduleur, de lui envoyer du texte et de récupérer ce qui en revient. Ce code est basé sur un projet (How-to Using the Comm Port) de GotDotNet. Il permet aux utilisateurs du framework 1.0 et 1.1 de bénéficier d’une classe super bien faite pour accéder aux ports séries. Ca me rappelle celle que j’avais dû faire et que j’utilisais à l’époque en C++. De base dans le framework 2.0, il y a tout ce qu’il faut avec la classe SerialPort, elle aussi bien faite. Je suis parti du projet de GotDotNet car je l’ai trouvé très rapidement et qu’il y avait du code pour m’inspirer. Donc, benj, ouvre les yeux, voici mon code :

' BtnSendOnduleur est un bouton :-)

Private
Sub BtnSendOnduleur_Click(ByVal sender As System.Object, ByVal e As

System.EventArgs) Handles BtnSendOnduleur.Click

' Les Ports, c’est comme les fichiers, dès qu’on s’en sert, faut les try catch.

Try

m_CommPort.Open(1, 2400, 8, Rs232.DataParity.Parity_None, Rs232.DataStopBit.StopBit_1, 64)

'
TxtSendOnduleur est une boîte de texte, texte à envoyer à l’onduleur

m_CommPort.Write(Encoding.ASCII.GetBytes(TxtSendOnduleur.Text & Chr(13)))

'Une pose quand on ouvre un port, ça fait toujours du bien

System.Threading.Thread.Sleep(200)

Application.DoEvents()

' Try pour la récupération des données.

Try

' Je lit une donnée et toutes celles qui suivent. Quand le retour est -1, c’est timeout, donc, en principe, plus de données à lire

While (m_CommPort.Read(1) <> -1)

' txtStatus est une boîte de texte qui permet de mettre le résultat de la lecture du port.

txtStatus.Text = txtStatus.Text + Chr(m_CommPort.InputStream(0))

End
While

 

m_CommPort.Close()

Return

Catch exc As Exception

' Rien à lire ou un problème

m_CommPort.Close()

Return

End
Try

Catch exc As Exception

' Impossible d’ouvrir le port

MsgBox("Port pas ouvert.", MsgBoxStyle.OkOnly, Me.Text)

Return

End
Try

End
Sub

Maintenant que tu as vu le code, je vais expliquer à quoi il me sert. Revenons sur l’exercice que j’ai fait avec PortMon. Voici la suite de ce que j’ai récupéré pas la suite :

28    0.00002172    RupsMon.exe    IRP_MJ_WRITE    Serial0    SUCCESS    Length 2: F.    

29    0.11777290    RupsMon.exe    IOCTL_SERIAL_WAIT_ON_MASK    Serial0    SUCCESS        

30    0.00445378    RupsMon.exe    IRP_MJ_WRITE    Serial0    SUCCESS    Length 3: Q1.    

31    0.00000703    RupsMon.exe    IOCTL_SERIAL_GET_COMMSTATUS    Serial0    SUCCESS        

32    0.00000273    RupsMon.exe    IOCTL_SERIAL_GET_COMMSTATUS    Serial0    SUCCESS        

33    0.00000576    RupsMon.exe    IRP_MJ_READ    Serial0    SUCCESS    Length 22: #230.0 2.2 12.00 50.0.    

34    2.09370086    RupsMon.exe    IOCTL_SERIAL_WAIT_ON_MASK    Serial0    SUCCESS        

35    0.00000559    RupsMon.exe    IOCTL_SERIAL_PURGE    Serial0    SUCCESS    Purge: TXCLEAR     

36    0.00002896    RupsMon.exe    IRP_MJ_WRITE    Serial0    SUCCESS    Length 3: Q1.    

37    0.00000333    RupsMon.exe    IOCTL_SERIAL_WAIT_ON_MASK    Serial0    INVALID PARAMETER        

38    0.00000359    RupsMon.exe    IOCTL_SERIAL_GET_COMMSTATUS    Serial0    SUCCESS        

39    0.00000191    RupsMon.exe    IOCTL_SERIAL_GET_COMMSTATUS    Serial0    SUCCESS        

40    0.00000388    RupsMon.exe    IRP_MJ_READ    Serial0    SUCCESS    Length 47: (239.0 239.0 239.0 022 50.0 13.9 32.0 00001000.    

41    0.00002742    RupsMon.exe    IRP_MJ_WRITE    Serial0    SUCCESS    Length 2: F.    

42    0.11799550    RupsMon.exe    IOCTL_SERIAL_WAIT_ON_MASK    Serial0    SUCCESS        

43    0.00000374    RupsMon.exe    IOCTL_SERIAL_GET_COMMSTATUS    Serial0    SUCCESS        

44    0.00000183    RupsMon.exe    IOCTL_SERIAL_GET_COMMSTATUS    Serial0    SUCCESS        

45    0.00000380    RupsMon.exe    IRP_MJ_READ    Serial0    SUCCESS    Length 22: #230.0 2.2 12.00 50.0.    

46    0.00011600    RupsMon.exe    IRP_MJ_WRITE    Serial0    SUCCESS    Length 3: Q1.    

47    0.22647997    RupsMon.exe    IOCTL_SERIAL_WAIT_ON_MASK    Serial0    SUCCESS        

48    0.00000381    RupsMon.exe    IOCTL_SERIAL_GET_COMMSTATUS    Serial0    SUCCESS        

49    0.00000182    RupsMon.exe    IOCTL_SERIAL_GET_COMMSTATUS    Serial0    SUCCESS        

50    0.00000407    RupsMon.exe    IRP_MJ_READ    Serial0    SUCCESS    Length 47: (239.0 239.0 239.0 022 50.0 13.9 32.0 00001000.    

En gros, quand la commande F ou Q1 est envoyée, il y a un retour. Pour définir ce que le point représente, j’ai fait une acquisition en binaire. Cela donne pour Q1 : IRP_MJ_WRITE    Serial0    SUCCESS    Length 3: 51 31 0D

0D = 13 en décimal, ce qui correspond au caractère « entrée ». Très classique dans le cas de communication avec des appareils en port série. D’où dans le code, TxtSendOnduleur.Text & Chr(13) qui permet d’envoyer du texte (F ou Q1 pour faire les premiers tests) avec le caractère « entrée ». Mon programme permet donc d’envoyer une commande, de récupérer le résultat et de l’afficher dans une boîte de texte. A noter que j’ai fait ce code uniquement pour me dérouiller. Inutile dans mon cas, j’aurais pu utiliser le bon vieux terminal Windows qui fait exactement la même chose (en mieux). J’ai écrit ce code, toujours dans l’optique de me dérouiller. De toute façon, ce n’est pas perdu, j’aurais besoin d’en écrire et à peu près le même pour mon application finale. Je reviendrais sur cet excellent outil qu’est le Terminal Windows et qui m’a presque fait oublier ma VT100

A noter qu’avec VB 2005, pour ouvrir un port, on peut utiliser l’excellente classe My en faisant un Dim MonPort As System.IO.Ports.SerialPort = My.Computer.Ports.OpenSerialPort("COM1", 2400, IO.Ports.Parity.None, 8, IO.Ports.StopBits.One). Ensuite, la classe SerialPort permet d’envoyer et récupérer des données. Je l’utiliserais dans mon application finale.

Du coup, cette application, m’a permit d’envoyer diverses commandes et de voir la réaction de l’onduleur et le retour qu’il a pu m’en faire. Cela m’a permit de déterminer (quasiment) toutes les commandes disponibles et donc de déterminer quel est le langage entre l’onduleur et le PC. La suite au prochaine numéro…