室内系的上位机Maker(二)

在上篇博文中我们完成了串口接收上位机软件的基本开发,但嵌入式系统给上位机发送的数据格式一般是固定的,有时还可以根据接收到的历史数据查看趋势,这时我们就需要对单片机发来的串口信息进行分割,然后使用chart控件进行绘图展示。

文本分割

之前我们采用

textBox_receive.AppendText(serialPort1.ReadExisting());


这一行来给消息接收区文本框一行一行的添加文字,但并不便于我们对接收到的文本进行处理和计数,所以这里运用了字符串构造类型StringBuilder来创建变量,用作存储接收到的文本。因此,需要在以下位置增加代码

publicpartialclassForm1 : Form
{
private StringBuilder sb = new StringBuilder();    //为了避免在接收处理函数中反复调用,依然声明为一个全局变量
……


这样就声明好了一个叫sb的StringBuilder类变量。

存储文本的容器建好了,还需要将串口缓冲区中的数据放进去,因此串口数据接收事件的写法会有很大不同,代码如下:

privatevoid serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
int num = serialPort1.BytesToRead;      //获取接收缓冲区中的字节数
byte[] received_buf = newbyte[num];    //声明一个大小为num的字节数据用于存放读出的byte型数据
            serialPort1.Read(received_buf, 0, num);   //读取接收缓冲区中num个字节到byte数组中
            sb.Clear();     //防止出错,首先清空字符串构造器
            sb.Append(Encoding.ASCII.GetString(received_buf));  //将整个数组解码为ASCII数组

这时候需要将文本增加到文本框中使用的代码变为:

textBox_receive.AppendText(sb.ToString());


这里注意到sb使用了ToString方法,可以了解StringBuilder类提供了诸多方法例如Append,Remove 等,具体可以阅读https://www.cnblogs.com/tonysuen/archive/2010/03/04/1678447.html

理解了用方法来对字符串构造类型进行处理之后,就可以对串口文本进行处理,本例中单片机向电脑发送的是"t=12.34-h=56.78",意为温度12.34°,湿度56.78%使用代码如下

                   label4.Text = sb.ToString().Split('=')[1].Split('-')[0];
                    label7.Text = sb.ToString().Split('=')[2];


即实现将两个标签的文本改为对应数字功能。以提取温度值为例,将接收到的"t=12.34-h=56.78"转化为字符串后,根据=分割,取分割出的第二段(从0开始数),再用-分割取第一段。

Split的详细用法可以参阅vs提供的帮助或者以下链接

https://www.cnblogs.com/atom-wangzh/p/8489135.html

最终实现了以下效果,且数字整数位小数位均可变化。

接下来讲解本例的折线图绘制,这里一定要参阅制作本例的另一篇笔记

http://www.fcxl9876.xin/index.php/archives/91

首先主要了解一下chart控件的两个集合属性:

ChartAreas:图表区集合,可以设置图表区域相关属性,如网格颜色,坐标轴名称等。

Series:图表序列集合,可以设置图表序列相关属性,如图表类型,坐标值显示类型,标记图案,图案颜色等。

最重要的是将Series属性中ChartType修改为Line,即折线图。

本图中横竖坐标名称、折线名称、颜色等均可以自定义,其他属性自行查询文档或使用搜索引擎,这里不再一一介绍。

由于数据是不断更新的,这里需要使用队列类(Queue),使用Enqueue方法可以向队列中添加一个数据,使用Dequeue方法可以删除一个最老的数据。因此更新时间队列的写法如下:

                   timex.Enqueue(DateTime.Now.ToLongTimeString());
                    timex.Dequeue();


这样就准备好了绘图的数据源,但是图表还没准备好,因为我们需要固定让图中始终有10个点,也就是队列中始终有10个数据,所以在Form1_Load事件中加入以下代码:

            for (int i = 0; i < 10 ;i++)
            {
                timex.Enqueue("0");

            }
            for (int i = 0; i < 10; i++)
            {
                temy.Enqueue("0");

            }
            for (int i = 0; i < 10; i++)
            {
                humy.Enqueue("0");

            }


这样就差最后一步,把代表数据的点打在折线图上了。以下引用小林文章中的解释。

由于Series中Points集合只能调用AddXY同时添加点的X值和Y值或添加Y值,无法单独添加X值,并且添加Y值后无法修改X值,所以只能同时给X值和Y值赋值。这里由于两个队列使用foreach循环无法实现,所以将队列暂时复制到新的数组中实现对折线图点的赋值。

先在队列定义处(Form1类中)定义数组:

 

Object[] stime;
Object[] stem;
Object[] shum;


之后在需要更新数据的地方(这里为invoke方式,也可使用计时器)执行如下操作:

 

队列入队一个元素,然后出队一个元素完成数据更新;

清空Series中所有点;

将队列元素复制到新的数组中;

使用for循环给Series中的点赋值。

在这个样例中,代码如下:

 

timex.Enqueue(DateTime.Now.ToLongTimeString());//获取系统时间入列
timex.Dequeue();//将队列最前的数据出列
temy.Enqueue(label4.Text);//获取温度入列
temy.Dequeue();//将队列最前的数据出列
humy.Enqueue(label7.Text);//获取湿度入列
humy.Dequeue();//将队列最前的数据出列
chart1.Series["温度"].Points.Clear();//清空温度Series中所有点
chart1.Series["湿度"].Points.Clear();//清空湿度Series中所有点
 
stime = timex.ToArray();//将时间队列复制到数组中
stem = temy.ToArray();//将温度队列复制到数组中
shum = humy.ToArray();//将湿度队列复制到数组中
for (int j = 0; j < 10; j++)
{
    chart1.Series["温度"].Points.AddXY(stime[j], stem[j]);//给温度Series的点赋值
    chart1.Series["湿度"].Points.AddXY(stime[j], shum[j]);//给湿度Series的点赋值
}


这样就完成了动态刷新的折线图。

发送功能

本例中只需要串口接收功能,这里附带开发一个简单的发送功能。

首先拖出文本框和按钮控件,将文本框改为可多行显示,名字命名为textBox_send,将按钮的文本改为发送。

我们不希望软件在串口未打开的情况下发送按钮可用,所以在窗体加载事件Form1_Load和打开串口按钮事件中加入行

button2.Enabled = false;


在关闭的串口被打开后,也需要使发送按钮可用,这一部分代码同理。

如图,串口关闭后发送按钮变为灰色。

这一篇有点水,等把Qt学一学再来更新。

Powered By Z-BlogPHP 1.5.2 Zero

Copyright By JIANGANQI.TOP. Some Rights Reserved. 桂ICP备17011252号