Как использовать событие dataReceived объекта порта SerialPort в С#?

Я пытаюсь создать небольшое приложение для сбора данных, полученных от внешнего датчика, подключенного к COM10. Я успешно создал небольшой объект консоли С# и приложение, которое открывает порт и передает данные в файл в течение фиксированного периода времени с использованием цикла for.

Я хотел бы преобразовать это приложение, чтобы вместо этого использовать событие dataReceived для потоковой передачи. После прочтения Top 5 советов по SerialPort, я до сих пор не могу заставить это работать и не знаю, что мне не хватает. Я переписал консольное приложение так, что весь код находится в Main и вставлен ниже. Может кто-нибудь, пожалуйста, помогите мне просветить, почему обработчик событий port_OnReceiveDatazz не вызывается, хотя я знаю, что аппаратное обеспечение отправляет данные на порт?

Спасибо

Благодаря @Gabe, @Jason Down и @abatishchev за все предложения. Я в тупике и не могу заставить работать обработчик событий. Возможно, это как-то связано с устройством. Я могу жить, просто читая порт в потоке и передавая данные прямо в файл.


Код


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));
        }
    }
}

person Azim J    schedule 21.01.2009    source источник
comment
@Jon B: обработчик событий никогда не вызывается, даже если данные принимаются.   -  person Azim J    schedule 21.01.2009


Ответы (7)


Я думаю, ваша проблема связана со строкой:**

sp.DataReceived += port_OnReceiveDatazz;

Разве не должно быть:

sp.DataReceived += новый SerialDataReceivedEventHandler (port_OnReceiveDatazz);

** Неважно, синтаксис в порядке (не понял ярлык в то время, когда я изначально отвечал на этот вопрос).

Я также видел предложения включить следующие параметры для последовательного порта:

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

Возможно, вам также придется установить для рукопожатия значение RequestToSend (через перечисление рукопожатий).


ОБНОВЛЕНИЕ:

Нашел предложение, в котором говорится, что вы должны сначала открыть свой порт, а затем назначить обработчик событий. Может быть, это ошибка?

Итак, вместо этого:

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

Сделай это:

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

Дайте мне знать, как это происходит.

person Jason Down    schedule 21.01.2009
comment
+= новый SerialDataReceivedEventHandler - person abatishchev; 21.01.2009
comment
да я тоже так пробовал. port_OnReceivedDatazz по-прежнему не вызывается, хотя я точно знаю, что данные принимаются. - person Azim J; 21.01.2009
comment
установка для DtrEnable и RtsEnable значения true, похоже, прерывает связь, и когда я закрываю порт, устройство не выполняет внутреннюю самопроверку :( - person Azim J; 21.01.2009
comment
Вам также может понадобиться поиграть с CtsHolding и DsrHolding. Я не использовал эти вещи в течение некоторого времени, поэтому я не могу точно, как они работают. Если вы включаете элементы rts и dtr, вам необходимо проверить наличие cts/dsr, прежде чем выполнять запись. - person Jason Down; 21.01.2009
comment
в порядке. Я рассмотрю свойства CtsHolding и DsrHolding. Я также заметил, что вы предлагаете мне добавить обработчик событий после открытия порта. Благодарность - person Azim J; 21.01.2009
comment
@Jason Down: изменен порядок открытия порта и добавления обработчика событий. событие получения данных по-прежнему не запускается. - person Azim J; 21.01.2009
comment
в порядке. Джейсон спасибо за ваши усилия. Я тоже в тупике! Возможно, это как-то связано с устройством. - person Azim J; 21.01.2009
comment
Может быть, попробуйте еще одну вещь, хотя я бы не стал вас обнадеживать... Попробуйте отправить пару символов \r\n в последовательный порт перед отправкой символа $. - person Jason Down; 21.01.2009
comment
попытался отправить четыре \r\n перед символом $. Все еще не запускает событие получения. - person Azim J; 21.01.2009
comment
Блэр! Может угрожать устройству ;) - person Jason Down; 21.01.2009
comment
Насчет выкинуть в окно. в любом случае спасибо за помощь! - person Azim J; 21.01.2009

Во-первых, я рекомендую вам использовать следующий конструктор вместо того, который вы используете в настоящее время:

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

Далее вам действительно следует удалить этот код:

// 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
}

И вместо этого просто зацикливайтесь, пока пользователь не нажмет клавишу или что-то в этом роде, например:

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();
        }
    }
}

Кроме того, обратите внимание на изменения в обработчике событий получения данных, теперь он должен распечатать буфер.

ОБНОВЛЕНИЕ 1


Я только что успешно запустил следующий код на своей машине (используя нуль-модемный кабель между COM33 и 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();
        }
    }
}

ОБНОВЛЕНИЕ 2


Учитывая весь трафик по этому вопросу в последнее время. Я начинаю подозревать, что либо ваш последовательный порт настроен неправильно, либо устройство не отвечает.

Я настоятельно рекомендую вам попытаться связаться с устройством, используя другие средства (я часто использую гипертерминал). Затем вы можете поиграть со всеми этими настройками (битрейт, четность, биты данных, стоповые биты, управление потоком), пока не найдете подходящий набор. В документации к устройству также должны быть указаны эти настройки. Как только я это выясню, я должен убедиться, что мой .NET SerialPort правильно настроен для использования этих параметров.

Несколько советов по настройке последовательного порта:

Обратите внимание: когда я сказал, что вы должны использовать следующий конструктор, я имел в виду, что используйте эту функцию, а не обязательно эти параметры! Вам необходимо заполнить параметры для вашего устройства, приведенные ниже настройки являются общими, но могут отличаться для вашего устройства.

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

Также важно, чтобы вы настроили .NET SerialPort на использование того же управления потоком, что и ваше устройство (как ранее заявляли другие люди). Вы можете найти больше информации здесь:

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

person Gabe    schedule 21.01.2009
comment
Привет спасибо. Я изменил свой код, как вы предложили, но обработчик событий по-прежнему не вызывается. - person Azim J; 21.01.2009
comment
Вы уверены, что рукопожатие настроено правильно? Какие настройки последовательного порта вы использовали в прошлом (в гипертерминале или где-то еще), чтобы иметь возможность общаться с ним? - person Gabe; 21.01.2009
comment
В вашем последнем редактировании вы запускаете событие, отправляя данные в поток записи. Поток записи просто имитирует отправку данных моим устройством. - person Azim J; 21.01.2009
comment
Это правильно. На самом деле у меня нет вашего устройства, поэтому мне пришлось его как-то сымитировать. Похоже, либо устройство не отправляет данные, либо ваш последовательный порт настроен неправильно. Как вы общались с устройством в прошлом? - person Gabe; 22.01.2009
comment
С настройками по умолчанию, когда я создаю объект serialport. Так что на самом деле Console.WriteLine в моем цикле for распечатывает полученные данные. Но обработчик событий никогда не вызывается. - person Azim J; 22.01.2009
comment
Вы должны удалить этот цикл for для вызова обработчика событий. Смотрите мой код выше. - person Gabe; 22.01.2009
comment
Да, я знаю. Я удалил цикл for. Я могу быть уверен, что устройство отправляет данные, потому что есть данные, которые нужно прочитать в буфере порта, прежде чем я закрою порт. Спасибо за обновления вашего вопроса. - person Azim J; 22.01.2009

У меня была такая же проблема с модемом, который раньше работал, а потом в один прекрасный день просто перестал вызывать событие DataReceived.

Решение в моем случае, очень случайным образом, заключалось в том, чтобы включить RTS, например.

sp.RtsEnable = true;

Понятия не имею, почему это сработало с этим конкретным комплектом (на самом деле, я вообще не связист), или почему это сработало, а затем перестало, но это может помочь кому-то еще однажды, так что просто опубликую это на всякий случай...

person Justin Wignall    schedule 08.06.2009
comment
То же самое для меня. Довольно странно, - person guiomie; 26.07.2013

Кстати, вы можете использовать следующий код в своем обработчике событий:

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

Я считаю, что это не сработает, потому что вы используете консольное приложение, а цикл событий не запущен. Цикл событий/насос сообщений, используемый для обработки событий, настраивается автоматически при создании приложения Winforms, но не для консольного приложения.

person Matt    schedule 14.03.2011
comment
Спасибо за информацию. К сожалению, я знаю, что у меня больше нет оборудования, поэтому я не могу проверить. В конце концов я просто постоянно читал порт вместо использования обработчиков событий. Я учту ваше предложение, если мы вернемся к этому коду. - person Azim J; 14.03.2011

На самом деле вполне может быть, что Console.ReadLine блокирует Console.Writeline вашего обратного вызова. Образец на MSDN выглядит ПОЧТИ идентично, за исключением того, что они используют ReadKey (который не блокирует консоль).

person Bart de Boer    schedule 12.03.2014

Имейте в виду, что при использовании .NET/C# и любого COM-порта выше COM9 могут возникать проблемы.

См.: HOWTO: укажите последовательные порты больше, чем COM9

Существует обходной путь в формате: "\\.\COM10", который поддерживается в базовом методе CreateFile, но .NET не позволяет использовать этот обходной формат; ни конструктор SerialPort, ни свойство PortName не разрешают использовать имя порта, начинающееся с «\».

Я изо всех сил пытался получить надежную связь с COM10 в С#/.NET. Например, если у меня есть устройство на COM9 и COM10, трафик, предназначенный для COM10, идет на устройство на COM9! Если я удаляю устройство на COM9, трафик COM10 идет на устройство на COM10.

Я до сих пор не понял, как использовать дескриптор, возвращаемый CreateFile, для создания объекта SerialPort в стиле С#/.NET, если бы я знал, как это сделать, то я думаю, что мог бы прекрасно использовать COM10+ из С#.

person MikeZ    schedule 02.10.2016