在给Arduino编程的时候,因为没有调试工具,经常要通过使用串口通讯的方式调用Serial.print和Serial.println输出Arduino运行过程中的相关信息,然后在电脑上用Arduino IDE的Serial Monitor来查看print出来的信息。Serial Monitor不仅可以接受Arduino发送到电脑的数据,还可以向Arduino发送数据,进行双向通讯。但是这种通讯方式太过于简陋,是纯粹的手工方式,只适合调试。如果需要在电脑上通过可视化界面与Arduino进行交互,或者对Arduino发送到电脑上的数据进行处理,就需要在电脑上编程了。说的专业一点就是上位机与下位机的通讯。本文就介绍一下如何使用C#实现Arduino与电脑进行串行通讯。
1、C#串口编程基础
在C#中有一个串口类System.IO.Ports.SerialPort,这个类的实例就对应设备管理器中的串口。
比如 SerialPort port = new SerialPort("COM4")
这句代码就定义了一个串口实例,对应下图中的USB Serial Port(COM4)
SerialPort常用方法包括Open, Close, Read, ReadLine, Write, WriteLine。这些方法通过名称就很容易理解它们的用法。
具体类信息可以参考MSDN:
2、Arduino串口编程基础
Arduino中的Serial和C#的SerialPort用法类似,有available, begin, read, readBytes, write, print, println,从名称上也很容易理解。具体用法可以参考官方文档:
一般我们会在Arduino代码的setup方法中添加Serial.begin(9600),然后在serialEvent方法中读取接收到的数据。
3、实例
实例的场景为:
1、Arduino上接一个光线传感器,通过模拟口周期性读取亮度值。
2、在电脑上向Arduino发送一个开始发送数据的命令后,点亮Arduino上13号数字口的LED,然后Arduino通过串口向电脑发送亮度值。
3、在电脑上向Arduino发送一个停止发送数据的命令后,关闭Arduino上13号数字口的LED,然后Arduino停止通过串口向电脑发送亮度值。
这个场景包含了Arduino和电脑的双向通讯。
示例采用WinForm,界面如下:
“串口列表”中自动加载电脑上的可用串口名称。
点击“开始读取”按钮,根据选择的串口名称实例化一个串口对象,指定串口的DataReceived事件处理方法。然后调用ChangeArduinoSendStatus方法向Arduino发送“serial start”命令。
点击“停止读取”按钮,向Arduino发送“serial stop”命令,关闭串口并销毁实例。
点击“开始发送”或“停止发送”按钮,调用ChangeArduinoSendStatus方法向Arduino发送“serial start”或“serial stop”命令,让Arduino开始通过串口向电脑发送数据或停止向电脑发送数据。
串口在接收到数据后出发DataReceived事件,在事件处理方法中调用RefreshInfoTextBox方法,读取串口的数据并追加到界面的文本框。注意:串口的DataReceived事件是由后台线程执行,要把读取到的数据显示在WinFrom界面,需要使用控件的Invoke方法才能刷新界面。
C#核心代码如下:
private SerialPort port = null; /// <summary> /// 初始化串口实例 /// </summary> private void InitialSerialPort() { try { string portName = this.cmbSerials.SelectedItem.ToString(); port = new SerialPort(portName, 9600); port.Encoding = Encoding.ASCII; port.DataReceived += port_DataReceived; port.Open(); this.ChangeArduinoSendStatus( true); } catch (Exception ex) { MessageBox.Show( "初始化串口发生错误:" + ex.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information); } } /// <summary> /// 关闭并销毁串口实例 /// </summary> private void DisposeSerialPort() { if (port != null) { try { this.ChangeArduinoSendStatus( false); if (port.IsOpen) { port.Close(); } port.Dispose(); } catch (Exception ex) { MessageBox.Show( "关闭串口发生错误:" + ex.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information); } } } /// <summary> /// 改变Arduino串口的发送状态 /// </summary> /// <param name="allowSend">是否允许发送数据</param> private void ChangeArduinoSendStatus( bool allowSend) { if (port != null && port.IsOpen) { if (allowSend) { port.WriteLine( "serial start"); } else { port.WriteLine( "serial stop"); } } } /// <summary> /// 从串口读取数据并转换为字符串形式 /// </summary> /// <returns></returns> private string ReadSerialData() { string value = ""; try { if (port != null && port.BytesToRead > 0) { value = port.ReadExisting(); } } catch (Exception ex) { MessageBox.Show( "读取串口数据发生错误:" + ex.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information); } return value; } /// <summary> /// 在读取到数据时刷新文本框的信息 /// </summary> private void RefreshInfoTextBox() { string value = this.ReadSerialData(); Action < string > setValueAction = text = > this.txtInfo.Text += text; if ( this.txtInfo.InvokeRequired) { this.txtInfo.Invoke(setValueAction, value); } else { setValueAction(value); } }
Arduino代码
代码注释很详细,就不再做解释。
int pinLed = 13; //定义连接LED的数字口,当允许通过串口发送数据时,点亮LED,否则关闭LED boolean sendFlag = false; //指示是否允许通过串口发送数据
boolean readCompleted = false; //指示是否完成读取串口数据 String serialString = ""; //串口数据缓存字符串
//Author:Alex Leo, Email:conexpress@qq.com, Blog:http://conexpress.cnblogs.com/ //参考:http://arduino.cc/en/Reference/Serial void setup() { pinMode(pinLed,OUTPUT); Serial.begin( 9600); serialString.reserve( 200); //初始化字符串 } void loop() { int lightValue = analogRead(A0); //从A0口读取光线传感器的值 if(readCompleted) //判断串口是否接收到数据并完成读取 { Serial.print( "read value:"); Serial.println(serialString); //将读取到的信息发送给电脑 if(serialString == "serial start") //当读取到的信息是"serial start"时,设置发送标志设置为true { sendFlag = true; } else if(serialString == "serial stop") //当读取到的信息是"serial stop"时,设置发送标志设置为false { sendFlag = false; } serialString = "";
readCompleted = false; } if(sendFlag) //如果允许通过串口发送数据,则点亮LED并发送数据,否则关闭LED
{ digitalWrite(pinLed, HIGH); Serial.print( "light value:"); Serial.println(lightValue); } else { digitalWrite(pinLed, LOW); } delay( 1000); //延时1000ms } void serialEvent() //串口事件处理方法,参考:http://arduino.cc/en/Tutorial/SerialEvent { while(Serial.available()) //参考://arduino.cc/en/Serial/Available { char inChar = ( char)Serial.read(); if(inChar != '\n') //以换行符作为读取结束标志 { serialString += inChar; } else { readCompleted = true; } } }
完整代码下载: