使用单声道时的 Serial.IO.Ports 问题,适用于 dotnet core 3.1 / arm / raspberry pi 4
Posted
技术标签:
【中文标题】使用单声道时的 Serial.IO.Ports 问题,适用于 dotnet core 3.1 / arm / raspberry pi 4【英文标题】:Serial.IO.Ports issue when using mono, works with dotnet core 3.1 / arm / raspberry pi 4 【发布时间】:2021-07-14 12:57:35 【问题描述】:在 Raspberry Pi 4 和串行 I/O 上使用 mono vs dotnet 时遇到了一个奇怪的问题。 使用 VS 2019 编译并使用 dotnet 运行时,它按预期工作,但使用单声道编译时,我从未收到任何数据。对于dotnet,我也使用了dotnet add package System.IO.Ports
这是组成项目的两个文件:
using System;
using System.Text;
using SerialPortListener.Serial;
namespace radio
class Program
private static SerialPortManager _spManager;
static void Main(string[] args)
Console.WriteLine("Starting");
_spManager = new SerialPortManager();
_spManager.NewSerialDataRecieved += new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved);
_spManager.StartListening();
Console.WriteLine("Waiting here forever...");
while (true) ;
static void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
string str = Encoding.ASCII.GetString(e.Data);
Console.WriteLine(str);
using System;
using System.IO.Ports;
namespace SerialPortListener.Serial
/// <summary>
/// Manager for serial port data
/// </summary>
public class SerialPortManager : IDisposable
public SerialPortManager()
~SerialPortManager()
Dispose(false);
#region Fields
private SerialPort _serialPort;
public event EventHandler<SerialDataEventArgs> NewSerialDataRecieved;
#endregion
#region Event handlers
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
Console.WriteLine("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
int dataLength = _serialPort.BytesToRead;
byte[] data = new byte[dataLength];
int nbrDataRead = _serialPort.Read(data, 0, dataLength);
if (nbrDataRead == 0)
return;
NewSerialDataRecieved?.Invoke(this, new SerialDataEventArgs(data));
#endregion
#region Methods
/// <summary>
/// Connects to a serial port defined through the current settings
/// </summary>
public void StartListening()
if (_serialPort != null && _serialPort.IsOpen)
_serialPort.Close();
_serialPort = new SerialPort("/dev/ttyUSB0", 115200, Parity.None, 8, StopBits.One)
Handshake = Handshake.None,
DtrEnable = false,
RtsEnable = false,
ReadTimeout = 400,
;
_serialPort.DataReceived += new SerialDataReceivedEventHandler(_serialPort_DataReceived);
_serialPort.Open();
/// <summary>
/// Closes the serial port
/// </summary>n
public void StopListening()
_serialPort.Close();
// Call to release serial port
public void Dispose()
Dispose(true);
// Part of basic design pattern for implementing Dispose
protected virtual void Dispose(bool disposing)
if (disposing)
_serialPort.DataReceived -= new SerialDataReceivedEventHandler(_serialPort_DataReceived);
if (_serialPort != null)
if (_serialPort.IsOpen)
_serialPort.Close();
_serialPort.Dispose();
#endregion
/// <summary>
/// EventArgs used to send bytes recieved on serial port
/// </summary>
public class SerialDataEventArgs : EventArgs
public SerialDataEventArgs(byte[] dataInByteArray)
Data = dataInByteArray;
/// <summary>
/// Byte array containing data from serial port
/// </summary>
public byte[] Data;
使用 mcs Program.cs SerialPortManager.cs 和 mono Program.exe 编译它,我得到了这个:
/dab/source/radio# mono Program.exe 开始 永远在这里等着……
使用 dotnet radio.dll,它工作正常。这是一小段摘录:
/dab/empeg# dotnet radio.dll 开始 永远在这里等待... 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 serial_notify_thread.cpp: 180:@@ #70008000 0:01:46 serial_notify_thread.cpp: 180:@@ #70008000 0:04:47 serial_notify_thread.cpp: 180:@@ #70008000 0:01:46 serial_notify_thread.cpp: 180:@@ #70008000 0:04:48 serial_notify_thread.cpp: 180:@@ # Poll wait 轮询失败
啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊
不确定我做错了什么,或者为什么它适用于 dotnet,而不是单声道。 Raspberry Pi Zero 上没有我需要使用单声道作为 dotnet 的任何想法。
约翰
【问题讨论】:
一如既往。发布此消息 5 分钟后,我找到了此 antanas.veiverys.com/…,它解决了我的问题。看起来单串口实现缺少事件触发部分。 很高兴你找到它!如果您不介意,您可以将链接添加到您的答案中吗?评论往往会被忽略;)欢迎您! 【参考方案1】:完全修复:
using System;
using System.Text;
using SerialPortListener.Serial;
namespace radio
class Program
private static SerialPortManager _spManager;
static void Main(string[] args)
Console.WriteLine("Starting");
_spManager = new SerialPortManager();
_spManager.NewSerialDataRecieved += new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved);
_spManager.StartListening();
Console.WriteLine("Waiting here forever...");
while (true) ;
static void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e)
string str = Encoding.ASCII.GetString(e.Data);
Console.WriteLine(str);
using System;
using System.IO.Ports;
namespace SerialPortListener.Serial
/// <summary>
/// Manager for serial port data
/// </summary>
public class SerialPortManager : IDisposable
public SerialPortManager()
~SerialPortManager()
Dispose(false);
#region Fields
private EnhancedSerialPort _serialPort;
public event EventHandler<SerialDataEventArgs> NewSerialDataRecieved;
#endregion
#region Event handlers
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
Console.WriteLine("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
int dataLength = _serialPort.BytesToRead;
byte[] data = new byte[dataLength];
int nbrDataRead = _serialPort.Read(data, 0, dataLength);
if (nbrDataRead == 0)
return;
NewSerialDataRecieved?.Invoke(this, new SerialDataEventArgs(data));
#endregion
#region Methods
/// <summary>
/// Connects to a serial port defined through the current settings
/// </summary>
public void StartListening()
if (_serialPort != null && _serialPort.IsOpen)
_serialPort.Close();
_serialPort = new EnhancedSerialPort("/dev/ttyUSB0", 115200, Parity.None, 8, StopBits.One)
ReadTimeout = 400,
;
_serialPort.DataReceived += new SerialDataReceivedEventHandler(_serialPort_DataReceived);
_serialPort.Open();
/// <summary>
/// Closes the serial port
/// </summary>n
public void StopListening()
_serialPort.Close();
// Call to release serial port
public void Dispose()
Dispose(true);
// Part of basic design pattern for implementing Dispose
protected virtual void Dispose(bool disposing)
if (disposing)
_serialPort.DataReceived -= new SerialDataReceivedEventHandler(_serialPort_DataReceived);
if (_serialPort != null)
if (_serialPort.IsOpen)
_serialPort.Close();
_serialPort.Dispose();
#endregion
/// <summary>
/// EventArgs used to send bytes recieved on serial port
/// </summary>
public class SerialDataEventArgs : EventArgs
public SerialDataEventArgs(byte[] dataInByteArray)
Data = dataInByteArray;
/// <summary>
/// Byte array containing data from serial port
/// </summary>
public byte[] Data;
// /*
// Copyright 2013 Antanas Veiverys www.veiverys.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// */
//
using System;
using System.IO.Ports;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;
namespace SerialPortListener.Serial
public class EnhancedSerialPort : SerialPort
public EnhancedSerialPort() : base()
public EnhancedSerialPort(IContainer container) : base(container)
public EnhancedSerialPort(string portName) : base(portName)
public EnhancedSerialPort(string portName, int baudRate) : base(portName, baudRate)
public EnhancedSerialPort(string portName, int baudRate, Parity parity) : base(portName, baudRate, parity)
public EnhancedSerialPort(string portName, int baudRate, Parity parity, int dataBits) : base(portName, baudRate, parity, dataBits)
public EnhancedSerialPort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits) : base(portName, baudRate, parity, dataBits, stopBits)
// private member access via reflection
int fd;
FieldInfo disposedFieldInfo;
object data_received;
public new void Open()
base.Open();
if (IsWindows == false)
FieldInfo fieldInfo = BaseStream.GetType().GetField("fd", BindingFlags.Instance | BindingFlags.NonPublic);
fd = (int)fieldInfo.GetValue(BaseStream);
disposedFieldInfo = BaseStream.GetType().GetField("disposed", BindingFlags.Instance | BindingFlags.NonPublic);
fieldInfo = typeof(SerialPort).GetField("data_received", BindingFlags.Instance | BindingFlags.NonPublic);
data_received = fieldInfo.GetValue(this);
new System.Threading.Thread(new System.Threading.ThreadStart(this.EventThreadFunction)).Start();
static bool IsWindows
get
PlatformID id = Environment.OSVersion.Platform;
return id == PlatformID.Win32Windows || id == PlatformID.Win32NT; // WinCE not supported
private void EventThreadFunction()
do
try
var _stream = BaseStream;
if (_stream == null)
return;
if (Poll(_stream, ReadTimeout))
OnDataReceived(null);
catch
return;
while (IsOpen);
void OnDataReceived(SerialDataReceivedEventArgs args)
SerialDataReceivedEventHandler handler = (SerialDataReceivedEventHandler)Events[data_received];
if (handler != null)
handler(this, args);
[DllImport("MonoPosixHelper", SetLastError = true)]
static extern bool poll_serial(int fd, out int error, int timeout);
private bool Poll(Stream stream, int timeout)
CheckDisposed(stream);
if (IsOpen == false)
throw new Exception("port is closed");
int error;
bool poll_result = poll_serial(fd, out error, ReadTimeout);
if (error == -1)
ThrowIOException();
return poll_result;
[DllImport("libc")]
static extern IntPtr strerror(int errnum);
static void ThrowIOException()
int errnum = Marshal.GetLastWin32Error();
string error_message = Marshal.PtrToStringAnsi(strerror(errnum));
throw new IOException(error_message);
void CheckDisposed(Stream stream)
bool disposed = (bool)disposedFieldInfo.GetValue(stream);
if (disposed)
throw new ObjectDisposedException(stream.GetType().FullName);
【讨论】:
以上是关于使用单声道时的 Serial.IO.Ports 问题,适用于 dotnet core 3.1 / arm / raspberry pi 4的主要内容,如果未能解决你的问题,请参考以下文章