Décodage de caractères ASCII

BENOITMSA - 2 avril 2013 à 13:00
 BENOITMSA - 5 avril 2013 à 21:08
Bonjour,

Je teste une communication série avec le protocole Modbus. Mon PC est maitre de la com. L'émission fonctionne parfaitement et l'esclave ma répond.
J'ai un soucis sur le décodage de la trame de réponse de l'esclave.
Je ne parviens pas à afficher les codes ASCII supérieurs à 127 malgré l'utilisation de l'instruction ENCODING. Ils sont remplacés par 63.

Voici un extrait de mon code:
Dim Tab_Rep As Byte() 'Tableau des réponses

Private Sub ReceivedText(ByVal [text] As String)
Dim y As Integer
'compares the ID of the creating Thread to the ID of the calling Thread
If Me.rtbReceived.InvokeRequired Then
Dim x As New SetTextCallback(AddressOf ReceivedText)
Me.Invoke(x, New Object() {(text)})
Else
y = ([text].Length) - 1
ReDim Tab_Rep(y)
Tab_Rep = Encoding.Default.GetBytes([text])
Me.rtbReceived.Text = Tab_Rep(0) & " " & Tab_Rep(1) & " " & Tab_Rep(2) & " " & Tab_Rep(3) & " " & Tab_Rep(4) & " " & Tab_Rep(5) & " " & Tab_Rep(6)
End If
End Sub

Merci pour votre aide

10 réponses

foliv57 Messages postés 420 Date d'inscription vendredi 17 novembre 2006 Statut Membre Dernière intervention 15 juillet 2014 9
2 avril 2013 à 13:09
Bonjour,

La table ASCII va de 0 à 127.

Donc c'est tout à fait normal...

Vous oubliez peut-être qu'un octet peut contenir 2 caractères ASCII. Il faut donc penser à le décomposer.
0
foliv57 Messages postés 420 Date d'inscription vendredi 17 novembre 2006 Statut Membre Dernière intervention 15 juillet 2014 9
2 avril 2013 à 13:30
Honte à moi... ne pas tenir compte de ma dernière phrase. Elle est complètement fausse. C'est un caractère ASCII par octet.

Je voulais en faite parler de décomposer la trame MODBUS qui est composée de Mots donc 2 octets soit 2 caractères par mot.

Par contre utilisé plutot Encoding.ASCII.GetBytes() si vous voulez être sur d'utiliser l'encodage ASCII.
0
Utilisateur anonyme
2 avril 2013 à 13:40
Bonjour,

Pour compléter la remarque de foliv57 que je salue, on lit ceci :
Private Sub ReceivedText(ByVal [text] As String) 

...et puis ceci :
Me.Invoke(x, New Object() {(text)}) 

Erreur de type. On devrait lire New String() {[text]}
0
Bonsoir,

J'ai appliqué les 2 modifications suggérées mais je ne constate pas d'amélioration.
Si la valeur lue dans un mot est égale à 127 j'obtiens :
Octet de poids fort: 0 / Octet de poids faible: 127
Si la valeur lue est égale à 255 j'obtiens :
Octet de poids fort: 0 / octet de poids faible: 63
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
foliv57 Messages postés 420 Date d'inscription vendredi 17 novembre 2006 Statut Membre Dernière intervention 15 juillet 2014 9
2 avril 2013 à 19:36
Si votre esclave répond avec une valeur d'octet de plus de 127, c'est tout simplement qu'il ne vous envoi pas un code ASCII.

Vous ne pouvez pas avoir un mot ayant la valeur 255 avec poids fort 0 poids faible 63. Ça n'a pas de sens.

Si un esclave doit vous répondre "AbCd", vous aurez deux mots de données dans la trame :
Mot 1 : 41|62 en base 16 (65|98 en base 10)
Mot 2 : 43|64 en base 16 (67|100 en base 10)

ou au pire :
Mot 1 : 62|41 en base 16 (98|65 en base 10)
Mot 2 : 64|43 en base 16 (100|67 en base 10)
si votre esclave inverse les octets de poids fort/poids faible

Si j'en reviens à votre code. Je ne vois pas quel est son intérêt. Il n'analyse en rien une trame reçue d'un esclave, sinon la méthode aurait comme paramètre une table d'octets (réponse de l'esclave) et la transformerait en chaîne de caractères.

Si cette méthode à pour but inverse de transformer une chaines de caractères en table d'octets pour l'envoyer à l'esclave, la fonction "Encoding.ASCII.GetBytes()" ne devrait en aucun cas, sauf erreur de ma part, renvoyer un octets de plus de 127. A moins bien entendu que vous fassiez l'erreur de passer en paramètre une chaîne de caractères contenant un caractère ne faisant pas parti de la table ASCII.

Car évidement, si on fait :
Encoding.ASCII.GetBytes("ç")

la fonction renverra 63 car "ç" ne fait pas parti de la table ASCII. Donc la fonction renvoi "?"

Si vous devez faire passer des caractères en dehors de la plage des caractères ASCII, il faudra utiliser un autre encodage et vérifier si votre esclave accepte bien cet encodage.
0
Bonjour,

Je te donne quelques précisions complémentaires en réponse à tes remarques.

Voici un extrait du code que j'utilise pour l'émission de la requête de lecture vers l'esclave.


Dim Tableau_Requete as byte()
' Codage de adresse début sur 2 bytes
TOTO = CUInt(tbxAdrDeb.Text)
If TOTO > 255 Then
Tableau_Requete(2) = CByte(TOTO \ 256)
Tableau_Requete(3) = CByte(TOTO Mod 256)
Else
Tableau_Requete(2) = CByte(0)
Tableau_Requete(3) = CByte(TOTO)
End If
txtTransmit.Text = Chr(Tableau_Requete(0)) & Chr(Tableau_Requete(1)) & Chr(Tableau_Requete(2)) & Chr(Tableau_Requete(3)) & Chr(Tableau_Requete(4)) & Chr(Tableau_Requete(5)) & Chr(Tableau_Requete(6)) & Chr(Tableau_Requete(7))
Tableau_Requete = Encoding.Default.GetBytes(txtTransmit.Text) 'permet de coder les CHR de valeur > 127
SerialPort1.Write(Tableau_Requete, 0, Tableau_Requete.Length & vbCr)

Et çà fonctionne parfaitement avec des valeurs de bytes comprises entre 0 et 255 et codées en catactères Ascii (non étendu ou étendu). L'esclave me transmets bien les valeurs des mots demandées.

A la reception j'utilise la procedure:
SerialPort1_DataReceived
qui appelle:
Private Sub ReceivedText(ByVal [text] As String)

Peut être faut il que je réécrive cette dernière avec:
[text] As Byte() ?

Merci pour vos conseils
0
foliv57 Messages postés 420 Date d'inscription vendredi 17 novembre 2006 Statut Membre Dernière intervention 15 juillet 2014 9
4 avril 2013 à 09:34
Plusieurs choses, car pour le moment on tourne en rond.

Un maître MODBUS envoi des octets et reçoit des octets. A mois que l'esclave soit sensé répondre du texte, il n'y a aucun intérêt à convertir les octets en caractères. Au pire, si on veut analyser la trame, on les converti en base 16 pour des questions de lisibilité.

Donc :
txtTransmit.Text = Chr(Tableau_Requete(0)) & Chr(Tableau_Requete(1)) & Chr(Tableau_Requete(2)) & Chr(Tableau_Requete(3)) & Chr(Tableau_Requete(4)) & Chr(Tableau_Requete(5)) & Chr(Tableau_Requete(6)) & Chr(Tableau_Requete(7)) 
Tableau_Requete = Encoding.Default.GetBytes(txtTransmit.Text) 'permet de coder les CHR de valeur > 127 

Ne sert à rien. Vous convertissez des octets en caractères pour les reconvertirent en octets...

Il y a une chose que vous pourriez poster pour faire avancer le sujet. Dans votre sub "Private Sub ReceivedText(ByVal [text] As String)", affichez la valeur de la variable "[text]" avant de faire "Tab_Rep = Encoding.Default.GetBytes([text])".
Je ne serai pas étonné que cette chaîne contienne des "?", vos fameux 63.

Si c'est le cas, et que ça ne devrait pas l'être, veuillez nous poster la partie de code dans votre sub "SerialPort1_DataReceived" qui converti/(lit) les données reçus en chaîne de caractères.

J'imagine que vous avez quelque chose du genre :
Dim message = Me.SerialPort1.ReadExisting()

Si c'est le cas, je vous renvoi vers la définition de la conction ReadExisting. Notez bien que la définition précise que les données sont encodées avec l'encodage de l'objet SerialPort.
Du coup je vous renvoi aussi vers la définition de la propriété Encoding de l'objet SerialPort.
En notant cette phrase importante
Objet Encoding. La valeur par défaut est ASCIIEncoding.

Donc, par défaut, ReadExisting transformera les octets de plus de 127 en "?".

Je vous conseil donc de travailler qu'avec les octets, sans conversion.

Exemple à la réception :
Private Sub SerialPort1_DataReceived(sender As System.Object, e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived

        'Reception des données
        Dim dataReceived(Me.SerialPort1.BytesToRead) As Byte
        Me.SerialPort1.Read(dataReceived, 0, dataReceived.Length)
        ReceivedData(dataReceived)

End Sub


Traitement des données :
Private Delegate Sub ReceivedDataDelegate(ByVal dataReceived() As Byte)
    Private Sub ReceivedData(ByVal dataReceived() As Byte)
        If Me.InvokeRequired Then
            Me.Invoke(New ReceivedDataDelegate(AddressOf ReceivedData), New Object() {dataReceived})
        Else
            Me.rtbReceived.Text = ""
            For Each octet As Byte In dataReceived
                Me.rtbReceived.Text &= octet & " "
            Next
        End If
End Sub


Si vous voulez garder les conversions, il faudra modifier l'encodage de l'objet SerialPort à l'initialisation :
Me.SerialPort1.Encoding = System.Text.Encoding.Default
0
Bonjour,

Le problème est bien ce que vous décrivez.

J'ai essayé le bout de code pour traiter directement des bytes, mais j'ai une erreur :

Impossible de convertir l'objet de type 'System.Object' en type 'System.Byte[]'.

sur la ligne:


Me.Invoke(New ReceivedDataDelegate(AddressOf ReceivedData), New Object())
Savez vous d'où çà peut provenir ?

Voici ma copie de code:


Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
'ReceivedText(SerialPort1.ReadExisting()) 'Automatically called every time a data is received at the serialPort
'Réception des données
Dim dataReceived(Me.SerialPort1.BytesToRead) As Byte
Me.SerialPort1.Read(dataReceived, 0, dataReceived.Length)
ReceivedData(dataReceived)
End Sub
Private Delegate Sub ReceivedDataDelegate(ByVal dataReceived() As Byte)
Private Sub ReceivedData(ByVal dataReceived() As Byte)
If Me.InvokeRequired Then
Me.Invoke(New ReceivedDataDelegate(AddressOf ReceivedData), New Object())
Else
Me.rtbReceived.Text = ""
For Each octet As Byte In dataReceived
Me.rtbReceived.Text &= octet & " "
Next
End If
End Sub

Cordialement
0
foliv57 Messages postés 420 Date d'inscription vendredi 17 novembre 2006 Statut Membre Dernière intervention 15 juillet 2014 9
5 avril 2013 à 08:08
Tout simplement parce que vous n'avez pas copié correctement mon exemple :
If Me.InvokeRequired Then
  Me.Invoke(New ReceivedDataDelegate(AddressOf ReceivedData), New Object() {dataReceived})
Else


Et pas
If Me.InvokeRequired Then 
  Me.Invoke(New ReceivedDataDelegate(AddressOf ReceivedData), New Object()) 
Else
0
Merci à foliv57,

J'ai fait les tests en lecture de plusieurs mots avec des valeurs sur 2 bytes (>255) et tout fonctionne parfaitement.

Je vais simplifier également l'émission pour ne plus utiliser une chaine et les codes Ascii, mais directement des Bytes.

Encore une fois mille mercis.
0
Rejoignez-nous