none
Lire retour sur com Rs 232 sans décallage RRS feed

  • Question

  • Bonjour,  je rencontre un problème avec un développement de com Rs232 lié à ma programmation certainement . Je cherche à lire sur ma com le retour suite à un envoi de string < _Seri_Port.Write("DD" + Chr(13))> 'DD est une commande reconnue par l'appareil distant qui doit me répondre par une valeur. Cette valeur je pensais la récupérer dans une variable par l'intermédiaire de la commande suivante  < DD_Brut = ( Seri_Port.ReadLine ) à travers le buffer. Le problème rencontré est le suivant : Pas de lecture sur le premier passage, par contre je récupère mon résultat sur l'interrogation suivante,  qui n'a rien à voir .  Bon il est toujours possible de bidouiller avec des boucles et contrôle de retour mais cela n'est pas très propre et chronophage en  cycle machine ( Une vingtaine d'interrogation à réaliser sur mon système sans conter le traitement )Avez une Idée ?  Nota : j'ai essayé avec ReadTo et Readexisting  mais c'est le même problème . Merci de votre réponse

    lundi 26 mai 2014 18:38

Réponses

  • Bonsoir,

    Ok cela fonctionne, j'ai fait quelques modifications sur mon code pour le simplifier au mieux J'ai donc un code plus propre et plus simple pour permettre l'affichage. Il me restera plus qu'a coder l'ensemble de mes variables pour permettre l'affichage des informations . Je verrai pour le calcul par la suite...

    Sub Display_Delay() ' READ DISLAY DELAY Dim DD_Brut As String Try _Seri_Port.WriteLine("DD") 'Waiting for command to be send Thread.Sleep(Value_Sleep) 'Read : timeout for Rs RS232 _Seri_Port.ReadTimeout = Value_Too_Long 'Sending the value DD_Brut = _Seri_Port.ReadExisting Display_Delay.Text = Val(DD_Brut.Substring(4)) * 0.1 Catch ex As Exception 'If there is timeout print "Wait " into textbox Display_Delay.Text = "Wait" End Try

    End Sub

    Merci pour l'aide

    Cordialement



    • Modifié FrThib mercredi 4 juin 2014 21:18
    • Marqué comme réponse FrThib mercredi 4 juin 2014 21:18
    mercredi 4 juin 2014 21:13

Toutes les réponses

  • Bonjour,

    Cela doit certainement venir de votre matériel. Avez vous essayé d'utiliser un terminal (comme Tera Term http://ttssh2.sourceforge.jp/) pour communiquer avec votre matériel ?

    Cordialement


    Gilles TOURREAU - MVP C#
    Architecte logiciel/Consultant/Formateur Freelance - P.O.S Informatique
    Blog : http://gilles.tourreau.fr - Suivez-moi sur Twitter
    - MCPD : Enterprise Developper / Windows Developper 3.5 / ASP .NET 3.5/4.0
    - MCSA : SQL Server 2012
    - MCITP : SQL Server 2008 Developper
    - MCTS : ADO .NET 3.5 / SQL Server 2008 Developper / Windows Forms 3.5 / ASP .NET 3.5/4.0 / TFS 2010 / Windows Azure

    lundi 26 mai 2014 23:22
    Modérateur
  • Bonjour et merci pour votre réponse , Non, j'ai utilisé HyperTerminal en phase évaluation et la réponse est immédiate et correspond bien à ma demande ( Pas de décalage des réponses).     
    
    mardi 27 mai 2014 17:56
  • Bonjour,

    Pour avoir travaillé avec du RS232 pour la création de caisses enregistreuse (pilotage de CB), affichage LCD de prix et pilotage de tiroir caisse, je peux simplement vous dire que ce protocole n'est pas aussi simple qu'on pourrait le penser. Les données arrivent en série... Et c'est là que ça fait souvent mal.

    Ca fait très longtemps que je n'en ai pas fait (et sincèrement, je m'en porte mieux, lol), mais voici quelques infos.

    Si vous envoyer une commande et demander le retour instantanément après ça se passe très mal car le buffer du port série n'a probablement même pas commencer à se remplir.

    Maintenant, comme vous travaillez avec ReadLine il bloque en attendant le retour d'une nouvelle ligne.

    A moins d'avoir mis un TimeOut et recevoir une exception qui est catché, si le premier ReadLine passe, c'est bien qu'une nouvelle ligne vous est envoyé !

    Je vous propose dans un premier temps de vous assurer que tous vos réglages sur SerialPort sont les mêmes que ceux dans hyperterminal. Faîtes surtout attention au Handshake, puis vérifié la parité, nombre de bauds, etc. La taille du Buffer doit être suffisante sinon il peut tomber en overflow.

    Le fait de lire en décaler signale tout simplement que vous lisez le Buffer après coup. Vérifiez vos réglage sur la propriété NewLine du SerialPort car elle est configurable.

    Réalisez que par défaut elle vaut par défaut Environment.NewLine, c'est à dire \r\n, autrement dit chr(13) et chr(10). Peut-être le problème est-il tout simplement là ^^.

    Si ça ne marche toujours pas, merci de nous le signaler.

    Bien cordialement,

    Fabrice JEAN-FRANCOIS


    mardi 27 mai 2014 22:25
  • Bonsoir et merci pour vos conseils. J'ai vérifié le paramétrage com, et j'ai modifié le Newline mais rien n'y fait.  Une autre explication non négligeable  c'est que je suis débutant... Est-il-possible de voir les caractères  reçus et envoyés dans le buffer, si oui où? Je vais chercher encore un peu et si pas de résultat alors Je vais réfléchir à une autre stratégie de programmation pour éviter ce problème et en préservant le cycle machine au mieux. Cordialement   
    • Modifié FrThib jeudi 29 mai 2014 08:22
    mercredi 28 mai 2014 21:39
  • Bonjour,

    Lire ce qu'il y a dans le Buffer n'est malheureusement pas accessible à partir d'un objet SerialPort.

    A plus bas niveau, c'est toujours possible, notamment en décompilant mais je ne vais pas vous faire aller jusque là (surtout si vous êtes débutant... Quoi qu'au passage, c'est toujours sympathique de découvrir se qui se passe à très bas niveau en .NET, langage intermédiaire MSIL, puis compilation en langage machine. Ce code machine généré est assez beau, et vous pouvez voir l'équivalent assembleur avec Visual Studio^^... Bref)

    Je vous propose d'augmenter le ReadBufferSize ainsi que le WriteBufferSize pour commencer.

    Ensuite, lors de votre Write, vous faîtes bien un chr(13) et pas un WriteLine... Pourriez-vous svp harmoniser le code, en configurant NewLine à Chr(13) puis en faisant un WriteLine, pour s'assurer que tout est OK.

    Avant de faire le ReadLine, je vous propose enfin de faire un Thread.Sleep(2000)... C'est peut-être beaucoup (2 secondes), mais juste pour s'assure que votre device est OK ^^.

    D'ailleurs sans indiscrétion, il s'agit de quel type de matériel ? Votre code est-il du même acabit que celui-là ? :

    http://msdn.microsoft.com/fr-fr/library/system.io.ports.serialport(v=vs.110).aspx

    N'hésitez pas à me faire part du retour de ces tests... Même si ça plante... Car Hyperterminal ne fait rien de magique. Il n'y aucune raison que le comportement soit différent avec SerialPort.

    Bien cordialement,

    Fabrice JEAN-FRANCOIS

    • Proposé comme réponse Aurel Bera vendredi 30 mai 2014 12:03
    • Marqué comme réponse FrThib samedi 31 mai 2014 20:13
    • Non marqué comme réponse FrThib samedi 31 mai 2014 20:13
    vendredi 30 mai 2014 07:35
  • bonsoir, Merci pour votre réponse mais malheureusement cela ne fonctionne toujours pas, Effectivement votre lien sur "Serialport, Class" était ma base line pour mon appli. Le matériel utilisé est un système permettant de faire du Ctle US ( Sorte d'oscilloscope) , ce qu'il faut savoir de mon appli c'est que je ne cherche pas à faire l'acquisition de données reçu par le contrôleur Us, mais je cherche simplement à lire et écrire  les paramètres du contrôleur (environ 20 variables)  L'écriture semble bien fonctionner. La com est figée par constructeur et non modifiable  ( 9600, 8, N, 1) et la syntaxe pour lire est la suivante "<Command><enter> " Command étant les caractères reconnus par le contrôleur.  Vous trouverez ci-joint mon code épuré avec simplement le nécessaire de ma  communication, J'ai laissé la déclaration et le principe simplifié au maximum pour la lecture d'une information du contrôleur ( Read Display delay)  . Désolé pour la présentation mais j'ai du mal à utiliser le forum ( pas de saut de ligne et insertion de texte difficile ), cordialement

    

    


    • Modifié FrThib vendredi 30 mai 2014 20:58
    vendredi 30 mai 2014 20:56
  • Bonsoir,

    Merci pour votre code, je commence plus clairement à voir le problème.

    Je ne suis toujours pas certain que cela va fonctionner mais on va au moins régler le 1er petit problème

    Il faudrait svp remplacer

    _Seri_Port.NewLine = "Chr(13)"

    Par

    _Seri_Port.NewLine = Chr(13)


    En effet, votre syntaxe contraint le SerialPort à attendre la chaîne "Chr(13)" comme marque de terminaison et non le caratère Chr(13). Pour information Chr(13) ce dit aussi retour charriot, Carriage Return, CR, on le note aussi \r dans des langages comme C++, Java ou C#.

    Sur Mac, \r est suffisant dans un texte pour passer à la ligne. Sur Unix, Linux, ce n'est plus \r mais \n (pour saut de ligne. On dit aussi Chr(10), Line Feed, LF) qu'il faut mettre.

    Sur Windows, ce n'est pas 1, mais 2 caractères par défaut (à savoir \r\n... on dit aussi CRLF) qui indique un retour à la ligne.

    Ainsi NewLine par défaut vaut Chr(13)+Chr(10). Il faut absolument le configurer comme indiqué plus haut pour qu'il ne gère que Chr(13).

    Ensuite, il faut que vous remplaciez

    _Seri_Port.WriteLine("DD" + Chr(13))

    Par

    _Seri_Port.WriteLine("DD")


    En effet, ReadLine et WriteLine vont tout seul gérer les retours, cela simplifiera votre code.

    Si ça ne marche toujours pas, alors j'opte clairement pour un problème de handshake (littéralement, une poignée de main entre l'ordinateur et votre Device. Le Handshake est variable en fonction des appareils).

    Il faudrait svp que vous fassiez un test en mettant

    _Seri_Port.DtrEnable = True
    _Seri_Port.RtsEnable = True

    Si ça ne marche toujours pas, commentez une ligne ou l'autre, puis commencez à jouer sur la propriété Handshake avec des réglages du genre

     _Seri_Port.Handshake = Handshake.RequestToSend

    Il vous faudra alors adapter les Dtr/RtsEnable en fonction.

    En espérant que ça fonctionne,

    Bien cordialement,

    Fabrice JEAN-FRANCOIS

    P.S : Si vous avez une documentaton sur votre device, les informations sur le Handshake seront précisés. Cela vous évitera de perdre trop de temps^^ 

    vendredi 30 mai 2014 23:43
  • Encore merci pour vos remarques intéressantes, mais je suis toujours face à ce problème, J'ai essayé toutes les configurations possibles entre  Handshake, DtrEnable et Rtsenable ( 16 possibilités) mais sans résultat , cela m'a amener à vérifier mon câble de liaison ( Ok ). Pour info , ce câble NulModem 9P femelles  est connecté en 2/3 3/2 avec shunt entre 4/6 et 7/8 de chaque coté et une masse en 5. Ce câble est connecté au Pc par un adaptateur Usb/Rs232. Pour le handshake avec HyperTerminal le système fonctionne quelque soit la configuration. J'ai quelques doutes sur ma décaration SerialPort :-(  ..   Merci encore pour votre aide . Codialement  

    samedi 31 mai 2014 20:17
  • Véritablement étrange pour le coup. Hyperterminal fait appel aux API Win32 directement, SerialPort engloble le tout... Mais si ça marche avec HyperTerminal, nous devrions pouvoir faire un fonctionnement avec C# (Sauf si Hyperterminal est "silencieux" sur certaines erreurs et les laisse passer mais nous n'en sommes encore pas là).

    Est-ce que vous lancez-vous un thread comme dans l'exemple http://msdn.microsoft.com/fr-fr/library/system.io.ports.serialport(v=vs.110).aspx ?

    Dans ce cas, vous pourriez avoir une erreur de threading lors de l'appel Display_Delay.Text si Display_Delay est un composant Winform genre une TextBox.

    Etes-vous passé en mode Debug pour voir si une erreur est générée ? Je vous propose d'enlever le Try qui englobe le ReadLine pour savoir si une exception est lancée et de nous la remonter.

    Dans le cas contraire, s'il ne se passe rien en mode debug, qu'aucune information n'est remontée, pourriez-vous svp essayer une implémentation comme la suivante : http://msdn.microsoft.com/fr-fr/library/system.io.ports.serialport.datareceived(v=vs.110).aspx

    Vous ne recevrez pas les données octets par octets mais cela nous permettra de voir s'il y a bien "échange". 

    Cordialement

    samedi 31 mai 2014 22:56
  • Bonjour, Effectivement c'est très curieux., Généralement,  Je lance toujours en mode debug , avec un suivi de ma variable qui est vide sur la premier passage , je dis le premier passage  car j'ai mis un boucle de ctrl qui, si ma variable est vide <"">, effectue une nouvelle demande, au deuxième passage je trouve la valeur attendu sous le format suivant <""& vbLf & "DD 16000"> . En imaginant que je réalise plusieurs type de demande différente, la réponse attendu arrive pendant l'interrogation suivante. Effectivement j'utilise un thread = 100 , que j'ai désactivé mais qui ne me posait pas de problème lors de la lecture, j'ai voulu épurer au maximum pour éviter les problèmes externes .  En enlevant le try même comportement.  Pour la méthode <sedrialport.datareceived> je regarde comment l'intégrer et je vous tiens informé du résultat. Cordialement    

    dimanche 1 juin 2014 12:51
  • Bonjour,

    Le VbLf est ici intéressant car cela pourrait signifier qu'il faudrait tester avec NewLine= Chr(10) voire NewLine= Chr(13) + Chr(10). Le retour à la ligne est peut-être tout simplement inversé sur votre device !!

    J'attends vos retours avec impatience ^^

    dimanche 1 juin 2014 16:09
  • Bonsoir , j'ai essayé avec le Chr(13) + Chr(10),  le Chr(10) seul puis  le Chr(10) + Chr(13), même comportement pour le premier cas et   aucune  réponse pour les deux derniers essais  par contre j'ai le message d'exception suivant <" l'exception TimeoutException n'a pas été gérée">  <" Le délai d'attente de l'opération a expiré ">. Nous sommes bien d'accord sur le formalisme --> question par HyperTerminal <" DD + validation "> et la réponse sur la ligne suivante est par exemple  <" DD 1800 "> ..Merci encore et bonne journée,   Cordialement


    • Modifié FrThib lundi 2 juin 2014 05:01 Complement d'informations
    dimanche 1 juin 2014 17:33
  • Bonjour,

    J'ai eu quelques problèmes pour vous répondre, mais il semble que ça aille mieux^^

    Pourriez-vous svp remplacer la ligne

    Display_Delay.Text = _Seri_Port.ReadLine()

    Par

    Dim msg As String = _serialPort.ReadLine()

    Puis rentrer en mode debug et observer le texte dans msg. Comme il y a plusieurs threads, on doit être certain qu'il n'y a pas de pb lié à l'interface (normalement votre appel devrait passer par un Invoke).

    Ensuite, si rien ne change, pourriez-vous essayer un appel avec Read à la place de ReadLine ? 

    Read lit un nombre de caractères dans un buffer, on saura ainsi rapidement si celui-ci se remplit bien après votre appel (Remettez un Thread.Sleep avant le Read pour être sûr d'avoir laissé du temps pour la réponse)

    Bien cordialement

    mardi 3 juin 2014 16:18
  • bonsoir,

    Pas de problème pour le silence des deux derniers jours, je suis très pris aussi avec des journée bien remplies et je dirai que  cette programmation est ma période de détente pour finir ma journée. Vous remarquerez aussi que la présentation du post est nettement plus agréable, problème de version internet explorer.

    bref, j'ai suivi vos derniers conseils, malheureusement j'ai du mal à comprendre la syntaxe à utiliser  pour faire fonctionner le Read. Néanmoins en essayant le ReadExisting  la lecture se fait au premier passage. Voici le résultat

    "" & vbCrLf & "DD 16000"

    Par contre, avec ce type de lecture,  un autre problème se présente pour la suite,   lors de la décomposition de la trame reçu ( He oui il faut que je  décompose cette trame  pour la  transformer en Interger, j'ai des calculs à réaliser par la suite avec les valeurs lus   )  cela se passe plutôt bizarrement . j'ai choisi pour l'instant la commande suivante pour la décomposition

    MaValInteger = Val(Mid(msg, 3)) 

    Le résultat donne MavalInteger = 0

    pour avoir MavalInteger = 16000 il faut ecrire cela

    MaValInteger = Val(Mid(msg, 6)

    pourquoi 6 ?

    Finalement j'ai fait plusieurs manip pour  comprendre dont une avec un résultat très curieux,  j'ai comptabilisé le nombre de DD dans la trame reçu en utilisant la commande ci-dessous

     Nb_De_DD = InStr(1, msg, "DD")

    Nb_De_DD = 3

    Pourquoi 3 alors qu'il en existe seulement 1 "DD" dans la trame lue.

    Donc beaucoup d'essais pour ce soir qui ne m'éclaire pas spécialement pour la suite de  mon travail .

     J'ai un peu dépassé les limites de votre proposition d'essais, mais peut-être que cela vous éclairera sur mon problème.

    Je tiens à vous remercier encore pour votre aide

    Cordialement  


    • Modifié FrThib mardi 3 juin 2014 21:21
    mardi 3 juin 2014 21:19
  • Bonsoir,

    C'est une super nouvelle pour le ReadExisting, on ne pouvait pas être plus proche du matériel.

    Concernant le reste des problèmes, c'est véritablement simple quand on sait ce qui se passe derrière.

    Ainsi quand on écrit 

      Dim msg = vbCrLf & "DD 16000"

    On a en réalité msg qui a une taille de 10 ! Oui, vous avez bien lu (Pour s'en assurer, vous pouvez demandez     msg.Length).

    Alors pourquoi 10 alors que "DD 16000" a une taille de 8 caractères (car il ne faut pas oublier l'espace entre DD et 16000). Tout simple parce que vbCrLf correspond à Chr(13) + Chr (10), c'est à dire 2 caractères.

    La fonction Mid vous renvoie une chaîne de caractère à partir d'une position. Si l'on tient compte des 2 caractères masqués par votre chaine en fait msg est équivalent à quelque-chose du genre "**DD 16000" où ** valent Chr(13) et Chr (10). Si vous comptez bien, la 6 ème position dans votre chaine correspond au chiffre 1 ! La fonction mid vous renvoie tout ce qu'il y a après la 6 ème position, c'est à dire 16000.

    Le Mid(msg,3) vous renvoie zéro car si vous comptez la 3ème position, vous arrivez sur la première lettre D ! La fonction mid vous renvoie alors "DD 16000", ce qui ne peut pas se convertir en entier.

    Enfin, chose très importante, la fonction Instr ne vous renvoie pas le nombre d'éléments dans une chaîne, il vous indique la 1ère position de ces éléments dans une chaîne. Si l'on part à nouveau de notre pseudo-chaine "**DD 16000" où "**" correspondant à votre VbCrLf, la première position de "DD" est bien 3.

    La position 1 correspond à CR, c'est à dire char(13), la seconde position à LF c'est à dire Char(10), et la 3ème position correspond à notre DD ^;-)

    Il faut l'avouer, ça fait une gymnastique intellectuelle particulière et risque de compliquer la relecture  de votre code. Je vous propose l'astuce suivante avant le travail de vos chaînes qui commence par VbCrLf. Il vous faut coder qqc du genre.

    Dim msg = vbCrLf & "DD 16000"
    msg = msg.Substring(2)

    Vous obtiendrez ainsi dans msg la valeur "DD 16000" débarrassé du VbCrLf du début.

    Avec cette astuce,  la formule suivante marche parfaitement :

    MaValInteger = Val(Mid(msg, 3)) 

    En espérant que tout fonctionne,

    Bien cordialement,

    Fabrice JEAN-FRANCOIS

    mardi 3 juin 2014 22:27
  • Bonsoir,

    Ok cela fonctionne, j'ai fait quelques modifications sur mon code pour le simplifier au mieux J'ai donc un code plus propre et plus simple pour permettre l'affichage. Il me restera plus qu'a coder l'ensemble de mes variables pour permettre l'affichage des informations . Je verrai pour le calcul par la suite...

    Sub Display_Delay() ' READ DISLAY DELAY Dim DD_Brut As String Try _Seri_Port.WriteLine("DD") 'Waiting for command to be send Thread.Sleep(Value_Sleep) 'Read : timeout for Rs RS232 _Seri_Port.ReadTimeout = Value_Too_Long 'Sending the value DD_Brut = _Seri_Port.ReadExisting Display_Delay.Text = Val(DD_Brut.Substring(4)) * 0.1 Catch ex As Exception 'If there is timeout print "Wait " into textbox Display_Delay.Text = "Wait" End Try

    End Sub

    Merci pour l'aide

    Cordialement



    • Modifié FrThib mercredi 4 juin 2014 21:18
    • Marqué comme réponse FrThib mercredi 4 juin 2014 21:18
    mercredi 4 juin 2014 21:13