c#温室监控系统

上位机采用串口与下位机信,使用modbus通信协议控制和采集数据

可以参考一下(直接翻到modbus的章节):

MODBUS技术协议(第三章).pdf · chuan/临时的 - 码云 - 开源中国 (gitee.com)

首先实现通信用的modbus类

代码先实现了3个基础的功能 读输出状态、读保存寄存器和强置单线圈

  public class Modbus{#region 基础设置private SerialPort serialPort;private byte[]? ReceivedData;private bool ReceiveFlag=false;public Modbus(string portName, int baudRate, Parity parity,StopBits stopBits, int dataBits){serialPort = new SerialPort();serialPort.PortName = portName;serialPort.BaudRate = baudRate;serialPort.Parity = parity;serialPort.StopBits = stopBits;serialPort.DataBits = dataBits;}public void OpenModbusConnet(){try{serialPort.Open();serialPort.DataReceived += SerialPort_DataReceived;}catch (Exception ex){throw new Exception("串口打开失败:" + ex.Message);}}private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e){int ReceivedDataLenth = serialPort.BytesToRead;ReceivedData = new byte[ReceivedDataLenth];serialPort.Read(ReceivedData, 0, ReceivedDataLenth);ReceiveFlag=true;}~Modbus(){if (serialPort is not null){if (serialPort.IsOpen)serialPort.Close();}}#endregion//等待数据返回,超过200毫秒退出等待private void awiteReceive(){ReceiveFlag= false;DateTime dateTime = DateTime.Now;while (ReceiveFlag == false){var t= DateTime.Now-dateTime;if(t.Milliseconds>200)ReceiveFlag= true;}}/// <summary>/// 发送报文/// </summary>/// <param name="cmd"></param>/// <exception cref="Exception"></exception>private void SendCommand(byte[] cmd){if (!serialPort.IsOpen){throw new Exception("modbus连接未打开");return;}try{serialPort.Write(cmd,0,cmd.Length);awiteReceive();}catch (Exception ex){throw new Exception("发送失败:"+ex.Message);}}#region 读输出状态 功能码0x01/// <summary>/// 读输出状态 功能码0x01/// </summary>/// <param name="SalavAddr"></param>/// <param name="StartAddr"></param>/// <param name="coils"></param>/// <returns>返回接收到的报文 <see langword="byte[]"/></returns>public byte[] ReadCoilsStatus(byte SalavAddr, int StartAddr, int coils){byte[] Cmd= FillMessage(SalavAddr,0x01, StartAddr, coils);try{SendCommand(Cmd);}catch (Exception ex){throw ex;}if (ReceivedData is not null && ReceivedData.Length is >3 ){if (ReceivedData[1] == (byte)0x01){if (verifyReciveCRC(ReceivedData)){return ReceivedData;}}}return null!;}#endregion#region 读取保持型(保存)寄存器 功能码0x03/// <summary>/// 读取保持型寄存器 功能码0x03/// </summary>/// <param name="SalavAddr"></param>/// <param name="StartAddr"></param>/// <param name="RegisterCount"></param>/// <returns>返回接收到的报文<see langword="byte[]"/></returns>public byte[] ReadRegister(byte SalavAddr, int StartAddr, int RegisterCount){byte[] Cmd = FillMessage(SalavAddr, 0x03, StartAddr, RegisterCount);try{SendCommand(Cmd);}catch (Exception ex){throw new Exception(ex.Message);}if (ReceivedData is not null && ReceivedData.Length is > 3){if (ReceivedData[1] == (byte)0x03){if (verifyReciveCRC(ReceivedData)){return ReceivedData;}}}return null!;}#endregion#region  强置单线圈 功能码0x05public enum ColiStatus{ ON=0xFF,OFF=0x00}/// <summary>/// 设置当个线圈的状态为ON高电平或低电平OFF/// </summary>/// <param name="SalavAddr"></param>/// <param name="CoilAddr"></param>/// <param name="ONOFF">断通标志=FF00,置线圈 ON(1)/// 断通标志=0000,置线圈 OFF(0)</param>/// <returns>返回接收到的报文<see langword="byte[]"/></returns>public byte[] ForceSingleCoil(byte SalavAddr, int CoilAddr, ColiStatus ONOFF){byte[] Cmd = FillMessage(SalavAddr, 0x05, CoilAddr,ONOFF== ColiStatus.ON ?65280:0000);//65280 ==0xFF00try{SendCommand(Cmd);}catch (Exception ex){throw ex;}if (ReceivedData is not null && ReceivedData.Length is > 3){if (ReceivedData[1] == (byte)0x05){if (verifyReciveCRC(ReceivedData)){return ReceivedData;}}}return null!;}#endregion/// <summary>/// 填充8位数据报文/// </summary>/// <param name="SalavAddr">从机地址</param>/// <param name="FCode">功能码</param>/// <param name="StartAddr">起始地址</param>/// <param name="dataBit">2位数据位</param>/// <returns>填充好的报文</returns>private byte[] FillMessage(byte SalavAddr, byte FCode, int StartAddr, int dataBit){byte[] message = new byte[8];message[0] = SalavAddr;message[1] = FCode;message[2] = (byte)((StartAddr - StartAddr % 256) / 256);//起始地址高位message[3] = (byte)(StartAddr % 256);//起始地址地位message[4] = (byte)((dataBit - dataBit % 256) / 256);message[5] = (byte)(dataBit % 256);var crc = CRC16(message);message[6] = crc[0];message[7] = crc[1];return message;}[DebuggerHidden]public byte[] CRC16(byte[] byteData){byte[] CRC = new byte[2];UInt16 wCrc = 0xFFFF;for (int i = 0; i < byteData.Length - 2; i++){wCrc ^= Convert.ToUInt16(byteData[i]);for (int j = 0; j < 8; j++){if ((wCrc & 0x0001) == 1){wCrc >>= 1;wCrc ^= 0xA001;}else{wCrc >>= 1;}}}CRC[1] = (byte)((wCrc & 0xFF00) >> 8);CRC[0] = (byte)(wCrc & 0x00FF);return CRC;}/// <summary>/// 校验接收到的数据是否正确/// </summary>/// <param name="byteData"></param>/// <returns>正确返回true</returns>[DebuggerHidden]private bool verifyReciveCRC(byte[] byteData){var resCrc = CRC16(byteData);if (resCrc[0] == byteData[byteData.Length - 2] &&resCrc[1] == byteData[byteData.Length - 1])return true;else return false;}}

如果需要其他可能码基本都是复制粘贴改一下 if (ReceivedData[1] == (byte)0x05) 这个功能码就好

使用modbus类要先实例化一个,然后调用打开连接方法,就可以正常调用(使用)功能方法了

可以使用仿真软件进行测试

试了一下是能正常使用并返回正确报文

Modbus拓展方法:

再编写一个类,为返回的报文提供数据解析

 public static class ModbusExtension{/// <summary>/// 提取读线圈返回的报文数据/// </summary>/// <param name="modbus"></param>/// <param name="bytes"></param>/// <returns><see langword="List<bool>"/> 下标0为起始地址线圈 </returns>public static List<bool> BytesOfReadColisToBoolList(this Modbus modbus, byte[] bytes) {List<bool> coilsStatas = new List<bool>();if (bytes is not null && bytes.Length >= 5){if (bytes[1] == (byte)0x01){for (int i = 0; i < Convert.ToInt32(bytes[2]); i++){for (int j = 0; j < 8; j++){coilsStatas.Add((byte)((byte)((byte)(bytes[i + 3] >> j) << 7) >> 7) == (byte)0x01 ? true : false);}}}}return coilsStatas;}/// <summary>/// 提取读寄存器返回的报文数据/// </summary>/// <param name="modbus"></param>/// <param name="bytes"></param>/// <returns><see langword="int[]"/>从0开始,每一项为一个寄存器的值/// error return null</returns>public static int[] BytesOfReadKeepRegisterToIntArr(this Modbus modbus,byte[] bytes){if (bytes is null || bytes.Length < 3) return null!;int dataLenth = (int)bytes[2];int[] result = new int[dataLenth / 2];int databitStart = 3;int RegisterLen = 2;int j = 0;try{for (int i = databitStart; i < dataLenth * 2; i += RegisterLen){if (i >= bytes.Length) break;result[j] = bytes[i];result[j] <<= 8;result[j] |= bytes[i + 1];j++;}}catch (Exception){}return result;}}

简单测试

使用Winform测试

只有一个窗体代码如下

using CModbus;
using System.IO.Ports;
using Timer = System.Windows.Forms.Timer;namespace WinFormsApp1
{public partial class Form1 : Form{private Modbus? modbus;public Form1(){InitializeComponent();string[] portName= SerialPort.GetPortNames();this.comboBox1.DataSource= portName;}private void button1_Click(object sender, EventArgs e){string portname=this.comboBox1.SelectedValue.ToString()!;modbus = new Modbus(portname,9600,Parity.None,StopBits.One,8);modbus.OpenModbusConnet();}private void button2_Click(object sender, EventArgs e){if (modbus is not null){Timer t = new Timer();t.Interval = 1000;t.Tick += (_, _) => {var coilsData=  modbus.ReadCoilsStatus(0x01,0000,0008);var str1= coilsData.Select(x => x.ToString("X2")+" ").ToArray();string t1="";string t2 = "";var coilStatus=modbus.BytesOfReadColisToBoolList(coilsData);for (int i = 0; i < str1.Length; i++){t1 += str1[i];}for (int i = 0; i < coilStatus.Count; i++){t2 += coilStatus[i].ToString()+"  ";}textBox1.Text= t1;textBox2.Text= t2;//var RegisterData = modbus.ReadRegister(0x01, 0000, 0008);//var str2 = RegisterData.Select(x => x.ToString("X2") + " ").ToArray();//string t3 = "";//string t4 = "";//var RegisterValue = modbus.BytesOfReadKeepRegisterToIntArr(RegisterData);//for (int i = 0; i < str2.Length; i++)//{//    t3 += str2[i];//}//for (int i = 0; i < RegisterValue.Length; i++)//{//    t4 = RegisterValue[i].ToString()+"  ";//}//textBox3.Text = t3;//textBox4.Text = t4;};t.Start();}}}
}

}

读取8个线圈结果如下:

 读寄存器:

 最后简单实现了一下温度采集和一些控制

下位机链接:http://t.csdn.cn/TC52y

最后直接附上源码链接了:(3条消息) 【免费】c#上位机(温室监控系统源码)资源-CSDN文库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/55294.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

es6 filter方法_极其有用的JavaScript数组方法,可提高您的编码技能

随着ES6的开始&#xff0c;在javascript语言中添加了许多改进以改进javascript编码标准。 今天&#xff0c;我们将看到一系列非常有用的数组方法&#xff0c;这些方法将提高您的编码技能。因此&#xff0c;让我们深入了解它。 数组 该Array.of语法如下 Array.of(element0[, ele…

互联网快讯:荣耀打响重回海外第一枪;极米投影产品成双十一爆款;工业富联发起灯塔领航计划

国内要闻 《2021胡润百富榜》发布&#xff1a;农夫山泉创始人钟睒睒以3900亿元首次成为中国首富&#xff0c;抖音创始人张一鸣跃居第二&#xff1b;荣耀打响重回海外第一枪&#xff1a;多国市场发布荣耀50系列&#xff0c;已恢复与主流运营商及渠道商合作&#xff1b; 3、财富…

推荐两本 Spark 好书

点击蓝色“有关SQL”关注我哟 加个“星标”&#xff0c;天天与10000人一起快乐成长 这两本书&#xff0c;分别是&#xff1a; 《Spark 快速大数据分析》 &#xff08;英文书名《Learning Spark: Lightning-fast Data Analysis》&#xff09;《Spark高级数据分析》影印版&#x…

宋城演艺业绩同比下滑两倍:受花房科技拖累,计提超10亿元减值

因2020年度业绩大幅预亏以及拟股权投资减值准备等&#xff0c;深交所于2月2日对向宋城演艺&#xff08;SZ:300144&#xff09;下发关注函&#xff0c;要求该公司进行补充说明。 据了解&#xff0c;今年1月30日&#xff0c;宋城演艺披露的《2020年度业绩预告》、《关于拟计提长…

花椒六间房“花房之夜”落幕 全新升级不止心动

中新网1月18日电 17日&#xff0c;直播界备受关注的年度盛典“2019花房之夜”在北京凯迪拉克中心开启&#xff0c;百位明星嘉宾与来自花椒直播和六间房的近300位人气主播共同出席&#xff0c;见证了这一盛大的年度颁奖典礼。 在“花房之夜”现场&#xff0c;揭晓了“年度最受观…

EDG刷屏、花椒沉寂,“初老”花房如何闯关IPO?

LPL全球总决赛&#xff0c;EDG刷屏社交网络。 在B站、腾讯等直播平台&#xff0c;不仅各大游戏直播间人满为患&#xff0c;其它类直播间EDG相关话题也炙手可热&#xff1b;抖音、微博、朋友圈更纷纷刷出Z世代节日氛围。 与之鲜明对比的是&#xff0c;曾经的“直播界奥斯卡”花…

花椒母公司花房更新招股书:年利润4亿 周鸿祎是大股东

雷递网 雷建平 5月1日报道 花椒母公司花房集团日前向港股递交招股书&#xff0c;准备在香港上市。 一旦花房集团上市&#xff0c;将成为继斗鱼、虎牙、映客、天鸽互动之后又一家上市的直播企业&#xff0c;也是360集团孵化的又一家上市企业。 这之前&#xff0c;360集团孵化的3…

《领航优配》这只A股一字涨停!千亿巨头也火了,突然强势暴拉!

今日早盘&#xff0c;A股继续强势上攻&#xff0c;上证指数6连阳&#xff0c;科创50指数7连阳&#xff0c;中证500指数则一举突破此前的收拾平台&#xff0c;创出年内新高。 盘面上&#xff0c;光热发电、房地产、家居用品、仓储物流等板块涨幅居前&#xff0c;石油、鸡肉、光刻…

迈向应用现代化,企业开发离不开VMware Spring

“比你优秀的人&#xff0c;比你还努力”&#xff0c;这不是在说VMware Spring高效开发框架吗&#xff1f; 在蓬勃发展的Java市场&#xff0c;Spring是最受欢迎的程序语言。2021年&#xff0c;Snyk公布的JVM生态系统报告显示&#xff1a;在Java市场&#xff0c;一半以上在使用S…

马斯克陷OpenAI诈捐门!口口声声1亿美元,结果只有1500万可追溯

西风 发自 凹非寺量子位 | 公众号 QbitAI 号称给OpenAI捐了1亿美元的马斯克&#xff0c;实际上可能连一半都没给到&#xff1f;&#xff01; 众所周知&#xff0c;马斯克此前一直宣称给OpenAI捐了1亿美元&#xff0c;而且还在推特上大放厥词&#xff1a; “我仍然对于我捐赠了大…

OpenAI创始人拿微软100亿,是在下一步大棋

丰色 编译自 凹非寺量子位 | 公众号 QbitAI OpenAI获得微软100亿美元投资的消息出来后&#xff0c;一些人的想法有些沮丧&#xff1a; 一方面&#xff0c;摆脱了经济压力的OpenAI可能将不再那么“open”、顺而放弃“开发造福每个人的AI技术”的精神&#xff1b; 另一方面&#…

全面了解大语言模型,这有一份阅读清单

机器之心报道 机器之心编辑部 进NLP群—>加入NLP交流群 了解当代大型语言模型背后的设计、约束和演变&#xff0c;你可以遵循本文的阅读清单。 大型语言模型已经引起了公众的注意&#xff0c;短短五年内&#xff0c;Transforme等模型几乎完全改变了自然语言处理领域。此外&a…

ChatGPT能给IOT行业带来哪些改变

引言# 随着移动互联网、传感器的发展&#xff0c;移动互联的潮流逐渐转移到物联网行业&#xff0c;每个设备成为了物联网连接的终端。 与传统的设备相比&#xff0c;智能设备最突出的特点就是智能化。目前&#xff0c;在市场上的智能设备通过智能程序设定或者语音来执行人类下…

女朋友的道歉方式

1 女朋友的道歉方式 2 看吧,无聊的时候什么事都干得出来3 这猫太坏了&#xff01;4 还以为是特效,原来是实物 5 叉子的妙用 6 也太信任这块玻璃了吧…… 7 棉花糖炸酱面......大家感受一下 你点的每个赞&#xff0c;我都认真当成了喜欢

史上最难的初等几何问题?分享一个参考答案

题目发出后&#xff0c;大家的回应似乎比我想象中的更积极&#xff0c;我看到了好几个不同的正确解答。加上我本来知道的几种做法&#xff0c;现在我已经知道了至少5种正确的解法。现在随便发一个&#xff0c;供大家参考。 我们首先从号称世界第二难的几何题“50-60三角形”…

一道有趣的几何证明题

上初中的时候在课外书上看到过一个数学题目&#xff0c;那时觉得特别有趣&#xff0c;后来了在大学里也常常会想起这个数学题。今天在这里跟大家分享下&#xff01; 题目是&#xff1a;证明任意一个个三角形都是等腰三角形。 初看这个题目&#xff0c;大家都可能会有疑问&…

【证明题】(一)微分中值定理

目录 微分中值定理单中值一阶导数中值定理问题二阶导数中值定理问题 双中值 ϵ &#xff0c; η \epsilon&#xff0c;\eta ϵ&#xff0c;η 可能相等 ϵ &#xff0c; η \epsilon&#xff0c;\eta ϵ&#xff0c;η 不可相等 中值不等式拉格朗日证明题泰勒公式证明题 参考资…

MathGraph: 一个用来自动求解高中数学习题的数学知识图谱

论文地址&#xff1a; MathGraph&#xff1a;A Knowledge Graph for Automatically Solving Mathematical Exercises 贡献&#xff1a; 设计了一个数学知识图MathGraph&#xff0c;包括实体和关系 设计几个算法&#xff0c;将数学习题与MathGraph对齐&#xff0c;用对齐后的子…

有趣数学1的证明

之前说了利用以10为底数巧算首位数字&#xff0c;答案是得到了&#xff0c;但是需要证明这种方法确实是正确的&#xff0c;其实证明非常简单。 证明&#xff1a;令x^y t 两边同时取以x为底&#xff0c;y 得到 y * . 由于数字都是以10进制展示的&#xff0c;我们设t的…