Reception modbus Rs485 [Résolu]

cs_chamicki 28 Messages postés mardi 14 octobre 2008Date d'inscription 15 avril 2011 Dernière intervention - 14 avril 2011 à 13:31 - Dernière réponse : cs_chamicki 28 Messages postés mardi 14 octobre 2008Date d'inscription 15 avril 2011 Dernière intervention
- 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
Afficher la suite 

Votre réponse

14 réponses

Meilleure réponse
lesdis 401 Messages postés mercredi 19 avril 2006Date d'inscription 6 juin 2011 Dernière intervention - 14 avril 2011 à 14:40
3
Merci
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

Merci lesdis 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 93 internautes ce mois-ci

Commenter la réponse de lesdis
Meilleure réponse
foliv57 423 Messages postés vendredi 17 novembre 2006Date d'inscription 15 juillet 2014 Dernière intervention - 14 avril 2011 à 16:41
3
Merci
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

Merci foliv57 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 93 internautes ce mois-ci

Commenter la réponse de foliv57
lesdis 401 Messages postés mercredi 19 avril 2006Date d'inscription 6 juin 2011 Dernière intervention - 14 avril 2011 à 14:15
0
Merci
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
Commenter la réponse de lesdis
cs_chamicki 28 Messages postés mardi 14 octobre 2008Date d'inscription 15 avril 2011 Dernière intervention - 14 avril 2011 à 14:18
0
Merci
Bonjour et merci de porte intérêt à mon problème.

Je ne comprend pas trop votre solution, pouvez vous me donner un exemple ?
Commenter la réponse de cs_chamicki
lesdis 401 Messages postés mercredi 19 avril 2006Date d'inscription 6 juin 2011 Dernière intervention - 14 avril 2011 à 14:20
0
Merci
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
Commenter la réponse de lesdis
cs_Jack 14010 Messages postés samedi 29 décembre 2001Date d'inscription 28 août 2015 Dernière intervention - 14 avril 2011 à 14:25
0
Merci
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)
Commenter la réponse de cs_Jack
cs_chamicki 28 Messages postés mardi 14 octobre 2008Date d'inscription 15 avril 2011 Dernière intervention - 14 avril 2011 à 14:27
0
Merci
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 ?
Commenter la réponse de cs_chamicki
cs_chamicki 28 Messages postés mardi 14 octobre 2008Date d'inscription 15 avril 2011 Dernière intervention - 14 avril 2011 à 14:30
0
Merci
@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 ?
Commenter la réponse de cs_chamicki
lesdis 401 Messages postés mercredi 19 avril 2006Date d'inscription 6 juin 2011 Dernière intervention - 14 avril 2011 à 14:33
0
Merci
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
Commenter la réponse de lesdis
cs_chamicki 28 Messages postés mardi 14 octobre 2008Date d'inscription 15 avril 2011 Dernière intervention - 14 avril 2011 à 15:02
0
Merci
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 ?
Commenter la réponse de cs_chamicki
lesdis 401 Messages postés mercredi 19 avril 2006Date d'inscription 6 juin 2011 Dernière intervention - 14 avril 2011 à 15:27
0
Merci
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
Commenter la réponse de lesdis
foliv57 423 Messages postés vendredi 17 novembre 2006Date d'inscription 15 juillet 2014 Dernière intervention - 14 avril 2011 à 15:50
0
Merci
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.
Commenter la réponse de foliv57
cs_chamicki 28 Messages postés mardi 14 octobre 2008Date d'inscription 15 avril 2011 Dernière intervention - 14 avril 2011 à 16:25
0
Merci
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.
Commenter la réponse de cs_chamicki
cs_chamicki 28 Messages postés mardi 14 octobre 2008Date d'inscription 15 avril 2011 Dernière intervention - 15 avril 2011 à 10:46
0
Merci
C'est bon tout marche bien avec vos 2 solutions !!!

Merci beaucoup pour votre aide !!!
Commenter la réponse de cs_chamicki

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.