I'm trying to send 9 bytes through a serial port (tested with RS232, RS485) with Python 2.7 pySerial package.
If I write out the bytes to the serial port, some of the bytes randomly get lost (do not arrive on the receiving end).
If I use a 1 millisec wait between every write of a single byte, all bytes arrive to the receiving end.
I tested the functionality between 2 serial terminals on the same OS.
Here is the code fragment which causes packet (byte) losses:
import serial
import struct
ser = serial.Serial()
ser.baudrate = 9600
ser.parity = "N"
ser.rtscts = False
ser.xonxoff = False
ser.write(struct.pack('B', 0x61))
ser.write(struct.pack('B', 0x62))
ser.write(struct.pack('B', 0x63))
...
ser.close()
The fragment which is working:
import serial
import struct
from time import sleep
ser = serial.Serial()
ser.baudrate = 9600
ser.parity = "N"
ser.rtscts = False
ser.xonxoff = False
ser.write(struct.pack('B', 0x61))
sleep(0.001)
ser.write(struct.pack('B', 0x62))
sleep(0.001)
ser.write(struct.pack('B', 0x63))
sleep(0.001)
...
ser.close()
What can be the root cause for the random packet losses?
System details:
OSX 10.9.4
Python 2.7
Minicom or screen was used for terminal emulation
Test environment:
ATC USB/RS485 converters
ATC Serial/RS485 converters with USB-Serial adapter
Typically USB serial converters have to be configured to not flush the buffers on close. As you're seeing if you provide a sleep to wait for the data to complete it works. But if you just dump a bunch of characters then close the device it's buffer may still have data which gets trashed on the close. I'd simply recommend either configuring your device to not flush the buffers (if possible) or wait the character time before performing your close.
Related
So I'm receiving data from a serial port using pySerial. I've a very simple code that reads the first byte, check if it's the start byte ( 0x02 in my case) and then read until it finds the end byte ( 0x03 in my case).
Config the serial communciation
port = 'COM3'
baud = 38400
ser = serial.Serial(port, baud, timeout=0)
if ser.isOpen():
ser.close()
ser.open()
ser.reset_input_buffer()
ser.reset_output_buffer()
The main loop is inside a while True staement as below.
while True:
data = ""
data_raw = ser.read(1)
if data_raw == b'\x02':
data_raw = ser.read_until(b'\x03')
print(str(data_raw))
ser.reset_input_buffer()
ser.reset_output_buffer()
time.sleep(.5)
The issue is that, for some reason, the read_until() actually reads only the first bye while the data I'm receiving from the serial port are actually b'\x02\xff\x9c\x81E1\x03\'
After reading correctly the \x02 the read_until() statement just read only the next \xff and I can't understand why
It seems a bug never fixed by the pySerial module itself https://github.com/pyserial/pyserial/issues/181
Using timeout=None in serial.Serial() resolved the issue
If I use Putty to connect with serial to COM1, 1200 baud I get a black screen but when I type Ctlr-E on the keyboard I get the value that I'm looking to record. If I use serial.tools.miniterm and I type Ctrl-E on the keyboard I get the proper value. When I use the following code it seems as though I connect and there is nothing waiting in the buffer:
import serial
ser = serial.Serial(
port='com1',\
baudrate=1200,\
parity=serial.PARITY_NONE,\
stopbits=serial.STOPBITS_ONE,\
bytesize=serial.EIGHTBITS,\
timeout=0)
print(ser.flush())
print(ser.flushInput())
print(ser.reset_input_buffer())
print(ser.flushOutput())
print(ser.reset_output_buffer())
print(ser.in_waiting)
print(ser.out_waiting)
print(ser.readline())
print(ser.read())
print(ser.read(1))
ser.close()
I get back on screen:
None
None
None
None
0
0
b''
b''
b''
I was expecting one of these results to be the value I get in Putty and Miniterm.
What am I doing wrong? After connecting with Python can I send that same key sequence that is sent by Putty when I type Ctrl-E?
I figured out what was needed / missing partially due to a post from here on Oct 2nd, 2013 by Chaosphere2112. I need to both send an encapsulated \x05 (thanks barny) but also wait and then read the value in the buffer.
here is the code that worked in the end:
import serial
import time
ser = serial.Serial(
port='com1',\
baudrate=1200,\
parity=serial.PARITY_NONE,\
stopbits=serial.STOPBITS_ONE,\
bytesize=serial.EIGHTBITS,\
timeout=0)
ser.write('\x05'.encode('utf-8'))
time.sleep(1)
read_val = ser.read(size=64)
if read_val != '':
print(read_val)
ser.close()
Thank you.
I have Python 3.6.1 and PySerial Installed. I am trying the
I am able to get the list of comports connected. I now want to be able to send data to the COM port and receive responses back. How can I do that? I am not sure of the command to try next.
Code:
import serial.tools.list_ports as port_list
ports = list(port_list.comports())
for p in ports:
print (p)
Output:
COM7 - Prolific USB-to-Serial Comm Port (COM7)
COM1 - Communications Port (COM1)
I see from the PySerial Documentation that the way to open a COM Port is as below:
import serial
>>> ser = serial.Serial('/dev/ttyUSB0') # open serial port
>>> print(ser.name) # check which port was really used
>>> ser.write(b'hello') # write a string
>>> ser.close() # close port
I am running on Windows and I get an error for the following line:
ser = serial.Serial('/dev/ttyUSB0')
This is because '/dev/ttyUSB0' makes no sense in Windows. What can I do in Windows?
This could be what you want. I'll have a look at the docs on writing.
In windows use COM1 and COM2 etc without /dev/tty/ as that is for unix based systems. To read just use s.read() which waits for data, to write use s.write().
import serial
s = serial.Serial('COM7')
res = s.read()
print(res)
you may need to decode in to get integer values if thats whats being sent.
On Windows, you need to install pyserial by running
pip install pyserial
then your code would be
import serial
import time
serialPort = serial.Serial(
port="COM4", baudrate=9600, bytesize=8, timeout=2, stopbits=serial.STOPBITS_ONE
)
serialString = "" # Used to hold data coming over UART
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
serialString = serialPort.readline()
# Print the contents of the serial data
try:
print(serialString.decode("Ascii"))
except:
pass
to write data to the port use the following method
serialPort.write(b"Hi How are you \r\n")
note:b"" indicate that you are sending bytes
So this is the script I am using to send each 2 seconds for four time the Time in microsecond to the microcontroller stm32f4 but unfoturnately it only sends some numbers(from 1-->4) which are not the same as when I do a print,it is like random numbers .
import time
import serial from datetime
import datetime from time
import gmtime, strftime
ser = serial.Serial(
port='/dev/ttyACM0',
baudrate=115200,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS
)
ser.writeTimeout = 0
ser.isOpen()
TM1 = int(round(time.time()*1000000))
ser.write(str(TM1).encode())
#ser.write( str(TM1)+" \r\n")
time.sleep(2)
TM2 = int(round(time.time()*1000000))
ser.write(str(TM2)+" \r\n")
time.sleep(2)
TM3 = int(round(time.time()*1000000))
ser.write(str(TM3)+" \r\n")
time.sleep(2)
TM4 = int(round(time.time()*1000000))
ser.write(str(TM4)+" \r\n")
I cannot see anything obviously wrong at first sight. I have a almost identical snippet of code running here that works. My guess would be that the settings of the serial port do not match. Double check that the baudrate, parity and stopbit settings match.
Second guess would be a mess-up with encodings. Have you set your default encoding in python to utf-8? If so you could try
ser.write(str(TM1).encode('ascii'))
Posting the output you get would help as well.
Edit: to avoid the microcontroller skipping some characters. You could use a small function like this. (I used that to send commands to a sensor that had the same issues. When I sent a command like this `ser.write("start logging") it would receive something like "sart lgging".
def write_safe(cmd):
for x in cmd:
ser.write(x)
sleep(0.05)
ser.write('\r\n')
I am using an XBee module connected to my RPI, serial communication is established between both, the problem is my code gets stuck if there is no data present by the XBee, is there a away to solve this, I tried timeout but wasn't successful.
code:
ser = serial.Serial (
port = "/dev/ttyAMAO",
baudrate = 9600,
parity = serial.PARITY_NONE,
stopbits = serial,STOPBITS_ONE,
bytesize = serial.EIGHTBITS,
timeout = 0
)
ser = serial.Serial("/dev/ttyAMAO")
for c in ser.read():
l.append(c)
ser.read()
is a blocking call it is probably better to check if there is anything there to read first
if ser.inWaiting(): #only true if there is data waiting to be read
for c in ser.read():
....
or if you want your serial thread to run in parallel to your main program (ie. dont block user interface ever at all..) you should maybe look into something like twisted or asyncio
although typically with serial you are working with some device that wants 2-way communication, usually initiated with a query to the serial device, and you do want to actually block until you get a response. I will usually make a class to handle this for me
class MySerial:
def __init__(self,port,baudrate):
self.ser = serial.Serial(port,baudrate)
def query(cmd,terminal_char="\r"):
self.ser.write(cmd)
return ''.join(iter(ser.read,terminal_char))
s = MySerial("COM9",11200)
result = s.query("get -temp\r")
print result
this will accumulate an entire response until the specified terminal character is recieved