前言:
用modbus_tk读取实物PLC(信捷)的浮点数值,发现数据不正常。经分析后发现modbus_tk只是把PLC的返回值的两个字存储器按低位在前,高位在后的顺序转成浮点数(ABCD)。而实物PLC(信捷),返回的数据是高位在前,低位在后(CDAB)。所以需要把高低位互换一下位置再转换。
【如果modbus_tk带这个高低位切换,此贴终结。】
可以转换读写浮点数 还有 双字寄存器。
使用:
modbus_tk读取的数据元祖直接给下面 ReadFloat()、ReadDint() 就能返回结果。
modbus_tk寄存器的写,支持填入列表,所以直接用下面模块返回的列表就行
#读浮点数
ReadFloat(master.execute(1, cst.READ_HOLDING_REGISTERS, 10, 2))
#读双字
ReadDint(master.execute(1, cst.READ_HOLDING_REGISTERS, 10, 2))
#写浮点数
master.execute(1,cst.WRITE_MULTIPLE_REGISTERS,10,output_value=WrtieFlote(3.14))
#写双字
master.execute(1,cst.WRITE_MULTIPLE_REGISTERS,10,output_value=WriteDint(65536))
一些琐碎事项:
我不知道三菱的PLC还有其他PLC的modbus返回值的高低位是怎样排列的,所以才加了个reverse参数,可能是多余的。
反正信捷PLC的modbus返回数据是高位在前,低位在后。
顺带一提,支持 WinXP 环境的,
最高只能用 python3.4.4 、pyserial2.7,
modbus_tk 我试过 0.5.0 是可以的。
最好用pip安装一下 six模块
import struct
def ReadFloat(*args,reverse=False):
for n,m in args:
n,m = '%04x'%n,'%04x'%m
if reverse:
v = n + m
else:
v = m + n
y_bytes = bytes.fromhex(v)
y = struct.unpack('!f',y_bytes)[0]
y = round(y,6)
return y
def WriteFloat(value,reverse=False):
y_bytes = struct.pack('!f',value)
# y_hex = bytes.hex(y_bytes)
y_hex = ''.join(['%02x' % i for i in y_bytes])
n,m = y_hex[:-4],y_hex[-4:]
n,m = int(n,16),int(m,16)
if reverse:
v = [n,m]
else:
v = [m,n]
return v
def ReadDint(*args,reverse=False):
for n,m in args:
n,m = '%04x'%n,'%04x'%m
if reverse:
v = n + m
else:
v = m + n
y_bytes = bytes.fromhex(v)
y = struct.unpack('!i',y_bytes)[0]
return y
def WriteDint(value,reverse=False):
y_bytes = struct.pack('!i',value)
# y_hex = bytes.hex(y_bytes)
y_hex = ''.join(['%02x' % i for i in y_bytes])
n,m = y_hex[:-4],y_hex[-4:]
n,m = int(n,16),int(m,16)
if reverse:
v = [n,m]
else:
v = [m,n]
return v
if __name__ == "__main__":
print(ReadFloat((15729,16458)))
print(WriteFloat(3.16))
print(ReadDint((1734,6970)))
print(WriteDint(456787654))