Unable to get arduino serial communication working

2019-06-10 22:42发布

问题:

This is the definition which is used to update the labels in the GUI:

    def updateV(self, event):
    """"""
    global v
    ser = serial.Serial( port='COM3', baudrate=9600)
    x = ser.read()          # read one byte
    ser.close()
    print x
    if v>3:
        self.labelOne.SetBackgroundColour('red')
        self.labelOne.SetLabel('Battery Voltage : ' + x)

    else:
        self.labelOne.SetBackgroundColour('white')
        self.labelOne.SetLabel('Battery Voltage : ' + str(v))


    self.Refresh()

This is the simple arduino code i have been using:

int a;
void setup() {
Serial.begin(9600);// put your setup code here, to run once:

}

void loop() {
a=5;
Serial.println(a);
delay(10);
}

I have been using this definition to update my labels for my GUI. I recently started to set up serial communication on my GUI using that code. Logically using the mainloop() of the wx library, i thought i could update the 'x' value and get it printed on the GUI. But all the GUI window shows in 0.0 even though the python console prints 5 regularly. Please help! I am pretty new to this.

回答1:

Your issue is that ser.read() will block. Even if you tweak the timeout of your serial.Serial instance, it still will keep the GUI busy. In that situation I do not know a method to "force" a refresh/wx.Yield(), it simply will not work. The standard solution for blocking calls is to spin up a thread or poll regularily (e. g. with wx.Timer). However, I was only able to make threading work. The example is based on wxTerminal in pyserial.

# -*- coding: utf-8 -*-

import wx
import serial
from threading import Thread

ARDUINO_NEWLINE = '\r\n'

class serial_reader(object):
    def __init__(self, callback=None):
        """Creates serial reader.

        :param callback: callable, gets called when byte on serial arrives.
        """
        self.callback = callback

        self.thread = None

        # Signal if serial is alive and should be read
        self.alive = False

    def start_reader(self, serial_cfg):
        """Start the receiver thread.

        :param serial_cfg: dictionary, gets unpacked to parameters for :class:`serial.Serial`
        """

        self.ser_cfg = serial_cfg
        self.serial = serial.Serial(**serial_cfg)
        # set != None so it will not block for longer than timeout on shutdown
        self.serial.timeout = 0.1
        self.alive = True

        self.thread = Thread(target=self.serial_read)
        self.thread.daemon = True
        self.thread.start()

    def stop_reader(self):
        """Stop the receiver thread, wait util it is finished."""
        if self.thread is not None:
            # signal no more reads
            self.alive = False
            # wait until thread has finished
            self.thread.join()
            self.thread = None

        # cleanup
        self.serial.close()

    def serial_read(self):
        """Thread that handles the incoming traffic."""

        while self.alive:
            try:
                text = self.serial.read()
                if text and self.callback:
                    # return value to main loop in thread-safe manner
                    wx.CallAfter(self.callback, text)
            except serial.serialutil.SerialException:
                # will happen when Windows goes in sleep mode
                print 'serial.serialutil.SerialException'


class ser_frm(wx.Frame):
    def __init__(self, *args, **kwds):
        wx.Frame.__init__(self, *args, **kwds)        
        self.txt = wx.TextCtrl(self, -1, '', style=wx.TE_MULTILINE)

class serial_controller(object):
    def __init__(self, app):
        self.app = app

        # buffer for serial data
        self.ser_buf = ''

        self.frm = ser_frm(None, -1, 'testfrm')

        # setup serial configuration
        self.serial_cfg = {'port': 'COM4', 'baudrate': 9600}        
        # When parameter dsrdtr is set to True, the Arduino
        # will not reset on serial open, for details see
        # http://playground.arduino.cc/Main/DisablingAutoResetOnSerialConnection
        self.serial_cfg['dsrdtr'] = True

        self.ser_rd = serial_reader(callback=self.on_serial)
        tit = 'Arduino on port {port} at baudrate {baudrate}'.format(**self.serial_cfg)
        self.frm.SetTitle(tit)
        self.ser_rd.start_reader(self.serial_cfg)

        self.frm.Show()

        self.frm.Bind(wx.EVT_CLOSE, self.on_close)

    def on_close(self, evt):
        """Shutdown serial read thread before closing."""
        if self.ser_rd.alive:
            self.ser_rd.stop_reader()
        evt.Skip()

    def on_serial(self, text):
        """Handle input from the serial port."""
        self.ser_buf += text
        if self.ser_buf.endswith(ARDUINO_NEWLINE):
            if self.frm.txt.GetInsertionPoint() > 1000:
                self.frm.txt.SetValue('')
            self.frm.txt.AppendText(self.ser_buf)
            self.ser_buf = ''


if __name__ == "__main__":
    app = wx.App(redirect=False)
    serialctr = serial_controller(app)
    app.MainLoop()

EDIT: It is not necessary to tinker with DSR/DTR on Arduinos with USB on chip (e.g. the Arduino micro), so delete the line

self.serial_cfg['dsrdtr'] = True

and it will still work properly.