前言: 实战开整
目前而言对于整个拌合楼管理软件开发,因为公司对这个项目还处于讨论中,包括个人对其中的商业逻辑也存在一些质疑,都是在做一些技术上的储备.很早就写好了串口与地磅对接获取代码,也大概知道真个逻辑,这次刚好跟库区沟通,远程连接到磅房电脑,开始实操一下.
一、地磅对接数据逻辑分析:
大多数地磅都是通过串口与电脑进行连接,所以编程实现的基本原理就是使用SerialPort来接收串口传过来的数据。从目前查到的一些文档分两种模式,一种是串口打开后,地磅会主动推送数据过来,这串数据包固定有开始符和结束符,16进制的模式。 第二种是需要主动去查询,目前我还没有碰到过这种地磅。
耀华的这款地磅每帧数据由 12 组数据组成,第1位是02(开始位) 第12位是03 (结束位) ,这里务必注意如果要判断是否是开始位或者结束位, 使用的是16进制来判断. 最开始我直接字符串比较是否=="02",遇到了坑. 需要firstByte == 0x02
第二位是符号位一般是 2B(+)和 2D(-) , 为什么会有负数, 因为有时候我们默认会有有毛重,比如实际重量10公斤, 净重就是0, 那么如果榜上重量低于10公斤,那么就是负数了.
第3到第8位就是称重重量的数据了,第九位是小数位, 他用来标记小数点所在的位置.第10位为异或校验高四位,第11位为异或校验低四位, 这两位的作为用我到现在还没太明白.
02 2D 30 30 30 30 36 30 30 31 42 03
以上就是我收到的一组地磅传过来的数据.称量的数据 16进制转换为ascii码为
30 30 30 30 36 30 => 0 0 0 0 6 0
这个磅我初始化设置的小数点位在第三位,称重单位为吨,上面第九位小数位为0x30 =>0 那么组合起来数字就是000.060 对应数据位 0.06吨
二、代码的实现:
1. Form 界面如下: 一个下拉框选择com口, 两个按钮,一个textbox
2. 定义类的属性
private string PortName;
private SerialPort port = new SerialPort();
private string _recv_string = "";
PortName用来接收下拉选择的端口, port 就是我们定义的SerialPort 类, 用来接收数据. 我印象中以前版本的.net 窗体,估计4之前,实际直接拖一个serialport组件到form窗体上的,就像textbox一样, .net 8 里面没有, 需要手工通过nuget 去下载 System.IO.Port
3. 定义一个函数遍历电脑所有的port返回一个list
public static List<string> loadComPorts()
{
List<string> portsList = new List<string>();
string[] ports = SerialPort.GetPortNames();
if (ports != null)
portsList = ports.ToList();
return portsList;
}
4. form1_load 时初始化下拉选择框
private void Form1_Load(object sender, EventArgs e)
{
comboBox1.DataSource = loadComPorts();
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
PortName = comboBox1.Text;
}
3和4 不是必须得,如果只是测试完全可以知道地磅所接的是那个端口直接设置端口号了.
5. 设置参数开启port监听.
private void button1_Click(object sender, EventArgs e)
{
if (!port.IsOpen)
{
port.PortName = PortName;
port.BaudRate = Convert.ToInt32("9600");
port.DataBits = 8;
port.Parity = Parity.None;
port.StopBits = StopBits.One;
port.ReceivedBytesThreshold = 1;
try
{
port.Open();
MessageBox.Show($"{PortName} 打开成功");
port.DataReceived += new SerialDataReceivedEventHandler(serialPort_DataReceived);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
这里有两个很关键的地方, 第一个 port.BaudRate 如果直接赋值 9600 会包参数错误, 没搞明白为什么会这样. 另外 port.ReceivedBytesThreshold = 1; 如果没有这一代码, 你会发现过很久才接收到一帧数据.
private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
_recv_string = "";
int len = port.ReadBufferSize;
byte[] bytesData = new byte[len];
byte byteData;
for (int i = 0; i < len - 1; i++)
{
byteData = Convert.ToByte(port.ReadByte());
if (byteData == 0x03)
{
break;
}
bytesData[i] = byteData;
_recv_string += bytesData[i];
}
// _recv_string = Encoding.Default.GetString(bytesData);
textBox1.Invoke(new EventHandler(delegate {
textBox1.AppendText(Environment.NewLine);
textBox1.AppendText( DateTime.Now.ToString("G") + " " + DateTime.Now.Microsecond + "|" + _recv_string ); }));
}
到此我们就把地磅数据接收出来了,并通过textbox不断地显示出来接收到的地磅数据. 地磅大概每100毫秒推送一次数据,textbox就会滚动显示, 这里我还碰到了一个坑. 如果使用
textbox1.text = info + "\r\n" +textbox1.text
本意是将info的新内容附加到textbox1中, 但实际出来的显示效果是把textbox1中的内容覆盖掉只是显示最新的info. 需要通过 AppendText 来实现, 另外换行需要用到 Enviroment.NewLine ,用"\r\n" 不行.
总结: 核心功能完成
到这里地磅读数已经完成了, 但还是留有一个小尾巴, 我们拿到的 _recv_string 还需要进一步解析为10进制的数据, 另外还有就是 地磅称重过程,数据会不停地变化,那么我们需要有个机制判断地磅是否已经稳定, 当前数据是否就应该是称重重量. 我考虑的逻辑是 如果经过 2s 或者 连续接收10次 数据都是一样,那么就判定磅的状态已经稳定, 可以对拿到的数据进行解析输出称重重量了.