I'm performing reads and writes with pyserial to a serial terminal running on a decawave DWM1001C. The serial connection goes from my Linux laptop to the device over a microUSB cable which also powers the device. The DWM1001C offers the serial connection internally on the chip. The connection is opened at the baudrate of 115200 with 8N1, as specified by the DWN1001C reference manual.
The problem I have is that when I call pyserial's write() on the serial device, I can only write a maximum of 7 characters per write call, sometimes as few as 4. Here's a minimal code to reproduce the issue:
import serial
import sys
import time
dev = "/dev/ttyACM0"
baudrate = 115200
try:
ser = serial.Serial(dev, baudrate, timeout=3)
except Exception as e:
print("Failed opening device: " + dev + ": " + str(e))
sys.exit(1)
try:
# Two CR's are required to open the terminal as the device default to "API" mode
ser.write(b"\r\r")
ser.flush()
time.sleep(2)
# only letters up to f are sent
ser.write(b"abcdefg hijklmn opqrstu vwxyz\n")
ser.flush()
time.sleep(2)
except Exception as e:
print("Failed writing instructions to device: " + str(e))
sys.exit(1)
while True:
try:
line = ser.readline()
if line == None:
continue
except Exception as e:
print("Failed reading from serial: " + str(e))
sys.exit(1)
s = line.decode("utf-8")
print("got data: " + s)
By using the readline() call and by monitoring the serial terminal over minicom, I have verified that only the 4-7 first characters appear in the terminal. Minicom can be used to interact with the serial terminal normally, so I assume minicom does something under the hood that this pyserial code doesn't.
I have a hacky workaround: Every time I perform writes, I split the input data into 4 character chunks and send them one-by-one over write(), then sleep for 0.1 seconds. This seems to work, but is likely about as platform specific as it can be.
The pyserial constructor takes a few different flow control flags and I tried setting each of them on independently. They are:
xonxoff=True
rtscts=True
dsrdtr=True
They didn't seem to do anything. I also tried lowering the baudrate to 57600 to no effect.
What should I read on to understand what's going on?
Related
I'm trying to read RFID cards with an RFID card reader plugged in USB on my raspi 3b+
Here is my code
ser = serial.Serial('/dev/tty1', 115200)
while True:
incomingByte = ser.read(10)
print(incomingByte.decode())
ser.flushInput()
ser.close()
When I scan a card, here is the output
0002429021
Which is what I expect. But after some time (few seconds) I have the following error message
line 45, in main
incomingByte = ser.read(10)
File "/usr/local/lib/python3.9/dist-packages/serial/serialposix.py", line 595, in read
raise SerialException(
serial.serialutil.SerialException: device reports readiness to read but returned no data (device disconnected or multiple access on port?)
So I've looked around and tried many things:
run raspi-config and under interfaces turn on serial, turn off console
Edit /boot/config.txt and add the line enable_uart=1
In /boot/cmdline.txt remove the references to console
Disabled getty sudo systemctl stop serial-getty#USB0.service
I've tried alternate codes:
**1/ with try...except
**
try:
incomingByte = ser.read(10)
print(incomingByte.decode())
ser.flushInput()
except Exception:
pass
==> I can read 1 card then the program get stuck (no errors but no more scan possible, prompt is stuck)
2/ with inwaiting() + try...except
while True:
while (ser.inWaiting()>0):
try:
incomingByte = ser.read(10)
print(incomingByte.decode())
ser.flushInput()
except (OSError, serial.serialutil.SerialException):
print("No data this time")
ser.close()
It outputs
No data this time
002429021
then the following error
line 549, in in_waiting
s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str)
OSError: [Errno 5] Input/output error
Note:
One of the main difference I see with many other people with similar code is that my rfid card reader is reachable on /dev/tty1 where I usually see /dev/ttyS1 or /dev/ttyAMA0 or /dev/ttyUSB0 or /dev/ttyACM0
I don't really know what's the difference between all of that but maybe it has an impact.
Thanks in advance for your help :)
I am trying to write a very basic script which will allow me to have full control over a device via serial. I can send data (and I know the device receives it, leaving screen open on the device allows me to see the input appearing).
But I cannot receive data, with screen open, and inputting data via screen I get the error:
Traceback (most recent call last): File "serialRec.py", line 4, in
for line in ser: File "/usr/lib/python2.7/dist-packages/serial/serialposix.py", line 456, in
read
raise SerialException('device reports readiness to read but returned no data (device disconnected?)')
serial.serialutil.SerialException: device reports readiness to read
but returned no data (device disconnected?)
There is no error if I open the port waiting for a message without screen.
My application as I said sends data no problem... What can I do? How can I get this reading? I am running this script on a Ubuntu 12.04 installation... Screen works fine on the device the Ubuntu laptop is attatched to
The sys arguments are: argv[1] = device (/dev/ttyUSB0) and argv[2] = braud rate (e.g. 9600).
import serial
import sys
import time
def enterdata():
ser = serial.Serial(sys.argv[1], sys.argv[2])
scom = raw_input("type away:" )
incli = str(scom)
time.sleep(.2)
if (incli == "exit the app"):
print ("Exiting the data send, nothing was sent from the exit command")
else:
while True:
print ser.readline()
enterdata()
print ("Welcome to the serial CLI")
enterdata()
UPDATE:
I now have it working, but limited and ugly it prints the return on multiple lines from sending one command. Though for this I am going to try a few things out. I will post and share some nice working code once i get it to a good place.
import serial
import sys
import time
def enterdata():
ser = serial.Serial(sys.argv[1], sys.argv[2])
scom = raw_input()
incli = str(scom)
if (incli == "exit the app"):
print ("Exiting the data send, nothing was sent from the exit command")
else:
ser.write(incli+"\r\n")
time.sleep(0.5)
while True:
data = ser.read(ser.inWaiting())
if (len(data) > 0):
for i in range(len(data)):
sys.stdout.write(data[i])
break
ser.close()
enterdata()
print ("Welcome to the serial CLI, hit enter to activate:")
enterdata()
This is the changes I have made, it works. Though it seems to always print double or maybe send an extra character
You might want to try with a listener first, but you have to make sure that your device is sending data to your serial port at the correct baudrate.
import serial, sys
port = your_port_name
baudrate = 9600
ser = serial.Serial(port,baudrate,timeout=0.001)
while True:
data = ser.read(1)
data+= ser.read(ser.inWaiting())
sys.stdout.write(data)
sys.stdout.flush()
I am trying to talk to a Stanford Research Systems SR760 spectrum analyzer on my mac (10.7.5) via Serial, using a Serial-to-USB adapter to connect to my laptop. I am using the Prolific USB-serial driver. Not sure which but I installed it recently. It probably is the PL2303 one.
Using Python, here's some sample code
import time
import serial
# configure the serial connections (the parameters differs on the device you
# are connecting to)
ser = serial.Serial(
port='/dev/cu.PL2303-0000201A',
baudrate=19200,
parity=serial.PARITY_NONE,
bytesize=serial.EIGHTBITS,
stopbits=serial.STOPBITS_ONE,
rtscts=0,
dsrdtr=0,
timeout=2,
)
if ser.isOpen():
ser.flushInput()
ser.flushOutput()
print """Enter your commands below.\r\nInsert "exit" to leave the
application."""
while 1:
# get keyboard input
input = raw_input(">> ")
if input == 'exit':
ser.close()
exit()
else:
ser.write(input + '\r')
out = ''
# let's wait one second before reading output (let's give device
# time to answer)
lines = 0
while 1:
time.sleep(1)
out = out + ser.readline()
lines = lines + 1
if lines > 5:
break
print "read data: " + out
Using the SR760's manual, I send it: *IDN?, a basic "identify" command. I expect for something to pop up in my terminal, nothing does. It just times out. However, if I look at the send queue on the SR760, it will show the identity string, and in fact responds to a bunch of different commands. I'm just not getting anything on my computer and that is the problem. I know it is supposed to work that way because my colleague wrote code that words on his computer (a windows laptop).
How do I even start debugging this? I've tweaked the timeout, and confirmed the sr760 had the same parameters I was expecting.
I am connecting with my Arduino through a USB port and sending data to it by using PySerial module. At first I can check if the device is connected by using this code:
try:
ser = serial.Serial("COM3", 9600)
except serial.serialutil.SerialException:
print "Arduino not connected"
Now what I want to do is to check periodically if the Arduino is still connected to the computer. I tried ser.isOpen() but this returns true even if the Arduino is disconnected. I would also like to know how to reconnect the device. I mean once you disconnect the device the program can no longer send any data to Arduino.
Most of the answers propose 2 approaches:
In some point of the code, send some sort of message through serial to check if your device is still alive
Start a separate thread and continuously check if the device is alive by opening a communication
The problem with the first solution is that you are not always checking the connection, but only checking in some specific points: this solution isn't very elegant and if badly written could even be not working.
The second solution solves the problem of the first solution, but introduces a new problem: checking the connection, or worst sending a message, in a threaded loop will cause problem or may even interrupt the connection to the device from other functions.
A solution that allows you to constantly check the connection without monopolizing the communication involves the reading of the existing COM:
import serial.tools.list_ports
myports = [tuple(p) for p in list(serial.tools.list_ports.comports())]
print myports
output:
[(u'COM3', u'Arduino Due Programming Port (COM3)', u'some more data...'),
(u'COM6', u'USB Serial Port (COM6)', u'some more data...'),
(u'COM100', u'com0com - serial port emulator (COM100)', u'some more data...')]
then we save the tuple that contains our port:
arduino_port = [port for port in myports if 'COM3' in port ][0]
then we create a function that checks if this port is still present:
import time
def check_presence(correct_port, interval=0.1):
while True:
myports = [tuple(p) for p in list(serial.tools.list_ports.comports())]
if arduino_port not in myports:
print "Arduino has been disconnected!"
break
time.sleep(interval)
At last, we run this function as a daemon thread:
import threading
port_controller = threading.Thread(target=check_presence, args=(arduino_port, 0.1,))
port_controller.setDaemon(True)
port_controller.start()
in this way, you'll check each 0.1 secs if the arduino is still connected, and the thread will end when arduino is disconnected or all other activities have ended
You can set a timeout.
import serial
ser = serial
try:
ser = serial.Serial("COM3", 9600, timeout=10)
while ser.read():
print 'serial open'
print 'serial closed'
ser.close()
except serial.serialutil.SerialException:
print 'exception'
Unfortunately, the best way I can find to do this is to try some communication and see if it fails. A pretty safe way would be:
try:
ser.inWaiting()
except:
print "Lost connection!"
You'll probably still want to close the connection with a ser.close() after the connection is lost, although you may need to place that in a "try:except" block also.
import serial
import time
ser = serial.Serial()
ser.braudrate = 115200
ser.port = "/dev/ttyUSB0"
ser.open()
print(ser.name)
if ser.isOpen():
print("serial is open!")
ser.close()
For example to detect ttyUSB0:
import os
x=os.system("ls /dev/ttyUSB0")
if x==0:
print "connected"
else:
print "disconnected"
i suggest to use a python thread class to istantiate a serial connection, in the run methos put your while loop , set an var that you use for kill iy at the end, the second public var that you use for store data if have receive and load data in main method.. soon paste an example
class Arduino():
def __init__(self,Port='/dev/ttyUSB0',Boud=9600,connState=0):
self.parent=self
self.port=Port
self.boud=Boud
self.connState=connState
self.timeount=1
self.ser=None
self.connect()
def connect(self):
try:
self.ser=serial.Serial(self.port,self.boud,timeout=0.0001)
self.connState=1
return [1,'connect']
except:
self.connState=0
return [0,'no hardware found']
def loadData(self):
self.buffer=self.ser.read(1)
if (self.buffer!=''):
try:
print self.buffer
except Exception, e:
pass
ard=Arduino()
while True:
if ard.connState:
ard.loadData()
else:
print "Arduino not found"
break
and start with:
import threading
class ThController( threading.Thread ):
# Override Thread's __init__ method to accept the parameters needed:
def __init__( self,parent):
self.parent = parent
threading.Thread.__init__ ( self )
def run ( self ):
while self.parent.ctrlattive:
j=json.loads(data)
self.parent.data=j
I did not find a reasonable good example of how to talk to a serial modem using pyserial. I have created a code snippet that should do the following, given an instantiated pyserial object ser:
Send an AT command to the modem
Return the modem answer as quickly as possible
Return e.g. None in the case of a timeout
Handle the communication between the script and the modem most reasonable, robust and easy.
Here is the snippet:
def send(cmd, timeout=2):
# flush all output data
ser.flushOutput()
# initialize the timer for timeout
t0 = time.time()
dt = 0
# send the command to the serial port
ser.write(cmd+'\r')
# wait until answer within the alotted time
while ser.inWaiting()==0 and time.time()-t0<timeout:
pass
n = ser.inWaiting()
if n>0:
return ser.read(n)
else:
return None
My question: Is this good, robust code, or can pieces be changed/simplified? I especially do not like the read(n) method, I would expect pyserial to offer a piece of code that just returns the whole buffer content. Also, do I / should I flush the output at the begin, to avoid having some crap in the output buffer before?
Thanks
Alex
Create the Serial object with the param timeout=2 for read timeout.
Mi recipe is:
def send(data):
try:
ser.write(data)
except Exception as e:
print "Couldn't send data to serial port: %s" % str(e)
else:
try:
data = ser.read(1)
except Exception as e:
print "Couldn't read data from serial port: %s" % str(e)
else:
if data: # If data = None, timeout occurr
n = ser.inWaiting()
if n > 0: data += ser.read(n)
return data
I think that this is a good form of manage the communications with the serial port.