C# 怎样打开电脑多个串口中扫描并打开发送指定数据的串口

number007cool 2019-12-02 11:28:10
如题

比如电脑有4个串口
1个没有收到数据 COM1
1个已经打开,无法操作 COM2
1个不停的接收到 A5 开头的数据 COM3
1个不停的接受到 AA 开头的数据 COM4


1、点击扫描按钮后,怎样识别出发送A5数据开头的串口号,并打开 ,通过await async的方式实现


2、串口接收数据是 Serail_ReceiveHandler(object sender, SerialDataReceivedEventArgs args) 事件的方式完成数据接收,

怎样完成接收事件和异步task的同步
...全文
664 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
chenlei 2020-01-05
  • 打赏
  • 举报
回复
打开串口,如果异常,则已占用,打开下一个,监听串口数据,查找信息头,符合就是了,不符合,则找下一个。
wanghui0380 2019-12-09
  • 打赏
  • 举报
回复
ps:我发现不是我虚拟串口驱动掉了,是vspd挂了,貌似好几台机器上装的vspd9都挂了。哎,头疼。
wanghui0380 2019-12-09
  • 打赏
  • 举报
回复
那是扩展方法,把上面那个放到一个静态类里
public static IObservable<byte[]> ReceivedMsg(this SerialPort serialPort)
        {
            Subject<byte[]> subject = new Subject<byte[]>();
 
            var ReceivSource = Observable.FromEventPattern<SerialDataReceivedEventArgs>(serialPort, "DataReceived");
 
            ReceivSource.Subscribe(p =>
            {
                SerialPort _serialPort =(SerialPort) p.Sender;
                var buf = new byte[_serialPort.BytesToRead];
                _serialPort.Read(buf, 0, buf.Length);
                subject.OnNext(buf);
            });
 
            return subject;
 
       }
number007cool 2019-12-09
  • 打赏
  • 举报
回复
引用 22 楼 wanghui0380 的回复:
前两天折腾驱动,把虚拟串口驱动给折腾掉了,我没测试效果
错误 18 “System.IO.Ports.SerialPort”不包含“ReceivedMsg”的定义,并且找不到可接受类型为“System.IO.Ports.SerialPort”的第一个参数的扩展方法“ReceivedMsg”(是否缺少 using 指令或程序集引用?) E:\vs_test\vs_cs_console\vs_ComHelper\FormCom.cs 906 25 vs_ComHelper
wanghui0380 2019-12-07
  • 打赏
  • 举报
回复
前两天折腾驱动,把虚拟串口驱动给折腾掉了,我没测试效果
wanghui0380 2019-12-07
  • 打赏
  • 举报
回复
 SerialPort serialPort1=new SerialPort();
                serialPort1.PortName = "com1";
                serialPort1.BaudRate = 9600;

                serialPort1.Open();
               
                byte[] checkdata=new byte[]{0xa5,0x11};

                serialPort1.ReceivedMsg()
                    //因你原来有wait,猜测你不想做粘包判定,想直接先缓存一秒,故这里我也先缓存1秒
                    .Buffer(TimeSpan.FromMinutes(1000))
                    // 因缓存了1秒,所以他是list<byte[]>代表可能不是一个包
                    //SelectMany重新产生一个替代数据,把list<byte[]>重整为首尾相连的一个byte[]
                    .SelectMany(p => p.SelectMany(c => c).ToObservable())
                    //因为你要判定连续2个byte,所以这里使用一个平滑移动窗口,重新产生一个窗口数据
                    //本来可以用传统循环,不管既然展示rx的功能,我干脆全用rx做了
                    //平滑移动窗口的其实比较有用,前面有人问了,如何计算股票这类的macd,kdj,这个就是
                    //像移动加权平均,像音频信号处理,包括图像的卷积运算,移动窗口都是常规手段
                    .Window(2, 1)
                    //因为这是rx,所以窗口出的数据观察数据,所以再次重整为2个byte的byte[]
                    .SelectMany(p => p.ToList()) 
                    //这里就没啥好说的了,就是byte[]序列相等比较
                    .Where(p => p.SequenceEqual(checkdata))
                    //超时设定3秒
                    .Timeout(TimeSpan.FromSeconds(3))
                    //如果没有超时,并且收到数据,他会进入下面的订阅,你可以用TaskCompletionSource通知外面有结果了
                    .Subscribe(p => { MessageBox.Show("3秒内收到了"); });
wanghui0380 2019-12-07
  • 打赏
  • 举报
回复
SerialPort serialPort1=new SerialPort(); serialPort1.PortName = "com1"; serialPort1.BaudRate = 9600; serialPort1.Open(); byte[] checkdata=new byte[]{0xa5,0x11}; serialPort1.ReceivedMsg() //因你原来有wait,猜测你不想做粘包判定,想直接先缓存一秒,故这里我也先缓存1秒 .Buffer(TimeSpan.FromMinutes(1000)) // 因缓存了1秒,所以他是list<byte[]>代表可能不是一个包 //SelectMany重新产生一个替代数据,把list<byte[]>重整为首尾相连的一个byte[] .SelectMany(p => p.SelectMany(c => c).ToObservable()) //因为你要判定连续2个byte,所以这里使用一个平滑移动窗口,重新产生一个窗口数据 //本来可以用传统循环,不管既然展示rx的功能,我干脆全用rx做了 //平滑移动窗口的其实比较有用,前面有人问了,如何计算股票这类的macd,kdj,这个就是 //像移动加权平均,像音频信号处理,包括图像的卷积运算,移动窗口都是常规手段 .Window(2, 1) //因为这是rx,所以窗口出的数据观察数据,所以再次重整为2个byte的byte[] .SelectMany(p => p.ToList()) //这里就没啥好说的了,就是byte[]序列相等比较 .Where(p => p.SequenceEqual(checkdata)) //超时设定3秒 .Timeout(TimeSpan.FromSeconds(3)) //如果没有超时,并且收到数据,他会进入下面的订阅,你可以用TaskCompletionSource通知外面有结果了 .Subscribe(p => { MessageBox.Show("3秒内收到了"); });
number007cool 2019-12-06
  • 打赏
  • 举报
回复
引用 15 楼 wanghui0380 的回复:
public static IObservable<byte[]> ReceivedMsg(this SerialPort serialPort)
        {
            Subject<byte[]> subject = new Subject<byte[]>();

            var ReceivSource = Observable.FromEventPattern<SerialDataReceivedEventArgs>(serialPort, "DataReceived");

            ReceivSource.Subscribe(p =>
            {
                SerialPort _serialPort =(SerialPort) p.Sender;
                var buf = new byte[_serialPort.BytesToRead];
                _serialPort.Read(buf, 0, buf.Length);
                subject.OnNext(buf);
            });

            return subject;

       }
那个扩展方法我简单示意一下,我这里只是接收数据,不做解析。也不做其他异常处理
大师,你这部分代码可以看懂,就是不知道怎么和之前的整合到一起。要不你再辛苦下,贴个完整版的,让我再学习学习
货郎大叔 2019-12-05
  • 打赏
  • 举报
回复
发送握手协议,等待对方返回符合期望的数据(带超时处理) 有设备应答退出循环,没有一台设备应答,休眠个几秒,重新遍历,直到找到(或手动放弃)为止
number007cool 2019-12-05
  • 打赏
  • 举报
回复
引用 15 楼 wanghui0380 的回复:
public static IObservable<byte[]> ReceivedMsg(this SerialPort serialPort)
        {
            Subject<byte[]> subject = new Subject<byte[]>();

            var ReceivSource = Observable.FromEventPattern<SerialDataReceivedEventArgs>(serialPort, "DataReceived");

            ReceivSource.Subscribe(p =>
            {
                SerialPort _serialPort =(SerialPort) p.Sender;
                var buf = new byte[_serialPort.BytesToRead];
                _serialPort.Read(buf, 0, buf.Length);
                subject.OnNext(buf);
            });

            return subject;

       }
那个扩展方法我简单示意一下,我这里只是接收数据,不做解析。也不做其他异常处理
大师 我用我的土办法实现了搜索功能, 查找 数据头为 A5 11 的串口

 /// <summary>
        /// 带参数的异步任务, Task 参数直接在lamda表达式内部直接使用
        /// </summary>
        /// <param name="strCombaud">串口的波特率</param>
        /// <returns></returns>
        private Task<string> TaskScan(string strCombaud)
        {
            return Task.Run( () =>
            {
                string ret = "";
                foreach(var port in SerialPort.GetPortNames()  )
                {
                    try
                    {
                        serialPort1.PortName = port;
                        serialPort1.BaudRate = Convert.ToInt32(strCombaud);
                        
                        serialPort1.Open();
                        Task.Delay(1000).Wait();

                         Console.WriteLine("{0} open suc", port);

                        if (serialPort1.BytesToRead > 0)
                        {
                            byte[] data = new byte[serialPort1.BytesToRead];

                            serialPort1.Read(data, 0, data.Length);

                            byte lastByte = data[0];
                            for(int i = 1; i< data.Length; i++)
                            {
                                if(lastByte == 0xa5 && data[i] ==0x11 )
                                {
                                    ret = port; break;
                                }
                                lastByte = data[i];
                            }
                        }

                        serialPort1.Close();

                       
                    }
                    catch
                    {
                        Console.WriteLine("{0} open failed", port);
                    }
                }

                return ret;

            });
        }

        private async void btnComScan_Click(object sender, EventArgs e)
        {
            string v = await TaskScan( comboBoxComBaud.Text ); 

            if( v != string.Empty )
            {
                comboBoxComPort.Text = v;
                MessageBox.Show("search right serial port , " + v);
            }
        }
你提到的 RX.NET 这些都是很美妙的编程思想, 也是我一直寻找的, 我再继续理解理解
number007cool 2019-12-05
  • 打赏
  • 举报
回复
引用 16 楼 货郎大叔 的回复:
发送握手协议,等待对方返回符合期望的数据(带超时处理) 有设备应答退出循环,没有一台设备应答,休眠个几秒,重新遍历,直到找到(或手动放弃)为止


        private Task<string> TaskScan()
        {
            return Task.Run( () =>
            {
                foreach(var port in SerialPort.GetPortNames()  )
                {
                    try
                    {
                        serialPort1.PortName = port;
                        serialPort1.BaudRate = Convert.ToInt32("460800");
                        
                        serialPort1.Open();
                        Task.Delay(1000).Wait();
                        serialPort1.Close();

                        Console.WriteLine("{0} open suc", port);
                    }
                    catch
                    {
                        Console.WriteLine("{0} open failed", port);
                    }
                }

                return "";

            });
        }

        private async void btnComScan_Click(object sender, EventArgs e)
        {
            string v = await TaskScan(); 
        }

    搞了个串口扫描的, 数据异步接收部分就不知道怎么整了
number007cool 2019-12-04
  • 打赏
  • 举报
回复
引用 12 楼 by_封爱 的回复:
按照你的说法 就把串口全部打开 挨个试就行了....
就是这个意思
by_封爱 版主 2019-12-04
  • 打赏
  • 举报
回复
按照你的说法 就把串口全部打开 挨个试就行了....
wanghui0380 2019-12-04
  • 打赏
  • 举报
回复
public static IObservable<byte[]> ReceivedMsg(this SerialPort serialPort)
        {
            Subject<byte[]> subject = new Subject<byte[]>();

            var ReceivSource = Observable.FromEventPattern<SerialDataReceivedEventArgs>(serialPort, "DataReceived");

            ReceivSource.Subscribe(p =>
            {
                SerialPort _serialPort =(SerialPort) p.Sender;
                var buf = new byte[_serialPort.BytesToRead];
                _serialPort.Read(buf, 0, buf.Length);
                subject.OnNext(buf);
            });

            return subject;

       }
那个扩展方法我简单示意一下,我这里只是接收数据,不做解析。也不做其他异常处理
number007cool 2019-12-04
  • 打赏
  • 举报
回复
引用 11 楼 wanghui0380 的回复:
我简单写个伪代码
  async Task<bool> test(string com)
        {
            //这是一个异步消息封装器,用来返回异步消息
            TaskCompletionSource<bool> tcs=new TaskCompletionSource<bool>();

            SerialPort port = new SerialPort(com);
            try
            {
                //这里还会有些波特率一类的附加设置,你自己完成
                port.Open(); //端口占用会直接异常



                port.ReceivedMsg //ReceivedMsg 这个我通常写成扩展方法,接收解析消息,并返回rx的Subject<object>
                    .where(此处可以放一些订阅条件) //where是rx自己的扩展方法,用来过滤条件
                    .Timeout(此处放超时时间,超时后会触发下面的异常) //timeout也是rx自己的扩展方法,用来设定超时,超时会直接异常
                    .订阅(p => 
                {
                    //当符后where条件,并且没有超时的情况下,收到消息,告诉外面他成功了
                    tcs.SetResult(true);
                });
                port.Write("发送指令");
            }
            catch (Exception e)
            {
                //打开占用异常,超时未收到消息异常
                tcs.SetResult(false);
            }
            finally
            {
                if(port.IsOpen)
                    port.Close();

            }

            return await tcs.Task;
        }
研究了一天 ,大致搞清楚了RX.NET 订阅响应式编程式怎么回事,以及TaskCompletionSource port.ReceivedMsg //ReceivedMsg 这个我通常写成扩展方法,接收解析消息,并返回rx的Subject<object> .where(此处可以放一些订阅条件) //where是rx自己的扩展方法,用来过滤条件 .Timeout(此处放超时时间,超时后会触发下面的异常) //timeout也是rx自己的扩展方法,用来设定超时,超时会直接异常 .订阅(p => 这里的一串还是不知道如何下手
wanghui0380 2019-12-03
  • 打赏
  • 举报
回复
我简单写个伪代码
  async Task<bool> test(string com)
        {
            //这是一个异步消息封装器,用来返回异步消息
            TaskCompletionSource<bool> tcs=new TaskCompletionSource<bool>();

            SerialPort port = new SerialPort(com);
            try
            {
                //这里还会有些波特率一类的附加设置,你自己完成
                port.Open(); //端口占用会直接异常



                port.ReceivedMsg //ReceivedMsg 这个我通常写成扩展方法,接收解析消息,并返回rx的Subject<object>
                    .where(此处可以放一些订阅条件) //where是rx自己的扩展方法,用来过滤条件
                    .Timeout(此处放超时时间,超时后会触发下面的异常) //timeout也是rx自己的扩展方法,用来设定超时,超时会直接异常
                    .订阅(p => 
                {
                    //当符后where条件,并且没有超时的情况下,收到消息,告诉外面他成功了
                    tcs.SetResult(true);
                });
                port.Write("发送指令");
            }
            catch (Exception e)
            {
                //打开占用异常,超时未收到消息异常
                tcs.SetResult(false);
            }
            finally
            {
                if(port.IsOpen)
                    port.Close();

            }

            return await tcs.Task;
        }
number007cool 2019-12-03
  • 打赏
  • 举报
回复
引用 9 楼 wanghui0380 的回复:
就是不知道串口接收事件怎样和task同步。这个我个人喜欢用Rx.net 协议解析这块我就不实现了,你自己实现 RX.net 过程伪代码如下 Subject<解析好的符合规定的byte[]> 数据观察源=Subject<解析好的符合规定的byte[]>(); 后面是常规的串口发送和解析,我就不多说了 这里多写两块 协议解析过程----------解析成功---------把数据发到rx里
引用
数据观察源.next(解析好的byte[])
准备工作完毕 发送过程就简单了
引用
串口发送(协议字节) 数据观察源.订阅条件().订阅处理(p=>{我收到了解析成功的数据,这个过程因为是异步我们通常用 TaskCompletionSource去通知外面});
大致过程如此,你需要看的资料如下
引用
TaskCompletionSource,CancelToken超时,rx.net 订阅和超时处理
好多 多谢提点 我去研究下这几个知识点
wanghui0380 2019-12-03
  • 打赏
  • 举报
回复
就是不知道串口接收事件怎样和task同步。这个我个人喜欢用Rx.net 协议解析这块我就不实现了,你自己实现 RX.net 过程伪代码如下 Subject<解析好的符合规定的byte[]> 数据观察源=Subject<解析好的符合规定的byte[]>(); 后面是常规的串口发送和解析,我就不多说了 这里多写两块 协议解析过程----------解析成功---------把数据发到rx里
引用
数据观察源.next(解析好的byte[])
准备工作完毕 发送过程就简单了
引用
串口发送(协议字节) 数据观察源.订阅条件().订阅处理(p=>{我收到了解析成功的数据,这个过程因为是异步我们通常用 TaskCompletionSource去通知外面});
大致过程如此,你需要看的资料如下
引用
TaskCompletionSource,CancelToken超时,rx.net 订阅和超时处理
number007cool 2019-12-03
  • 打赏
  • 举报
回复
引用 7 楼 wanghui0380 的回复:
这个就只能先规定一个握手协议 或者叫 发现协议 然后SerialPort.GetPortNames 取得所有串口,然后挨个遍历,发送握手协议,等待对方返回符合期望的数据(带超时处理) 有设备应答退出循环,没有一台设备应答,休眠个几秒,重新遍历,直到找到(或手动放弃)为止 ps:通常情况下设备发现都是这个过程(etcd,consoul这类主动注册另算),比如upnp,ssdp都是如此,不过ssdp是个udp的,情况类似ssdp主机发一个udp广播(查询指定服务名的udp),谁收到udp广播了认为我就是这个服务,就对ssdp返回一个udp响应(当然ssdp协议服务本身也会没事对外发出一个udp广播,告诉你们我存在啊,我是xx服务)
嗯 就是大概这个思路 就是不知道串口接收事件怎样和task同步,
wanghui0380 2019-12-03
  • 打赏
  • 举报
回复
这个就只能先规定一个握手协议 或者叫 发现协议 然后SerialPort.GetPortNames 取得所有串口,然后挨个遍历,发送握手协议,等待对方返回符合期望的数据(带超时处理) 有设备应答退出循环,没有一台设备应答,休眠个几秒,重新遍历,直到找到(或手动放弃)为止 ps:通常情况下设备发现都是这个过程(etcd,consoul这类主动注册另算),比如upnp,ssdp都是如此,不过ssdp是个udp的,情况类似ssdp主机发一个udp广播(查询指定服务名的udp),谁收到udp广播了认为我就是这个服务,就对ssdp返回一个udp响应(当然ssdp协议服务本身也会没事对外发出一个udp广播,告诉你们我存在啊,我是xx服务)
加载更多回复(6)

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧