这样学上位机,是不是有意思多了
写在前面
有小伙伴跟我说:

为了兑现承诺,这次给大家安排一个小项目案例,不管你是小白,还是有一定基础,都可以参与进来,一起把这个项目做出来。
项目需求
1、建立一个室内空气质量检测系统,要求电脑软件+单片机硬件,编程语言及硬件可以自行选择。
2、系统分为三部分:数据采集部分、数据传输部分、系统软件交互部分。
3、需求检测的数据:IAQ(Indoor Air Quality)、温度、湿度、PM2.5、CO2、CO。
4、数据传输可以选择串口、以太网、WIFI、蓝牙中的一种。
5、当检测到空气质量过差时,系统需要给出相应的解决方案(如:提示开窗通风等)。
协议分析
这个项目其实涉及到单片机开发,但这块并不是上位机的范畴。在确定好上位机与单片机之间的通信协议之后,双方都按照这个协议来开发,最终进行对接就可以了。 这个协议一般是由单片机开发人员制定,本例协议制定如下:
单片机发送的数据格式:#TXXX,YYY,S@
1、#后面的大写英文T表示对应的参数类型,具体对应关系如下:

2、XXX:代表检测数值,占三个字符,例如010就是10。
3、YYY:代表该数组的限值,占三个字符,例如010就是10。
4、符号@前的S表示报警状态,1表示限值报警,0表示正常
上位机进行限值设置发送数据格式:&SXXX$
1、&号后面的大写英文T表示对应的参数类型,对应关系同上。
2、XXX代表设置的限值数据,占三个字符
3、发送报文以$结束。
界面设计
根据项目需求,并结合协议设计界面如下:

本案例的一个关键知识点在于自定义组合控件的使用。
如果让你来设计这个界面,你是否会考虑组合控件应用,如果全部用的是系统控件,那么代码会写得很复杂,而且容易出错,更重要的是扩展性会很差——如果需求发生变化,会需要改很多东西。
这里把参数的一系列状态及操作做成一个自定义控件,然后将相关的功能抽象成属性及事件。

实时通信
这里采用串口通信,针对SerialPort封装一个串口类,这里会涉及到串口通信、锁处理、委托技术,核心为串口接收事件,代码如下:
private void MyCom_DataReceived(object sender, SerialDataReceivedEventArgs e) { Thread.Sleep(50); int ByteToRead = MyCom.BytesToRead; lock (Lock) { //定义一个字节数组 byte[] rcv = new byte[ByteToRead]; //读取缓冲区里的数据放到字节数组中 MyCom.Read(rcv, 0, ByteToRead); //这里如果想把字节数组给到主线程,需要使用委托 ShowMsg?.Invoke(rcv); } }
参数设置功能:
public bool SetValue(int Speed,Type type) { StringBuilder stringBuilder = new StringBuilder("&"); stringBuilder.Append(type.ToString()); stringBuilder.Append(Speed.ToString().PadLeft(3, '0')); stringBuilder.Append("$"); lock (Lock) { try { this.MyCom.Write(stringBuilder.ToString()); } catch (Exception) { return false; } return true; } }
主界面功能
有了以上的基础之后,主界面的功能开发就很容易了。
主界面的功能主要是两部分内容,第一部分内容是数据解析,将解析结果赋值给控件的属性进行显示,第二部分内容是通用的参数设置,将6个Monitor控件的参数设置事件都绑定同一个事件,然后在该事件里调用SetValue方法即可。
一般我们软件开发完成后,都应该先自我测试一下,便于及时发现问题,减少调试时间。
对于串口通信,可以我们通过虚拟串口来进行测试,仿真数据发送与数据接收,测试结果如下:

