Reception modbus Rs485

Résolu
cs_chamicki Messages postés 28 Date d'inscription mardi 14 octobre 2008 Statut Membre Dernière intervention 15 avril 2011 - 14 avril 2011 à 13:31
cs_chamicki Messages postés 28 Date d'inscription mardi 14 octobre 2008 Statut Membre Dernière intervention 15 avril 2011 - 15 avril 2011 à 10:46
Bonjour,

J'utilise VB.net 2010 express pour communiquer via le protocole Modbus et un liaison RS485.

J'utilise le SerialPort qui est dans VB.

Or j'ai un souci de réception des données du produit.

Voici le code :

Private Sub SerialPort_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort.DataReceived
        Dim Taille As Integer
        Dim recep() As Byte
        Dim recepPourVerif() As Byte
        Dim Valeur As Single
        Dim Data(3) As Byte
        Dim Crc16 As Boolean
        'Quand il y a quelquechose dans le buffer de réception on lit les données et les ajoutes dans recep
        ReDim recep(SerialPort.BytesToRead - 1)
        ReDim recepPourVerif(SerialPort.BytesToRead - 1)
        Taille = SerialPort.BytesToRead
        SerialPort.Read(recep, 0, SerialPort.BytesToRead)


Or j'ai souvent le problème suivant : l'évènement se déclenche alors que toute la trame Modbus n'est pas arriver. Je traite donc une trame imcomplète et j'ai des erreur ensuite....

Est ce que quelqu’un à une idée sur le problème ?

Merci

14 réponses

lesdis Messages postés 403 Date d'inscription mercredi 19 avril 2006 Statut Membre Dernière intervention 7 août 2020
14 avril 2011 à 14:40
Tiens je suis de bonne humeur aujourd'hui, voici la méthode que j'utilise pour récupérer les infos d'un port serie :

Private WithEvents _SP as IO.Ports.SerialPort
Private _trame as new List(Of Byte)

Private Sub SP_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles _SP.DataReceived
        Dim nbData As Integer = _SP.BytesToRead
        Dim tab(nbData - 1) As Byte

        _SP.Read(tab, 0, nbData)
        
        _trame.addRange(tab)
        VerifTrame()
End Sub



Et dans VerifTrame, je fais les vérification correspondant au protocole utilisé.

Bonne Prog
3
foliv57 Messages postés 420 Date d'inscription vendredi 17 novembre 2006 Statut Membre Dernière intervention 15 juillet 2014 9
14 avril 2011 à 16:41
Si le premier octet recu est bien le numéro d'esclave de la requete et que le bit de poids fort du deuxième octet est à 1 vous savez que c'est un code d'exception et donc qu'il faudra lire 5 octets.

CQFD
3
lesdis Messages postés 403 Date d'inscription mercredi 19 avril 2006 Statut Membre Dernière intervention 7 août 2020
14 avril 2011 à 14:15
Bonjour,

2 solutions s'offre à toi :

- Si la taille de la trame est constante et que tu la connait, tu peut modifier la propriété ReceivedBytesThreshold qui demande un minimum d'octet avant le déclenchement de l'évènement DataReceived.

Malheureusement, a ce que je pense me rappeler, c'est que la trame Modus peux avoir des tailles différentes.

- Donc il n'y a pas d'autre choix que de faire un buffer qui stocke toutes les informations reçues par le port série.
Ensuite tu va pouvoir analyser les données pour trouver le caractère de début, celui de fin, calculer le CRC et enfin exploiter les données.
Une fois tout cela fait, tu peux effacer la trame de ton buffer et continuer a vérifier si d'autres trames sont contenus dans le buffer.

Pour faire tout ceci, je te conseille d'utiliser un objet Collection qui permet de manipuler facilement ses éléments (ajouts, effacemment, tri...)
Perso, j'utilise des List(of Byte) lorsque je doit faire ce genre de manipulation.

Bonne Prog
0
cs_chamicki Messages postés 28 Date d'inscription mardi 14 octobre 2008 Statut Membre Dernière intervention 15 avril 2011
14 avril 2011 à 14:18
Bonjour et merci de porte intérêt à mon problème.

Je ne comprend pas trop votre solution, pouvez vous me donner un exemple ?
0

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

Posez votre question
lesdis Messages postés 403 Date d'inscription mercredi 19 avril 2006 Statut Membre Dernière intervention 7 août 2020
14 avril 2011 à 14:20
Une autre chose, attention à l'utilisation de BytesToRead, il te renvoi le nombre d'octet dans le buffer au moment de la demande.
Il m'est déjà arriver qu'entre la déclaration de mon tableau et la lecture du buffer que des octets sont arrivés au port série et cela a provoquer un dépassement de capacité.

Je te propose donc de faire plutôt cela :
Private Sub SerialPort_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort.DataReceived
        Dim Taille As Integer
        Dim recep() As Byte
        Dim recepPourVerif() As Byte
        Dim Valeur As Single
        Dim Data(3) As Byte
        Dim Crc16 As Boolean
        Dim ByteToRead as Integer

        ByteToRead = SerialPort.BytesToRead - 1
        'Quand il y a quelquechose dans le buffer de réception on lit les données et les ajoutes dans recep
        ReDim recep(BytesToRead)
        ReDim recepPourVerif(BytetoRead)
        Taille = SerialPort.BytesToRead
        SerialPort.Read(recep, 0, BytesToRead+1)
End Sub


Donc même si des octets sont arrivés entre temps, ce n'est pas grave il seront analyser au prochain déclenchement de l'évènement

Bonne Prog
0
cs_Jack Messages postés 14007 Date d'inscription samedi 29 décembre 2001 Statut Modérateur Dernière intervention 28 août 2015 79
14 avril 2011 à 14:25
Salut
Dans ton code, on ne voit pas quand (à quel moment) tu exploites les données.
Le buffer se déclenchera à chaque fois qu'il y auras X octets disponibles.
Cela ne veut pas dire que toutes les données sont arrivées.
Il faut donc que tu testes si ta trame est complète.
Si elle ne l'ai pas, il faut la mémoriser sans rien faire d'autre, puis, à la réception suivante, ajouter les nouvelles données à celles mémorisées.
Refaire le test de complitude (merci Ségolène, je l'avait au bout de la langue).
Si, cette fois, tu détectes bien la fin de trame, isoler la trame à traiter et surtout, pensez à remettre dans le buffer les données qui pourraient se trouver après cette trame complète.

Vala
Jack, MVP VB
NB : Je ne répondrai pas aux messages privés

Le savoir est la seule matière qui s'accroit quand on la partage (Socrate)
0
cs_chamicki Messages postés 28 Date d'inscription mardi 14 octobre 2008 Statut Membre Dernière intervention 15 avril 2011
14 avril 2011 à 14:27
Et bien il semblerai que ce sois un peu ce qu'il se passe...

Car avec un point d'arrêt plus loin dans ma fonction.

J'ai eu dans BytesToRead 4 et si je regarde SerialPort.BytesToRead il y en a 3
Or ma trame contient 7 bytes.

Comment puis-je remédier à cela ?
0
cs_chamicki Messages postés 28 Date d'inscription mardi 14 octobre 2008 Statut Membre Dernière intervention 15 avril 2011
14 avril 2011 à 14:30
@Jack: Actuellement je traite en effet mes données à la suite de cette fonction.
Si je comprend bien ta réponse ce n'est pas ce qu'il faut faire.
Comment savoir si je reçois la fin de ma trame dans ce cas ?
0
lesdis Messages postés 403 Date d'inscription mercredi 19 avril 2006 Statut Membre Dernière intervention 7 août 2020
14 avril 2011 à 14:33
Je ne peux vous donner d'exemple, car je ne connais pas le protocole utilisé. Mais je suppose que cette trame à un type conteneur définis.
Par ex :
Protocole exemple :
Caractère de début de trame : 02H (sur un octet)
Longueur de la trame entre le numéro de la trame (inclus) et le caractère de fin (non inclus) : 1+longueur Data (sur 2 octets)
Numéro d'identification de la trame : 01H (sur un octet)
Data : XXH XXH ..... XXH (nb d'octet variable)
Caractère de fin : 03H (sur un octet)
CRC (caractère de contrôle) : XXH (sur un octet)


Dans ce cas, on peut faire en sorte de rechercher dans notre buffer un octet de valeur 02H, dès qu'on tombe dessus, on lit les 2 octets suivants qui devrait nous donner la taille de la trame.

Imaginons que l'octet 02H se trouve à l'index 0 de notre buffer, on va lire buffer(1) et buffer(2) pour connaître la taille de la trame. Disons que nous trouvons la valeur 12H (18 en décimal), nous allons lire au 18ème index + 3 (debut de trame+lognueur de trame) et nous devrions tomber sur un octet valant 03H.
Si ce n'est pas le cas, ce 02H n'est pas le début d'une trame, donc on continue à chercher un 02H dans le buffer a partir de l'index 1, etc.

Dans le cas contraire, on récupère le caractère de contrôle et on vérifie que la trame est bonne. Si c'est le cas, on analyse les data, sinon on jette à la poubelle et on continu de chercher une trame dans le buffer.

Une fois une trame traitée, on peut la supprimer du buffer (mais en gardant le reste).

J'espère avoir été compréhensible dans mon explication
Bonne Prog
0
cs_chamicki Messages postés 28 Date d'inscription mardi 14 octobre 2008 Statut Membre Dernière intervention 15 avril 2011
14 avril 2011 à 15:02
Dans le protocole Modbus, il n'y a pas de caractère de début de trame ou de fin de trame. Je ne peux donc pas utiliser cette méthode...

Comment puis-je donc procéder ?
0
lesdis Messages postés 403 Date d'inscription mercredi 19 avril 2006 Statut Membre Dernière intervention 7 août 2020
14 avril 2011 à 15:27
Le protocole Modbus utilise deux méthode sur des laision série, RTU ou ASCII. La méthode ASCII utilise des caractères de début de trame.
Le RTU se base sur les temps de silence sur la ligne et je ne saurais te dire comment implémenter celà.

Par contre, tu as une source ici en C#. Il te suffit juste de transcrire la syntaxe en VB.net. (petit indice, il existe des appliweb qui le font automatiquement )

Bonne Prog
0
foliv57 Messages postés 420 Date d'inscription vendredi 17 novembre 2006 Statut Membre Dernière intervention 15 juillet 2014 9
14 avril 2011 à 15:50
Bonjour à tous,

@chamicki -> Petite question de base, êtes vous en train de programmer un maitre ou un esclave modbus ?

Car si vous programmez un maitre, vous connaissez le nombre d'octets à recevoir ainsi que la valeur du premier octet en fonction de la requête émise.

Exemple :
Si vous demandez à l'escalve 1 une lecture de 10 mots, vous savez que votre réponse devra commencer par l'octet de valeur 1 et devra faire 26 octets.

Dans le cas d'un esclave, comme l'a dis lesdis, en RTU (soit 95% des cas) c'est le silence sur le ligne qui défini le début d'une nouvelle trame.
0
cs_chamicki Messages postés 28 Date d'inscription mardi 14 octobre 2008 Statut Membre Dernière intervention 15 avril 2011
14 avril 2011 à 16:25
Je programme un maitre.

En effet je connais la taille de la donnée supposée être reçu SAUF si il s'agit d'un code d'erreur par exemple.
0
cs_chamicki Messages postés 28 Date d'inscription mardi 14 octobre 2008 Statut Membre Dernière intervention 15 avril 2011
15 avril 2011 à 10:46
C'est bon tout marche bien avec vos 2 solutions !!!

Merci beaucoup pour votre aide !!!
0
Rejoignez-nous