1

I have a Raspberry Pi Pico connected via USB to a Windows 10 PC running a Python script using PyQt.

I've found that the serial comms (over USB) between my Pico and the Python script on the PC doesn't work. If I open a connection using Arduino's Serial Monitor, or using pyserial, it does work. You'd think this would be a simple case of my use of PyQt's QSerialPort just being wrong, but if I try to connect to the PyQt-based script after having connected to the Serial Monitor, or another Python script that uses pyserial, it works with no issue.

I don't have to have sent anything using the pyserial-based script, just made the connection and ended the program. The same with the Arduino Serial Monitor, just made the connection and then closed the Serial Monitor window. If I reset the Pico by unplugging the USB cable, I have to connect to the Serial Monitor again or run the pyserial script before I can connect to the PyQt script.

I did find this similar issue in Raspberry Pi forums, but the suggested fix there (10uF capacitor between RESET and GND on an Arduino hasn't helped me. (I didn't expect it would.)

Minimal code to reproduce:

Arduino on the Pico (a standard program I use to test serial comms)

#include <Arduino.h>

#define ONBOARD_LED_GPIO 25

void setup() {
  Serial.begin(115200, SERIAL_8N1);

  pinMode(ONBOARD_LED_GPIO, OUTPUT);
  digitalWrite(ONBOARD_LED_GPIO, LOW);
}

void loop() {
  if (Serial.available())
  {
    digitalWrite(ONBOARD_LED_GPIO, HIGH);
    String test = Serial.readStringUntil('\n');
    test += ".";
    Serial.print(test);
    digitalWrite(ONBOARD_LED_GPIO, LOW);
  }
}

PyQt Python on the PC (stripped down from my code)

from PyQt5.QtCore import QObject, QIODevice, QByteArray
from PyQt5.QtWidgets import QMessageBox, QInputDialog, QMainWindow, QApplication, QPushButton, QWidget
from PyQt5.QtSerialPort import QSerialPort, QSerialPortInfo
import sys


class SerialTester(QMainWindow):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setupUi(self)

        self.show()

        self._serial = QSerialPort(self)
        self._connect_serial()

        self._serial.readyRead.connect(self._read_serial)
        self.testButton.clicked.connect(lambda: self.write("test\n"))

    def setupUi(self, MainWindow):
        MainWindow.setObjectName("SerialTester")
        MainWindow.resize(1000, 1000)
        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.testButton = QPushButton(self.centralwidget)

    def _connect_serial(self):
        port, ok_pressed = self._get_serial_port_dialog()
        if ok_pressed and port:
            self._serial.setPortName(port)
            if self._serial.open(QIODevice.ReadWrite):
                # Connect success
                self._serial.setBaudRate(115200)
                self._serial.setDataBits(8)
                self._serial.setParity(0)
                self._serial.setStopBits(1)
                return True
        return False
            
    def _get_serial_port_dialog(self):
        ports = [item.portName() for item in QSerialPortInfo.availablePorts()]
        if len(ports) == 0:
            msg = QMessageBox()
            msg.setIcon(QMessageBox.Warning)
            msg.setText("No ports available!")
            msg.setWindowTitle("Connection Error")
            msg.setStandardButtons(QMessageBox.Cancel)
            msg.exec()
            return None, False
        port, ok_pressed = QInputDialog.getItem(
            self,
            "Choose port",
            "Valve Controller Port:",
            ports,
            0,
            False)
        return port, ok_pressed

    def _read_serial(self):
        received_str = self._serial.readAll().data().decode()
        print("Received: " + received_str)

    def write(self, write_str):
        self._serial.write(QByteArray(write_str.encode()))

if __name__ == "__main__":
    app = QApplication(sys.argv)
    view = SerialTester()
    view.show()    

    # Execute event loop, and exit properly when it's finished
    sys.exit(app.exec())

A working pyserial example

if __name__ == "__main__":

    ser = serial.Serial(
        port="COM11",
        baudrate=115200,
        bytesize=8,
        timeout=1,
        stopbits=serial.STOPBITS_ONE,
        parity=serial.PARITY_NONE
        )
    while True:
        ser.write("test".encode())
        time.sleep(1)
        print("Read: " + ser.readall().decode())
Jonas
  • 121,568
  • 97
  • 310
  • 388
JRVeale
  • 394
  • 2
  • 10
  • I think you need to call QSerialPortInfo as a function. `QSerialPortInfo().availablePorts()` – bfris May 19 '22 at 23:54
  • Same issue, but using the C++ version of QtSerialPort. Also I have same behaviour: After opening the port with the arduino's serial monitor, the communication works with QtSerialPort. So this is not a python issue – Dragoner Oct 19 '22 at 11:43

1 Answers1

0

I could solve the issue with the information of Windows COM access to PICO via USB trouble. After you opened the port, call this

QSerialPort::setDataTerminalReady(true)
Dragoner
  • 123
  • 1
  • 12