How do I recover from a serialException using pySerial - python

I have an application that reads and transmits data to a device connected via USB. I'm using pySerial to facilitate this communication. Everything works fine until the USB cable is unplugged from the PC and an exception is thrown. Once the cable is plugged back in, I can't seem to recover and reconnect to my device. The only way for me to recover is to close down the application and unplug and plug the cable in again. Any help in understanding what's going on would be much appreciated.
This is basic test code that I'm useing to help me understand the process.
# Class used to communicate with USB Dongle
import serial
import time
import sys
class LPort:
def __init__(self, port=0):
"initialize the LPort class"
self.error = ""
self.traffic = ""
self.dest = None
if port == None:
self.simulation = True
else:
self.simulation = False
self.port = port # serial port we should use
self.reset()
self.time = time.time()
def reInit(self):
self.close()
def reset(self):
"flush port, reset the LPort, initialize LPort"
if self.simulation:
r = "LPort simulator"
else:
self.port.flushInput()
self.port.flushOutput()
self.fail = False
self.command("/H1")
self.dest = None
r = "reset"
self.error = ""
self.traffic = ""
return r
def status(self):
"return accumulated status info, reset collection"
s = self.error
self.error = ""
return s
def data(self):
"return accumulated traffic data, reset collection"
s = self.traffic
self.traffic = ""
return s
def set_dest(self, addr):
"set the destination address (if necessary)"
if addr != self.dest:
self.dest = addr
self.command("/O")
r = self.command("/D%02X" % addr)
if r != "*":
self.dest = None
self.error += r
else:
r = True
return r
def checksum(self, bytes):
"calculate the CRC-8 checksum for the given packet"
crc_table = [
# this table is taken from the CP rectifier code
0x00,0x07,0x0E,0x09,0x1C,0x1B,0x12,0x15,0x38,0x3F,
0x36,0x31,0x24,0x23,0x2A,0x2D,0x70,0x77,0x7E,0x79,
0x6C,0x6B,0x62,0x65,0x48,0x4F,0x46,0x41,0x54,0x53,
0x5A,0x5D,0xE0,0xE7,0xEE,0xE9,0xFC,0xFB,0xF2,0xF5,
0xD8,0xDF,0xD6,0xD1,0xC4,0xC3,0xCA,0xCD,0x90,0x97,
0x9E,0x99,0x8C,0x8B,0x82,0x85,0xA8,0xAF,0xA6,0xA1,
0xB4,0xB3,0xBA,0xBD,0xC7,0xC0,0xC9,0xCE,0xDB,0xDC,
0xD5,0xD2,0xFF,0xF8,0xF1,0xF6,0xE3,0xE4,0xED,0xEA,
0xB7,0xB0,0xB9,0xBE,0xAB,0xAC,0xA5,0xA2,0x8F,0x88,
0x81,0x86,0x93,0x94,0x9D,0x9A,0x27,0x20,0x29,0x2E,
0x3B,0x3C,0x35,0x32,0x1F,0x18,0x11,0x16,0x03,0x04,
0x0D,0x0A,0x57,0x50,0x59,0x5E,0x4B,0x4C,0x45,0x42,
0x6F,0x68,0x61,0x66,0x73,0x74,0x7D,0x7A,0x89,0x8E,
0x87,0x80,0x95,0x92,0x9B,0x9C,0xB1,0xB6,0xBF,0xB8,
0xAD,0xAA,0xA3,0xA4,0xF9,0xFE,0xF7,0xF0,0xE5,0xE2,
0xEB,0xEC,0xC1,0xC6,0xCF,0xC8,0xDD,0xDA,0xD3,0xD4,
0x69,0x6E,0x67,0x60,0x75,0x72,0x7B,0x7C,0x51,0x56,
0x5F,0x58,0x4D,0x4A,0x43,0x44,0x19,0x1E,0x17,0x10,
0x05,0x02,0x0B,0x0C,0x21,0x26,0x2F,0x28,0x3D,0x3A,
0x33,0x34,0x4E,0x49,0x40,0x47,0x52,0x55,0x5C,0x5B,
0x76,0x71,0x78,0x7F,0x6A,0x6D,0x64,0x63,0x3E,0x39,
0x30,0x37,0x22,0x25,0x2C,0x2B,0x06,0x01,0x08,0x0F,
0x1A,0x1D,0x14,0x13,0xAE,0xA9,0xA0,0xA7,0xB2,0xB5,
0xBC,0xBB,0x96,0x91,0x98,0x9F,0x8A,0x8D,0x84,0x83,
0xDE,0xD9,0xD0,0xD7,0xC2,0xC5,0xCC,0xCB,0xE6,0xE1,
0xE8,0xEF,0xFA,0xFD,0xF4,0xF3]
for i in range(len(bytes)):
b = int(bytes[i])
if i == 0: chksum = crc_table[b]
else: chksum = crc_table[chksum ^ b]
return chksum
def command(self, cmd):
"transmit distinct commands to unit, and accept response"
if self.simulation:
r = "*"
else:
try:
self.port.write(cmd + chr(13))
except serial.serialutil.SerialTimeoutException:
r = "/TO"
return r
except:
print "Unexpected error:", sys.exc_info()[0]
r = "/Unknown"
return r
r = ""
eol = False
while True:
c = self.port.read(1)
if not c:
r = "/FAIL " + r + " " + cmd
self.error = r
break
else:
r += c
ordc = ord(c)
if ordc == 13 or ordc == 42:
break
return r
def checkRawDataForErrors(self, raw, errors = []):
errorCodes = {'/SNA':'Slave Not Acknowledging',
'/I81':'Busy, Command Ignored',
'/I88':'Connection Not Open',
'/I89':'Invalid Command Argument',
'/I8A':'Transmit Not Active',
'/I8F':'Invalid Command',
'/I90':'Buffer Overflow',
'/DAT':'Data Error',
'/BADPEC':'Bad PEC Value',
'/NO_MRC':'No Master Read Complete Signal',
'/FAIL':'General Failure',
'/LEN':'Data Length Error'}
for ekey, eval in errorCodes.items():
if ekey in raw:
errors.append(eval)
return errors
# self-testing module
if __name__ == "__main__":
com = serial.Serial(port=4, baudrate=115200, timeout=1, xonxoff=0)
if com:
port = LPort(com)
print port
time.sleep(5)
port = LPort(com)
print "/V =", port.command("/V")
print "/V", port.data(), port.status()
print "/O =", port.command("/O")
print "/O", port.data(), port.status()
print "/A =", port.command("/A")
print "/A", port.data(), port.status()
print "/L =", port.command("/L")
print "/L", port.data(), port.status()
com.close()
else:
print "cannot open com port"
UPDATE:
The following is the code around the creatfile() in serialwin32.py which returns the following message:
serial.serialutil.SerialException: could not open port COM5: [Error 2] The system cannot find the file specified.
self.hComPort = win32.CreateFile(port,
win32.GENERIC_READ | win32.GENERIC_WRITE,
0, # exclusive access
None, # no security
win32.OPEN_EXISTING,
win32.FILE_ATTRIBUTE_NORMAL | win32.FILE_FLAG_OVERLAPPED,
0)
if self.hComPort == win32.INVALID_HANDLE_VALUE:
self.hComPort = None # 'cause __del__ is called anyway
raise SerialException("could not open port %s: %s" % (self.portstr, ctypes.WinError()))

Assuming your device is well-behaved, all you must do is this:
close your serial port (serial.Serial instance)
find the COMX name of your port again
open the serial port
The 2nd part is problematic because Windows tries to be clever. In your case the following happens:
USB device is connected and is assigned name COM2
Your program opens the device
USB disconnects
USB reconnects quickly before your program noticed that device died
Windows sees that COM2 is busy and assigns a different name to this USB device
(optional) your program closes the device
your program tries to open COM2 again, but there's no hardware at that name
The are way to get around Windows being clever -- you can specifically assign fixed COMX name to this device in Device Manager, COM ports, your port, advanced options.
Another option is to detect device dying very fast and closing the file handle. If you are lucky then by the time device reconnects original COM2 is free again.
Yet another option is to use a USB-serial converter from another manufacturer that uses another driver. Somehow COMX letter assignment is driver-specific. Better drivers may give you a stable name.

I've come across this problem as well. Sometimes my program has locked up when the device is plugged in again.
NB. I have fixed the COMx name of the port as mentioned by #qarma
I've rearranged my program so that as soon as an exception is thrown from the read() or write() methods of Serial I stop calling those methods.
I then have a function which periodically retries opening the port to try to detect when the device has been plugged in again.
This function creates a new instance of Serial with the same parameters as the original and tries to open it:
def try_to_open_new_port(self):
ret = False
test = serial.Serial(baudrate=9600, timeout=0, writeTimeout=0)
test.port = self.current_port_name
try:
test.open()
if test.isOpen():
test.close()
ret = True
except serial.serialutil.SerialException:
pass
return ret
A return of True indicates that the port is present once again.

Related

RS485 modbus incomplete packets

I have two usb to serial cables connected to each other on 2 different usb ports on the same pc.
The ground and A and B cables are all connected to eachother. the voltage cable is isolated.
When I send ASCII characters using a serial terminal i can communicate both ways.
But when I run a python script for a modbus slave and a master It works sometimes and sometimes it doesnt. I cannot find a pattern in when it works and when not.
I can see when it works all the sent bytes are received by the slave but when it doesnt work the slave receives atleast 1 byte but not the rest.
Then it obviously gives a CRC error and crashes.
How come some bytes are missing through modbus and not ASCII serial terminals?
Is this a hardware problem? How can I find out? I didnt connect extra resistors.
I can show the code for the slave and master, Thanks
As you can see I have three USB-to-serial cables connected in parallel, the orange and yellow cables are the A and B cables and the black ones are the grounds cables.
The cables are these http://www.ftdichip.com/Products/Cables/USBRS485.htm
This is the slave code but I tried different libraries and got the same results so I doubt its the code:
#!/usr/bin/env python
# -*- coding: utf_8 -*-
"""
Modbus TestKit: Implementation of Modbus protocol in python
(C)2009 - Luc Jean - luc.jean#gmail.com
(C)2009 - Apidev - http://www.apidev.fr
This is distributed under GNU LGPL license, see license.txt
"""
import sys
import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_rtu
import serial
#PORT = 0
PORT = '/dev/ttyUSB2'
def main():
"""main"""
logger = modbus_tk.utils.create_logger(name="console", record_format="%(message)s")
#Create the server
server = modbus_rtu.RtuServer(serial.Serial(port=PORT, baudrate=9600, bytesize=8, parity='N', stopbits=1, xonxoff=0))
try:
logger.info("running...")
logger.info("enter 'quit' for closing the server")
server.start()
slave_1 = server.add_slave(1)
slave_1.add_block('0', cst.HOLDING_REGISTERS, 0, 100)
while True:
cmd = sys.stdin.readline()
args = cmd.split(' ')
if cmd.find('quit') == 0:
sys.stdout.write('bye-bye\r\n')
break
elif args[0] == 'add_slave':
slave_id = int(args[1])
server.add_slave(slave_id)
sys.stdout.write('done: slave %d added\r\n' % (slave_id))
elif args[0] == 'add_block':
slave_id = int(args[1])
name = args[2]
block_type = int(args[3])
starting_address = int(args[4])
length = int(args[5])
slave = server.get_slave(slave_id)
slave.add_block(name, block_type, starting_address, length)
sys.stdout.write('done: block %s added\r\n' % (name))
elif args[0] == 'set_values':
slave_id = int(args[1])
name = args[2]
address = int(args[3])
values = []
for val in args[4:]:
values.append(int(val))
slave = server.get_slave(slave_id)
slave.set_values(name, address, values)
values = slave.get_values(name, address, len(values))
sys.stdout.write('done: values written: %s\r\n' % (str(values)))
elif args[0] == 'get_values':
slave_id = int(args[1])
name = args[2]
address = int(args[3])
length = int(args[4])
slave = server.get_slave(slave_id)
values = slave.get_values(name, address, length)
sys.stdout.write('done: values read: %s\r\n' % (str(values)))
else:
sys.stdout.write("unknown command %s\r\n" % (args[0]))
finally:
server.stop()
if __name__ == "__main__":
main()
EDIT: I just changed to baudrate to 115200 and the errors are way less. Is there anything else i can do?

Error on socket.recv (Python)

I got a small python program that communicates with an EV3 robot (lego's robot) via BT. The program sends the EV3 a number 1/2 or 3, the robot makes a predefined movement and send back 'A' to indicate that the movement is done and that it is ready for next command.
The system works great but once in a while the python app crushes with this error message:
'An established connection was aborted by the software in your host machine.' this comes from socket.recv that is called inside btListener() thread.
The relevant python parts:
import bluetooth
from gmail import *
import re
from gtts import gTTS
from time import sleep
import pygame
import serial
import thread
import os
import ftplib
from StringIO import StringIO
from blynkapi import Blynk
def a(): #Send 'a' to 'Status' mailbox
print "Send a to robot"
for i in commandA:
client_sock.send(chr(i))
sleep(1)
def b(): # Send 'b' to 'Status' mailbox
def c(): # Send 'c' to 'Status' mailbox
def clear(): # Send clear array to 'Status' mailbox
for i in clearArray:
client_sock.send(chr(i))
def btListener():
# Listen for end of run reply from the EV3
global ev3Flag, listenFlag
while True:
if listenFlag and (not ev3Flag):
try:
data = client_sock.recv(1024) #Check if EV3 is ready for new command
if data[-2] == 'A':
ev3Flag = True
print "Received 'Ready' from EV3 "
sleep(1)
except Exception as e:
print(e)
print "Failed to read data from socket"
def queueHandler():
# Read next command from QueueArray, call sendFunc and clear the queue
global ev3Flag, listenFlag, queueArray
while True:
if len(queueArray) > 0 and ev3Flag:
sendFunc(queueArray[0])
queueArray.pop(0)
def sendFunc(cmd):
#Send the next command on QueueArray to the EV3
global ev3Flag, listenFlag
if cmd == 1:
try:
ev3Flag = False
listenFlag = False
a()
listenFlag = True
sleep(3)
clear() # clear the EV3 btsocket with a default message
except Exception as e:
print "Error on sendFunc cmd = 1"
print(e)
elif cmd == 2:
try:
except Exception as e:
elif cmd == 3:
try:
except Exception as e:
if __name__ == "__main__":
# Blynk setup
blynk = Blynk(auth_token)
switch1 = Blynk(auth_token, pin = "V0")
switch2 = Blynk(auth_token, pin = "V1")
switch3 = Blynk(auth_token, pin = "V2")
print "Blynk connected"
queueArray = [] # Queue array to hold incoming commands
listenFlag = True # Listen to message from EV3
ev3Flag = True # EV3 ready for new command flag
# BT CONNECTION WITH EV3 #
print "Searching for BT connections: "
nearby_devices = bluetooth.discover_devices()
for bdaddr in nearby_devices:
print bdaddr + " - " + bluetooth.lookup_name(bdaddr)
if target_name == bluetooth.lookup_name(bdaddr):
target_address = bdaddr
break
server_sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
port = 1
server_sock.bind(("", port))
server_sock.listen(1)
client_sock, address = server_sock.accept()
print "Accepted connection from ", address
if target_address is not None:
print "found target bluetooth device with address ", target_address
else:
print "could not find target bluetooth device nearby"
# END BT CONNECTION WITH EV3 #
try:
thread.start_new_thread(queueHandler, ())
except Exception as e: print(e)
try:
thread.start_new_thread(btListener, ())
except Exception as e: print(e)
while True:
res1 = switch1.get_val()
res2 = switch2.get_val()
res3 = switch3.get_val()
if (int)(res1[0]) == 1:
print "Add 1 to queue"
queueArray.append(1)
if (int)(res2[0]) == 1:
print "Add 2 to queue"
queueArray.append(2)
if (int)(res3[0]) == 1:
print "Add 3 to queue"
queueArray.append(3)
Edit 1:
I tested it a bit more and it seems that the crush happens when the program tries to recv data and send data the same time. (via the clear() or a()/b()/c() functions), could that be the situation?
I'm new to sockets so the first solution that comes in mind is create a flag to limit the action of the socket, is there a better/smarter way to keep that from happening?
Edit 2:
I moved the 'listenFlag = True' line inside sendFunc() to after my call to clear() and it seems to solve the problem which was probably due to the python program trying to receive and sand at the same time.
I moved the 'listenFlag = True' line inside sendFunc() to after my call to clear() and it seems to solve the problem which was probably due to the python program trying to receive and sand at the same time.

pyserial doesn't accept port from script

I have a nice little script, which should give me the open serial port in def serial_port(): Which is '/dev/ttyACM0' as usual. However my next function def connect_phone(): doesn't accept this as an input (giving me an serial.serialutil.SerialException:, but only when its typed manually. Does anyone get whats the issue here?
the complete script is this:
import sys
import glob
import serial
import time
def serial_ports():
""" Lists serial port names
:raises EnvironmentError:
On unsupported or unknown platforms
:returns:
A list of the serial ports available on the system
"""
if sys.platform.startswith('win'):
ports = ['COM%s' % (i + 1) for i in range(256)]
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
# this excludes your current terminal "/dev/tty"
ports = glob.glob('/dev/tty[A-Za-z]*')
elif sys.platform.startswith('darwin'):
ports = glob.glob('/dev/tty.*')
else:
raise EnvironmentError('Unsupported platform')
result = []
for port in ports:
try:
s = serial.Serial(port)
s.close()
result.append(port)
except (OSError, serial.SerialException):
pass
return result
def connect_phone():
ser = serial.Serial("'"+serial_ports()[0]+"'", #'/dev/ttyACM0', (this is the problem here)
460800,
timeout=5,
xonxoff = False,
rtscts = False,
bytesize = serial.EIGHTBITS,
parity = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE)
ser.write(bytes('AT+CGMI' + '\r\n'))
ser.timeout = 1
ser.write('AT+CGMM' + '\r\n')
ser.timeout = 1
time.sleep(2)
ser.write('AT+CNMI=?\r') #checks whether mobile phone can receive delivery reports
response = ser.read(999)
return response
print("'"+serial_ports()[0]+"'")
time.sleep(1)
print(connect_phone())

Opening 2 serial ports simultaneously in python (one tx one for rx)

I am making a throughput test for a bluetooth link, and I need to send data through a serial port to one bluetooth device which will then transport that data wirelessly to another bluetooth device. The other device will then complete the circuit by sending the data back to the host PC via a different serial port.
The problem seems to be when I attempt to open up 2 different instances of PySerial, the program simply hangs. I have isolated it down to running vs. hanging when I comment out one of the two serial port instantiations. Anyone see a problem with how I'm doing this? If so, what is the proper way to do this? See code below:
#/usr/bin/python
import serial
import time
import sys
DEFAULT_BAUD = 115200
SEND_SIZE = 100
def addPath(file):
pth, fl = os.path.split(__file__)
return os.path.join(pth, file)
def is_number(s):
try:
int(s, 16)
return True
except:
return False
class SerialReader():
def __init__(self, portRx, portTx):
self.portTx = portTx
self.portRx = portRx
self.start_time__sec = time.time()
self.interval__sec = 0
self.buffer = []
self.sendtext = ''.join([str(i) for i in range(SEND_SIZE)])
# send first batch of data
self.portTx.write(self.sendtext)
def didDataArrive(self):
# Read port
self.buffer.extend(list(self.portRx.read(1024)))
# Step through the buffer byte and byte and see if the tick text
# is at the front.
while len(self.buffer) >= len(self.sendtext):
if self.buffer[:len(self.sendtext)] == self.sendtext:
# Discard the tick text
self.buffer = self.buffer[len(self.sendtext):]
# Record time
snapshot__sec = time.time()
self.interval__sec = snapshot__sec - self.start_time__sec
self.start_time__sec = snapshot__sec
# send more data
self.portTx.write(self.sendtext)
return True
else:
self.buffer.pop(0)
return False
def main(port1, port2, baudrate1 = DEFAULT_BAUD, baudrate2 = DEFAULT_BAUD):
try:
import serial
except:
traceback.print_exc()
print "="*60
print "You need to install PySerial"
print "Windows: easy_install pyserial"
print "Mac/Linux: sudo easy_install pyserial"
try:
s1 = serial.Serial(port1, baudrate1, timeout = 0.1)
s2 = serial.Serial(port2, baudrate2, timeout = 0.1)
print "Loading serial ports"
except:
print "Serial port error"
exit()
plot_stop = False
dataread = SerialReader(s2, s1)
try:
while plot_stop == False:
if dataread.didDataArrive():
print dataread.interval__sec
except KeyboardInterrupt:
print "Keyboard Interrupt"
plot_stop = True
finally:
print "Closing"
s1.close()
s2.close()
if __name__ == '__main__':
if (len(sys.argv) < 3):
print "Usage: python extract_data.py phonelink_serialport phonelinkclient_serialport [baudrate1] [baudrate2]"
else:
main(*sys.argv[1:])
If I remove one of the following lines (doesn't matter which one), the python script runs (although it eventually crashes because in the code it eventually tries to reference both ports). If I leave these lines in, the program seems to just hang (it just seems to sit there and run indefinitely):
s1 = serial.Serial(port1, baudrate1, timeout = 0.1)
s2 = serial.Serial(port2, baudrate2, timeout = 0.1)

how to realize socket connection was lost in server side after client run receive function in python

I have a program hande some devices in network. the program create a process for each device and each process run socket.recv function in a loop to receive messages from connected device.
when I turn device off and then turn it on, the connection between program and device would be lost but the recv function would not return any things or raise any exception and the process stay blocked.
How can handle this problem
this is my code :
if __name__ == '__main__':
ip = sys.argv[1]
port = sys.argv[2]
pingInterval = float(sys.argv[3])
deviceId = sys.argv[4]
print("A card reader started for ", ip)
Thread(target=setThePingerThread, args=(pingInterval,)).start()
while True:
clientsock = socket (AF_INET, SOCK_STREAM)
connectionSuccessful = False
while not connectionSuccessful:
try:
clientsock.connect((ip, int(port)))
connectionSuccessful = True
print('socket connected to ', ip, port)
except Exception as e:
print('Can''t connect to the device! device ip =>', ip)
time.sleep(5000)
pass
try:
mybuffer = bytes()
data = bytes()
cardNumbers = []
while True:
mybuffer = clientsock.recv(100)
data = data + mybuffer
cardNumbers, data = retreiveCardNumbers(data)
#print('debug: cardNumbers=', cardNumbers, ' data=',data, ' data-decoded=',data.decode("ascii"))
for cardNumber in cardNumbers:
print('A card hit:', cardNumber)
sendToQueue(cardNumber, deviceId)
except Exception as e:
print('Error processing card number! device ip =>', ip, e)
Referring to
the process stay blocked
I do not know the code of setThePingerThread but I guess the error is solved with this:
thread = Thread(target=setThePingerThread, args=(pingInterval,))
thread.deamon = True
thread.start()
thread.deamon is usually False which makes the program wait for the end of that thread.

Categories

Resources