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

相关文章

Python中logging模块用法示例总结

《Python中logging模块用法示例总结》在Python中logging模块是一个强大的日志记录工具,它允许用户将程序运行期间产生的日志信息输出到控制台或者写入到文件中,:本文主要介绍Pyt... 目录前言一. 基本使用1. 五种日志等级2.  设置报告等级3. 自定义格式4. C语言风格的格式化方法

Spring 中的切面与事务结合使用完整示例

《Spring中的切面与事务结合使用完整示例》本文给大家介绍Spring中的切面与事务结合使用完整示例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录 一、前置知识:Spring AOP 与 事务的关系 事务本质上就是一个“切面”二、核心组件三、完

sky-take-out项目中Redis的使用示例详解

《sky-take-out项目中Redis的使用示例详解》SpringCache是Spring的缓存抽象层,通过注解简化缓存管理,支持Redis等提供者,适用于方法结果缓存、更新和删除操作,但无法实现... 目录Spring Cache主要特性核心注解1.@Cacheable2.@CachePut3.@Ca

QT Creator配置Kit的实现示例

《QTCreator配置Kit的实现示例》本文主要介绍了使用Qt5.12.12与VS2022时,因MSVC编译器版本不匹配及WindowsSDK缺失导致配置错误的问题解决,感兴趣的可以了解一下... 目录0、背景:qt5.12.12+vs2022一、症状:二、原因:(可以跳过,直奔后面的解决方法)三、解决方

MySQL中On duplicate key update的实现示例

《MySQL中Onduplicatekeyupdate的实现示例》ONDUPLICATEKEYUPDATE是一种MySQL的语法,它在插入新数据时,如果遇到唯一键冲突,则会执行更新操作,而不是抛... 目录1/ ON DUPLICATE KEY UPDATE的简介2/ ON DUPLICATE KEY UP

Python中Json和其他类型相互转换的实现示例

《Python中Json和其他类型相互转换的实现示例》本文介绍了在Python中使用json模块实现json数据与dict、object之间的高效转换,包括loads(),load(),dumps()... 项目中经常会用到json格式转为object对象、dict字典格式等。在此做个记录,方便后续用到该方

MySQL分库分表的实践示例

《MySQL分库分表的实践示例》MySQL分库分表适用于数据量大或并发压力高的场景,核心技术包括水平/垂直分片和分库,需应对分布式事务、跨库查询等挑战,通过中间件和解决方案实现,最佳实践为合理策略、备... 目录一、分库分表的触发条件1.1 数据量阈值1.2 并发压力二、分库分表的核心技术模块2.1 水平分

SpringBoot请求参数传递与接收示例详解

《SpringBoot请求参数传递与接收示例详解》本文给大家介绍SpringBoot请求参数传递与接收示例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋... 目录I. 基础参数传递i.查询参数(Query Parameters)ii.路径参数(Path Va

RabbitMQ 延时队列插件安装与使用示例详解(基于 Delayed Message Plugin)

《RabbitMQ延时队列插件安装与使用示例详解(基于DelayedMessagePlugin)》本文详解RabbitMQ通过安装rabbitmq_delayed_message_exchan... 目录 一、什么是 RabbitMQ 延时队列? 二、安装前准备✅ RabbitMQ 环境要求 三、安装延时队

Redis实现高效内存管理的示例代码

《Redis实现高效内存管理的示例代码》Redis内存管理是其核心功能之一,为了高效地利用内存,Redis采用了多种技术和策略,如优化的数据结构、内存分配策略、内存回收、数据压缩等,下面就来详细的介绍... 目录1. 内存分配策略jemalloc 的使用2. 数据压缩和编码ziplist示例代码3. 优化的