web-dev-qa-db-fra.com

Comment utiliser l'événement dataReceived de l'objet port SerialPort en C #?

J'essaie de créer une petite application pour collecter les données reçues d'un capteur externe connecté à COM10. J'ai réussi à créer un petit objet et une application de console C # qui ouvre le port et diffuse des données dans un fichier pendant une période de temps fixe à l'aide d'une boucle for.

Je voudrais convertir cette application pour utiliser l'événement dataReceived pour diffuser à la place. Après avoir lu le Top 5 SerialPort Tips , je n'arrive toujours pas à le faire fonctionner et je ne sais pas ce qui me manque. J'ai réécrit l'application console pour que tout le code soit dans Main et soit collé ci-dessous. Quelqu'un peut-il m'aider à comprendre pourquoi le gestionnaire d'événements port_OnReceiveDatazz n'est pas appelé même si je sais que le matériel envoie des données au port?

Merci

Azim

PS: Merci à @ Gabe , @ Jason Down , et @ abatishchev pour toutes les suggestions. Je suis perplexe et je n'arrive pas à faire fonctionner le gestionnaire d'événements. Cela a peut-être quelque chose à voir avec l'appareil. Je peux vivre en lisant simplement le port dans un fil et en diffusant les données directement dans un fichier.


Code


namespace serialPortCollection
{   class Program
    {
        static void Main(string[] args)
        {

            const int bufSize = 2048;
            Byte[] buf = new Byte[bufSize]; //To store the received data.

            SerialPort sp = new SerialPort("COM10", 115200);
            sp.DataReceived += port_OnReceiveDatazz; // Add DataReceived Event Handler

            sp.Open();
            sp.WriteLine("$"); //Command to start Data Stream

            // Wait for data or user input to continue.
            Console.ReadLine();

            sp.WriteLine("!"); //Stop Data Stream Command
            sp.Close();
        }

       // My Event Handler Method
        private static void port_OnReceiveDatazz(object sender, 
                                   SerialDataReceivedEventArgs e)
        {
            SerialPort spL = (SerialPort) sender;
            const int bufSize = 12;
            Byte[] buf = new Byte[bufSize];
            Console.WriteLine("DATA RECEIVED!");
            Console.WriteLine(spL.Read(buf, 0, bufSize));
        }
    }
}
19
Azim

Je pense que votre problème est la ligne:**

sp.DataReceived + = port_OnReceiveDatazz;

Cela ne devrait-il pas être:

sp.DataReceived + = new SerialDataReceivedEventHandler (port_OnReceiveDatazz);

** Peu importe, la syntaxe est correcte (je n'ai pas réalisé le raccourci au moment où j'ai répondu à l'origine à cette question).

J'ai également vu des suggestions selon lesquelles vous devriez activer les options suivantes pour votre port série:

sp.DtrEnable = true;    // Data-terminal-ready
sp.RtsEnable = true;    // Request-to-send

Vous devrez peut-être également définir la négociation sur RequestToSend (via l'énumération de la négociation).


MISE À JOUR:

Trouvé une suggestion qui dit que vous devez d'abord ouvrir votre port, puis affecter le gestionnaire d'événements. C'est peut-être un bug?

Donc au lieu de ça:

sp.DataReceived += new SerialDataReceivedEventHandler (port_OnReceiveDatazz);
sp.Open();

Faites ceci:

sp.Open();
sp.DataReceived += new SerialDataReceivedEventHandler (port_OnReceiveDatazz);

Laissez-moi savoir comment ça se passe.

12
Jason Down

Tout d'abord, je vous recommande d'utiliser le constructeur suivant au lieu de celui que vous utilisez actuellement:

new SerialPort("COM10", 115200, Parity.None, 8, StopBits.One);

Ensuite, vous devez vraiment supprimer ce code:

// Wait 10 Seconds for data...
for (int i = 0; i < 1000; i++)
{
    Thread.Sleep(10);
    Console.WriteLine(sp.Read(buf,0,bufSize)); //prints data directly to the Console
}

Et au lieu de cela, bouclez jusqu'à ce que l'utilisateur appuie sur une touche ou quelque chose, comme ceci:

namespace serialPortCollection
{   class Program
    {
        static void Main(string[] args)
        {
            SerialPort sp = new SerialPort("COM10", 115200);
            sp.DataReceived += port_OnReceiveDatazz; // Add DataReceived Event Handler

            sp.Open();
            sp.WriteLine("$"); //Command to start Data Stream

            Console.ReadLine();

            sp.WriteLine("!"); //Stop Data Stream Command
            sp.Close();
        }

       // My Event Handler Method
        private static void port_OnReceiveDatazz(object sender, 
                                   SerialDataReceivedEventArgs e)
        {
            SerialPort spL = (SerialPort) sender;
            byte[] buf = new byte[spL.BytesToRead];
            Console.WriteLine("DATA RECEIVED!");
            spL.Read(buf, 0, buf.Length);
            foreach (Byte b in buf)
            {
                Console.Write(b.ToString());
            }
            Console.WriteLine();
        }
    }
}

Notez également les révisions apportées au gestionnaire d'événements de données reçues, il devrait en fait imprimer le tampon maintenant.

MISE À JOUR 1


Je viens d'exécuter le code suivant avec succès sur ma machine (en utilisant un câble null modem entre COM33 et COM34)

namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread writeThread = new Thread(new ThreadStart(WriteThread));
            SerialPort sp = new SerialPort("COM33", 115200, Parity.None, 8, StopBits.One);
            sp.DataReceived += port_OnReceiveDatazz; // Add DataReceived Event Handler

            sp.Open();
            sp.WriteLine("$"); //Command to start Data Stream

            writeThread.Start();

            Console.ReadLine();

            sp.WriteLine("!"); //Stop Data Stream Command
            sp.Close();
        }

        private static void port_OnReceiveDatazz(object sender, 
                                   SerialDataReceivedEventArgs e)
        {
            SerialPort spL = (SerialPort) sender;
            byte[] buf = new byte[spL.BytesToRead];
            Console.WriteLine("DATA RECEIVED!");
            spL.Read(buf, 0, buf.Length);
            foreach (Byte b in buf)
            {
                Console.Write(b.ToString() + " ");
            }
            Console.WriteLine();
        }

        private static void WriteThread()
        {
            SerialPort sp2 = new SerialPort("COM34", 115200, Parity.None, 8, StopBits.One);
            sp2.Open();
            byte[] buf = new byte[100];
            for (byte i = 0; i < 100; i++)
            {
                buf[i] = i;
            }
            sp2.Write(buf, 0, buf.Length);
            sp2.Close();
        }
    }
}

MISE À JOUR 2


Compte tenu de tout le trafic sur cette question récemment. Je commence à soupçonner que votre port série n'est pas configuré correctement ou que l'appareil ne répond pas.

Je vous recommande fortement d'essayer de communiquer avec l'appareil en utilisant d'autres moyens (j'utilise l'hyperterminal fréquemment). Vous pouvez ensuite jouer avec tous ces paramètres (débit binaire, parité, bits de données, bits d'arrêt, contrôle de flux) jusqu'à ce que vous trouviez l'ensemble qui fonctionne. La documentation de l'appareil doit également spécifier ces paramètres. Une fois que je les aurais compris, je m'assurerais que mon .NET SerialPort est correctement configuré pour utiliser ces paramètres.

Quelques conseils sur la configuration du port série:

Notez que lorsque j'ai dit que vous devriez utiliser le constructeur suivant, je voulais dire que vous utilisez cette fonction, pas nécessairement ces paramètres! Vous devez remplir les paramètres de votre appareil, les paramètres ci-dessous sont communs, mais peuvent être différents pour votre appareil.

new SerialPort("COM10", 115200, Parity.None, 8, StopBits.One);

Il est également important que vous configuriez le .NET SerialPort pour utiliser le même contrôle de flux que votre appareil (comme d'autres personnes l'ont indiqué précédemment). Vous pouvez trouver plus d'informations ici:

http://www.lammertbies.nl/comm/info/RS-232_flow_control.html

9
Gabe

Soit dit en passant, vous pouvez utiliser le code suivant dans votre gestionnaire d'événements:

switch(e.EventType)
{
  case SerialData.Chars:
  {
    // means you receives something
    break;
  }
  case SerialData.Eof:
  {
    // means receiving ended
    break;
  }
}
3
abatishchev

Je crois que cela ne fonctionnera pas parce que vous utilisez une application console et qu'aucune boucle d'événement n'est en cours d'exécution. Une boucle d'événement/pompe de messages utilisée pour la gestion des événements est configurée automatiquement lorsqu'une application Winforms est créée, mais pas pour une application console.

1
Matt

Pourrait très bien être le Console.ReadLine blocage de votre rappel Console.Writeline, En réalité. L'exemple sur MSDN semble presque identique, sauf qu'ils utilisent ReadKey (qui ne verrouille pas la console).

1
Bart de Boer

Sachez qu'il existe des problèmes d'utilisation de .NET/C # et de tout port COM supérieur à COM9.

Voir: HOWTO: spécifier des ports série plus grands que COM9

Il existe une solution de contournement au format: "\\.\COM10" qui est prise en charge dans la méthode CreateFile sous-jacente, mais .NET empêche d'utiliser ce format de solution de contournement; ni le constructeur SerialPort ni la propriété PortName n'autoriseront un nom de port commençant par "\"

J'ai eu du mal à obtenir des communications fiables vers COM10 en C # /. NET. Par exemple, si j'ai un appareil sur COM9 et COM10, le trafic destiné à COM10 va à l'appareil sur COM9! Si je supprime le périphérique sur COM9, le trafic COM10 va vers le périphérique sur COM10.

Je n'ai toujours pas compris comment utiliser le handle retourné par CreateFile pour créer un objet SerialPort de style C #/.NET, si je savais comment le faire, alors je pense que je pourrais utiliser COM10 + très bien à partir de C #.

0
MikeZ