How to read the response from serial port - python

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.

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.

PySerial Sending last byte of old message instead of first byte of intended message

I am trying to build a python program that ties a GUI to a serial interface to talk to a nordic nrf52 DK. It I can open and close the port fine, but I when I send data the first byte that is received by the dev kit (which i have running in debug mode to monitor what data is rx'd) is always the last byte that was transmitted in the previous write cycle of the python script. This is true not only between opening and closing the COM port but even between different debug sessions and resets of the DK. The only time I am able to get the correct first byte to send is the first time I write data after having unplugged and re-plugged the peripheral back into my machine.
Has anyone seen this before? I have tried resetting the output buffer via the different methods afforded by the pyserial module but nothing has worked so far. I think this is some kind of windows issue, but in case its not, here is some relevant code:
COM port settings (port property is set dynamically according to the available devices and thus is not part of this block)
serial_client.baudrate = 38400
serial_client.bytesize = cli.EIGHTBITS
serial_client.stopbits = cli.STOPBITS_ONE
serial_client.parity = cli.PARITY_NONE
serial_client.xonxoff = False
serial_client.dsrdtr = False
serial_client.inter_byte_timeout = None
serial_client.exclusive = True
serial_client.rtscts = False
serial_client.timeout = 1
opening the serial port
serial_client.port = port_struct.device
try:
serial_client.open()
except cli.SerialException:
window[serial_conn_status_key].update(value="Serial Exception raised when opening port")
else:
window[serial_conn_status_key].update(value="{} Open".format(port_struct.device))
time.sleep(0.1)
closing the port
try:
serial_client.flushOutput()
except cli.SerialException:
window[output_buffer].print("**Exception when flushing output**")
try:
serial_client.close()
except cli.SerialException:
window[serial_conn_status_key].update(value="Serial Exception raised when closing port")
else:
window[serial_conn_status_key].update(value="COM unconnected")
function to write out data
def write_port(msg):
if serial_client.is_open:
serial_client.write(msg)
serial_client.reset_output_buffer()
else:
print("serial port not open")
Relevant Specs:
Machine OS: Windows 10
python version: 3.8.4
pyserial version: 3.4
Driver File: JINKCDC.sys
Driver Version: 1.34.0.44950

RS485 communication with 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.

How to make a serial port sniffer sniffing physical port using a python

I have a PC Software (OS: Win 64bit) that communicates with a machine via physical serial port RS232 and I want to make a sniffer for that port using a python. Please note that I am beginner to serial ports.
I've read multiple documents and questions posted online but most of them asks to just use 3rd-party software, but I cannot do this way because raw bytes have to be decoded into string message (I have my way own of decode/encode method).
Currently I have setup like this:
/////////////////// Physical COM1 /////////////
// (PC) Software // <------------------------> // Machine //
/////////////////// /////////////
And I want a python to output any bytes that went through COM1.
Desired Behavior diagram (Virtual serial port has a question mark because I'm not sure if that is the right approach):
/////////////////// Physical COM1 /////////////
// (PC) Software // <------------------------> // Machine //
/////////////////// | Virtual /////////////
| serial port?
v
//////////////////
// (PC) Sniffer // (Python)
//////////////////
|
v
(output bytes)
Those of who knows Advanced Serial Port Monitor, its "spymode" functionality is exactly what I am trying to achieve using python.
I've tried to use com0com and PortMon but I can't find a way to configure com0com to sniff physical port (as far as my observation goes, com0com only makes virtual ports) and PortMon does not support Windows 64-bit.
I've been stuck at this for days... any comments/links/answers are appreciated.
Thank you,
You should go through pySerial
Only one function can acquire the serial port at a time.
For one-way communication(from machine to PC software), the only way I can think of to sniff from a serial port is to read from a port1 and write to port2, where your machine is writing to port1 and PC software has been modified to read from port2.
import serial
baud_rate = 4800 #whatever baudrate you are listening to
com_port1 = '/dev/tty1' #replace with your first com port path
com_port2 = '/dev/tty2' #replace with your second com port path
listener = serial.Serial(com_port1, baudrate)
forwarder = serial.Serial(com_port2, baudrate)
while 1:
serial_out = listener.read(size=1)
print serial_out #or write it to a file
forwarder.write(serial_out)
To achieve full duplex(asynchronous two way communication), you need to have a two processes, one for each direction. You will need to synchronize these process in some way. One way to do it could be, while one process reads from port1, the other writes to port2, and vice-versa.
Read this question
Why not echo something like:
PC S/W <--> COMn(COM0COM)COMm <--> python monitor & forward <--> COM1 <--> Machine
Software wise you need 2 serial tasks one opens COMm and one opens COM1 and a central logger and anything that comes in on COMm gets logged then forwarded to COM1 and vice verca.
We can use the code above without the need to go through threading to achieve a half duplex communication.
we are going to use an infinite loop, and a variable which gonna specify in which port we are reading.
import serial
import time
baud_rate = 9600 # whatever baudrate you are listening to
com_port1 = '/dev/ttyUSB0' # replace with your first com port path
com_port2 = '/dev/ttyUSB1' # replace with your second com port path
ComRead_timeout = 0.1 # Read timeout to avoid waiting while there is no data on the buffer
ComWr_timeout = 0.1 # Write timeout to avoid waiting in case of write error on the serial port
log = open('log.txt', 'a+') # Open our log file, to put read data
From_PC_To_Device = True # this variable is used to specify which port we're gonna read from
listener = serial.Serial(port=com_port1, baudrate=baud_rate, timeout=ComRead_timeout,
write_timeout=ComWr_timeout)
forwarder = serial.Serial(port=com_port2, baudrate=baud_rate, timeout=ComRead_timeout,
write_timeout=ComWr_timeout)
while 1:
while (listener.inWaiting()) and From_PC_To_Device:
serial_out = listener.readline()
localtime = time.asctime(time.localtime(time.time()))
Msg = "PC " + localtime + " " + serial_out
Msg += "\n"
log.write(Msg)
print(serial_out) # or write it to a file
forwarder.write(serial_out)
else:
From_PC_To_Device = False
while (forwarder.inWaiting()) and not From_PC_To_Device:
serial_out = forwarder.readline()
localtime = time.asctime(time.localtime(time.time()))
Msg = "DEVICE " + localtime + " " + serial_out + "\n"
log.write(Msg)
print(serial_out) # or write it to a file
listener.write(serial_out)
else:
From_PC_To_Device = True

PySerial write() instant timeout

EDIT
I found out what the problem was and have answered my own question.
Original question below this line
I have a serial bridge between COM4 and COM5 implemented in software (Specifically, HDD's Free Virtual Serial Configuration Utility)
I have two different python scripts starting up in two different instances of Powershell, receive first:
import serial
receive = serial.Serial(port = 'COM5', baudrate = 9600)
text = receive.read(100)
receive.close()
print text
And then the sender:
import serial
send = serial.Serial(port = 'COM4', baudrate = 9600, timeout = 0)
send.write("Hello")
send.close()
When starting the sender script, the receiver script gets the sent message (So communication is clearly established) but the sender script immediately ends with an error:
Traceback (most recent call last):
File ".\sending.py", line 3, in <module>
send.writelines("Hello")
File "C:\Python27\lib\site-packages\serial\serialwin32.py", line 270, in write
raise writeTimeoutError
serial.serialutil.SerialTimeoutException: Write timeout
I get the same error when I change the sender script to
send = serial.Serial(port = 'COM4', baudrate = 9600)
So my question is: What exactly is timing out? How do I prevent that from happening? I mean, the data IS being sent so I could probably just put the whole thing in a try/except(and do nothing) block but that seems like a bad solution in the long run.
The clue is in the error message[1]
File "C:\Python27\lib\site-packages\serial\serialwin32.py", line 270, in write
raise writeTimeoutError
so we open that file and find:
if self._writeTimeout != 0: # if blocking (None) or w/ write timeout (>0)
# Wait for the write to complete.
#~ win32.WaitForSingleObject(self._overlappedWrite.hEvent, win32.INFINITE)
err = win32.GetOverlappedResult(self.hComPort, self._overlappedWrite, ctypes.byref(n), True)
if n.value != len(data):
raise writeTimeoutError
Read that first conditional again:
if self._writeTimeout != 0:
so let us rewrite our code from before
send = serial.Serial(port = 'COM4', baudrate = 9600, timeout = 0)
becomes
send = serial.Serial(port = 'COM4', baudrate = 9600, writeTimeout = 0)
and Et Voila: No exception.
[1] Well Designed Error Messages? That's new!
The problem may be that the interface tries to comply with RTS, CTS, DSR, or DTS signals. It is possible that if they are not properly virtually connected, they can mysteriously affect communication through a timeout.
I would also recommend looking at the configuration of the used virtual serial bridge.
One solution may be to ignore influence using rtscts=False and/or dsrdtr=False when opening the serial port in Python.
I could use an alternative solution for sniffing communication using hub4com, where I used the parameter --octs = off, for example in this way, but the virtual ports had to be created correctly before. hub4com --baud=115200 --route=0:All --route=1:All --route=2:All --no-default-fc-route=All:All --octs=off \\.\COM1 \\.\CNCB0 \\.\CNCB1

Categories