c# 从0实现一个温湿度监测的小工具 (2)

655 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

上一节,我们已经实现了硬件的连接,已经通过常见的串口工具,实现了对硬件连接的测试,那么接下来。我们就要实现一个小的winfrom,来读取温湿度变送器的数据。

  1. 打开vs,新建一个winform项目

image.png 起名做thsensor的项目

image.png 使用.net5开发

image.png 默认是由一个helloword窗口,如图,点击运行,会自动弹出helloworld窗口

image.png

image.png

  1. 写一个串口操作类,用来通过USB转485模块,使用485通讯 右键项目名称,选择添加,添加一个新项,选择类,名字为comport,具体实现如下,注意namespace使用thsensor(注意,net5.0 需要安装nuget扩展包System.IO.Ports
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace THSensor
{
    internal class comport
    {
        /// <summary>
        /// 串口操作函数
        /// </summary>
        public static class ComPort
        {
            /// <summary>
            /// 打开串口
            /// </summary>
            /// <param name="ComName">串口名称</param>
            /// <param name="baudrate">波特率</param>
            /// <returns></returns>
            public static SerialPort V_OpenPort8N1(String ComName, int baudrate)
            {
                try
                {
                    //初始化串口对象
                    SerialPort port = new SerialPort(ComName);
                    //波特率
                    port.BaudRate = baudrate;
                    //奇偶校验
                    port.Parity = Parity.None;
                    //停止位
                    port.StopBits = StopBits.One;
                    //数据位
                    port.DataBits = 8;
                    //流控
                    port.Handshake = Handshake.None;
                    //打开发送
                    port.RtsEnable = true;
                    //打开串口
                    port.Open();

                    return port;
                }
                catch (Exception e)
                {
                    Console.WriteLine("串口-- {0} -- 初始化失败" + e.Message);
                    return null;
                }
            }
            /// <summary>
            /// 关闭串口
            /// </summary>
            /// <param name="port"></param>
            /// <returns></returns>
            public static bool ClosePort(SerialPort port)
            {
                if (port.IsOpen)
                    port.Close();
                return true;
            }
            /// <summary>
            /// 扫描串口号
            /// </summary>
            /// <param name="debug">日志输出</param>
            /// <returns>返回串口个数</returns>
            public static string[] V_ScanPort(bool debug = false)
            {
                string[] Serial_Name = SerialPort.GetPortNames();
                if (debug)
                {
                    if (Serial_Name.Length > 0)
                    {
                        Console.WriteLine(string.Format("获取到{0}个串口", Serial_Name.Length));
                        foreach (string str in Serial_Name)
                        {
                            Console.WriteLine(str);
                        }
                    }
                }
                return Serial_Name;
            }
        }
}

新建一个类,叫做thsensor.cs,实现温湿度的指令下发和解析

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.Threading;

namespace THSensor
{
    /// <summary>
    /// 温湿度传感器,默认地址001
    /// </summary>
    internal class THSensor
    {
        /// <summary>
        /// 读取温湿度值
        /// </summary>
        /// <param name="addr">地址</param>
        /// <param name="port">端口</param>
        /// <returns></returns>
        public static string ReadTHDataFromSensor(SerialPort port, byte addr, bool debug = false)
        {
            if (port.IsOpen)
            {
                byte[] recdata = new byte[16];
                byte[] sendata = new byte[16];
                //addr
                sendata[0] = addr;
                //cmd
                sendata[1] = 0x03;
                //reg addr
                sendata[2] = 0x00;
                sendata[3] = 0x00;
                //data len
                sendata[4] = 0x00;
                sendata[5] = 0x02;
                //crc
                byte[] crc = new byte[2];
                crc = VTool.CRCCalc(sendata, 6);
                sendata[6] = crc[0];
                sendata[7] = crc[1];
                //发送
                port.Write(sendata, 0, 8);
                //等待
                Thread.Sleep(50);
                //读取
                if (port.BytesToRead > 0)
                {
                    int RecvLen = port.BytesToRead;
                    //将数据按照字节依次读取
                    for (int i = 0; i < RecvLen; i++)
                    {
                        recdata[i] = (byte)port.ReadByte();
                    }
                    //crc
                    crc = VTool.CRCCalc(recdata, RecvLen - 2);
                    if (recdata[RecvLen - 2] == crc[0] &&
                       recdata[RecvLen - 1] == crc[1])
                    {
                        if (debug)
                            Console.WriteLine($"数据:{VTool.HexToString(recdata, RecvLen)}");
                        return PraseData(recdata, debug);
                    }
                }
            }
            return null;
        }
        /// <summary>
        /// 解析数据
        /// </summary>
        /// <param name="databuf"></param>
        /// <returns></returns>
        private static string PraseData(byte[] databuf, bool debug)
        {
            double currentemp;
            double currenthumi;

            //湿度
            char temp = (char)((databuf[3] << 8) + databuf[4]);
            currenthumi = temp / 10.0;
            //温度
            temp = (char)((databuf[5] << 8) + databuf[6]);
            //零下温度
            if (temp > 0xFF00)
            {
                temp = (char)(temp - 1);
                temp = (char)~temp;
                currentemp = temp / 10.0;
                if (debug)
                    Console.WriteLine($"温度:-{currentemp}    湿度:{currenthumi}");
                return $"-{currentemp}&{currenthumi}";
            }
            else
            {
                currentemp = temp / 10.0;
                if (debug)
                    Console.WriteLine($"温度:{currentemp}    湿度:{currenthumi}");
                return $"{currentemp}&{currenthumi}";
            }
        }
    }
}

实现一个工具类,vtool.cs 包含一些常用的工具函数

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace THSensor
{
    /// <summary>
    /// 工具类
    /// </summary>
    internal static class VTool
    {
        /// <summary>
        /// hex byte数组转string
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string HexToString(byte[] data, int len)
        {
            StringBuilder ret = new StringBuilder();
            for (int loop = 0; loop < len; loop++)
            {
                //{0:X2} 大写
                ret.AppendFormat(" {0:X2}", data[loop]);
            }
            return ret.ToString();
        }
        /// <summary>
        /// CRC 16校验
        /// </summary>
        /// <param name="crcbuf">校验数据</param>
        /// <param name="datalen">有效数据长度</param>
        /// <returns>高低8位</returns>
        public static byte[] CRCCalc(Byte[] crcbuf, int datalen)
        {
            //计算并填写CRC校验码
            int crc = 0xffff;
            byte[] ret = new byte[2];

            for (int n = 0; n < datalen; n++)
            {
                byte i;
                crc = crc ^ crcbuf[n];
                for (i = 0; i < 8; i++)
                {
                    int TT;
                    TT = crc & 1;
                    crc = crc >> 1;
                    crc = crc & 0x7fff;
                    if (TT == 1)
                    {
                        crc = crc ^ 0xa001;
                    }
                    crc = crc & 0xffff;
                }
            }
            ret[0] = (byte)((crc & 0xff));
            ret[1] = (byte)((crc >> 8) & 0xff);
            return ret;
        }
    }
}

至此,我们基本的操作类和工具类已经实现完毕,接下来就是做界面了。 界面的制作我们下一节细讲