所需道具
- 树莓派,我用的是树莓派zero wh;
- 具有光电直读功能的有线水表,我用的是咸鱼淘的埃美柯 LXSY-20E1;
- TTL3.3 转 485模块,带隔离的贵些;
- 5V转12V升压模块。
原理
树莓派有UART(GPIO14 Tx 和 GPIO15 Rx)串口通讯功能,电器特性是3.3V TTL,直读远传水表支持M-Bus/485通讯接口,我买的光电直读水表485要求12V供电(根据仪表厂家说明书)。TTL和485虽然都是串口通讯,但电器特性不一样,不能直接接!我已经烧掉一个树莓派0了,切记。
接线
- 树莓派GRIO14 Tx针脚接TTL转485模块的Tx,GPIO15 Rx接Rx;
- TTL转485模块的A+接水表485+(黄线或白线),B-接水表485-(蓝线或绿线);
- 树莓派3.3V和GND针脚接TTL转485模块,给模块供电,万用表测模块电压;
- 树莓派5V和GND针脚分别接升压模块IN+和IN-(GND),给升压模块供电,万用表测升压模块OUT+、OUT-电压;
- 升压模块供电OUT+、OUT分别接水表的红线、黑线。
python代码
根据水表Modbus规约,读数的格式为 01 03 00 00 00 02 C4 0B,01 为表地址,根据水表厂家说明书,或者从0开始到255轮询;03为Modbus读寄存器命令;00 00为数据起始地址;00 02为读2个字,即4个字节;C4 0B为CRC16校验码。另外,读取到的数据要根据水表的说明书乘以倍率,我的要乘以0.01。
python代码如下(没有优化,欢迎大家改进分享)。
import sys, time, math, serial ,struct
import RPi.GPIO as GPIO
def crc16(x):
a = 0xFFFF
b = 0xA001
for byte in x:
a ^= byte
for i in range(8):
last = a % 2
a >>= 1
if last == 1:
a ^= b
return (0x00FF & a)*256 + (0xFF00 & a)/256
def modbusEncode(address, words=1):
sendbuf = [address,0x03,0x00,0x00,0x00,words]
crc = crc16(sendbuf)
sendbuf.append((0xFF00 & crc)>>8)
sendbuf.append(0x00FF & crc)
print(sendbuf)
return sendbuf
def readFlux(usart, address):
sendbuf = modbusEncode(address,2)
usart.write(sendbuf)
recvbuf = bytearray(usart.read(9))
if len(recvbuf)==9:
b3 = int(recvbuf[3])
b2 = int(recvbuf[4])
b1 = int(recvbuf[5])
b0 = int(recvbuf[6])
result = (b3<<24) |(b2<<16) |(b1<<8) | b0
return result*0.01
return 0
if __name__ == "__main__":
port="/dev/ttyAMA0"
usart=serial.Serial(port,9600,timeout=5)
usart.flushInput()
while(True):
print(readFlux(usart,0))#0为表地址,要根据水表地址修改.
time.sleep(1)
GPIO.cleanup()