-2

I am making a GUI in Python for a machine. My python program runs on a Raspberry Pi 3 and it is communicating by serial with an Arduino UNO.

I've made a protocol; the arduino sends a "1". The raspberry knows the "1" stands for Run/Stop, and gives back the value of the RunStop variable (which is either 0 or 1). After that the arduino sends a "2". The raspberry knows it stands for the Speed value, and sends back the Speed variable (Which is between 1 and 4).

I've made the 2 following programs that work, but I don't know how to implement it in the GUI. Because when I add it before the root.mainloop() , the GUI won't start. And when I put it after, the while loop never starts.

Arduino Code:

int RunStop, Speed = 0;

void setup() {

  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
}


void loop() {
  Serial.write(1);
  delay(2000);
  RunStop = Serial.read(); 
  if (RunStop == 1) {
    digitalWrite(13, HIGH);
  } else {
    digitalWrite(13, LOW);
  }
  delay(2000);


  Serial.write(2);
  delay(2000);
  Speed = Serial.read(); 
  if (Speed == 3) {
    digitalWrite(12, HIGH);
  } else {
    digitalWrite(12, LOW);
  }
  delay(2000);
}

Python Communication Code:

import time
import serial

# configure the serial connections (the parameters differs on the device you are connecting to)
ser = serial.Serial(
    port='/dev/ttyAMA0',
    baudrate=9600,
)

ser.isOpen()

##Status variabelen

RunStop = b'\x01'
Speed = b'\x03'

while 1:
uartIn = ser.read()
print (uartIn)
time.sleep(1)
if uartIn == b'\x01':
    ser.write(RunStop)
    print ("Run/Stop Register")
elif uartIn == b'\x02':
    ser.write(Speed)
    print ("Speed Register")
else:
    print ("No Register Requested")

GUI code:

from tkinter import *
import time
import serial

# configure the serial connections (the parameters differs on the device you are connecting to)
ser = serial.Serial(
    port='/dev/ttyAMA0',
    baudrate=9600,
    parity=serial.PARITY_ODD,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS
)

ser.isOpen()

root = Tk()
root.title("Cutter")

## Layout







label_1 = Label(root, text="Pieces")
label_2 = Label(root, text="Length")
entry_1 = Entry(root, width=9)

entry_2 = Entry(root, width=9)


label_1.grid(row=3, sticky=W, padx=(10))
label_2.grid(row=4, sticky=W, padx=(10))
entry_1.grid(row=3, column=1, pady=(10,10))
entry_2.grid(row=4, column=1, pady=(10,15))


runStop = b'\x00'
Speed = b'\x01'
Pieces = b'\x00'
Length = b'\x00'


##pieces OK button function
def piecesOKphase():
    x = entry_1.get()
    print("pieces: " + x)

##length OK button function
def lengthOKphase():
    x = entry_2.get()
    print("length: " + x)

def runPhase():
    global runStop
    runStop = b'\x01'

def stopPhase():
    global runStop
    runStop = b'\x00'





#OK and RUN / STOP buttons
piecesOK = Button(root, text="OK", command=piecesOKphase)
piecesOK.grid(row=3, column=2)

lengthOK = Button(root, text="OK", command=lengthOKphase)
lengthOK.grid(row=4, column=2)

runButton = Button(root, text="Run", width=6, bg='#58FA58', command=runPhase)
stopButton = Button(root, text="Stop", width=6, bg='#FA5858', command=stopPhase)

runButton.grid(row=0, column=0,padx=(10,10), pady=(10,10))
stopButton.grid(row=0, column=2)



##speed container

CurrentSpeed = 1




def delSpeed():
    global CurrentSpeed
    global speedLabel

    if CurrentSpeed > 1:
        CurrentSpeed -= 1
        speedLabel.config(text=str(CurrentSpeed))
        speedLabel.config(text=CurrentSpeed)

    else:
        pass

def addSpeed():
    global CurrentSpeed

    if CurrentSpeed < 4:
        CurrentSpeed += 1
        speedLabel.config(text=CurrentSpeed)

    else:
        pass


speedMin = Button(root, text="Speed -", width=6, command=delSpeed)

speedLabel = Label(root, text=CurrentSpeed, font=20)
speedLabel.grid(row=1, column=1, padx=(10))

speedPlus = Button(root, text="Speed +", width=6, command=addSpeed)

speedMin.grid(row=1, column=0)
speedPlus.grid(row=1, column=2)


#Number keyboard functions

def B1phase():
    try:
        root.focus_get().insert(END, "1")
    except AttributeError:
        pass
def B2phase():
    try:
        root.focus_get().insert(END, "2")
    except AttributeError:
        pass
def B3phase():
    try:
        root.focus_get().insert(END, "3")
    except AttributeError:
        pass
def B4phase():
    try:
        root.focus_get().insert(END, "4")
    except AttributeError:
        pass
def B5phase():
    try:
        root.focus_get().insert(END, "5")
    except AttributeError:
        pass
def B6phase():
    try:
        root.focus_get().insert(END, "6")
    except AttributeError:
        pass
def B7phase():
    try:
        root.focus_get().insert(END, "7")
    except AttributeError:
        pass
def B8phase():
    try:
        root.focus_get().insert(END, "8")
    except AttributeError:
        pass
def B9phase():
    try:
        root.focus_get().insert(END, "9")
    except AttributeError:
        pass
def B0phase():
    try:
        root.focus_get().insert(END, "0")
    except AttributeError:
        pass
def clearPhase():
    try:
        root.focus_get().delete(0, 'end')
    except AttributeError:
        pass


## Number keyboard buttons
B1 = Button(root, text="1", width=6, command=B1phase)
B2 = Button(root, text="2", width=6, command=B2phase)
B3 = Button(root, text="3", width=6, command=B3phase)
B4 = Button(root, text="4", width=6, command=B4phase)
B5 = Button(root, text="5", width=6, command=B5phase)
B6 = Button(root, text="6", width=6, command=B6phase)
B7 = Button(root, text="7", width=6, command=B7phase)
B8 = Button(root, text="8", width=6, command=B8phase)
B9 = Button(root, text="9", width=6, command=B9phase)
B0 = Button(root, text="0", width=6, command=B0phase)
clearButton = Button(root, text="Clear", width=6, bg='#FA5858', command=clearPhase)


B1.grid(row=5, column=0)
B2.grid(row=5, column=1)
B3.grid(row=5, column=2, padx=(10,10))
B4.grid(row=6, column=0)
B5.grid(row=6, column=1)
B6.grid(row=6, column=2, padx=(10,10))
B7.grid(row=7, column=0)
B8.grid(row=7, column=1)
B9.grid(row=7, column=2, padx=(10,10))
B0.grid(row=8, column=1)
clearButton.grid(row=8, column=0)



## Manual

label_4 = Label(root, text="")
label_4.grid(row=2, sticky=W)
label_3 = Label(root, text="Manual")
label_3.grid(row=9, sticky=W, padx=(10), pady=(10,10))

manualCut = Button(root, text="Cut", width=4)
manualCut.grid(row=10, column=0, pady=(1,15))

manualFeed = Button(root, text="Feed", width=4)
manualFeed.grid(row=10, column=1, pady=(1,15))


# Test function
def testPhase():

    print (CurrentSpeed)

manualTest = Button(root, text="Test", width=4, command=testPhase)
manualTest.grid(row=10, column=2, pady=(1,15))




## OTHER


root.mainloop()

EDIT

I have used the after from Tkinter.

The GUI starts, and the while loop runs. But I cannot click on any buttons.

from tkinter import *
import time
import serial

# configure the serial connections (the parameters differs on the device you are connecting to)
ser = serial.Serial(
    port='/dev/ttyAMA0',
    baudrate=9600,
    parity=serial.PARITY_ODD,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS
)
ser.isOpen()





## GUI Title
root = Tk()
root.title("Cutter")





##Communication
RunStop = b'\x01' ## 0 = OFF, 1 = ON

Speed = b'\x03' ## 1, 2, 3, 4
CurrentSpeed = 1

Pieces = b'\x00'

Pieces100 = b'\x00'
Pieces10 = b'\x00'
Pieces1 = b'\x00'

length10000 = b'\x00'
Length1000 = b'\x00'
Length100 = b'\x00'
Length10 = b'\x00'
Length1 = b'\x00'



def Communication():
    while 1:
        uartIn = ser.read()
        print (uartIn)
        time.sleep(1)
        if uartIn == b'\x01':
            ser.write(RunStop)
            print ("Run/Stop Register")
        elif uartIn == b'\x02':
            ser.write(Speed)
            print ("Speed Register")
        else:
            print ("No Register Requested")
        root.after(500, Communication)





## Functions
def runBOT():
    global RunStop
    RunStop = b'\x01'

def stopBOT():
    global RunStop
    RunStop = b'\x00'

def delSpeedBOT():
    global CurrentSpeed
    global speedLabel

    if CurrentSpeed > 1:
        CurrentSpeed -= 1
        speedLabel.config(text=str(CurrentSpeed))
        speedLabel.config(text=CurrentSpeed)
    else:
        pass

def addSpeedBOT():
    global CurrentSpeed

    if CurrentSpeed < 4:
        CurrentSpeed += 1
        speedLabel.config(text=CurrentSpeed)
    else:
        pass

def piecesBOT():
    x = entryPieces.get()
    print("pieces: " + x)

def lengthBOT():
    x = entryLength.get()
    print("length: " + x)





## Layout

labelPieces = Label(root, text="Pieces")
labelLength = Label(root, text="Length")
entryPieces = Entry(root, width=9)
entryLength = Entry(root, width=9)

labelPieces.grid(row=3, sticky=W, padx=(10))
labelLength.grid(row=4, sticky=W, padx=(10))
entryPieces.grid(row=3, column=1, pady=(10,10))
entryLength.grid(row=4, column=1, pady=(10,15))





## Buttons

runButton = Button(root, text="Run", width=6, bg='#58FA58', command=runBOT)
runButton.grid(row=0, column=0,padx=(10,10), pady=(10,10))

stopButton = Button(root, text="Stop", width=6, bg='#FA5858', command=stopBOT)
stopButton.grid(row=0, column=2)

speedMin = Button(root, text="Speed -", width=6, command=delSpeedBOT)
speedMin.grid(row=1, column=0)

speedLabel = Label(root, text=CurrentSpeed, font=20)
speedLabel.grid(row=1, column=1, padx=(10))

speedPlus = Button(root, text="Speed +", width=6, command=addSpeedBOT)
speedPlus.grid(row=1, column=2)

piecesOK = Button(root, text="OK", command=piecesBOT)
piecesOK.grid(row=3, column=2)

lengthOK = Button(root, text="OK", command=lengthBOT)
lengthOK.grid(row=4, column=2)





#Number keyboard functions

def B1phase():
    try:
        root.focus_get().insert(END, "1")
    except AttributeError:
        pass
def B2phase():
    try:
        root.focus_get().insert(END, "2")
    except AttributeError:
        pass
def B3phase():
    try:
        root.focus_get().insert(END, "3")
    except AttributeError:
        pass
def B4phase():
    try:
        root.focus_get().insert(END, "4")
    except AttributeError:
        pass
def B5phase():
    try:
        root.focus_get().insert(END, "5")
    except AttributeError:
        pass
def B6phase():
    try:
        root.focus_get().insert(END, "6")
    except AttributeError:
        pass
def B7phase():
    try:
        root.focus_get().insert(END, "7")
    except AttributeError:
        pass
def B8phase():
    try:
        root.focus_get().insert(END, "8")
    except AttributeError:
        pass
def B9phase():
    try:
        root.focus_get().insert(END, "9")
    except AttributeError:
        pass
def B0phase():
    try:
        root.focus_get().insert(END, "0")
    except AttributeError:
        pass
def clearPhase():
    try:
        root.focus_get().delete(0, 'end')
    except AttributeError:
        pass





## Number keyboard buttons
B1 = Button(root, text="1", width=6, command=B1phase)
B2 = Button(root, text="2", width=6, command=B2phase)
B3 = Button(root, text="3", width=6, command=B3phase)
B4 = Button(root, text="4", width=6, command=B4phase)
B5 = Button(root, text="5", width=6, command=B5phase)
B6 = Button(root, text="6", width=6, command=B6phase)
B7 = Button(root, text="7", width=6, command=B7phase)
B8 = Button(root, text="8", width=6, command=B8phase)
B9 = Button(root, text="9", width=6, command=B9phase)
B0 = Button(root, text="0", width=6, command=B0phase)
clearButton = Button(root, text="Clear", width=6, bg='#FA5858', command=clearPhase)

B1.grid(row=5, column=0)
B2.grid(row=5, column=1)
B3.grid(row=5, column=2, padx=(10,10))
B4.grid(row=6, column=0)
B5.grid(row=6, column=1)
B6.grid(row=6, column=2, padx=(10,10))
B7.grid(row=7, column=0)
B8.grid(row=7, column=1)
B9.grid(row=7, column=2, padx=(10,10))
B0.grid(row=8, column=1)
clearButton.grid(row=8, column=0)





## Manual

label_4 = Label(root, text="")
label_4.grid(row=2, sticky=W)
label_3 = Label(root, text="Manual")
label_3.grid(row=9, sticky=W, padx=(10), pady=(10,10))

manualCut = Button(root, text="Cut", width=4)
manualCut.grid(row=10, column=0, pady=(1,15))

manualFeed = Button(root, text="Feed", width=4)
manualFeed.grid(row=10, column=1, pady=(1,15))


# Test function
def testPhase():
    pass

manualTest = Button(root, text="Test", width=4, command=testPhase)
manualTest.grid(row=10, column=2, pady=(1,15))





## Running GUI and communication
root.after(500, Communication)


root.mainloop()

This doesn't work: Communication() root.mainloop()

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
DowinskiField
  • 133
  • 2
  • 15
  • In your edit, `Communication` has a `while` loop, which is completely missing the point. You need to remove the `while` loop and have the function try _exactly once_ to read the serial port. – Bryan Oakley Oct 05 '16 at 14:39

2 Answers2

1

Use the Tkinter .after method to periodically call the code that you use to read data from the serial port.

http://effbot.org/tkinterbook/widget.htm

tkinter: how to use after method

Tkinter needs to be able to process the UI so placing any loop in the code means that the UI becomes un-responsive. Using .after allows you to periodically schedule tasks to run.

Community
  • 1
  • 1
scotty3785
  • 6,763
  • 1
  • 25
  • 35
  • I edited my question, when I use your after method, I can't click on any button in my GUI. – DowinskiField Oct 05 '16 at 14:18
  • If you had used .after correctly this wouldn't be the case. A function scheduled by after should not contain any infinite loops. It should quickly read the serial port, return the data and then use .after to schedule itself to be called again after a specified period. As your code stands, it won't work. Get rid of `while 1` – scotty3785 Oct 06 '16 at 12:59
0

Assuming you can do a non-blocking read on the serial port, you can write a function that reads the serial port, and then schedules itself to be called again after a delay.

For example:

def poll_serial(root):

    uartIn = ser.read()
    if uartIn:
        process_value_from_serial(uartIn)

    root.after(1000, poll_serial, root)

In your main program, you call this immediately before starting mainloop and it will run every second for the life of the program.

root = Tk()
...
poll_serial(root)
root.mainloop()

If calling ser.read() blocks, you can modify the polling function to look like this1:

def poll_serial(root):
    if (ser.inWaiting()>0):
        uartIn = ser.read()
        if uartIn:
            process_value_from_serial(uartIn)
    root.after(1000, poll_serial, root)

1 Code for checking the serial port before reading came from this answer: https://stackoverflow.com/a/38758773/7432

Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • I tried this, and the GUI won't start: Communication() root.mainloop() – DowinskiField Oct 05 '16 at 14:28
  • @jarnohh: where is the code stuck? Is `ser.read()` waiting for some input? – Bryan Oakley Oct 05 '16 at 14:29
  • No, it keeps getting input. It just keeps looping (that's the purpose) it keeps getting and sending input, and that's what it is supposed to do. But with your method, the GUI won't start (if I use my own function though) but when I use the after method from Tkinter, it does start, but I can't click any button. – DowinskiField Oct 05 '16 at 14:31
  • @jarnohh: I don't see how it's possible that it "keeps looping" without showing the GUI. That seems impossible, since it's the GUI's mainloop that allows the looping. I've updated my answer, since it looks like `ser.read()` may be blocking even though you seem to think its looping. – Bryan Oakley Oct 05 '16 at 14:32
  • @jarnohh: note: do _not_ put a while loop inside the code that calls `ser.read()`. – Bryan Oakley Oct 05 '16 at 14:40