0

While using an arduino code (also connecting pin 13 to A0 in an arduino uno) to have changing values

int PinOutput = 13;
int PinInput = A0;
int inph;
int inpl;

void setup() {
  // put your setup code here, to run once:
Serial.begin(9600);
pinMode(PinInput, INPUT);
pinMode(PinOutput, OUTPUT);

}

void loop() {
  // put your main code here, to run repeatedly:
inpl = analogRead(PinInput)/4;
Serial.println(inpl);
analogWrite(PinOutput,255);
delay(1000);
inph = analogRead(PinInput)/4;
Serial.println(inph);
analogWrite(PinOutput,0);
delay(1000);
}

And then trying to read the code with python using,

from PyQt5 import QtCore, QtGui, QtWidgets
import serial
import time
import sys 
import random 
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
import matplotlib
matplotlib.use('Qt5Agg')

ser = serial.Serial('COM3', baudrate = 9600, timeout = 1)
time.sleep(3)
class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(325, 237)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(110, 20, 61, 16))
        self.label.setObjectName("label")
        self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
        self.textEdit.setGeometry(QtCore.QRect(90, 60, 104, 71))
        self.textEdit.setObjectName("textEdit")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(100, 150, 75, 23))
        self.pushButton.setObjectName("pushButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 325, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)


    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "test window"))
        self.label.setText(_translate("MainWindow", "pyqt5 tests"))
        self.pushButton.setText(_translate("MainWindow", "test button"))
        self.pushButton.clicked.connect(self.label_change)  
        self.thread_start = MyThread()
        self.thread_start.ard_signal.connect(self.label.setText)        
        self.thread_start.start()
    
    def label_change(self):
        self.pushButton.setText('Button Clicked!')
        self.textEdit.setText('taco')

class MainWindowm(QtWidgets.QMainWindow):

    def __init__(self, *args, **kwargs):
        super(MainWindowm, self).__init__(*args, **kwargs)

        self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
        self.setCentralWidget(self.canvas)

        n_data = 50
        self.xdata = list(range(n_data))
        self.ydata = [random.randint(0, 10) for i in range(n_data)]

        self._plot_ref = None
        self.update_plot()

        self.show()

        self.timer = QtCore.QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.update_plot)
        self.timer.start()

    def update_plot(self):

        time.sleep(1)
        self.ydata = self.ydata[1:] +  [float(ser.readline().decode().split('\r\n')[0].strip())]
        self.canvas.axes.cla()  
        self.canvas.axes.plot(self.xdata, self.ydata, 'r')

        self.canvas.draw()

class MyThread(QtCore.QThread):
    ard_signal = QtCore.pyqtSignal(str)

    
    def __init__(self):
        QtCore.QThread.__init__(self)
        
    def run(self):
        counter = 0
        while 1:
            time.sleep(1)
            self.ard_signal.emit(str(ser.readline().decode().split('\r\n')[0]))
            counter += 1
        sys.exit()

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    w = MainWindowm()

    sys.exit(app.exec_())

If I set the delays in the arduino code to delay(1) then I don't see any error (at least, not for as long as I've tried waiting, about 20 minutes), and values that show up in the plot and window are 0 or 255. But with the delay at 1000, I get errors like,


Traceback (most recent call last):

  File "C:\Documents\ardmatlibpyqt5.py", line 141, in update_plot
    self.ydata = self.ydata[1:] +  [float(ser.readline().decode().split('\r\n')[0].strip())]

ValueError: could not convert string to float: '25\r0'

or with the ending like

ValueError: could not convert string to float: '255\r0'

with the value showing up in the window being digits that may be like 255 or 0 or nothing, or parts of them (the digits used, not numbers in between). So when the arduino is running fast, there are no errors, but when it's slow, there are. I'm trying to get multiple things running at once, pyqt5 with arduino and matplotlib. I've gotten all of these to run separately together (like pyqt5 and arduino, matplotlib and arduino, arduino and pyqt5), there are no errors if I just run pyqt5 with arduino, printing out values. Any ideas on what could be causing the error would be helpful, thanks.

1 Answer 1

1

Do not use pyserial but QSerialPort since it offers the advantage of notifying if there is data or not through signals and thus you will avoid using timers or time.sleep().

import random
import sys

from PyQt5 import QtCore, QtGui, QtWidgets, QtSerialPort

from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg


class MainWindowm(QtWidgets.QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindowm, self).__init__(*args, **kwargs)

        self.canvas = FigureCanvasQTAgg(Figure(figsize=(5, 4), dpi=100))
        self.setCentralWidget(self.canvas)

        self.axes = self.canvas.figure.subplots()

        n_data = 50
        self.xdata = list(range(n_data))
        self.ydata = [random.randint(0, 10) for i in range(n_data)]

        self.serial_port = QtSerialPort.QSerialPort("COM3")
        self.serial_port.setBaudRate(QtSerialPort.QSerialPort.Baud9600)
        self.serial_port.errorOccurred.connect(self.handle_error)
        self.serial_port.readyRead.connect(self.handle_ready_read)
        self.serial_port.open(QtCore.QIODevice.ReadWrite)

    def handle_ready_read(self):
        while self.serial_port.canReadLine():
            codec = QtCore.QTextCodec.codecForName("UTF-8")
            line = codec.toUnicode(self.serial_port.readLine()).strip().strip('\x00')
            try:
                print(line)
                value = float(line)
            except ValueError as e:
                print("error", e)
            else:
                self.update_plot(value)

    def handle_error(self, error):
        if error == QtSerialPort.QSerialPort.NoError:
            return
        print(error, self.serial_port.errorString())

    def update_plot(self, value):
        self.ydata = self.ydata[1:] + [value]
        self.axes.cla()
        self.axes.plot(self.xdata, self.ydata, "r")
        self.canvas.draw()


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindowm()
    w.show()

    sys.exit(app.exec_())
Sign up to request clarification or add additional context in comments.

9 Comments

Great. This usually works, but sometimes I run it and get a message 2 Access is denied. and it prints out 0 and 255 seemingly faster than the arduino light is flashing, and nothing shows up on the plot. Another time I get a message 9 The device does not recognize the command. but then it starts working, but with an error near the start, like error could not convert string to float: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00255' but with more \x00's
Or I get the error at the start 0 error could not convert string to float: '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000' but then it seems to run fine after
@dsfgh try again
Cool, yeah makes sense to just strip the \x00. It's good when there are Qt versions of things. I still get a message 2 Access is denied. sometimes, but I think that's a general issue I have with Arduino's, I'll have to unplug and replug in the arduino, running the arduino code again, for python code to work. Like when I try to rerun my original code, I get the error SerialException: could not open port 'COM3': PermissionError(13, 'Access is denied.', None, 5) every time. At least with your code it can be rerun sometimes, maybe if one waits long enough.
@dsfgh Those are HW problems, if the same problem appears in Qt and pyserial then check the power supply of your arduino, do not use the serial connector as power, many times I have had that type of problem because the output pins consume a lot of current that there is no enough for serial communication. Also, do not place motors (or systems that generate a lot of noise) near your device.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.