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

相关文章

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Java中StopWatch的使用示例详解

《Java中StopWatch的使用示例详解》stopWatch是org.springframework.util包下的一个工具类,使用它可直观的输出代码执行耗时,以及执行时间百分比,这篇文章主要介绍... 目录stopWatch 是org.springframework.util 包下的一个工具类,使用它

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

springboot security快速使用示例详解

《springbootsecurity快速使用示例详解》:本文主要介绍springbootsecurity快速使用示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录创www.chinasem.cn建spring boot项目生成脚手架配置依赖接口示例代码项目结构启用s

golang 日志log与logrus示例详解

《golang日志log与logrus示例详解》log是Go语言标准库中一个简单的日志库,本文给大家介绍golang日志log与logrus示例详解,感兴趣的朋友一起看看吧... 目录一、Go 标准库 log 详解1. 功能特点2. 常用函数3. 示例代码4. 优势和局限二、第三方库 logrus 详解1.

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Redis 中的热点键和数据倾斜示例详解

《Redis中的热点键和数据倾斜示例详解》热点键是指在Redis中被频繁访问的特定键,这些键由于其高访问频率,可能导致Redis服务器的性能问题,尤其是在高并发场景下,本文给大家介绍Redis中的热... 目录Redis 中的热点键和数据倾斜热点键(Hot Key)定义特点应对策略示例数据倾斜(Data S

JavaScript Array.from及其相关用法详解(示例演示)

《JavaScriptArray.from及其相关用法详解(示例演示)》Array.from方法是ES6引入的一个静态方法,用于从类数组对象或可迭代对象创建一个新的数组实例,本文将详细介绍Array... 目录一、Array.from 方法概述1. 方法介绍2. 示例演示二、结合实际场景的使用1. 初始化二

C#中的 StreamReader/StreamWriter 使用示例详解

《C#中的StreamReader/StreamWriter使用示例详解》在C#开发中,StreamReader和StreamWriter是处理文本文件的核心类,属于System.IO命名空间,本... 目录前言一、什么是 StreamReader 和 StreamWriter?1. 定义2. 特点3. 用