在实际工作中使用到了类似于485模块的功能想着与大家分享一下,此篇用TCP与UDP两种连接,使用串口连接主/从站,实现报文以网口发送串口获取再返回给网口展示,同时使用Timer实现心跳操作。
1.TCP/UDP在winform中的页面布局

TCP代码
using System;
using System.Configuration;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace 空气质量变送器
{
public partial class Form1 : Form
{
TcpClient tcpClient;
System.Windows.Forms.Timer timer;
CancellationTokenSource cts;
public Form1()
{
InitializeComponent();
txtIP.Text = ConfigurationManager.AppSettings["IPAddress"];
txtPort.Text = ConfigurationManager.AppSettings["Port"];
timer = new System.Windows.Forms.Timer();
timer.Interval = 1000;
timer.Tick += Timer_Tick;
}
#region 连接及关闭
private async void btnConnOrClose_Click(object sender, EventArgs e)
{
if (btnConnOrClose.Text == "连接")
{
try
{
tcpClient = new TcpClient();
await tcpClient.ConnectAsync(IPAddress.Parse(txtIP.Text), int.Parse(txtPort.Text));
btnConnOrClose.Text = "关闭";
txtIP.Enabled = false;
txtPort.Enabled = false;
if (cbRealTimeRead.Checked)
{
timer.Enabled = true;
}
else
{
AcceptData(); // 只接收一次数据
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
else
{
cts?.Cancel();
tcpClient?.Close();
btnConnOrClose.Text = "连接";
txtIP.Enabled = true;
txtPort.Enabled = true;
}
}
#endregion
#region 接收数据
private void AcceptData()
{
if (tcpClient == null || !tcpClient.Connected) return;
cts = new CancellationTokenSource();
Task.Run(async () =>
{
if (cts.IsCancellationRequested) return;
// 准备数据
byte[] data = new byte[] { 0x01, 0x03, 0x00, 0x00, 0x00, 0x04 };
byte[] crc = CreateCRC16(data);
byte[] buffer = new byte[8];
Array.Copy(data, 0, buffer, 0, 6);
Array.Copy(crc, 0, buffer, 6, 2);
NetworkStream stream = tcpClient.GetStream();
stream.Write(buffer, 0, buffer.Length); // 发送数据
await Task.Delay(50);
// 接收数据
byte[] bufferReceive = new byte[13];
int length = stream.Read(bufferReceive, 0, bufferReceive.Length);
if (length != 13) return;// 防止数据丢包
int pm2_5 = BytesToUshort(bufferReceive[3], bufferReceive[4]);//PM2.5
int pm10 = BytesToUshort(bufferReceive[5], bufferReceive[6]); //PM10
float sd = BytesToUshort(bufferReceive[7], bufferReceive[8]) * 0.1F; //湿度
float wd = BytesToUshort(bufferReceive[9], bufferReceive[10]) * 0.1F; //温度
// 准备数据0
data = new byte[] { 0x01, 0x03, 0x0, 0x08, 0x00, 0x02 };
crc = CreateCRC16(data);
buffer = new byte[8];
Array.Copy(data, 0, buffer, 0, 6);
Array.Copy(crc, 0, buffer, 6, 2);
stream.Write(buffer, 0, buffer.Length);//发送数据
await Task.Delay(50);
// 接收数据
bufferReceive = new byte[9];
length = stream.Read(bufferReceive, 0, bufferReceive.Length);
if (length != 9) return;
int eyht = BytesToUshort(bufferReceive[3], bufferReceive[4]); //二氧化碳
float jq = BytesToUshort(bufferReceive[5], bufferReceive[6]) * 0.01F;//甲醛
// 展示
Invoke(new Action(() =>
{
txtPM2_5.Text = pm2_5.ToString();
txtPM10.Text = pm10.ToString();
txtSD.Text = sd.ToString();
txtWD.Text = wd.ToString();
txtEYHT.Text = eyht.ToString();
txtJQ.Text = jq.ToString();
}));
}, cts.Token);
}
#endregion
#region 定时器事件及切换
private void Timer_Tick(object sender, EventArgs e)
{
AcceptData();
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
timer.Enabled = cbRealTimeRead.Checked;
}
#endregion
#region 窗体关闭
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
cbRealTimeRead.Checked = false;
cts?.Cancel();
tcpClient?.Close();
}
#endregion
#region 生成CRC
public byte[] CreateCRC16(byte[] data)
{
//crc计算赋初始值
int crc = 0xffff;

2246

被折叠的 条评论
为什么被折叠?



