python - 树莓派 RS485/Uart Modbus
问题描述
我正在尝试在 UART 上连接一个 RS485 适配器,以通过 Raspberry Pi 上的 modbus 进行通信。我的最终目标是让所有这些都与 Node 应用程序一起工作,但到目前为止,我的开发人员一直在使用 Python。
我的硬件连接如下所示:
[Modbus 设备] <===> [RS485 芯片 <==> Raspberry PI GPIO] 引脚。RS485 有三根线(发送、接收、方向),它们的连接方式如下
RaspiPi <=>适配器
GPIO 14 (8) - Tx <=> 数据+
GPIO 15 (10)- Rx <=>- 数据-
GPIO 18 (12) - 方向
RS485 不是典型的 9 针适配器。我有三根电线从芯片上脱落。用作差分装置和地线的双绞线。
通过手动翻转 GPIO18 进行发送/接收,我已经能够在此适配器和 USB-RS485 适配器之间发送串行通信。(代码如下)[1]。此代码纯粹用于证明适配器工作
我一直坚持让 modbus 在 GPIO 适配器上工作。我试过使用最小的modbus,它适用于USB-RS485适配器,但不适用于GPIO适配器。我怀疑这是因为没有设置方向销。
理想的解决方案是在 pi 上为 GPIO 找到 RS485 驱动程序,但除此之外,我看到了三个选项
1 - 制作我自己的驱动程序(我完全不熟悉的东西) 2 - 以某种方式获得一个 modbus 库来翻转内核空间中的 GPIO 引脚 3 - 通过串行手动发送 modbus 消息并调整用户空间中的 GPIO 引脚。这似乎是最简单的,但在速度和可靠性方面也是最差的。我的代码尝试低于 [2]
非常感谢有关此主题的任何建议。如果有人以前做过类似的事情并且可以权衡我的选择,那将很有帮助。我从来没有在这个级别的软件上工作过,所以如果有一些我完全忽略的明显答案,我不会感到惊讶。
[1] 此代码与通过 USB 连接的树莓派上的另一个 RS485 适配器进行通信。这是为了证明 GPIO 适配器正在工作,并且我可以使用 Raspberry pi 上的 Pin 12 控制方向
import time
import serial
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT);
ser = serial.Serial(
port= '/dev/ttyS0',
baudrate= 57600,
parity= serial.PARITY_NONE,
stopbits= serial.STOPBITS_ONE,
bytesize= serial.EIGHTBITS,
timeout=1
)
def write_add():
counter = 0;
message = 0
while (True):
print "writing",
GPIO.output(12,1) #set high/transmit
ser.write('%d \n'%(message))
time.sleep(0.005) #baud for 57600
#time.sleep(0.5) #baud for 9600
GPIO.output(12, 0) #pin set to low/receive
loop_count = 0
res =""
while (res == ""):
res =ser.readline();
if(res != ""):
print ""
print "Read Cycles: "+str(loop_count)+" Total: "+str(counter)
print res
message = int(res) + 1
counter = counter + 1
elif(loop_count > 10):
res = "start over"
else:
print ".",
loop_count = loop_count + 1
write_add()
[2] 此代码正在尝试与另一个 modbus 设备通信。我的消息正在发送,但回复是垃圾。我的假设是 GPIO 方向引脚被翻转得太快并切断了消息,或者太迟而丢失了一些响应。
import serial
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT)
ser = serial.Serial(
port='/dev/ttyS0',
baudrate = 57600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1
)
GPIO.output(12,1) #set high/transmit
ByteStringToSend = "\x00\x03\x01\xb8\x00\x01\x04\x02"
ser.write(ByteStringToSend)
time.sleep(0.005) #baud for 57600
GPIO.output(12, 0) #pin set to low/receive
ReceivedData = ""
while (ReceivedData == ""):
RecievedData = ser.readline();
print RecievedData
[3] 工作 USB-RS-485 代码。Pi 上的 USB 适配器连接到 Modbus 设备 此代码每秒读取寄存器 440。
#1/usr/bin/env python
import minimalmodbus
import time
print minimalmodbus._getDiagnosticString()
minimalmodbus.BAUDRATE=57600
minimalmodbus.PARITY='N'
minimalmodbus.BYTESIZE=8
minimalmodbus.STOPBITS=1
minimalmodbus.TIMEOUT=0.1
instrument = minimalmodbus.Instrument('/dev/ttyUSB0', 0) #port and slave
#instrument.debug = True
while True:
batterVolt = instrument.read_register(440, 2) #register number, number decimals
print batterVolt
time.sleep(1)
编辑:澄清方案。Edit2:进一步阐明方案和添加/编辑的代码
解决方案
example-2 中的代码实际上可以正常工作。我只需要格式化响应。
print RecievedData.encode('hex')
这将以 modbus 响应格式提供十六进制字符串。正如 Andrej Debenjak 提到的那样, time.sleep (x)将取决于波特率和消息大小。
旁注:我发现此页面有助于破译 modbus 传输。
http://www.modbustools.com/modbus.html
编辑:我发送的 ByteString 不应该在正确的 modbus 设置上工作。第一个字节x00是广播字节,不应请求响应。看来我正在使用的硬件发生了一些奇怪的事情。在典型的 modbus 设置中,您需要确定您尝试与之通信的设备。感谢 Marker 指出这一点。
推荐阅读
- spartacus-storefront - SearchboxComponent服务的SPARTACUS定制
- python - 安装auto-py-to-exe时如何解决错误?
- javascript - 如何让 'foo()' 和 'foo().bar()' 输出完全不同的东西?
- c++ - c++中的通配符扩展
- python-3.x - Session() 如何从具有约束的两个不同表中进行 upsert
- flutter - 在 _WidgetsAppState 中找不到路由 RouteSettings("SecondScreens", null) 的生成器
- azure - 如何“docker login”到 OpenShift Docker 注册表
- python - 如何在 azure webapp 中的控制台等屏幕上查看日志?
- mysql - 在 MySQL (8.0.17) 中将行转换为单列
- redux - redux/toolkit 结合了动作、reducers 和服务,用于身份验证和登录