Проблема с последовательным портом C # - слишком просто, чтобы потерпеть неудачу, но - PullRequest
3 голосов
/ 20 апреля 2011

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

Я использую Unity 3.3 (поддержка .Net 2.0), а «последовательный порт» - это адаптер последовательного интерфейса USB. Кстати: с помощью Hyperterminal все работает отлично, поэтому я знаю, что это не драйвер и не оборудование.

Я могу открыть порт нормально. Кажется, я могу отправить свое пространство с помощью port.Write (""); Но если я даже попробую вызвать ReadChar, ReadByte или ReadLine (например, опрос), он зависнет до тех пор, пока я не отключу USB, и мой вывод на консоль ничего не показывает (исключения были перехвачены).

Поэтому вместо этого я настроил DataReceviedHandler, но он никогда не вызывается.

Я читал некоторые посты, где люди делали подобные вещи с Arduinos и т. Д. (Это не Arduino, а эй), используя не что иное, как ReadLine. Их код не работает для меня (и пока нет ответов от этих авторов).

Итак, есть какие-нибудь советы? Нужно ли использовать другой поток? Если вам известно какое-либо кодирование Unity (Mono), любые советы в этом направлении будут высоко оценены.

Этот код представляет собой коллаж из http://plikker.com/?p=163 и http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx#Y537

using UnityEngine;
using System.Collections;
using System.IO.Ports;
using System;

public class SerialTest : MonoBehaviour {

 SerialPort stream;

 void Start () {
  try {
   stream = new SerialPort("COM3", 9600);
   stream.Parity = Parity.None;
   stream.StopBits = StopBits.One;
   stream.DataBits = 8;
   stream.Handshake = Handshake.None;
   stream.DataReceived += new SerialDataReceivedEventHandler(DataReceviedHandler);


   stream.Open();
   Debug.Log("opened ok"); // it DOES open ok!
  } catch (Exception e){
   Debug.Log("Error opening port "+e.ToString()); // I never see this message
  }
 }

 void Update () { // called about 60 times/second
  try {
   // Read serialinput from COM3
   // if this next line is here, it will hang, I don't even see the startup message
   Debug.Log(stream.ReadLine());
   // Note: I've also tried ReadByte and ReadChar and the same problem, it hangs
  } catch (Exception e){
   Debug.Log("Error reading input "+e.ToString());
  }
 }

 private static void DataReceviedHandler(
                        object sender,
                        SerialDataReceivedEventArgs e)
 {
  SerialPort sp = (SerialPort)sender; // It never gets here!
  string indata = sp.ReadExisting();
  Debug.Log("Data Received:");
  Debug.Log(indata);
 }

 void OnGUI() // simple GUI
 {
   // Create a button that, when pressed, sends the 'ping'
   if (GUI.Button (new Rect(10,10,100,20), "Send"))
 stream.Write(" ");
 }
}

Ответы [ 5 ]

5 голосов
/ 21 апреля 2011

События не реализованы в классе Mono SerialPort, поэтому вы не будете получать никаких уведомлений, вам придется выполнять (блокировать) явное чтение. Другая возможная проблема - я не уверен, как работают Unity Behaviors. Вы уверены, что все методы, обращающиеся к SerialPort, вызываются в одном потоке? И вы не избавляетесь от объекта порта, это также вызовет проблемы.

2 голосов
/ 20 апреля 2011

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

serial = new SerialPort();

serial.ReadBufferSize = 8192;
serial.WriteBufferSize = 128;

serial.PortName = "COM1";
serial.BaudRate = 115200;
serial.Parity = Parity.None;
serial.StopBits = StopBits.One;

// attach handlers
// (appears to be broken in some Mono versions?)
serial.DataReceived += SerialPort_DataReceived;
serial.Disposed += SerialPort_Disposed;

serial.Open();

Я рекомендую терминал с открытым исходным кодом RealTerm , он имеет богатый набор функций и может помочь вам в отладке.Попробуйте написать байт вручную с помощью такого программного обеспечения, и, если оно работает, проблема в вашей программе.В противном случае это может быть проблема с драйвером (но, скорее всего, это не так).

[Редактировать]

Вызов SerialPort.ReadLine на самом делепредполагается блокировать поток до получения SerialPort.NewLine.Также ReadChar и ReadByte будут зависать, пока не будет получен хотя бы один байт.Вам нужно убедиться, что вы на самом деле получаете символы с другой стороны, и вы не будете получать их, если ваше приложение зависло и не может отправить пробел.

Поскольку я никогда не использовал Unity, я неконечно, как вызывается Update, но я предполагаю, что он запускается в потоке переднего плана через регулярные промежутки времени (иначе ваше приложение не зависнет).

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

Ну, возможно, нет, потому что в .NET 1.1 значение по умолчанию для ReadTimeout не было бесконечным, как в .NET 2.0.

Итак, что вы можете сделать:

a. Установите для свойства ReadTimeout разумное значение.По умолчанию в .NET 2.0 используется InfiniteTimeout, который не соответствует вашим потребностям.Минусы: ваш метод обновления будет зависать некоторое время при каждом вызове, но не бесконечно.

b. Кто-то сказал, что события не реализованы в MONO SerialPort, поэтому я предполагаю, что с помощью DataReceived только не вариант.

c. Переместите логику отправки в метод Update, чтобы вы вообще не читали данные,пока не настало время его прочитать:

private volatile bool _shouldCommunicate = false;
void Update ()
{
  if (_shouldCommunicate) // this is a flag you set in "OnGui"
  {
     try {
       stream.Write(" ");
       Debug.Log(stream.ReadLine());
     } catch (Exception e){
       Debug.Log("Error reading input "+e.ToString());
     }
  }
}

void OnGUI() // simple GUI
{
   if (GUI.Button (new Rect(10,10,100,20), "Send"))
      _shouldCommunicate = true;
}

Обратите внимание, что если ваше устройство не отправляет данные, оно также будет блокироваться на stream.ReadLine(), поэтому убедитесь, что для вашего ReadTimeout установлено разумное значение.Вы также захотите прекратить отправку в какой-то момент, но я оставлю это вам.

d. Отправьте пробел в OnGui, как вы делаете сейчас, но всегда проверяйте, есть лиесть ли данные в вашем буфере перед чтением:

void Update () { // called about 60 times/second
 try {
   // call our new method
   Debug.Log(ReadLineNonBlocking());
 } catch (Exception e){
  Debug.Log("Error reading input "+e.ToString());
 }
}

private StringBuilder sb = new StringBuilder();
string ReadLineNonBlocking()
{
    int len = stream.BytesToRead;
    if (len == 0)
        return "";

    // read the buffer
    byte[] buffer = new byte[len];
    stream.Read(buffer, 0, len);
    sb.Append(ASCIIEncoding.ASCII.GetString(buffer));

    // got EOL?
    if (sb.Length < 2 ||
        sb[sb.Length-2] != '\r' ||
        sb[sb.Length-1] != '\n')
        return ""; 

    // if we are here, we got both EOL chars
    string entireLine = sb.ToString();
    sb.Length = 0;
    return entireLine;
 }

Отказ от ответственности: это прямо из моей головы, не проверено, поэтому могут быть некоторые синтаксические ошибки, с которыми, я уверен, вы справитесь.

0 голосов
/ 21 апреля 2011

Не смешивать событие данных и блокировку чтения. Что вы ожидаете, если данные поступят? Что и метод чтения, и событие должны получать одинаковые полученные данные?

Вы также должны прочитать о:

Небольшое руководство по последовательному порту, описывающее все состояния: http://www.wcscnet.com/Tutorials/SerialComm/Page1.htm

0 голосов
/ 21 апреля 2011

Была аналогичная проблема с Mono, помогло обновление до 2.6.7.

0 голосов
/ 20 апреля 2011

Возможно, ваша проблема в конфигурации последовательного порта. Важно не только проверять BaudRate или StopBits. Также вы должны настроить DTR, RTS, Handshake, все. Это важно, потому что, может быть, другая программа установила некоторые некрасивые значения, и конфигурация должна быть явно установлена ​​при каждом запуске, или некоторые настройки старого соединения могут привести к неприятностям.

Также, возможно, взгляните на один из этих инструментов:

Они могут помочь вам заглушить последовательный интерфейс или глубже изучить соединение. Также, возможно, попытайтесь поговорить с вашим последовательным устройством, используя HyperTerminal или другой аналогичный инструмент, который доказал свою работоспособность.

...