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