Modebus通信协议 温控器示例

2024-06-05 12:36

本文主要是介绍Modebus通信协议 温控器示例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1 指令解释

2 获取动态的CRC

3 crc在线验证

4 16进制正负温度互转

4.2 16进制转温度

4.2 温度转16进制

5 完整工具类


最近安卓工作接了很多硬件,其他的都是发个固定指令,比较有代表性就是温控器和打印机自定义内容所以这个记录接入示例,纯搞安卓或者没有搞过的同学可能还是有点云里雾里,因为很多参数需要自己算,我这里记录一下,有什么疑问留言讨论

1,指令解释

注意协议可能有所不同,但协议思想是差不多 我们以为例

01(从机地址) 03(功能码) 00 00(温度查询) 00 01(寄存个数) 84 0A(校验码 动态)为查询温度举例    

参数(例)含义解释
0x01 从机地址为1基本固定 查厂家文档确认下
0x03 功能码读一个寄存器查询 功能表     
0X0014 起始地址为20 查询地址 20转16进制为14,这里举例20 是为了解释16进制的 
0X0001寄存个数1 基本固定 查厂家文档确认下
0X**CRC16低位CRC根据前面12位生成
0X** CRC16高位CRC根据前面12位生成

    

2,获取动态的CRC

注意这里是说01 03 00 00 00 01生成的84 0A,如果我们写入程序中则需要动态计算,因为前面的参数根据功能不同在变化,所以后面也是变化的,我理解这其实简单理解类似请求参数加密,确保参数没有更改。

傻瓜式使用示例

1.确定 01 03 00 00 ** ** ** ** 地址和功能码

2.再确定 01 03 00 00 00 01 ** ** 转化好的温度

3.再将01 03 00 00 00 01 丢入 getCRC 为 84 0A高低位已经替换到

4.所以查询温度直接输入 01 03 00 00 00 01 84 0A

  /*** 计算CRC16校验码** @param bytes* 字节数组* @return [String] 校验码* @since 1.0*/fun getCRC(bytes: ByteArray): String {// CRC寄存器全为1var CRC = 0x0000ffff// 多项式校验值val POLYNOMIAL = 0x0000a001var i: Intvar j: Inti = 0while (i < bytes.size) {CRC = CRC xor (bytes[i].toInt() and 0x000000ff)j = 0while (j < 8) {if (CRC and 0x00000001 != 0) {CRC = CRC shr 1CRC = CRC xor POLYNOMIAL} else {CRC = CRC shr 1}j++}i++}// 结果转换为16进制var result = Integer.toHexString(CRC).uppercase(Locale.getDefault())if (result.length != 4) {val sb = StringBuffer("0000")result = sb.replace(4 - result.length, 4, result).toString()}//高位在前地位在后//return result.substring(2, 4) + " " + result.substring(0, 2);// 交换高低位,低位在前高位在后return result.substring(2, 4) + " " + result.substring(0, 2)}

3,crc在线验证

CRC(循环冗余校验)在线计算_ip33.com,注意上面的代码已为你交换好了

查询温度

4,16进制正负温度互转

单次温度查询返回结果示例

origin: {"bRec":[1,3,2,0,-26,57,-50],"sComPort":"/dev/ttyS1","sRecTime":"08:47:29"}

Hex: 010302 00E6 39CE

温度Hex: 00E6

温度解析: 23.0 ℃

前后缀去掉拿到数据,返回数据暂时就不验证了直接用,先转16进制再转10进制,因为单位是0.1℃除以10得到℃,但这个温度存在负数,所以咱们还需要解决 温度和进制转化的问题,不仅在查询的时候需要,再设置温度的时候也更需要  反过来所以下面看看互转

4.2 16进制转温度

例 -25.2=0XFF04 25.2=0X00FC -100.0=0XFC18 500.0=0X1388

首先得判断正负数有的地方是根据FF即16进制极值,而我这里根据实际需求判断一个F即可,

然后负数按位取反+1,inv和~取反你们也可以试试 我这里比较直接,不知道是不是版本问题,先这样算,这里的文档厂家标为0.1℃所以还要除以10

      println("hexToTemp FF04:${hexToTempString("FF04")}℃")println("hexToTemp 00FC:${hexToTempString("00FC")}℃")println("hexToTemp FC18:${hexToTempString("FC18")}℃")println("hexToTemp 1388:${hexToTempString("1388")}℃")/*** 16进制带正负转化为10进制*/private fun hexToTempString(hex: String): Float {println("")println("")println("--------------->$hex")// 判断16进制字符串是否为正负val isNegative = hex.uppercase().startsWith("F")// 如果是负数则转为二进制再按位取反再然后得到10进制数值并加上-号return if (isNegative) {val num = hexToIntTemp(hex)// 除10保留两位小数,单位为0.1℃(-num * 100.00f / 1000)} else {val num = hex.toInt(16)(num * 100.00f / 1000)}}/*** 按位取反+1 获取负数问题*/private fun hexToIntTemp(hex: String): Int {// 将 16 进制转换为二进制val binaryString = hex.toInt(16).toString(2)println("binary:$binaryString")// 按位取反val binaryStringInverted = binaryString.map {if (it == '0') '1' else '0'}.joinToString("")println("binaryInverted:$binaryStringInverted")// 两个二进制字符串相加val sum = binaryStringInverted.toInt(2) + 1println("+1:$sum")return sum}

打印结果

4.2 温度转16进制

举例设置温度为1℃

傻瓜式使用示例

1. 01 06 ** ** ** ** ** ** 功能码 06为写入

2. 01 06 00 14 ** ** ** **  地址 为20转化16进制为00 14

3. 01 06 00 14 00 01 1℃转化(看下面)000A

3. 01 06 00 14 00 0A  getCRC获取验证码 49 C9

4.所以查询温度直接输入 01 06 00 14 00 0A 49 C9

如果失败请检查机器是否有最大最小温度限制

分别用tempToHexString方法验证1和2转化16进制,再用上面的方法验证-2的ffec是否正确

  println("tempToTemp 1℃:${tempToHexString("1")}")println("tempToTemp -2℃:${tempToHexString("-2")}")println("验证-2℃ 逆转 ffec:${hexToTempString("ffec")}℃")/*** 整数直接转16进制负数 -1 按位取反*/private fun tempToHexString(num: String): String {println("")println("")println("--------------->$num")val toFloat = num.toFloat()// 将℃转化为0.1℃val toInt = (toFloat* 10).toInt()if (toInt >= 0){// int 转16进制return toInt.toString(16).uppercase().padStart(4, '0')}else{val temp = abs(toInt) -1println("temp-1:${temp}(0.1温度)")// 不足16位进进行0补全val tempBinary = temp.toString(2).padStart(16, '0')println("binary:${tempBinary}")// 按位取反val binaryStringInverted = tempBinary.map {if (it == '0') '1' else '0'}.joinToString("")println("binaryInverted:${binaryStringInverted}")return  binaryStringInverted.toLong(2).toString(16).uppercase()}}

5,完整工具类

package 改为自己的import 改为自己的log your.LogUtils
import java.util.Locale
import kotlin.math.absobject TempUtils {/*** 业务举例 设置温度封装* @param 温度只支持整数(结合实际需求也可以支持小数)*/fun setTemp(temp: String): String {// 功能地址前缀val header = "01 06 00 14"val tempToHexString = tempToHexString(temp)val start = "$header$tempToHexString"LogUtils.log("start:$start")val crc = getCRC(start)LogUtils.log("crc:$crc")val instruct = "$start $crc"LogUtils.log("setTemp:$temp---------------> \ninstruct:$instruct")return instruct}/*** 整数直接转16进制负数 -1 按位取反 单位℃*/private fun tempToHexString(num: String): String {LogUtils.log("")LogUtils.log("")LogUtils.log("--------------->$num")val toFloat = num.toFloat()// 将℃转化为0.1℃val toInt = (toFloat * 10).toInt()if (toInt >= 0) {// int 转16进制return toInt.toString(16).uppercase().padStart(4, '0')} else {val temp = abs(toInt) - 1LogUtils.log("temp-1:${temp}(0.1温度)")// 不足16位进进行0补全val tempBinary = temp.toString(2).padStart(16, '0')LogUtils.log("binary:${tempBinary}")// 按位取反val binaryStringInverted = tempBinary.map {if (it == '0') '1' else '0'}.joinToString("")LogUtils.log("binaryInverted:${binaryStringInverted}")return binaryStringInverted.toLong(2).toString(16).uppercase()}}/*** 16进制带正负转化为10进制*/fun hexToTempString(hex: String): Float {LogUtils.log("")LogUtils.log("")LogUtils.log("--------------->$hex")// 判断16进制字符串是否为正负val isNegative = hex.startsWith("F")// 如果是负数则转为二进制再按位取反再然后得到10进制数值并加上-号return if (isNegative) {val num = hexToIntTemp(hex)(-num * 100.00f / 1000)} else {val num = hex.toInt(16)(num * 100.00f / 1000)}}/*** 按位取反+1 获取负数问题*/fun hexToIntTemp(hex: String): Int {// 将 16 进制转换为二进制val binaryString = hex.toInt(16).toString(2)LogUtils.log("binary:$binaryString")// 按位取反val binaryStringInverted = binaryString.map {if (it == '0') '1' else '0'}.joinToString("")LogUtils.log("binaryInverted:$binaryStringInverted")// 两个二进制字符串相加val sum = binaryStringInverted.toInt(2) + 1LogUtils.log("+1:$sum")return sum}/*** MODBUS协议 CRC16校验码*/fun getCRC(data: String): String {var data = datadata = data.replace(" ", "")val len = data.lengthif (len % 2 != 0) {return "0000"}val num = len / 2val para = ByteArray(num)for (i in 0 until num) {val value = data.substring(i * 2, 2 * (i + 1)).toInt(16)para[i] = value.toByte()}return getCRC(para)}/*** 计算CRC16校验码** @param bytes* 字节数组* @return [String] 校验码* @since 1.0*/fun getCRC(bytes: ByteArray): String {// CRC寄存器全为1var CRC = 0x0000ffff// 多项式校验值val POLYNOMIAL = 0x0000a001var i: Intvar j: Inti = 0while (i < bytes.size) {CRC = CRC xor (bytes[i].toInt() and 0x000000ff)j = 0while (j < 8) {if (CRC and 0x00000001 != 0) {CRC = CRC shr 1CRC = CRC xor POLYNOMIAL} else {CRC = CRC shr 1}j++}i++}// 结果转换为16进制var result = Integer.toHexString(CRC).uppercase(Locale.getDefault())if (result.length != 4) {val sb = StringBuffer("0000")result = sb.replace(4 - result.length, 4, result).toString()}//高位在前地位在后//return result.substring(2, 4) + " " + result.substring(0, 2);// 交换高低位,低位在前高位在后return result.substring(2, 4) + " " + result.substring(0, 2)}}

这篇关于Modebus通信协议 温控器示例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1033073

相关文章

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java高效实现PowerPoint转PDF的示例详解

《Java高效实现PowerPoint转PDF的示例详解》在日常开发或办公场景中,经常需要将PowerPoint演示文稿(PPT/PPTX)转换为PDF,本文将介绍从基础转换到高级设置的多种用法,大家... 目录为什么要将 PowerPoint 转换为 PDF安装 Spire.Presentation fo

Python中isinstance()函数原理解释及详细用法示例

《Python中isinstance()函数原理解释及详细用法示例》isinstance()是Python内置的一个非常有用的函数,用于检查一个对象是否属于指定的类型或类型元组中的某一个类型,它是Py... 目录python中isinstance()函数原理解释及详细用法指南一、isinstance()函数

python中的高阶函数示例详解

《python中的高阶函数示例详解》在Python中,高阶函数是指接受函数作为参数或返回函数作为结果的函数,下面:本文主要介绍python中高阶函数的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录1.定义2.map函数3.filter函数4.reduce函数5.sorted函数6.自定义高阶函数

Vue实现路由守卫的示例代码

《Vue实现路由守卫的示例代码》Vue路由守卫是控制页面导航的钩子函数,主要用于鉴权、数据预加载等场景,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、概念二、类型三、实战一、概念路由守卫(Navigation Guards)本质上就是 在路

JAVA实现Token自动续期机制的示例代码

《JAVA实现Token自动续期机制的示例代码》本文主要介绍了JAVA实现Token自动续期机制的示例代码,通过动态调整会话生命周期平衡安全性与用户体验,解决固定有效期Token带来的风险与不便,感兴... 目录1. 固定有效期Token的内在局限性2. 自动续期机制:兼顾安全与体验的解决方案3. 总结PS

C#中通过Response.Headers设置自定义参数的代码示例

《C#中通过Response.Headers设置自定义参数的代码示例》:本文主要介绍C#中通过Response.Headers设置自定义响应头的方法,涵盖基础添加、安全校验、生产实践及调试技巧,强... 目录一、基础设置方法1. 直接添加自定义头2. 批量设置模式二、高级配置技巧1. 安全校验机制2. 类型

Python屏幕抓取和录制的详细代码示例

《Python屏幕抓取和录制的详细代码示例》随着现代计算机性能的提高和网络速度的加快,越来越多的用户需要对他们的屏幕进行录制,:本文主要介绍Python屏幕抓取和录制的相关资料,需要的朋友可以参考... 目录一、常用 python 屏幕抓取库二、pyautogui 截屏示例三、mss 高性能截图四、Pill

Java中的Schema校验技术与实践示例详解

《Java中的Schema校验技术与实践示例详解》本主题详细介绍了在Java环境下进行XMLSchema和JSONSchema校验的方法,包括使用JAXP、JAXB以及专门的JSON校验库等技术,本文... 目录1. XML和jsON的Schema校验概念1.1 XML和JSON校验的必要性1.2 Sche

使用MapStruct实现Java对象映射的示例代码

《使用MapStruct实现Java对象映射的示例代码》本文主要介绍了使用MapStruct实现Java对象映射的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、什么是 MapStruct?二、实战演练:三步集成 MapStruct第一步:添加 Mave