RS485 communication with Python - python

Here's my problem:
I need to send Hexadecimal commands to a device via RS485 to make it work.
I can communicate perfectly with this device using RealTerm, I send my requests and I receive consistent responses.
However now I would like to send them with a python script and I don't know what I'm doing wrong but I don't get any response from the device when I try to send commands to it with my script.
Here is the script :
import serial # import the module
ComPort = serial.Serial('COM3') # open COM3
ComPort.baudrate = 9600 # set Baud rate to 9600
ComPort.bytesize = 8 # Number of data bits = 8
ComPort.parity = 'N' # No parity
ComPort.stopbits = 1 # Number of Stop bits = 1
data = bytearray(b'\xfa\x02\x02\x2a\xfe\x0c')
No = ComPort.write(data)
print(data) # print the data
dataIn = ComPort.readline() # Wait and read data
print(dataIn) # print the received data
ComPort.close() # Close the Com port

I founded the solution to my problem,
it was just my reading that wasn't getting through, I used .read(6) and it works now.

Related

Python, Data Received from RS232 COM1 port is returning b'\x05' and b'\x04'

I am doing an instrument integration where the instrument name is Horiba ES60. I am using an RS232 port for communicating for PC and instrument.
I have tested the PC and instrument connection through the Advance Serial Port logger and am getting the monitor result.
I have confirmed that the instrument setting and my script setting are the same.
I have written a simple script in python to read the port data. below is the script.
import time
import serial
sSerialPort = serial.Serial(port = "COM1", baudrate=9600,
bytesize=8, timeout=1, stopbits=serial.STOPBITS_ONE)
sSerialString = "" # Used to hold data coming over UART
print("Connected to : ",sSerialPort.name)
while(True):
# Wait until there is data waiting in the serial buffer
if(sSerialPort.in_waiting > 0):
# Read data out of the buffer until a carraige return / new line is found
sSerialString = sSerialPort.readline()
# Print the contents of the serial data
print(sSerialString)
Output:
b'\x05'
b'\x04'
The expected output is different and I am getting the above one.
Can someone please help to understand what's going wrong?
how to deal with port data in python.

Establishing communication with device through RS232 using PySerial or PyVisa

Trying to communicate with a McPherson 747 Device Controller for the Filter Wheel on a lab instrument. It is connected to the lab computer through RS232 to usb. I've connected and communicated with other devices through serial before.
It seems that for a read request, I need to send an enquiry and header and then the Device will acknowledge and send data.
The manual for the Device says that all data is exchanged in ASCII format. Though there are also a few tables in the manual that has inputs in Hex ASCII code. The manual says that a three-byte enquiry is Hex 4E2105 or text N!< ENQ >. Acknowledge is Hex 4E2106 or text N!< ACK > . The header is a mix of several Hex ASCII inputs for reading and writing based on what information the user wants.
The issue I am having is when I code the serial information and send the enquire input, it always sends an error due to communication with the device. I am unfamiliar with Hex. Do I need to be sending the entire enquire, headeras one input, or can I send them separately? I can also try to set up communication with PyVisa but I've gotten similar errors.
Through serial:
import codecs
import serial
try:
ser = serial.Serial(port='COM1',
baudrate = 9600,
timeout = None,
xonxoff = False,
parity = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE,
bytesize = serial.EIGHTBITS,
)
ser.close()
string = '4E2105'
enquire = codecs.decode(string,'hex')
ser.open()
ser.write(enquire); #ascii intput for pressing enter on keyboard
ser.read_until(size=None) #reads out feedback until no data is left
ser.close()
msg = f"Program communication initialized"
print(msg)
except Exception as ex:
msg = f"Error: {ex}"
print(msg)
Error: could not open port 'COM1': FileNotFoundError(2, 'The system cannot find the file specified.', None, 2)
Through Pyvisa:
import pyvisa
import serial
import codecs
string = '4E2105'
enquire = codecs.decode(string,'hex')
rm = pyvisa.ResourceManager()
rm.list_resources()
# ('ASRL1::INSTR', 'ASRL2::INSTR', 'GPIB0::12::INSTR')
inst = rm.open_resource('asrl1::instr')
inst.write_termination='\r'
inst.read_termination='\r'
inst.baud_rate = 9600
inst.data_bits = 8
inst.parity = visa.constants.Parity.none
inst.flow_control = visa.constants.VI_ASRL_FLOW_NONE
inst.write(enquire)
print(inst.query("N!<ENQ>"))
rm.close()
VisaIOError: VI_ERROR_RSRC_NFOUND (-1073807343): Insufficient location information or the requested device or resource is not present in the system.

Trouble reading MODBUS register using Python

I am trying to use Python (PyCharm) to read a register on a modbus device. I have confirmed the COM port, Baud rate and other communication settings and I can use the devices application to read the value (it is a water level logger). I am getting no response from the instrument.
Register is readable in mbpoll using -
mbpoll -B -m RTU -t 4:float -a 1 -b 19200 -r 46 -c 2 /dev/ttyUSB0
(Address different as running on Pi not PC)
And MBPOLL -
My code is as follows -
import minimalmodbus
import serial
instrument = minimalmodbus.Instrument('COM5', 1) # port name, slave address (in decimal)
instrument.serial.port = 'COM5' # this is the serial port name
instrument.serial.baudrate = 19200 # Baud
instrument.serial.bytesize = 8
instrument.serial.parity = serial.PARITY_EVEN
instrument.serial.stopbits = 1
instrument.serial.timeout = 3 # seconds
instrument.address = 1 # this is the slave address number
instrument.mode = minimalmodbus.MODE_RTU # rtu or ascii mode
instrument.clear_buffers_before_each_transaction = True
temperature = instrument.read_float(registeraddress=40046, functioncode=3, number_of_registers=2, byteorder=0) # Registernumber, number of decimals
print(temperature)
Error Received -
import minimalmodbus
import serial
instrument = minimalmodbus.Instrument('COM5', 1) # port name, slave address (in decimal)
instrument.serial.port = 'COM5' # this is the serial port name
instrument.serial.baudrate = 19200 # Baud
instrument.serial.bytesize = 8
instrument.serial.parity = serial.PARITY_EVEN
instrument.serial.stopbits = 1
instrument.serial.timeout = 0.1 # seconds
instrument.address = 1 # this is the slave address number
instrument.mode = minimalmodbus.MODE_RTU # rtu or ascii mode
#nstrument.clear_buffers_before_each_transaction = True
temperature = instrument.read_float(registeraddress=45, functioncode=4, number_of_registers=2, byteorder=0) # Registernumber, number of decimals
try:
print(temperature)
except:
print(temperature)
Edit to include a try - except
Any help appreciated!
EDIT:
Link to device manual - https://in-situ.com/en/pub/media/support/documents/Modbus_Manual.pdf
Device is a Level Troll 400 Connected to PC via manufactures cable
EDIT 2:
I have tried to incorporate minimal modbus structure but to no avail.
EDIT 3:
I am able to read a register using Modbus Poll. Register is 40046, so I understand this to be register 45 of the holding registers? How do I translate this to minimalmodbus?
EDIT 4:
I am not married to minimal modbus - I am happy to use any tool to get this done
EDIT 5:
I have also tried depth = instrument.read_long(x, x) with different values
Just updating with the solution if anyone happens to stumble upon this and has the same problem. As semi-alluded to in other suggestions the device I am connecting to has a 'sleep' period or the like and needs to be polled once, unsuccessfully, before successfully returning values for any subsequent polls. I apologies for my armature code but the solution that works for me is the below -
import minimalmodbus
import serial
instrument = minimalmodbus.Instrument('COM5', 1) # port name, slave address (in decimal)
instrument.serial.port = 'COM5' # this is the serial port name
instrument.serial.baudrate = 19200 # Baud
instrument.serial.bytesize = 8
instrument.serial.parity = serial.PARITY_EVEN
instrument.serial.stopbits = 1
instrument.serial.timeout = 1 # seconds
instrument.address = 1 # this is the slave address number
instrument.mode = minimalmodbus.MODE_RTU # rtu or ascii mode
#nstrument.clear_buffers_before_each_transaction = True
try:
temperature = instrument.read_long(registeraddress=9001, functioncode=3,
byteorder=0) # Registernumber, number of decimals
print(temperature)
except:
pass
try:
temperature = instrument.read_long(registeraddress=9001, functioncode=3,
byteorder=0) # Registernumber, number of decimals
print(temperature)
except:
pass
The device manual isn't clear about the register start address, but the first register it mentions has the address of 1.
Similarly, the mbpoll command-line utility (not the one with GUI) isn't very clear about the start address. But its documentation mentions that the default value for -r parameter is 1.
I think it's safe to assume that both use the same addressing which starts from 1, as the command-line tool has no problems accessing the value.
But MinimalModbus API clearly mentions that its register start address is 0. So when using this library, you need to use registeraddress = 45 for accessing the temperature, not 46 or 40046.
But why won't 46 work? Normally, one would expect it to grab data starting from the next register and print some garbage, but not timeout. But we can't know how the device works internally. Maybe a request to access the temperature register actually triggers some measurement function and then returns a value. A request to access an unaligned data (with a wrong register value) can be simply rejected by the firmware.
If you still get timeouts with registeraddress = 45, your Python runtime may have some problems accessing the serial port. As I stated in my comment, I recommend using a logic analyzer to see what's going on on the wire. Without such a tool, you're doing blind-debugging.

How can i transmit received data into some other controller in raspberry-pi3 using uart?

From lpc2148 I'm using a single switch button to transmit hex data(ex: char ch[6]={0x12, 0x13,0x14, 0x15, 0x16, 0x17}) to raspi receiver uart(ttyS0),
and then I want to transmit the received data into some other controller. If I press the switch button, the rpi receives the data fine, but when I press and hold the button data should transmit and be received continuously in raspi but I'm unable to transmit the received data into another controller when using python. In C, it's working fine, but the same program I converted to python is not working. Please do help me, I'm stuck here. I'm a beginner to python.
NOTE: I'm working on raspi-3
c-code
while(1)
{
serial_port = serialOpen ("/dev/ttyS0", 9600)
if(serialDataAvail (serial_port) )
{
for(i=0;i<6;i++)
{
data[i] = serialGetchar (serial_port);
}
}
serialclose(serial_port)
serial_port2=serialOpen("/dev/ttyUSB0",9600)
for(j=0;j<6;j++)
serialputchar(serial_port2,data[j])
serialclose(serial_port2)
}
The above C code is working fine with the help of this page
http://wiringpi.com/reference/serial-library/
When I try to convert the program into python it's not working:
import serial
from time import sleep
while True:
ser = serial.Serial ("/dev/ttyS0", 9600)
received_data = ser.read() #read serial port
while ser.inWaiting()<1 :
data_left = ser.inWaiting() #check for remaining byte
received_data += ser.read(data_left)
ser.close #closed port1 once data received
ser2= serial.Serial ("/dev/ttyUSB0", 9600)
#ser2.write(received_data)
for i in received_data
ser2.write(i)
ser2.close()
The data is receiving perfectly and it's not coming out from the inner while loop (while ser.inWaiting()<1 :)
How can I check if the data is available on the port, like in C-code serialDataAvail().
I traced ser.inWaiting() returning always zero.
I Googled this, but no explanations are available for pyserial library's equivalent library function to serialDataAvail() for python

How to read the response from serial port

I'm new with the python language
I have a uart bluetooth dongle, I wrote this code below and the write method works fine because I can see the response using the gtkterm software
Code:
import serial
ser = serial.Serial()
ser.baudrate = 115200
ser.port = '/dev/ttyUSB0'
ser.open()
print(ser.is_open)
ser.write(b'info\r\n') # get info command
ser.write(b'scan=00\r\n') # start scan command
The response displayed in gtkterm software:
Device information
firmware: nrf_dongle
firmware_version: 0.2.5-ba519b3
firmware_build: 20180413-104249
device_name: amine
serial_number: a58f2080352ac55bd1850576df54
mac_address: d1850576df54
device_state: 1
adv_state: 0
scan_state: 0
END
#scan:d1850576df54,20fabb03c064,-71,2,30,0201041aff4c00021570996ffaa2c34f00b776a3852c4bbd790cb90006c2
#scan:d1850576df54,20fabb044b2c,-62,2,30,0201041aff4c000215023f3d601143013582ba2e1e1603bcb9ffff02e5c5
#scan:d1850576df54,20fabb044b51,-54,3,30,0201041aff4c000215023f3d601143013582ba2e1e1603bcb9ffff02c8c5
#scan:d1850576df54,20fabb044b2c,-62,2,30,0201041aff4c000215023f3d601143013582ba2e1e1603bcb9ffff02e5c5
.
.
.
So my question is how can I read this data using the pyserial module or any other approach ?
There are many approaches to this problem. First of all the question is - do you want to implement every detail yourself as an exercise? If so then you can implement a function that will read from the serial port one byte at the time, like so:
def readline(port):
message = ""
byte = ""
while True:
byte = port.read()
if byte == "\n":
break
message += byte
return message
It will stop reading from the port when the newline character is encountered and return the message so far. But be aware that there are some problems here (is the end-of-line character always "\n"? What if there is a timeout on the read function?)
Here is the link to the documentation about how the read function behaves. Note, that if the Serial object has not been set with a timeout the function will block, which means that it will wait for the incoming data from the serial port.
The PySerial documentation is a great source of information on the topic - they also provide an example for using the readline function that takes into account problems connected to the newline differences (end-of-line characters). Here is the example from the docs rewritten for your example:
import serial
import io
ser = serial.Serial()
ser.baudrate = 115200
ser.port = '/dev/ttyUSB0'
ser.open()
print(ser.is_open)
sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser))
sio.write(b'info\r\n')
sio.flush() # it is buffering. required to get the data out *now*
response = sio.readline()
print(response)
I strongly suggest to look at the miniterm.py module that is supplied with the PySerial module. Although it might be quite hard at first it is in my opinion a good source of learning material to get accustomed with this library.

Categories