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寄存器全为1
        var CRC = 0x0000ffff
        // 多项式校验值
        val POLYNOMIAL = 0x0000a001
        var i: Int
        var j: Int
        i = 0
        while (i < bytes.size) {
            CRC = CRC xor (bytes[i].toInt() and 0x000000ff)
            j = 0
            while (j < 8) {
                if (CRC and 0x00000001 != 0) {
                    CRC = CRC shr 1
                    CRC = 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:${hexToIntTemp("FF04")}℃")
      println("hexToTemp 00FC:${hexToIntTemp("00FC")}℃")
      println("hexToTemp FC18:${hexToIntTemp("FC18")}℃")
      println("hexToTemp 1388:${hexToIntTemp("1388")}℃")
      println("tempToTemp2 -2℃:${tempToHex("-2")}")


    /**
     * 16进制指令转int温度
     */
    private fun hexToIntTemp(hex: String): Int {
        // 将 16 进制转换为int
        val intNum = hex.toInt(16)
        val value = (intNum.inv() and 0xFFFF) + 1
        val sum = abs(value)
        println("hexToIntTemp2:$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℃:${hexToTempString("1")}")
  println("tempToTemp -2℃:${hexToTempString("-2")}")
  // println("验证-2℃ 逆转 ffec:${hexToTempString("ffec")}℃")


      /**
     * int温度转16进制指令
     */
    private fun hexToTempString(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) - 1
            println("tempToHex2 temp-1:${temp}(0.1温度)")
            // 不足16位进进行0补全
            val value = (temp.inv() and 0xFFFF)
            println("tempToHex value:${value}")
            return value.toString(16).uppercase()
        }
    }


    /**
     * 16进制指令转int温度
     */
    private fun hexToIntTemp(hex: String): Int {
        
        val intNum = hex.toInt(16)
        val value = (intNum.inv() and 0xFFFF) + 1
        val sum = abs(value)
        println("hexToIntTemp2:$sum")
        return sum
    }

5,完整工具类

package 改为自己的

import 改为自己的log your.LogUtils
import java.util.Locale
import kotlin.math.abs

object 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
    }

  /**
     * int温度转16进制指令
     */
    private fun hexToTempString(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) - 1
            println("tempToHex2 temp-1:${temp}(0.1温度)")
            // 不足16位进进行0补全
            val value = (temp.inv() and 0xFFFF)
            println("tempToHex value:${value}")
            return value.toString(16).uppercase()
        }
    }


    /**
     * 16进制指令转int温度
     */
    private fun hexToIntTemp(hex: String): Int {
    
        val intNum = hex.toInt(16)
        val value = (intNum.inv() and 0xFFFF) + 1
        val sum = abs(value)
        println("hexToIntTemp2:$sum")
        return sum
    }


    /**
     * MODBUS协议 CRC16校验码
     */
    fun getCRC(data: String): String {
        var data = data
        data = data.replace(" ", "")
        val len = data.length
        if (len % 2 != 0) {
            return "0000"
        }
        val num = len / 2
        val 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寄存器全为1
        var CRC = 0x0000ffff
        // 多项式校验值
        val POLYNOMIAL = 0x0000a001
        var i: Int
        var j: Int
        i = 0
        while (i < bytes.size) {
            CRC = CRC xor (bytes[i].toInt() and 0x000000ff)
            j = 0
            while (j < 8) {
                if (CRC and 0x00000001 != 0) {
                    CRC = CRC shr 1
                    CRC = 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)
    }

}

  • 29
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个C#代码示例,用于通过Modbus RTU协议设置欧姆龙温控器的SP(设定温度)。请注意,此代码需要使用Modbus RTU通信协议,并使用串口连接欧姆龙温控器。 首先,你需要安装一个Modbus RTU库,例如NModbus或EasyModbus。然后,你需要设置好串口参数,例如COM口号、波特率、数据位、停止位等等。最后,你可以使用以下代码设置欧姆龙温控器的SP。 ```csharp using Modbus.Device; // 引用Modbus库 // 串口参数设置 SerialPort port = new SerialPort("COM1", 9600, Parity.None, 8, StopBits.One); port.Open(); // 创建Modbus RTU主站对象 ModbusSerialMaster master = ModbusSerialMaster.CreateRtu(port); // 设置欧姆龙温控器的从站地址 byte slaveAddress = 1; // 设置要写入的SP值 float spValue = 60.0f; // 将SP值转换为16位整数 short spValueInt = (short)(spValue * 10); // 设置写入的寄存器地址和数据 ushort registerAddress = 0x0114; // 0x0114为SP设定值的寄存器地址 ushort[] data = new ushort[] { (ushort)spValueInt }; // 向欧姆龙温控器写入SP值 master.WriteMultipleRegisters(slaveAddress, registerAddress, data); ``` 在上面的代码中,我们首先设置了串口参数,然后创建了一个Modbus RTU主站对象。接着,我们设置了欧姆龙温控器的从站地址,以及要写入的SP值。然后,我们将SP值转换为16位整数,将寄存器地址和数据存储在一个ushort数组中,最后使用WriteMultipleRegisters方法向欧姆龙温控器写入SP值。 需要注意的是,不同型号的欧姆龙温控器可能有不同的寄存器地址和参数设置,你需要查阅相应的说明文档。此外,使用Modbus RTU通信需要注意通信速度和错误处理,以确保通信的可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值