For a few weeks I'm stuck with, how to open a serial COM port from a thread,
to be able to write/read it from another thread. In my example, when I write to the port from another thread, there is access denied.
When I open the port from Main GUI, it works fine, I can write it from the thread, but I need to give a user possibility to choose the COM number.
This is my code, if anybody could take a look and help, it would be great...
class Connect(QThread):
connectResult = QtCore.pyqtSignal(str)
position1 = QtCore.pyqtSignal(str)
actuPosResult = QtCore.pyqtSignal(str)
def __init__(self, myvar, parent=None):
QThread.__init__(self, parent)
self.myvar = str(myvar) # value from spinbox
def run(self):
self.pserial = serial.Serial()
try:
COMnumber= self.myvar
self.pserial = serial.Serial('COM'+COMnumber, 115200,timeout=None)
r='COM '+COMnumber+' connected.'
self.pserial.write("c".encode('ascii')+"\n".encode('ascii'))
incomingByte = self.pserial.read()
decodedByte = incomingByte.decode("utf-8")
if decodedByte == ('c'):
r='Atmega On-Line'
self.connectResult.emit(r)
pos1='---'
self.position1.emit(pos1)
else :
r=' No answer from Atmega.'
self.connectResult.emit(r)
def stop(self):
self.terminate()
class ReadingEncoder(QThread):
actuPosResult = QtCore.pyqtSignal(str)
def __init__(self, mojazmienna, parent=None):
QThread.__init__(self, parent)
self.mojazmienna = str(mojazmienna)
def run(self):
Try:
self.pserial = serial.Serial()
self.pserial = serial.Serial('COM3', 115200,timeout=1)
self.pserial.write("p".encode('ascii')+"\n".encode('ascii'))
incomingByte = self.pserial.read()
decodedByte = incomingByte.decode("utf-8")
actualPos = ''
if decodedByte == ('a'):
while decodedByte != ('\n'):
incomingByte = self.pserial.read()
decodedByte = incomingByte.decode("utf-8")
actualPos = actualPos + decodedByte
pos= actualPos.rstrip('\n')# pozycja w formacie string
print(pos)
self.actuPosResult.emit(pos)
except (EOFError, OSError, IOError, ValueError, RuntimeError, BrokenPipeError, InterruptedError, TimeoutError):
print('Thread readingEncoder error')
self.pserial.close()
You just open the serial port and start the thread.
import atexit
class SerialPort(QThread):
connectResult = QtCore.pyqtSignal(str)
position1 = QtCore.pyqtSignal(str)
actuPosResult = QtCore.pyqtSignal(str)
def __init__(self, port=None, baud=115200, timeout=1):
super().__init__()
self.ser = serial.Serial()
self.ser.port = port
self.ser.baudrate = baud
self.ser.timeout = timeout
self.running = False
atexit.register(self.ser.close) # Make sure the serial port closes when you quit the program.
def set_port(port_num):
self.ser.port = "COM"+str(port_num)
def start(self, *args, **kwargs):
self.running = True
self.ser.open()
super().start()
self.ser.write("c\n".encode("ascii"))
def run(self):
while self.running:
try:
incomingByte = self.ser.read()
decodedByte = incomingByte.decode("utf-8")
if decodedByte == ('c'):
r='Atmega On-Line'
self.connectResult.emit(r)
pos1='---'
self.position1.emit(pos1)
else:
r=' No answer from Atmega.'
self.connectResult.emit(r)
except:
pass
# time.sleep(0.01) # You may want to sleep or use readline
You use this class by having a button call the start method.
serial = SerialPort("COM3")
btn = QtGui.QPushButton("Connect")
btn.clicked.connect(serial.start)
You can typically only open a serial port once unless you know that you have both ends available to you.
The serial port exists on the main thread and exists the entire time. You don't need to continuously open and close the serial port. All of the reading happens in the thread. To write just call serial.write(b"c\n"). You shouldn't need the writing to happen in the thread.
To have the user select the com port just use a QLineEdit.
myserial = QtGui.QLineEdit("3")
myserial.textChanged.connect(serial.set_port)
You're trying to open the port multiple times.
Instead of doing that, you have a couple of alternatives;
Create a Serial object once, and pass it to the thread that has to work with it.
Provide the name of the serial port to use to the the thread that has to work with it, and let it open the port.
In both cases make sure that your thread closes the port properly when it exits!
Related
I've got a gui that I'm playing with that uses pyserial. In it I'm using pyserial's ReaderThread to monitor the serial output of my serial device and print it out on a console window.
I also am using pyserial's Serial() implementation for sending commands to the serial device.
Usually I don't need to grab the response to a ser.write() and just let the ReaderThread handle it.
However there are now occasions where I'd like in pause the ReaderThread do a ser.read() to a variable, act on the variable, and unpause the ReaderThread to let it continue it's thing.
Tried ReaderThread.stop(), but it seems to be dropping the connection.
Also tried creating my own readerThread.run() function that has mutex locking and replacing the run method with it, but that's turning out to be a bit squirrelly.
Am I missing an easy way to do this?
Figured a way by monkey patching the ReaderThread Class:
def localinit(self, serial_instance, protocol_factory):
"""\
Initialize thread.
Note that the serial_instance' timeout is set to one second!
Other settings are not changed.
"""
super(ReaderThread, self).__init__()
self.daemon = True
self.serial = serial_instance
self.protocol_factory = protocol_factory
self.alive = True
self._lock = threading.Lock()
self._connection_made = threading.Event()
self.protocol = None
self._stop_event = threading.Event()
print("****************************************************")
print(" localinit ")
print("****************************************************")
def localrun(self):
"""Reader loop"""
print("****************************************************")
print(" localrun ")
print("****************************************************")
if not hasattr(self.serial, 'cancel_read'):
self.serial.timeout = 1
self.protocol = self.protocol_factory()
try:
self.protocol.connection_made(self)
except Exception as e:
self.alive = False
self.protocol.connection_lost(e)
self._connection_made.set()
return
error = None
self._connection_made.set()
while self.alive and self.serial.is_open:
while self._stop_event.is_set():
#print("local run while")
time.sleep(1)
try:
data = self.serial.read(self.serial.in_waiting or 1)
except serial.SerialException as e:
# probably some I/O problem such as disconnected USB serial
# adapters -> exit
error = e
break
else:
if data:
# make a separated try-except for called user code
try:
self.protocol.data_received(data)
except Exception as e:
error = e
break
self.alive = False
self.protocol.connection_lost(error)
self.protocol = None
def localpause(self):
self._stop_event.set()
def localresume(self):
self._stop_event.clear()
Then in my main code:
ReaderThread.run = localrun
ReaderThread.__init__ = localinit
ReaderThread.pause = localpause
ReaderThread.resume = localresume
self.reader = ReaderThread(serialPort, SerialReaderProtocolLine)
self.reader.start()
def write_read_cmd(self, cmd_str):
if(serialPort.isOpen() == False):
print("Serial port not yet open")
return
app.serialcom.reader.pause()
serialPort.reset_input_buffer() # flush the buffer
serialPort.reset_input_buffer() # flush the buffer
serialPort.reset_input_buffer() # flush the buffer
serialPort.write(bytes(cmd_str, encoding='utf-8'))
line = serialPort.readline()
app.serialcom.reader.resume()
line = line.decode("utf-8")
return line
I have an interface which has button for the read serial line.
class MainWindow(QMainWindow):
def __init__(self,parent = None):
QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
########################################################################
# APPLY JSON STYLESHEET
########################################################################
# self = QMainWindow class
# self.ui = Ui_MainWindow / user interface class
loadJsonStyle(self, self.ui)
########################################################################
QSizeGrip(self.ui.size_grip)
self.connection = SerialConnection()
self.Rs232 = RS232_Data()
self.ui.propertyListBtn.clicked.connect(self.get_property_list)
self.show()
def get_property_list(self):
self.Rs232.read(46,0,self.propertyDataArray) #Read Request
self.getAllByte = self.connection.serial_read_byte(236)
print(list(self.getAllByte))
# self.connection.connection_close()
#self.connection.connection_start()
This is the SerialConnection class:
import time
import serial
# serialPort = serial.Serial(port = "COM4", baudrate=115200,
# bytesize=8, timeout=2, stopbits=serial.STOPBITS_ONE)
import serial.tools.list_ports
class SerialConnection():
def __init__(self):
portList = serial.tools.list_ports.comports()
for p in portList:
if p.pid == 12345:
print("Confirmed")
self.getPortName = p.device
print(self.getPortName)
self.serialPort = serial.Serial(port=self.getPortName, baudrate=230400, bytesize=8, timeout=5,
stopbits=serial.STOPBITS_ONE)
break
else:
## Show dialog
print("There is no device on the line")
def connection_start(self):
try:
self.serialPort.open()
except Exception:
print("Port is already open")
def connection_close(self):
self.serialPort.close()
def serial_write(self, data):
self.data = data
self.serialPort.write(self.data)
def serial_read_string(self):
readData = self.serialPort.readline()
return readData
def serial_read_byte(self,size):
readData = self.serialPort.read(size)
return readData
RS232 Class:
class RS232_Data():
def __init__(self):
self.startOfTheFrame = "0x2621"
self.endOfTheFrame = "0x0D0A"
self.startOfTheFrameL = 0x26
self.startOfTheFrameH = 0x21
self.endOfTheFrameL = 0x0D
self.endOfTheFrameH = 0x0A
self.connection = SerialConnection()
def read(self,length,type,propertyData = []):
self.datagram = []
self.datagram.extend([startOfTheFrameL,startOfTheFrameH])
self.connection.serial_write(self.dataGram)
When I press the button only one time. I get correct value. But If I press second-three-third.. time I get wrong values.
But if I add below lines:
self.connection.connection_close()
self.connection.connection_start()
end of the get_property_list function, I get correct value everytime.
Is there any other way to do this? Do I have to close and open the connection every time?
I am trying to do simple serial link program for school project and I have small problem. I created class to maintain serial communication, but when I unplug and plug back serial port, it doesnt change state of serial connection (self.ser have still same object like when it was initalized). I want it to recconnect and continue to work. Can you please help me and tell mi what I am doing wrong? I am new to python. Thank you very much!
import serial
import uuid
class SerialWrapper:
def __init__(self):
self.ser = serial.Serial("COM3", 9600)
def handleconnect(self):
try:
if self.ser is None:
self.ser = serial.Serial("COM3", 9600)
self.ser.close()
self.ser.open()
print("Reconnecting...")
except:
if not (self.ser is None):
self.ser.close()
self.ser = None
print("Disconnecting")
else:
print("No Connection")
def serialwrite(self, data):
self.handleconnect()
try:
datalength = self.ser.write(data.encode())
print("Writing data...")
return datalength
except:
print("Error in writing data")
def serialread(self, datalength):
self.handleconnect()
try:
data = self.ser.read(datalength).decode()
print("Reading data...")
return data
except:
print("Error in reading data")
def main():
ser = SerialWrapper()
while 1:
value = uuid.uuid4().hex
length = ser.serialwrite(value)
print(ser.serialread(length))
main()
So I found that on every end of cycle must be port closed with self.ser.close() and then at the start of new cycle openened again with self.ser.open()
I'm making a program that reads a UDP socket then decodes this information by STEIM1 function, I want it to plot that information on PyQwt but when I try to upload the information to graph the error (Core Dumped) appears. I searched for information on the web but I would like some support, especially for the part of the graph.
#!/usr/bin/env python
import random, sys
from PyQt4 import QtGui, QtCore
import socket
import threading
import Queue
from datetime import datetime, date, time
import milibreria
from PyQt4.Qwt5 import *
host = '0.0.0.0'
port = 32004
buffer = 1024
my_queue = Queue.Queue()####################################################
my_queue1= Queue.Queue()
class readFromUDPSocket(threading.Thread):
def __init__(self, my_queue):
threading.Thread.__init__(self)
self.setDaemon(True)
self.my_queue = my_queue
def run(self):
while True:
buffer1,addr = socketUDP.recvfrom(buffer)
self.my_queue.put(buffer1)
print 'UDP received'
class process(threading.Thread):
def __init__(self, my_queue,my_queue1):
threading.Thread.__init__(self)
self.setDaemon(True)
self.my_queue = my_queue
self.my_queue1 = my_queue1
self.alive = threading.Event()
self.alive.set()
def run(self):
while True:
buffer3 = self.my_queue.get()
le=len(buffer3)
#today = datetime.now()
#timestamp = today.strftime("%A, %B %d, %Y %H:%M:%S")
#print 'Dato recibido el:', timestamp
DTj,le=milibreria.STEIM1(buffer3)
self.my_queue1.put(DTj)
class Plot(threading.Thread):
def __init__(self, my_queue1):
threading.Thread.__init__(self)
self.setDaemon(True)
self.my_queue1 = my_queue1
self.alive = threading.Event()
self.alive.set()
def run(self):
while True:
Datos = self.my_queue1.get()
print Datos,len(Datos)
milibreria.plotmat(Datos)
if __name__ == '__main__':
# Create socket (IPv4 protocol, datagram (UDP)) and bind to address
socketUDP = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
socketUDP.bind((host, port))
# Instantiate & start threads
myServer = readFromUDPSocket(my_queue)
#mySerial = readFromSerial(my_queue)
myDisplay = process(my_queue,my_queue1)
myPlot = Plot(my_queue1)
myServer.start()
myDisplay.start()
#mySerial.start()
myPlot.start()
#plotThread = threading.Thread(target=main)
#plotThread.start()
while 1:
pass
UDPSock.close()
Your application is crashing because you are plotting from a thread. You cannot interact with a PyQt GUI from within a thread. Interaction with the GUI (this includes PyQwt) must be done in the main thread only.
Refactoring your code to remove the plotting thread is probably beyond the scope of a stack overflow answer. However, if you run into a specific problem when removing your plotting thread, posting a new question (with details on that problem) on stack overflow is encouraged.
I have got tcp server on python with asyncore:
class AsyncClientHandler(asyncore.dispatcher_with_send):
def __init__(self,sock):
asyncore.dispatcher_with_send.__init__(self,sock)
self.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self.message=""
self.protocol=Protocol(DBSession, logger)
def handle_read(self):
data = self.recv(8192)
if data:
self.message+=data
while TERMINATOR in self.message:
index=self.message.index(TERMINATOR)
msg=self.message[:index]
self.message=self.message[index+len(TERMINATOR):]
answer=self.protocol.process_msg(msg, DBSession, tarif_dict)
if answer:
msg = HEADER+answer+TERMINATOR
self.send(msg)
def handle_close(self):
self.close()
class AsyncServer(asyncore.dispatcher):
def __init__(self, host, port):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
self.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self.set_reuse_addr()
self.bind((host, port))
self.listen(5)
def handle_accept(self):
pair = self.accept()
if pair is None:
pass
else:
sock, addr = pair
logging.info("Incoming connection from %s",repr(addr))
AsyncClientHandler(sock)
Some clients do not close the connection, so at some point the server crashes due to the large number of sockets.
How can I close an inactive socket after some time? settimeout not work.
To achieve this you could use TCP's Keepalive (like you already did) and set its delay, pings... But this apporach should only be used for long-lasting connections and is only available on Unixes. Have some read here.
You can also make some scheduling of sockets, closing them when some time passes or delay them when they're active. I made an example working with your code:
import sched, time
class SocketSched(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.daemon = True
self.to_run = []
self.scheds = {}
self.start()
def add(self, what):
self.to_run.append(what.values()[0])
self.scheds.update(what)
def run(self):
while True:
if self.to_run:
run = self.to_run.pop()
if not run.empty(): run.run()
else: self.to_run.append(run)
Here we define new class of scheduler in different thread - this is important, sched module would continuously block, like asyncore.loop().
This needs modificating your code a bit:
class AsyncClientHandler(asyncore.dispatcher_with_send):
def __init__(self,sock, sch_class):
...
self.delay = 10
self.sch_class = sch_class
self.sch = sched.scheduler(time.time, time.sleep)
self.sch_class.add({self.fileno(): self.sch})
self.event = self.sch_class.scheds[self.fileno()].enter(self.delay, 1, self.handle_close, ())
...
def delay_close(self):
self.sch_class.scheds[self.fileno()].cancel(self.event)
self.event = self.sch_class.scheds[self.fileno()].enter(self.delay, 1, self.handle_close, ())
...
def handle_close(self):
try:
self.sch_class.scheds[self.fileno()].cancel(self.event)
except:
pass
...
self.delay is a timeout in seconds. After this time passes, and no action delays it, socket will be closed. Line in handle_close() ensures it won't be called twice due to task in scheduler.
Now you have to add self.delay_close() to the beginning of every method that ensures socket is active, eg. handle_read().
Server class (gets instance of SocketSched and passes it to new channels):
class AsyncServer(asyncore.dispatcher):
def __init__(self, host, port, sch_class):
...
self.sch_class = sch_class
...
def handle_accept(self):
...
AsyncClientHandler(sock, self.sch_class)
Ready. Using this:
server = AsyncServer('', 1337, SocketSched())
asyncore.loop()
This solution works, but can be error-prone on some close events. Anyway, sockets will read, delay, and close when given timeout occurs. Unfortunately running such scheduling loop uses some CPU.