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())