C# ModBus
简单汇总一下不使用NModBus包的C#上位机侧的ModBus实现;1. ModBus Tcppublic class ModBusSerer { private int _index; private int _slaveIndex 1; private TcpClient _tcpClient; public NetworkStream _stream; public void Start(string ip, int port) { _tcpClient new TcpClient(); // 上位机作为主站要主动给从站发送连接请求因此作为Tcp的客户端 _tcpClient.Connect(ip, port); _stream _tcpClient.GetStream(); } public async Taskushort[] ReadSingleRegister(ushort startAddress, ushort count) { byte[] content new byte[5]; // ModBus实际发送内容 content[0] 0x03; // 功能码0x03 读单个保持寄存器0x06 写单个寄存器0x10 写多个寄存器 BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(1, 2), startAddress); // 寄存器地址ModBus所用地址和实际寄存器地址之间的映射见表格 BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(3, 2), count); // 读取寄存器的数量 byte[] trans new byte[12]; // Tcp发送内容MBAP头内容 ModBus实际内容 BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(0, 2), _index); // ModBus通信索引用于校验 BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(2, 2), 0); // ModBus规定这两位为0 BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(4, 2), (ushort)(content.Length 1)); // 实际发送内容长度 1 trans[6] _slaveIndex; // 从站索引 content.CopyTo(trans, 7); // 拷贝实际发送内容 await _stream.WriteAsync(trans); // ModBus写 byte[] header new byte[7]; await _stream.ReadAsync(header, 0, 7); // ModBus读响应头 ushort responseId BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(0, 2)); // 自增索引 ushort protoId BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(2, 2)); // ModBus规定这两位为0 ushort length BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(4, 2)); // 结果长度 1(从站Id) if (responseId ! _index || responseId ! 0 || length 2) return null; byte[] response new byte[length - 1]; await _stream.ReadAsync(response, 0, length - 1); // ModBus读响应 if((response[0] 0x80) ! 0) // response第一个字节是功能码如果出现异常则功能码最低位(最低地址)的位会被赋值1既0x03变成0x83 return response[1]; // 返回错误码到时候的response长度问题 else if(response.Length ! 2 count * 2 || response[0] ! 0x03 || response[1] ! count * 2) // 响应字节数组成功能码一字节 后续数据长度一字节 每个寄存器各两字节 return null; ushort[] ans new ushort[count]; for(int i 0; i ans.Length; i) ans[0] BinaryPrimitives.ReadUInt16BigEndian(response.AsSpan(2 i * 2, 2)); // 将某个寄存器读上来的两字节转换为一个short return response; } public async Taskbool WriteSingleRegister(ushort startAddress, float value) { byte[] content new byte[5]; content[0] 0x06; BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(1, 2), startAddress); BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(3, 2), value); // 写入寄存器的值 // 下面内容基本和ReadSingleRegister一样 byte[] trans new byte[12]; BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(0, 2), _index); BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(2, 2), 0); BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(4, 2), (ushort)(content.Length 1)); trans[6] _slaveIndex; content.CopyTo(trans, 7); await _stream.WriteAsync(trans); byte[] header new byte[7]; await _stream.ReadAsync(header, 0, 7); // ModBus读响应头 ushort responseId BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(0, 2)); ushort protoId BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(2, 2)); ushort length BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(4, 2)); if (responseId ! _index || responseId ! 0 || length 2) return false; byte[] response new byte[length - 1]; await _stream.ReadAsync(response, 0, length - 1); if((response[0] 0x80) ! 0) return false; else if(response ! trans) // 写单个寄存器时只要写入字节数组与相应字节数组一致就算成功 return false; return true; } public async Taskbool WriteMultiRegister(ushort startAddress, ushort[] values) { int count values.Length; // 写入寄存器的数量 byte[] content new byte[6 values.Length * 2]; content[0] 0x10; BinaryPrimitives.WriteUIInt16BigEndian(content.AsSpan(1, 2), startAddress); BinaryPrimitives.WriteUIInt16BigEndian(content.AsSpan(3, 2), count); // 写入寄存器的数量 content[5] (byte(count * 2)); for (int i 0; i count; i) BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(6 i * 2, 2), values[i]); // 写入各个寄存器的值 // 也差不多 byte[] trans new byte[7 content.Length]; BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(0, 2), _index); BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(2, 2), 0); BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(4, 2), (ushort)(content.Length 1)); trans[6] _slaveIndex; content.CopyTo(trans, content.Length); await _stream.WriteAsync(trans); byte[] header new byte[7]; await _stream.ReadAsync(header, 0, 7); // ModBus读响应头 ushort responseId BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(0, 2)); ushort protoId BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(2, 2)); ushort length BinaryPrimitives.ReadUInt16BigEndian(header.AsSpan(4, 2)); if (responseId ! _index || responseId ! 0 || length 2) return false; byte[] response new byte[length - 1]; await _stream.ReadAsync(response, 0, length - 1); if((response[0] 0x80) ! 0) return false; else if(response.Length ! 5 | response[0]| ! 0x10) // response.Length为5第一个字节功能码二三字节起始地址四五字节寄存器长度和写入的content前五个字节一致 return false; } }2. ModBus SerialPortpublic class ModBusSerer { private int _index; private int _slaveIndex 1; public void Start() { _serialPort new new SerialPort(COM1, 9600, Parity.None, 8, StopBits.One); // 串口对象 } public async Taskushort[] ReadSingleRegister(ushort startAddress, ushort count) { byte[] content new byte[5]; // ModBus实际发送内容 content[0] 0x03; // 功能码0x03 读单个保持寄存器0x06 写单个寄存器0x10 写多个寄存器 BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(1, 2), startAddress); // 寄存器地址ModBus所用地址和实际寄存器地址之间的映射见表格 BinaryPrimitives.WriteUInt16BigEndian(content.AsSpan(3, 2), count); // 读取寄存器的数量 byte[] trans new byte[1 content.Length 2]; BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(0, 2), _slaveIndex); // 从站索引 content.CopyTo(trans, 1); // 拷贝实际发送内容 ushort sendCrc CalculateCrc16(trans.AsSpan(0, 1 content.Length)); BinaryPrimitives.WriteUInt16BigEndian(trans.AsSpan(1 content.Length, 2), sendCrc); _serialPort.Write(trans, 0, trans.Length); // 串口写 byte[] ans; byte[] buffer new byte[2]; _serialPort.Read(buffer, 0, 2); // 先从串口读取功能码和长度这两个字节 ushort code (ushort)buffer[1]; // 功能码 if ((code 0x80) ! 0) ans _serialPort.Read(ans, 0, 3); // 出现异常读取一字节异常码 2字节CRC else { byte[] count new byte[1]; _serialPort.Read(count, 0, 1); // 读取长度 ushort count (ushort)count[0]; _serialPort.Read(ans, 0, count 2); // 读取所有数据内容 2字节CRC } ushort receiveCrc CalculateCrc16(ans.AsSpan(0, 5)) if (receiveCrc ! sendCrc) return null; else return ans; } private ushort CalculateCrc16(byte[] data) { ushort crc 0xFFFF; foreach(var b in data) { crc ^ b; for(var i 0; i 8; i) { if((crc 0x0001) ! 0) crc (ushort)((crc 1) ^ 0xA001); else crc 1; } } return crc; } }

相关新闻

个人知识库搭建工具对比:有道云笔记和印象笔记,谁更适合构建长期知识体系?

个人知识库搭建工具对比:有道云笔记和印象笔记,谁更适合构建长期知识体系?

** 从"记笔记"到"建知识库",两款工具走出了不同的路 一、从"记笔记"到"建知识库",两款工具的不同路径 讨论个人知识库搭建这个话题时,有道云笔记和印象笔记这两款国内的老牌笔记软件经常被放在一起。…

2026/8/18 11:48:53 阅读更多 →
贝叶斯不确定性传播在Agentic RAG中的应用:构建可信的多跳问答系统

贝叶斯不确定性传播在Agentic RAG中的应用:构建可信的多跳问答系统

1. 项目概述:当RAG有了“自知之明”最近在折腾Agentic RAG(智能体化检索增强生成)项目时,我一直在琢磨一个问题:我们费尽心思构建的这条“检索-理解-生成”流水线,它对自己给出的答案到底有多自信&#xff…

2026/8/18 11:48:53 阅读更多 →
GitHub高星项目方法论:从my_ai_town看开源成功的核心要素

GitHub高星项目方法论:从my_ai_town看开源成功的核心要素

你有没有想过,一个看似“顺手”做出来的项目,是如何在 GitHub 上获得数万 Star,甚至跻身全球排名前列的?这背后,真的只是运气吗?我最近在 GitHub 上看到了一个名为 “my_ai_town” 的项目,它获得…

2026/8/18 11:48:53 阅读更多 →

最新新闻

手把手玩转LiquidBounce战斗模块:KillAura与CrystalAura完整配置避坑指南

手把手玩转LiquidBounce战斗模块:KillAura与CrystalAura完整配置避坑指南

手把手玩转LiquidBounce战斗模块:KillAura与CrystalAura完整配置避坑指南 【免费下载链接】LiquidBounce A free Minecraft hacked client (utility mod) for Fabric 项目地址: https://gitcode.com/gh_mirrors/li/LiquidBounce 如果你在Minecraft PVP里总是…

2026/8/18 14:45:15 阅读更多 →
数据采集卡从入门到精通(26):热电偶温度采集——塞贝克效应、冷端补偿与线性化

数据采集卡从入门到精通(26):热电偶温度采集——塞贝克效应、冷端补偿与线性化

温度是工业测量第一大物理量,热电偶是温度传感器的第一大族群:便宜、皮实、量程宽(-270~1800℃)。但它也是"最难伺候"的传感器之一:μV级信号、非线性、需要冷端补偿。本文从物理原理讲到信号链设计的每一环…

2026/8/18 14:45:15 阅读更多 →
AntiDupl图片查重实操:千张照片里的重复图,三分钟全部揪出来

AntiDupl图片查重实操:千张照片里的重复图,三分钟全部揪出来

AntiDupl图片查重实操:千张照片里的重复图,三分钟全部揪出来 【免费下载链接】AntiDupl A program to search similar and defect pictures on the disk 项目地址: https://gitcode.com/gh_mirrors/an/AntiDupl 你的手机相册、下载文件夹、素材库…

2026/8/18 14:45:15 阅读更多 →
2026年锯切新趋势:高效锯条与设备的完美搭配指南

2026年锯切新趋势:高效锯条与设备的完美搭配指南

随着制造业对生产效率、精度及自动化要求的不断提升,锯切技术也在不断革新。2026年的锯切行业正迎来一场深刻的变革,从材料科学到智能化制造,每一个环节都在追求更高的性能和更低的成本。本文将深入探讨高效锯条与锯切设备的完美搭配策略&…

2026/8/18 14:45:15 阅读更多 →
【单片机课程设计/毕业设计】基于 STM32/51 单片机的 MPU6050 与 DS18B20 多传感器监护终端设计 基于 STM32/51 单片机的按键可控智能跌倒预警与照明设备设(021203)

【单片机课程设计/毕业设计】基于 STM32/51 单片机的 MPU6050 与 DS18B20 多传感器监护终端设计 基于 STM32/51 单片机的按键可控智能跌倒预警与照明设备设(021203)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/8/18 14:45:15 阅读更多 →
RVC变声器保姆级上手实录:10分钟语音数据,练出属于你的AI声音分身

RVC变声器保姆级上手实录:10分钟语音数据,练出属于你的AI声音分身

RVC变声器保姆级上手实录&#xff1a;10分钟语音数据&#xff0c;练出属于你的AI声音分身 【免费下载链接】Retrieval-based-Voice-Conversion-WebUI Easily train a good VC model with voice data < 10 mins! 项目地址: https://gitcode.com/GitHub_Trending/re/Retriev…

2026/8/18 14:44:13 阅读更多 →

日新闻

告别逐帧截图:用 extract-video-ppt 快速提取视频中的 PPT 并一键导出 PDF

告别逐帧截图:用 extract-video-ppt 快速提取视频中的 PPT 并一键导出 PDF

告别逐帧截图&#xff1a;用 extract-video-ppt 快速提取视频中的 PPT 并一键导出 PDF 【免费下载链接】extract-video-ppt extract the ppt in the video 项目地址: https://gitcode.com/gh_mirrors/ex/extract-video-ppt 如果你还停留在"看网课 不停暂停 截图 …

2026/8/18 0:00:57 阅读更多 →
思源宋体TTF一站式上手:7个字重免费商用,从下载到上线的完整走查

思源宋体TTF一站式上手:7个字重免费商用,从下载到上线的完整走查

思源宋体TTF一站式上手&#xff1a;7个字重免费商用&#xff0c;从下载到上线的完整走查 【免费下载链接】source-han-serif-ttf Source Han Serif TTF 项目地址: https://gitcode.com/gh_mirrors/so/source-han-serif-ttf 你是不是也经历过这种时刻&#xff1a;设计稿里…

2026/8/18 0:00:58 阅读更多 →
华硕笔记本控制权回收指南:GHelper 如何用一个 10MB 文件替代 Armoury Crate

华硕笔记本控制权回收指南:GHelper 如何用一个 10MB 文件替代 Armoury Crate

华硕笔记本控制权回收指南&#xff1a;GHelper 如何用一个 10MB 文件替代 Armoury Crate 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, …

2026/8/18 0:00:59 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者&#xff0c;最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent&#xff0c;从本地部署到云端API&#xff0c;我们正处在一个技术栈快速重构的节点。然而&#xff0c;面对层出不穷的模型、框架和工具&#xf…

2026/8/18 9:15:35 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/18 9:06:28 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。&#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室&#x1f447; 关注我领取海量matlab电子书和…

2026/8/18 9:04:56 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速&#xff1a;macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/17 18:54:37 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南&#xff1a;3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗&#xff1f;ncmdump解密工具帮你轻松解决这个困…

2026/8/17 18:55:16 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片&#xff1a;为英语学习 App 打造桌面级学习助手适用平台&#xff1a;HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0&#xff08;API 26 Beta&#xff09;新增了 AgentCard 智能体卡片能力&#xff0c;这是继 HMAF&#xff08;鸿蒙智能体框架&#x…

2026/8/17 18:55:55 阅读更多 →