Why would QtSerialPort not read after running more than once or twice?

531 Views Asked by At

I am just trying to read values from a QtSerialPort connection to an arduino after a value was went to signify the start. I could have a maximum count in the python code, but that doesn't change the outcome of it getting not reading the println from arduino after running the python script more than once. At first it'll print out 'start', 'to read', and 'V', and maybe again if I run it again soon. But after that, it stops reading after printing out 'start'. Here is the python code.

from PyQt5 import QtCore, QtSerialPort
import sys 

app = QtCore.QCoreApplication([]) 

serial_port = QtSerialPort.QSerialPort('COM3') 

serial_port.open(QtCore.QIODevice.ReadWrite) 

def handle_ready_read(): 
    print('to read')
    while serial_port.canReadLine():
        serialrd = serial_port.readLine().data().decode().strip()
        print(serialrd)
        serial_port.close()
        app.quit() 

serial_port.readyRead.connect(handle_ready_read) 

serial_port.write(bytes([100])) 
print('start')
sys.exit(app.exec_())

And here is the arduino code.

int measured;
int counter = 0;
int maxnum = 10;

void setup()
{
Serial.begin(9600);
}

void loop()
{
  if(Serial.available() > 0)
  {
    while(counter < maxnum)
    {

        Serial.println("V");
        counter++;
        delay(100);

    }
  }
}

Any ideas that would explain why it won't read again would be useful. I have to unplug and replug in the arduino each time to get it to read, but avoiding having to do that is preferable. I also notice I can't run a PyQt5 serial port connection after a pyserial connection, with disconnecting the arduino first, but I can run pyserial after a PyQt5 serial port connection, though I don't know if that issue is related somehow.

Edit: I added in the 'to read' print statement in handle_ready_read, which sometimes would print out twice when running the first time, so though there is something to be read, it's not always readable by readline, so I added in to handle_ready_read at the end it,

    while serial_port.bytesAvailable(): 
        dataByte = serial_port.readLineData(1)
        print(dataByte)
        serial_port.close()
        app.quit()

which will thus sometimes print out b'V' when that is read instead of just V. But it still doesn't read after one or two tries.

Edit 2: I added a timer and function to check if the bytes were written with,

attempts = []
def check_bytes_written():    
    print('bytes to write' , serial_port.bytesToWrite())
    attempts.append(1)
    if len(attempts) > 10:
        serial_port.close()
        timer.stop()
        app.quit()

timer = QtCore.QTimer()
timer.setInterval(50) 
timer.timeout.connect(check_bytes_written)
timer.start()

This doesn't print out anything with the first or second try, and then says 'bytes to write 0' with later tries, still not printing out any form of 'V', even though the port and app were ended, unless I still disconnect the arduino and plug it back in (at least the kernal doesn't need to be restarted each time now though). I also added code into the arduino to check if the value written can be used to change the voltage of an output pin, it works and the output voltage is changed each time the python script is run, whether a 'V' is read or not. So I changed the question title a bit and previous text, since it's not really crashing or freezing, it's just not reading what the arduino is printing.

Edit 3: I put if(Serial.available() > 0) { int counter = 0; in the arduino code, instead of initializing the counter at the start, the python code will read from the arduino when rerunning the script (though with a bunch of spaces around the V, or just printing b'\x00' from the readlineData part, but that may be another issue, which is cleared by putting serial_port.clear() before the readyRead, but then either V or b'V' is still printed). So it seems like the python script is not restarting the arduino code, maybe it is an issue with how qserialport closes or opens (like with DTR or RTS), unlike how pyserial does? More is discussed here.

Edit 4: If I add (while initializing the counter at the start in the arduino code, as originally shown, undoing the last edit)

serial_port.setDataTerminalReady(1)
serial_port.setDataTerminalReady(0)
serial_port.setDataTerminalReady(1)

after the serial_port.open statement and run it, it still doesn't print from the serial read (without disconnecting the arduino and reconnecting it first), but then if I comment the DTR sets out, then a V is printed out as expected when running again, no disconnect needed, but then it goes back to not printing V. The same effect occurs using just serial_port.setDataTerminalReady(1), commenting it in/out every other time.

Edit 5: I found that (once it has run properly at least once, by unplugging first or using the method in edit 4), if I put

serial_port.setDataTerminalReady(0)
serial_port.setDataTerminalReady(1)
serial_port.setDataTerminalReady(0)

right before serial_port.close() it can be run again multiple times and print out 'V'. I guess it is resetting the DTR to 0. If there is an explanation of why that needs to happen, that would be good to know.

1

There are 1 best solutions below

10
Adehad On

Is your function logic indented correctly?

To me it looks like you read a single line and immediately close the serial port and quit your app.

Perhaps you intended:

def handle_ready_read(): 
    while serial_port.canReadLine():
        serialrd = serial_port.readLine().data().decode().strip()
        print(serialrd)
    # Close serial port and quit app when no more data is available
    serial_port.close()
    app.quit()