python auto repeating serial data - python

Hello I have the bellow code which sends a character to the serial port depending on the state of a check button in tkinter. This works well but I require the character to be repeatability sent over the serial port (every 10 seconds or so) regardless to whether or not the state of the check button has changed. I have tried adding in a while loop but this makes the program crash. Any help would be grand.
(This is a small section of a larger code, as it stands it works as described above)
def setCheckButtonText():
if varCheckButton.get():
ser.write(bytes('L'))
else:
ser.write(bytes('H'))
def setCheckButtonText2():
if varCheckButton2.get():
ser.write(bytes('F'))
else:
ser.write(bytes('O'))
def setCheckButtonText3():
if varCheckButton3.get():
ser.write(bytes('N'))
else:
ser.write(bytes('Y'))
ser = serial.Serial('/dev/ttyUSB0', 9600)
varCheckButton = tk.IntVar()
tkCheckButton = tk.Checkbutton(
mGui,
variable=varCheckButton,
command=setCheckButtonText)
tkCheckButton.place(x=60, y=50)
varCheckButton2 = tk.IntVar()
tkCheckButton = tk.Checkbutton(
mGui,
variable=varCheckButton2,
command=setCheckButtonText2)
tkCheckButton.place(x=60, y=90)
varCheckButton3 = tk.IntVar()
tkCheckButton = tk.Checkbutton(
mGui,
variable=varCheckButton3,
command=setCheckButtonText3)
tkCheckButton.place(x=60, y=130)

You may want to capsule sending and setting the variables.
In your methods, you can just set the appropriate variables to the characters instead of sending them directly.
In another method, you can use those characters to send them to the serial port. You can use after to call a method after a delay.
def send():
#Send variables here
mGui.after(10000, send)
mGui.after(10000, send)

Related

Problem using threading in tkinter GUI interface

I am trying to create a GUI python interface to display the value from sensors which are connected to an arduino via serial communication. For this interface I am using a class named application().
I first created the GUI application using Tkinter which is functional. That is to say that the different elements (frame, widgets, etc.) are displayed correctly.
Then I created a function (get_data), to retrieve the data from the arduino. Each byte I retrieve the value and store it in an array with the associated key (the name of the sensors). Then I retrieve the array for the parse and assign the values ​​in variables (integer or float) so that I can retrieve them in the GUI via the function (update_data).
To avoid having to worry between displaying and looping data, I decided to use the library threading to run the get_data() and update_data() functions continuously in other threads.
when I launch the application, the code loops on get_data() (see the print below), but the Tkinter interface does not launch.
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Here are the two functions to retrieve data:
def get_data(self):
ser = serial.Serial('/dev/cu.usbmodem14201', 9600)
ser.flushInput()
index=0
self.currentDataArray = np.array(np.zeros([index]))
while True:
try:
for c in ser.readline():
current_decoded_bytes = float(ser_bytes[0:len(ser_bytes)-2].decode("utf-8"))
print(current_decoded_bytes)
self.currentDataArray = np.append(self.currentDataArray,current_decoded_bytes)
if c == '\n':
return self.currentDataArray
self.currentDataArray = np.array(np.zeros([index]))
except:
print("Wait, sending data")
pass
def Update_value(self, currentDataArray,update_period):
print("updating data")
new = time.time()
print(self.currentDataArray)
analogValue=float()
VoltageEcSensor=float()
tempWaterSensor=float()
TemperatureB=float()
HumidityB=float()
pHvalue=float()
EcSensorValue=float()
extractorStatement=int()
ligthStatement=int()
intractorStatement=int()
fanStatement=int()
while(1):
currentDataArray.all()
try:
self.analogValue=self.currentDataArray[0]
self.VoltageEcSensor=self.currentDataArray[1]
self.tempWaterSensor=self.currentDataArray[2]
self.TemperatureB=self.currentDataArray[3]
self.HumidityB=self.currentDataArray[4]
self.pHvalue=self.currentDataArray[5]
self.EcSensorValue=self.currentDataArray[6]
self.extractorStatement=self.currentDataArray[7]
self.ligthStatement=self.currentDataArray[8]
self.intractorStatement=self.currentDataArray[9]
self.fanStatement=self.currentDataArray[10]
except:
pass
if time.time() - new >= update_period:
self.analogValue=0
self.VoltageEcSensor=0
self.tempWaterSensor=0
self.TemperatureB=0
self.HumidityB=0
self.pHvalue=0
self.EcSensorValue=0
self.extractorStatement=0
self.ligthStatement=0
self.intractorStatement=0
self.fanStatement=0
new = time.time()
pass
Here is a function to acquire data continuously using other threads:
def threading(self):
multi = threading.Thread(target = self.get_data())
# multi.deamon = True
multi.start()
multi2 = threading.Thread(target = self.Update_value(self.currentDataArray, self.update_period))
# multi2.deamon = True
multi2.start()
This is how I initialize in the tkinter interface:
class Application(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
update_period = 5
tempBox=self.TemperatureB
tempExtBox=23
humidityBox=self.HumidityB
TempSolution=self.tempWaterSensor
pH_solution=self.pHvalue
Ec_solution=self.EcSensorValue
Global_conso=110
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
# set all width and height for each Pane
width_MODEpane=screen_width/3
height_MODEpane=screen_height/4
self.MainWindow(screen_height, screen_height)
if __name__ == "__main__":
app = Application()
app.threading()
app.title("Automate GUI app")
app.mainloop()
I think I'm not using correctly the threading library, do you have any suggestion to solve this issue?

Tkinter GUI updating depending on serial connection

Good afternoon thank you all who look at my issue,
I am building a script to do initial configuration on switches through console port, I can make a connection to the console port the issue comes when building a GUI in TKINTER.
I want the gui to connect through a specified some port when the button is pressed and when the connection is made make a light(using canvas) go green showing a successful connection went through.
My issue is getting the light to change to green and stay when a connection is made I have tried
my initally also tried to use global then realized the window loop constantly set it back to red
nested while loop--- breaks Tkinker
importing a file I created called variableset which stores variable to set green but since it constantly has a new instance it just sets the variable in tinker back to red.
any help would be greatly appreciated
GUI CODE
import tkinter as tk
from tkinter import *
import connect
import variableset
setting = variableset.setting
window = tk.Tk()
window.title("Network Wizard 1")
window.geometry('300x500')
# setting =tk.IntVar()
# setting.set(variableset.setting)
#serial port pick the right one
serialportlabel = tk.Label(text="COM Number")
serialport = tk.Entry(width= 7)
serialportlabel.pack()
serialport.pack()
#color alerting!! red bad
alert = Canvas(window, width=50, height=20)
alert.pack()
if setting == 0:
a=alert.create_rectangle(5, 0, 50, 50, fill='red')
else:
a=alert.create_rectangle(5, 0, 50, 50, fill='green')
# #connection part woop woop
connection = tk.Button(text="connect", command = lambda: bus())
connection.pack()
# #firmwarecheck
# firmwarecheck = tk.Button(text="Firmware check")
# firmwarecheck.pack()
# #firmware update
# firmwareupdate= tk.Button(text="Firmware update")
# firmwareupdate.pack()
# #software update
# software = tk.Button(text="software update")
# connect.pack()
# #vmlans
# vlanupdate = tk.Button(text="Vlanupdate")
# vlanupdate.pack()
# disconnect = tk.Button(text="disconnect")
# disconnect.pack()
# qut = tk.Button(text="quite")
# qut.pack()
def bus():
global ser
ser = connect.connect(serialport.get())
global setting
setting = variableset.initial
# def firmware(ser):
# if ser.isOpen() == true :
window.mainloop()
Connect code
import serial
import time
import sys
#connect
def connect(com):
ser = serial.Serial(
port = com, #COM
baudrate=9600,
parity='N',
stopbits=1,
bytesize=8,
timeout=8
)
ser.isOpen()
print(ser.name)
#set variables
enter = str.encode('\r\n') #enter
user = str.encode('admin#sytem\r\n') #default user name
pwd = str.encode('\r\n') #defualt password
qut = str.encode('quit\r')
time.sleep(1.0)
# ser.inWaiting()
ser.write(enter) #promt login
time.sleep(0.5)
ser.write(user) #enter user name
time.sleep(0.5)
ser.write(pwd) #enter password
time.sleep(0.5)
ser.write(enter)
time.sleep(0.5)
ser.write(enter)
time.sleep(0.5)
ser.write(str.encode("sytem\r\n"))
time.sleep(0.5)
ser.write(qut)
ser.write(qut)
input_read = ser.read(500)
input_read = input_read.decode("utf-8","ignore")
print(input_read)
ser.close()
return ser
def write(ser):
ser.write(str.encode(''+'\r\n'))
def disconnect(ser):
ser.write(str.encode('quit\r\n'))
time.sleep(.2)
ser.write(str.encode('quit\r\n'))
time.sleep(.2)
ser.write(str.encode('quit\r\n'))
time.sleep(.2)
ser.close()
Variableset Code
global setting
setting = 0
def initial():
global setting
setting = 1
return setting
def unset():
global setting
setting = 0
return setting
Any help greatly appreciated
Your if-else statement only runs once, before the setting variable has been set, as far as we can tell. It's not in a loop or a function that gets called from somewhere else. What exactly is the problem you're having with the code that you posted? The issues you described seem to relate to a loop setting it back to red in a different version of the code.
In any event, I suspect that your problem is trying to use infinite loops to keep checking the setting, which prevents the tkinter mainloop() from running, and therefore blocks the GUI from updating. If so, the best method to fix it is probably to change your code for "check the setting and then change the color" into its own function, which gets called for the first time at the end of your connect function. Then, the end of the check setting function should schedule itself to be run after a time delay using the tkinter after() method, which is non-blocking (asnychronous) and allows the mainloop() to keep running. Alternative methods include a separate thread to run the check-setting code, or using the tkinter update() method, but after() is easiest.
Note that your sleep() functions in the connect code will also block the mainloop while they're running. I'm also not clear what you're trying to do with the variableset code; it appears to set the setting to 1 (aka green light) when you call initial(), regardless of the actual status on the serial port.

Threading in python on a raspberry pi issues

I am trying to run a GUI that spawns threads that perform very basic and not computationally complicated tasks on a raspberry pi 1 and I cannot seem to get the threads to work.
I developed the code on a x86 intel computer and it works perfectly well. The threading commands basically just allow for button presses and listening for serial data concurrently.
def extra_thread_disable():
# Disables buttons that would interfere with data that is currently being sent
while threading.active_count() == 3:
run_file_butt.config(state = 'disabled')
run_butt.config(state = 'disabled')
serial_butt.config(state = 'disabled')
popup_butt.config(state = 'disabled')
homing_butt.config(state = 'disabled')
level_butt.config(state = 'disabled')
zero_button1.config(state = 'disabled')
zero_button2.config(state = 'disabled')
zero_button3.config(state = 'disabled')
else:
run_file_butt.config(state = 'normal')
run_butt.config(state = 'normal')
serial_butt.config(state = 'normal')
popup_butt.config(state = 'normal')
homing_butt.config(state = 'normal')
level_butt.config(state = 'normal')
zero_button1.config(state = 'normal')
zero_button2.config(state = 'normal')
zero_button3.config(state = 'normal')
pass
def thread_data():
# Starts a thread to send data while allowing stop button to be pressed
try:
global t2
t2 = threading.Thread(name='send_line', target = send_data, daemon = True)
t_disable = threading.Thread(name='disable', target = extra_thread_disable, daemon = True)
t2.start()
t_disable.start()
except:
update_textbox("Threading Error: data thread not properly created")
def send_data():
# Sends single motion commands and waits for response to continue
global save_path
global motor_param
vals = get_vals()
try:
data = struct.pack("!llllhhhhhhhh", vals['dist1'], vals['dist2'], vals['dist34'], vals['dist34'], vals['speed1'], vals['speed2'], vals['speed34'], vals['speed34'], vals['accel1'], vals['accel2'], vals['accel34'], vals['accel34'])
try:
ser.write(data)
update_textbox("Running...")
except:
update_textbox("Error: Data not sent")
try:
motor1pos = int(ser.readline())
motor2pos = int(ser.readline())
motor3pos = int(ser.readline())
motor4pos = int(ser.readline())
ready = ser.read(1)
update_textbox("Movement complete")
axis1_current.set(str(reverse_convert(motor1pos, 1)))
axis2_current.set(str(reverse_convert(motor2pos, 2)))
axis3_current.set(str(reverse_convert(motor3pos, 3)))
writetofile()
except:
update_textbox("Error: reading data improperly")
except:
update_textbox("Error: data not sent properly")
pass
The code basically just allows the main GUI thread to allow for a stop button to be pressed and disable all the buttons that could interfere with the sent data. That thread then just waits for the response from an arduino it is connected to. Again this all works flawlessly on a normal computer. I get no errors or warnings in the terminal when run on a raspberry pi but it seems to be blocking. I thought maybe it was just such a slow computer or the infamous GIL. It seems like that might be the reason. If so, should I switch to the multiprocessing library in python? is there a way to get around this? It doesn't work when run in terminal calling python3 and it doesn't work when it was compiled to a static binary using pyinstaller.
The answer is to get better hardware. I changed nothing in my code but instead bought a newer raspberry pi 4 instead of the original raspberry pi and the threading worked as it originally did on my other pc running arch.

how to deliver a variable to stringvar in Tkinter and set own bitmap in title?

I am doing my Graduation Project,and i meet some problem.
I have search for a long time and read some documents. But no use.
Please help or try to give some ideas how to achieve this.
I want to achieve a function that get local ip and show it by using label when entering the interface.I don't understand how to deliver a variable from the"get_ip_address" to textvariable.
My os is Linux,python version is 2.7.
Here is some code:
import fcntl,struct,Tkinter,socket
def get_ip_address(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(s.fileno(),
0x8915,struct.pack('256s',
ifname[:15])
)[20:24])
top = Tkinter.Tk()
top.geometry('400x300+200+300')
ip_address = Tkinter.StringVar()
ip_address.set(get_ip_address)
icon = top.iconbitmap('#/root/Downloads/python/sdju.xbm')
top.tk.call('wm','iconphoto',top._w,icon)
lable1 = Tkinter.Label(top,textvariable = ip_address)
lable1.pack()
top.mainloop()
Try the following.
You are using ip_address.get(get_ip_address) getter functions generally take no arguments and any arguments given here will produce an error. What you want to do is set the value. So instead you need to call ip_address.set(get_ip_address).
The next issue is that you don't call get_ip_address you pass a reference to the function. So make sure you call it and the string will be set to it's return value and not a string representation of the function itself. You should pass the name of the network interface, you want the ip address for.
So ip_address.set(get_ip_address) becomes ip_address.set(get_ip_address('lo')).
complete code:
import fcntl, struct, Tkinter, socket
def get_ip_address(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915,struct.pack('256s', ifname[:15]))[20:24])
top = Tkinter.Tk()
top.geometry('400x300+200+300')
ip_address = Tkinter.StringVar()
ip_address.set(get_ip_address('lo'))
icon = Tkinter.PhotoImage(file='/root/Downloads/python/sdju.xbm')
top.tk.call('wm', 'iconphoto', top._w, icon)
lable1 = Tkinter.Label(top, textvariable=ip_address)
lable1.pack()
top.mainloop()

How do I make a client socket receive data from another socket in a non-blocking manner?

I am trying to make my client socket non-blocking. But unsuccessful. I'm not sure how to fix it.
I am trying to build a simple chat box and I am using tkinter for the GUI.
The main part that is to note is the while loop that I have segmented within the code
My client code is:
.
.
.
class GroupFrame(Frame):
def __init__(self, master):
super().__init__(master)
global memList
global rootHome
self.master.geometry("400x500")
self.master.resizable(width=FALSE, height=FALSE)
self.master.title("PowerPuff Chat Girls")
#TextArea
self.ChatLog = Text(self, bd=0, bg="light grey", height="13", width="55", font="Arial")
self.ChatLog.insert(END, 'Welcome to the PowerPuff Chat, ' + username + '\n', 'INIT')
self.ChatLog.config(state=DISABLED)
self.ChatLog.tag_config('INIT', foreground='red', justify=CENTER)
self.ChatLog.tag_config('BLUE', foreground='blue', justify=LEFT)
self.ChatLog.tag_config('BLK', foreground='black', justify=RIGHT)
#ScrollBar
self.scrollbar = Scrollbar(self, command = self.ChatLog.yview, cursor="heart")
self.ChatLog['yscrollcommand'] = self.scrollbar.set
#EntryBox
self.EntryBox = Text(self, bg="white", width="29", height="5", font="Arial")
self.EntryBox.bind("<KeyRelease-Return>", lambda event: sendData(self.EntryBox.get(1.0, END)))
#SendButton
self.SendButton = Button(self, font=30, text="Send", width="11", height=1,
bg="white", fg='navy blue', activebackground="#FACC2E", command=lambda: sendData(self.EntryBox.get(1.0, END)))
#Place them on Screen
self.scrollbar.place(x=380, y=6, height=386)
self.ChatLog.place(x=8, y=6, height=405, width=370)
self.EntryBox.place(x=128, y=425, height=60, width=248)
self.SendButton.place(x=6, y=425, height=60)
def sendData(param):
if param == '\n\n':
self.EntryBox.delete(1.0, END)
return
if len(param) > 1:
if '\n\n' in param:
# strip both the carriage return and appened with only one.
param = param.rstrip('\n')
param = param + '\n'
self.EntryBox.delete(1.0, END)
insertText(1, '>>' + param)
s.sendall(str.encode(param))
# data = s.recv(4500)
def inspectData(data):
if 'joined the chat **\n' in data.decode('utf-8'):
insertText(3, data.decode('utf-8'))
else:
insertText(2, '>>' + data.decode('utf-8'))
self.ChatLog.see(END) # this shows the END of the chatlog; auto scroll down
def insertText(num, param):
self.ChatLog.config(state=NORMAL)
if num == 1:
self.ChatLog.insert(END, param, 'BLK')
if num ==2 :
self.ChatLog.insert(END, param, 'BLUE')
if num ==3:
self.ChatLog.insert(END, param, 'INIT')
self.ChatLog.config(state=DISABLED)
*******************************************************
while True:
try:
data = s.recv(4096)
if(data):
inspectData(data)
except:
break
*******************************************************
page = Tk()
page.geometry("400x500")
page.resizable(width=FALSE, height=FALSE)
pf = HomeFrame(page)
page.mainloop()
With my current code, when I run it the client just freezes when it is currently in the GroupFrame Frame.
I am not sure why this happens. And If i haven't made my question clear please let me know so I can provide some images and parts of code that will help.
There are a few different problems with the code as presented. The main issue is the while loop in the __init__ function. This will make __init__ run forever (assuming the socket is blocking). This, in turn, will block the main loop of Tk, and therefore the whole program.
When working with Tk, one must realize that Tk owns the main loop. This means that one must always allow Tk to run, to dequeue events posted to its internal event queue. If your program needs to do something else, like, for example, listen to a socket, this has to be done, either in a separate thread or to be incorporated in the Tk loop.
Unfortunately there is no general way to make the socket input part of the Tk loop. However, what we can do, is to post a certain event using after that will ensure the main loop will call a predefined function after a certain time. This can be used in order to create a solution, where it is possible to poll the socket at certain times, and check if there are new data to process.
Another way is to run the socket in a separate thread, that posts messages (through a synchronized Queue) to the Tk thread. That might by a more general solution, but it still requires the main loop to poll the queue instead of the socket.
All of this is quite a bit to digest. I suggest that you try to reduce your example further, and then ask questions on smaller examples if there are parts that are still hard to grasp.
I managed to fix my issue. In case anyone else has a similar problem of polling for a data received from another socket within a Tkinter loop. This is how I fixed it:
Create a function that polls through the socket within your Frame and make a separate thread within that Frame that runs it.
Code:
def polling():
while True:
try:
polledData = s.recv(4096)
print(polledData.decode('utf-8'))
except:
pass #or you can print out the exception if you need to.
threading.Thread(target = polling, args=[]).start()

Categories

Resources