Pymodbus RTU connection troubles - python

I am a beginner with both python and modbus and I'm trying to control a fan connected to a serial port with pymodbus for hours now. Using a proprietary software from the manufacturer I was able to control the fan, so the connection itself works. Yet, my own code does not.
According to the manual the fan is set to the following values:
mode: RTU
baudrate: 19200
parity: even
timeout: 1
slave ID: 247 (F7h)
databits: 8
I modified the pymodbus example code from the Pymodbus Library Examples. This is my code:
from pymodbus3.client.sync import ModbusSerialClient as ModbusClient
Fan = ModbusClient(method='rtu',port ='/dev/ttymxc1', parity = 'E', baudrate='19200', timeout=1)
connection = Fan.connect()
a = Fan.write_register(2,1, unit=0xF7)
b = Fan.read_holding_registers(2,1, unit = 0xF7)
Fan.close()
Both the read and write command result in the following error:
pymodbus3.exceptions.ModbusIOException: Modbus Error: [Input/Output] Server responded with bad response
The same message I get even when the cable is not plugged in.
Does anyone have a suggestion what's wrong?

Timeout is the time for the port to wait for an answer.
Set the timeout to something like ~2000, 1 is 1ms and they probably meant 1s.
And try also address 1 (if the above is not working), check also if the Modbus address can be configured to something else (it might be some switchs on the fan).

Related

PySerial Attributes - Issues, Documentation Current Reference?

I am very new, learning Python specifically geared toward hardware (serial port and TCP/IP device) testing.
I have been trying to get PySerial based code to work and keep hitting roadblocks. Running Python 3.10.8 on Windows 10.
I worked through the 'import serial' problem (uninstalled and reinstalled Python); the serial.Serial problem (needed to add 'from serial import *). Now, it seems like all of the read syntax does not work. All I want to do at this point is open the port, read and print data - from here I will start working on which data I want).
Here is the code I am working with (this was found in a couple of places on the internet):
#test_sport
import serial
from serial import *
s = serial.Serial(port='COM9', baudrate=9600)
serial_string = ""
while(1):
# Wait until there is data waiting in the serial buffer
if(serialPort.in_waiting > 0):
# Read data out of the buffer until a carraige return / new line is found
serial_string = serial.readline()
# Print the contents of the serial data
print(serial_string.decode('Ascii'))
# Tell the device connected over the serial port that we recevied the data!
# The b at the beginning is used to indicate bytes!
#serialPort.write(b"Thank you for sending data \r\n")
Running this results in an error on serialPort.in_waiting (says serialPort not defined) if I change that to serial.in_waiting (says serial has no attribute 'in_waiting' (PySerial API site says this is correct(?). I've also tried simple commands like serial.read(), serial.readline(), ser.read(), etc. All fail for attributes.
Is the PySerial documentation online current? Does anyone know where to find basic serial port examples?
Thank you!

Pyserial: readline() is blocking, although timeout is defined

I have an issue with readline() from pyserial.
My code is:
import serial
uart = serial.Serial(port='/dev/ttyS0', baudrate=9600, timeout=3)
data = uart.readline().decode()
print(data)
uart.close()
I'm receiving data from a microcontroller. The problem is, if no data is send from the mc, the program is waiting "forever", although I defined a timeout of 3 seconds.
What am I doing wrong?
Ok, I have discovered the solution
The problem is, that the Raspberry Pi 3 and 4 uses the "miniUART" as the primary UART and the Raspberry Pi 1 and 2 uses the "PL011".
You can the details here: https://www.raspberrypi.org/documentation/configuration/uart.md
To get the timeout break working, you have to change the "PL011" to UART0. By default UART0 (GPIO 14 and 15) is set to "miniUART" and "PL011" is used for the bluetooth modem.
You have to edit the /boot/config.txt and add dtoverlay=disable-bt.
You also have to disable the system service, that initialises the modem, so it does not connect to the UART, using sudo systemctl disable hciuart.
I have done that and now the program waits the timeout for a message and goes on, if no message is received.
timeout will only affect the maximum time that readline() will wait
timeout = x: set timeout to x seconds (float allowed) returns immediately when the requested number of bytes are available, otherwise wait until the timeout expires and return all bytes that were received until then
try this :
import serial
uart = serial.Serial(port='/dev/ttyS0', baudrate=9600, timeout=3)
while True:
data = uart.readline().decode()
print(data)
if not data :
break
uart.close()

Pyserial can't read device

I'm trying to read data off of a sensor that I bought, using a conversion module (SSI to RS232). I have the module plugged into my Windows laptop via USB/serial converter.
When I use Putty in Serial mode, I can send the command $2RD and receive the appropriate response from the sensor unit. When I run a script to try to do the same thing, the unit returns: ''
Here is the code I am using:
import sys
import serial
import time
ser = serial.Serial(
port='COM4',
baudrate=9600,
timeout=1,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
)
while True:
ser.write('$2RD'.encode())
#time.sleep(1)
s = ser.read(26)
print s
A few other notes:
I've tried some variations using flushInput, flushOutput, sleeping, waiting, etc...nothing seems to help.
I know I have the COM ports right/the hardware all works in Putty, so pretty sure this is something with my code.
I've also tries 13,400 BAUD with no difference in outcome.
If I connect the TX and RX lines from the USB, I can read the command I'm sending...so it should be at least getting to the RS232/SSI conversion device.
s = ser.read(26) should probably be ser.read(size=26) since it takes keyword argument and not positional argument.
Also, you can try to set a timeout to see what was sent after a specific time because otherwise the function can block if 26 bytes aren't sent as specified in the read docs of pyserial :
Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read.

Connecting via USB/Serial port to Newport CONEX-PP Motion Controller in Python

I'm having trouble getting my Windows 7 laptop to talk to a Newport CONEX-PP motion controller. I've tried python (Spyder/Anaconda) and a serial port streaming program called Termite and in either case the results are the same: no response from the device. The end goal is to communicate with the controller using python.
The controller connects to my computer via a USB cable they sold me that is explicitly for use with this device. The connector has a pair of lights that blink when the device receives data (red) or sends data (green). There is also a packaged GUI program that comes with the device that seems to work fine. I haven't tried every button, the ones I have tried have the expected result.
The documentation for accessing this device is next to non-existant. The CD in the box has one way to connect to it and the webpage linked above has a different way. The first way (CD from the box) creates a hierarchy of modules that ends in a module it does not recognize (this is a code snippet provided by Newport):
import sys
sys.path.append(r'C:\Newport\MotionControl\CONEX-PP\Bin')
import clr
clr.AddReference("Newport.CONEXPP.CommandInterface")
from CommandInterfaceConexPP import *
import System
instrument="COM5"
print 'Instrument Key=>', instrument
myPP = ConexPP()
ret = myPP.OpenInstrument(instrument)
print 'OpenInstrument => ', ret
result, response, errString = myPP.SR_Get(1)
That last line returns:
Traceback (most recent call last):
File "< ipython-input-2-5d824f156d8f >", line 2, in
result, response, errString = myPP.SR_Get(1)
TypeError: No method matches given arguments
I'm guessing this is because the various module references are screwy in some way. But I don't know, I'm relatively new to python and the only time I have used it for serial communication the example files provided by the vendor simply worked.
The second way to communicate with the controller is via the visa module (the CONEX_SMC_common module imports the visa module):
import sys
sys.path.append(r'C:\Newport\NewportPython')
class CONEX(CONEXSMC): def __init__(self):
super(CONEX,self).__init__() device_key = 'com5'
self.connect=self.rm.open_resource(device_key, baud_rate=57600, timeout=2000, data_bits=8, write_termination='\r\n',read_termination='\r\n')
mine.connect.read()
That last mine.connect.read() command returns:
VisaIOError: VI_ERROR_TMO (-1073807339): Timeout expired before operation completed.
If, instead, I write to the port mine.connect.write('VE') the light on the connector flashes red as if it received some data and returns:
(4L, < StatusCode.success: 0 >)
If I ask for the dictionary of the "mine" object mine.__dict__, I get:
{'connect': <'SerialInstrument'(u'ASRL5::INSTR')>,
'device_key': u'ASRL5::INSTR',
'list_of_devices': (u'ASRL5::INSTR',),
'rm': )>}
The ASRL5::INSTR resource for VISA is at least related to the controller, because when I unplug the device from the laptop it disappears and the GUI program will stop working.
Maybe there is something simple I'm missing here. I have NI VISA installed and I'm not just running with the DLL that comes from the website. Oh, I found a Github question / answer with this exact problem but the end result makes no sense, the thread is closed after hgrecco tells him to use "open_resource" which is precisely what I am using.
Results with Termite are the same, I can apparently connect to the controller and get the light to flash red, but it never responds, either through Termite or by performing the requested action.
I've tried pySerial too:
import serial
ser = serial.Serial('com5')
ser.write('VE\r\n')
ser.read()
Python just waits there forever, I assume because I haven't set a timeout limit.
So, if anyone has any experience with this particular motion controller, Newport devices or with serial port communication in general and can shed some light on this problem I'd much appreciate it. After about 7 hours on this I'm out of ideas.
After coming back at this with fresh eyes and finding this GitHub discussion I decided to give pySerial another shot because neither of the other methods in my question are yet working. The following code works:
import serial
ser = serial.Serial('com5',baudrate=115200,timeout=1.0,parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS)
ser.write('1TS?\r\n')
ser.read(10)
and returns
'1TS000033\r'
The string is 9 characters long, so my arbitrarily chosen 10 character read ended up picking up one of the termination characters.
The problem is that python files that come with the device, or available on the website are at best incomplete and shouldn't be trusted for anything. The GUI manual has the baud rate required. I used Termite to figure out the stop bit settings - or at least one that works.
3.5 years later...
Here is a gist with a class that supports Conex-CC
It took me hours to solve this!
My device is Conex-CC, not PP, but it's seem to be the same idea.
For me, the serial solution didn't work because there was absolutely no response from the serial port, either through the code nor by direct TeraTerm access.
So I was trying to adapt your code to my device (because for Conex-CC, even the code you were trying was not given!).
It is important to say that import clr is based on pip install pythonnet and not pip install clr which will bring something related to colors.
After getting your error, I was looking for this Pythonnet error and have found this answer, which led me to the final solution:
import clr
# We assume Newport.CONEXCC.CommandInterface.dll is copied to our folder
clr.AddReference("Newport.CONEXCC.CommandInterface")
from CommandInterfaceConexCC import *
instrument="COM4"
print('Instrument Key=>', instrument)
myCC = ConexCC()
ret = myCC.OpenInstrument(instrument)
print('OpenInstrument => ', ret)
response = 0
errString = ''
result, response, errString = myCC.SR_Get(1, response, errString)
print('Positive SW Limit: result=%d,response=%.2f,errString=\'%s\''%(result,response,errString))
myCC.CloseInstrument()
And here is the result I've got:
Instrument Key=> COM4
OpenInstrument => 0
Positive SW Limit: result=0,response=25.00,errString=''�
For Conex-CC serial connections are possible using both pyvisa
import pyvisa
rm = pyvisa.ResourceManager()
inst = rm.open_resource('ASRL6::INSTR',baud_rate=921600, write_termination='\r\n',read_termination='\r\n')
pos = inst.query('01PA?').strip()
and serial
import serial
serial = serial.Serial(port='com6',baudrate=921600,bytesize=8,parity='N',stopbits=1,xonxoff=True)
serial.write('01PA?'.encode('ascii'))
serial.read_until(b'\r\n')
All the commands are according to the manual

Pyserial code to connect peripheral device to PC automatically when using USB- Serial cable

I have one peripheral device (say hardware circuit with microcontroller). I have to iput some commands to this peripheral device via serial communication. These commands are embedded into a python script.
I am using USB-Serial cable to connect peripheral device to PC.
Now I have to write the code in pyserial so that PC will automatically detect the com port on which Peripheral device is connected and connects the devide with PC successfully.(loop back can be possible)
Currently I am using following Code in Pyserial. I have explicitely mentioned that Peripheral is connected to PC on COM1 ---
try:
self.ser = serial.Serial(0)
#self.ser.port='/dev/ttyS1'
self.ser.baudrate = 9600
self.ser.bytesize = serial.EIGHTBITS
self.ser.parity = serial.PARITY_NONE
self.ser.stopbits = serial.STOPBITS_ONE
self.ser.timeout = 1
self.ser.xonxoff = False #disable software flow control
self.ser.rtscts = False #disable hardware (RTS/CTS) flow control
self.ser.dsrdtr = False #disable hardware (DSR/DTR) flow control
self.ser.writeTimeout = 2 #timeout for write
except Exception, e:
print "error open serial port: " + str(e)
Please let me know that how can a COM port is automatically detected and gets connect afterwards?
This is a common issue and can be solved by checking for a specific return code (usually an identification string) from the peripheral device. Here's an example, using pyserial:
from serial.tools import list_ports
def locate_port():
"""Attempt to locate the serial port to which the device
is connected."""
status_request_string = 'OI;' # Output information
expected_response = 'DISPENSEMATE'
device_port = None
for port_name, port_desc, hw_id in list_ports.comports():
with serial.Serial(port=port_name, **device_serial_settings) as ser:
ser.write(status_request_string)
if ser.readline().startswith(expected_response):
device_port = port_name
break
if not device_port:
raise UserWarning('Could not find a serial port belonging to '
'the asymtek dispensemate.')
return device_port
Usually, the manual of the device you're communicating with has at least one command that does not change the state of the device, but merely echoes back your last line or returns its configuration, hardware ROM version or simply its name. It is this response (and the command that requests it), that you 'll need to fill in for expected_response and status_request_string respectively. The device_serial_settings is a dictionary that contains the parameters such as baudrate and parity bits; everything needed to connect properly to the device, except for its name.
As you can see, the code above was written for an Asymtek Dispensemate (an old one too and thus difficult to get support for).
If you call that function, you can use its return value to simply connect to the device:
port = locate_port()
my_device = serial.Serial(port, **device_serial_settings)
There is one caveat though: if the computer is connected to several serial devices that are all powered on, it is possible that you send an illegal command to the other devices. In the best case, they simply reply with an error code and their state will be unaffected, but the command could also make changes to these devices, so check all other peripherals for their dictionaries of "allowed opcodes".

Categories