赞
踩
目录
本学期我的上位机课程已经结束了,那么这次分享一下期末考核所做的一个简易版温室监测。首先先给大家介绍一下什么是上位机以及下位机。
上位机是指控制系统中通过人机接口与操作者进行交互的计算机。它般用于监控、控制和调试下机(如工业机器人自动化设备等)的运行和行为。上位机可以通过图形界面、命令行或者其他形式提供给操作者一些控制和操作选项,同时也能够接收和显示下位机的数据和反馈信息。上位机通常具备一定的计算能力,能够处理和分析从下位机传输来的数据,并作出相应的决策和控制命令。
下位机是指控制系统中与上位机相对应的计算机或者部件。它负责执行上位机发送的控制命令,并将执行结果或者反馈信息传输给上位机。下位机通常是一些嵌入式设备或者控制器,其主要任务是实时控制和操作外部设备,例如机器人的关节控制器、传感器读取器或者执行器。下位机一般采用专用硬件和实时操作系统,以保证稳定和准确的控制执行。上位机通过与下位机的连接,可以对下位机进行配置和监控,实时获取相关数据、调整控制参数,并对下位机进行控制。这次做的温室监测,也是用到下位机,我这边用的是一个51单片机。
本次的温室监测,需要实现的功能有:喷灌(利用一个直流电机转动来模拟)、通风(利用一个直流电机转动来模拟)、采光(点亮单片机上的LED灯来模拟)、显示温度、声光报警。
以上就是我们C#上位机的界面布局.
首先是串口数据的接收
在串口的接收事件里面编写以下代码
Thread.Sleep(20);//每间隔20ms接收一次数据
this.Invoke(new EventHandler(delegate
{
try
{
if (serialPort1.BytesToRead > 0)//当串口缓存区中有数据时
{
//以字符串方式读取所有接收到的数据
string Str = serialPort1.ReadExisting();
//将接收到的所有0x00替换成\0显示在界面上,因为0x00是一个控制字符,不会在文本框中显示
Str = Str.Replace("\0", "\\0");
//把16进制字符串转换成整数以字符串输出
txtTemperature.Text = Str + "℃";//把温度显示在文本框中
//在信息框中追加接收的数据,其中包括接收时间和接收内容
AppendTextToInfoBox("[" + DateTime.Now.ToString("HH:mm:s") + "]" + "收" + "<-", txtReceive);
AppendTextToInfoBox(Str + "℃" + "\r\n", txtReceive);
temp = float.Parse(Str);//获取温度
}
}
catch (Exception ex)
{
//在信息框中追加异常信息
AppendTextToInfoBox("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "收" + "< -", txtReceive);
AppendTextToInfoBox(ex.Message + "\r\n", txtReceive);
AppendTextToInfoBox("数据接收错误!\r\n", txtReceive);
}
}));
这部分是先从下位机接收到温度之后,把温度显示在文本框中。
喷灌按钮实现:
//判断串口是否打开
if (serialPort1.IsOpen)
{
try
{
if (SprayIrrigation == true)
{
btnSprayIrrigation.Text = "停止喷灌";
SprayIrrigation = false;
byte[] instruct = new byte[] { 0x06 };//创建数组存字节数组数据
serialPort1.Write(instruct, 0, instruct.Length); //数据写入端口
}
else
{
btnSprayIrrigation.Text = "喷灌";
SprayIrrigation = true;
byte[] instruct = new byte[] { 0x07 };//创建数组存字节数组数据
serialPort1.Write(instruct, 0, instruct.Length); //数据写入端口
}
}
catch (Exception ex)
{
//在信息框中追加异常信息
AppendTextToInfoBox("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "收" + "< -", txtReceive);
AppendTextToInfoBox(ex.Message + "\r\n", txtReceive);
}
}
else
{
//出现错误 提示
MessageBox.Show("出现错误:\r\n串口是否打开!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
按下按钮给下位机发送我们自定义的一个指令。
采光按钮实现:
//判断串口是否打开
if (serialPort1.IsOpen)
{
try
{
if (Lighting == true)
{
btnLighting.Text = "停止采光";
Lighting = false;
byte[] instruct = new byte[] { 0x02 };//创建数组存字节数组数据
serialPort1.Write(instruct, 0, instruct.Length); //数据写入端口
}
else
{
btnLighting.Text = "采光";
Lighting = true;
byte[] instruct = new byte[] { 0x03 };//创建数组存字节数组数据
serialPort1.Write(instruct, 0, instruct.Length); //数据写入端口
}
}
catch (Exception ex)
{
//在信息框中追加异常信息
AppendTextToInfoBox("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "收" + "< -", txtReceive);
AppendTextToInfoBox(ex.Message + "\r\n", txtReceive);
}
}
else
{
//出现错误 提示
MessageBox.Show("出现错误:\r\n串口是否打开!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
通风按钮实现:
//判断串口是否打开
if (serialPort1.IsOpen)
{
try
{
if (Ventilate == true)
{
btnVentilate.Text = "停止通风";
Ventilate = false;
byte[] instruct = new byte[] { 0x04 };//创建数组存字节数组数据
serialPort1.Write(instruct, 0, instruct.Length); //数据写入端口
}
else
{
btnVentilate.Text = "通风";
Ventilate = true;
byte[] instruct = new byte[] { 0x05 };//创建数组存字节数组数据
serialPort1.Write(instruct, 0, instruct.Length); //数据写入端口
}
}
catch (Exception ex)
{
//在信息框中追加异常信息
AppendTextToInfoBox("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "收" + "< -", txtReceive);
AppendTextToInfoBox(ex.Message + "\r\n", txtReceive);
}
}
else
{
//出现错误 提示
MessageBox.Show("出现错误:\r\n串口是否打开!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
那么这三个按钮的代码部分其实基本差不多,唯一不同的就是我们的指令,这个指令是根据我们自定义的指令,来选择发送,然后下位机接收到,进行判断是否为我们需要的一个指令,在进行下一步操作。
声光报警:
//判断串口是否打开
if (serialPort1.IsOpen)
{
try
{
if (txtUpperLimit.Text == "" || txtLowerLimit.Text == "")//文本框为空 提示错误
{
MessageBox.Show("出现错误:\r\n请检查文本框是否为空", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
txtUpperLimit.Text = "0"; //赋初始值
txtLowerLimit.Text = "0"; //赋初始值
}
else if (Lock_Unlock == true && txtUpperLimit.Text != "" && txtLowerLimit.Text != "")//按键按下
{
upper_limit = float.Parse(txtUpperLimit.Text); //获取上限温度
Lower_Limit = float.Parse(txtLowerLimit.Text); //获取下限温度
btnLock.Text = "已锁定";
txtUpperLimit.Enabled = false; //关闭文本框
txtLowerLimit.Enabled = false; //关闭文本框
Lock_Unlock = false; //按键状态
timer2.Start();//启动温度报警
}
else
{
Lock_Unlock = true;//按键状态
btnLock.Text = "锁定";
txtUpperLimit.Enabled = true; //文本框打开
txtLowerLimit.Enabled = true;//文本框打开
timer2.Stop();//关闭温度报警
}
}
catch (Exception ex)
{
//在信息框中追加异常信息
AppendTextToInfoBox("[" + DateTime.Now.ToString("HH:mm:ss") + "]" + "收" + "< -", txtReceive);
AppendTextToInfoBox(ex.Message + "\r\n", txtReceive);
MessageBox.Show("出现错误:\r\n请检查文本框是否为空或者输入的不是数字!\r\n", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
btnLock.Text = "锁定";
txtUpperLimit.Enabled = true;//文本框打开
txtLowerLimit.Enabled = true;//文本框打开
txtUpperLimit.Text = ""; //清空
txtLowerLimit.Text = ""; //清空
timer1.Stop();//关闭串口工具异常检测
timer2.Stop();//关闭温度报警
}
}
else
{
//出现错误 提示
MessageBox.Show("出现错误:\r\n串口是否打开!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
这边是按下锁定按钮之后,然后把我们在文本框输入的上下限温度提取出来,然后打开定时器,那么定时器是干什么用的呢,这边是在定时器里面进行一个判断,当我们锁定之后,我们会在定时器里面进行判断,判断有没有超过上限或者低于下限,如果达成某一个条件,则定时器会给下位机一直发送一个指令,这个指令就是打开我们的蜂鸣器以及一个LED,来实现声光报警功能。
定时器代码部分:
/当温度超过上限低于下限,发出声光警报指令给下位机
if (temp > upper_limit || temp < Lower_Limit)
{
byte[] instruct = new byte[] { 0x01 };//创建数组存字节数组数据
serialPort1.Write(instruct, 0, instruct.Length); //数据写入端口
}
最后是利用注册表获取可用串口的部分:
打开串口按钮部分:
主函数:
串口中断部分:
下位机通过在串口接收中断里面判断,接收的数据是否为我们需要的一个指令,是就存进一个数组,然后在数组里面判断我们的指令,来实现功能,而在主函数里面,我们首先要获取到我们的温度数据,然后通过公式计算出真正的温度值,通过串口发送给上位机,上位机接收到,通过转换成字符串,显示在文本框中。
我们的考核项目大概就那么多内容,经过这一次的考核之后,我也意识到自己的不足,我们自定义的指令,其实是最简单粗暴的,这部分的代码还可以优化的更好,做的更加精密,总的来说,上位机作为控制系统中重要的一环,为用户提供了方便快捷的操作界面和功能,使得用户可以方便地进行监控、调试和控制,提高了系统的灵活性和效率。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。