Plot Real Time Serial Data using Python - with Arduino - python

I am trying to build a code to make communications between my code and my Arduino Uno. I have a temperature sensor (tmp36) connected to the Arduino board, in the COM3, and I want to plot the temperature values in real time. I also have a start button, to start getting data and plot it in the graph, and a stop button to stop doing that.
When I run my code, I get a message saying: "could not convert string to float: b'Anal'", refering to line 19: " data = np.append(data,float(a[0:4]))". Can you tell me what is wrong? And would you be able to stop any more errors?
Thank You for your time!
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from tkinter import *
import numpy as np
import serial as sr
root = Tk()
root.title("Communications")
root.geometry('1920x1080')
root.resizable(False, False)
root.configure(background='black')
data = np.array([])
cond = False
def plot_data():
global cond, data
if (cond == True):
a = s.readline()
a.decode()
if(len(data) < 100):
data = np.append(data,float(a[0:4]))
else:
data[0:99] = data[1:100]
data[99] = float(a[0:4])
lines.set_xdata(np.arange(0, len(data)))
lines.set_ydata(data)
graph_1.draw()
root.after(1, plot_data)
def plot_start():
global cond
cond = True
s.reset_input_buffer()
def plot_stop():
global cond
cond = False
#graph
fig1 = Figure(facecolor="#090b80", figsize=(16,9), dpi=75)
ax1 = fig1.add_subplot(111)
ax1.set_title('Temperature', color='white', fontsize=14, fontweight="bold")
ax1.set_xlabel('Time, s', color='white', fontsize=10, fontweight="bold")
ax1.set_ylabel('ºC', color='white', fontsize=10, fontweight="bold")
ax1.set_xlim(0, 100)
ax1.set_ylim(0, 50)
ax1.spines['bottom'].set_color('#ffffff')
ax1.spines['top'].set_color('#090b80')
ax1.spines['right'].set_color('#090b80')
ax1.spines['left'].set_color('#ffffff')
ax1.tick_params(axis='x', colors='#ffffff')
ax1.tick_params(axis='y', colors='#ffffff')
lines = ax1.plot([], [], color='white')[0]
ax1.set_facecolor("#090b80")
graph_1 = FigureCanvasTkAgg(fig1, master=root)
graph_1.get_tk_widget().place(x=10, y=10, width=540, height=344)
graph_1.draw()
control_frame = Frame(root, bg="#242729", width=893, height=501)
control_frame.place(x=663, y=467)
root.update()
button_1 = Button(control_frame, text="Start", bg="#1cba26", fg="white", font=('arial', 20,'bold'), padx=101, activebackground="#109419", activeforeground="white", borderwidth=0, relief=RAISED, cursor="hand2", command=lambda:plot_start())
button_1.place(x=10, y=107.5)
root.update()
button_2 = Button(control_frame, text="Stop", bg="#eb0c0c", fg="white", font=('arial', 20,'bold'), padx=101.4, activebackground="#990909", activeforeground="white", borderwidth=0, relief=RAISED, cursor="hand2", command=lambda:plot_stop())
button_2.place(x=304, y=107.5)
#start serial port
s = sr.Serial('COM3', 9600)
s.reset_input_buffer()
root.after(1, plot_data)
root.mainloop()

I think so set a timeout: s = sr.Serial('COM3', 9600, timeout=1), but the main problem is this: a=a.decode(), you don't use a=. And if decode ypu the data, are this string type, but you use list. This problem can you solution:
import ast
i = b'0,1,2,3,4,5,6,7,8,999' # i is a bytes type val
dec=i.decode() #decode this
print(type(dec))
ldat=ast.literal_eval(dec) #convert str to list
print (ldat[3])
print (ldat[9])#here can use more characters data
And you can help the responders, if write an example line of readed data, arduino example and the full error message.
Good luck!
Sorry my english isn't perfect.

Related

pyserial .readline() tries sending data after port was closed - PortNotOpenError

This is my first try at creating an app - I am only a couple months into my programming experience so please have patience ;)
So, I have a temperature logger attached via USB port. It is called Almemo. It sends data in a bytestream every second, separated by ;. I have created a module called 'almemotempmodule.py' which connects the device, gets data and closes it again:
#%% Setup Almemo, use St saving mode
def init():
global ALMEMOPORT
try:
ALMEMOPORT = serial.Serial(
port='COM29',
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1)
#Confirm Almemo connection
except IOError:
print("Almemo connection failed")
print("Almemo connection retry, close port")
ALMEMOPORT.close()
print("Almemo connection retry, re-open port")
ALMEMOPORT.open()
if ALMEMOPORT.isOpen() == True:
print('Almemo connected succesfully')
ALMEMOPORT.flushInput()
else:
print('Error!')
#%% get temperature from Almemo
def al_get_data():
while True:
try:
data = ALMEMOPORT.readline().removeprefix(b'\x03').removesuffix(b'\r\n')
#convert data from bytes to strings
decoded = data.decode(encoding = 'cp1252')
cleaned = decoded.split(sep=';')
#create variables and define data types for measurements of interest
#Date from almemo: alDate = cleaned[0].lstrip('"').rstrip('"')
#alDate = dt.datetime.strptime(alDate, '%d.%m.%y').date()
alDate = dt.date.today()
alTime = cleaned[1].lstrip('"').rstrip('"')
alTime = dt.datetime.strptime(alTime, '%H:%M:%S').time()
alTemp = float(cleaned[4].replace(',', '.'))
print(alDate, alTime, alTemp)
#return alDate, alTime, alTemp
return alTemp
continue
except KeyboardInterrupt:
print('Keyboard Interrupt, closing port...')
time.sleep(2)
ALMEMOPORT.close()
print('Port successfully closed')
break
def al_close_port():
ALMEMOPORT.close()
time.sleep(2)
if ALMEMOPORT.isOpen() == False:
print('Port successfully closed')
else:
print('Port close FAILED')
Here the problem starts: I run the init() command followed by while True: al_get_data() and I get the expected ouptut:
2023-01-24 09:10:15 22.06
2023-01-24 09:10:16 22.06
2023-01-24 09:10:17 22.06
2023-01-24 09:10:18 22.06
2023-01-24 09:10:19 22.06
2023-01-24 09:10:20 22.06
that's nice. Then I stop the execution with Ctrl+C and the code continues correctly:
Keyboard Interrupt, closing port...
Port successfully closed
But then I get the following error:
Traceback (most recent call last):
Cell In[4], line 2al_get_data()
File g:\bu process analytics\pa innovation\merz_chantal\buffers and standards\labor\pyproj standards, buffer zert\certiphy\almemotempmodule.py:58 in al_get_datadata = ALMEMOPORT.readline().removeprefix(b'\x03').removesuffix(b'\r\n')
File ~\Anaconda3\envs\working\Lib\site-packages\serial\serialwin32.py:269 in readraise PortNotOpenError()
PortNotOpenError: Attempting to use a port that is not open
In a second .py script, I also continued and made a GUI with tkinter. There everything works nicely, I can start the measurement and perform live plotting and then stop it again. But there I think the same problem rises, when I click on my Stop button.
Here is the GUI script: The part that ends the measurement is defined as STOPMEASUREMENT():
#%% Import Libraries
from tkinter import Tk, Label, Frame, Entry, Button, StringVar, sys, Text, Scrollbar
from tkinter import *
from tkinter import ttk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
# Implement the default Matplotlib key bindings.
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
#stops showing extra matplotlib window
plt.ioff()
#import tempplotterlive
import almemotempmodule
import numpy as np
import matplotlib.animation as animation
import datetime as dt
import time
#%% Setup basic frameworks
ROOT = Tk()
ROOT.title('Hamilton pH Measurement App')
ROOT['bg'] = '#E6ECF2'
# Variables
Experimentdate = StringVar()
# plot
fig1 = Figure(figsize=(5,3))
fig1, ax = plt.subplots(1,1)
# lists for data
xs = []
ys = []
# Create Labels
DATEMESSAGE = Label(ROOT, text='''Date:''',
font='Raleway',
bg='#E6ECF2',
justify='right')
ENTERDATEFIELD = Entry(ROOT, width=20,
font=('Raleway', 12,'italic'),
textvariable=Experimentdate)
LIVEPLOTTITLE = Label(ROOT,
text="Live Plot",
font=('Raleway', 15, 'bold'),
padx=20,
bg='#E6ECF2')
LOGBOX = Label(ROOT, text='Log entries',
font=('Raleway', 12, 'italic'),
bg='#E6ECF2',
fg='grey')
STARTMESSAGE = Label(ROOT, text='''To start measurement, enter date and click GO''',
font=('Raleway', 15, 'bold'),
bg='#E6ECF2',
justify='center')
STOPMEAS = Label(ROOT,
text="End Measurement",
font='Raleway',
bg='#E6ECF2')
QUITAPP = Label(ROOT,
text="Quit App",
font='Raleway',
bg='#E6ECF2',
justify='right')
# Define functions
# Date entry, start connection with Almemo
def DATEENTRY():
DATEENTRY = Label(ROOT,
text='''Live plotting of Almemo Temperature
today's date is: ''' + ENTERDATEFIELD.get(),
font='Raleway',
bg='#E6ECF2',
justify='center')
DATEENTRY.grid(column=0, row=5, columnspan=3)
#initialize communication with Almemo
almemotempmodule.init()
# Stop measurement function:
def STOPMEASUREMENT():
almemotempmodule.al_close_port()
# Quit programm:
def QUIT():
ROOT.quit()
ROOT.destroy()
# Print statements from function in GUI
def DECORATOR(func):
def INNER(inputStr):
try:
TEXTBOX.insert('1.0', inputStr)
return func(inputStr)
except:
return func(inputStr)
return INNER
# Define live plot function
def ANIMATE(i, xs, ys):
almemotempmodule.al_get_data()
temp_c = almemotempmodule.al_get_data()
# Add x and y to lists
xs.append(dt.datetime.now().strftime('%H:%M:%S'))
ys.append(temp_c)
ax.clear() #clears previous plot so a new one can be drawn
# Draw directly in the axes to get updated plot from each reading
ax.plot(xs, ys)
# format plot
ax.xaxis.set_major_locator(plt.MaxNLocator(15))
plt.xticks(rotation=45, ha='right')
plt.subplots_adjust(bottom=0.30)
plt.title('Almemo Temperature over Time')
plt.ylabel('Temperature [°C]')
SCROLLBAR1 = Scrollbar(ROOT)
def SHOW():
global Ani
Ani = animation.FuncAnimation(fig1,
ANIMATE,
fargs=(xs, ys),
interval = 1000)
plt.show()
#---------------------------------------------------------------------------
# Print on screen
# Startmessage
STARTMESSAGE.grid(column=0,
row=0,
columnspan=2)
# Enter experiment date
DATEMESSAGE.grid(column=0,
row=1)
ENTERDATEFIELD.grid(column=1,
row=1)
ENTERDATEFIELD.insert(0, 'enter date')
Button(ROOT,
text="Enter",
padx=20,
pady=5,
font='Raleway',
fg='dark blue',
bg='white',
command=DATEENTRY).grid(column=2,
row=1)
# Stop measurement
STOPMEAS.grid(column=0, row=2)
Button(ROOT,
text="Stop",
command=STOPMEASUREMENT,
padx=20,
pady=5,
font='Raleway',
fg='dark blue',
bg='#D78F9A').grid(column=1,
row=2)
# Quit
QUITAPP.grid(column=0, row=7)
Button(ROOT,
text="Quit",
command=QUIT,
padx=20,
pady=5,
font='Raleway',
fg='dark blue',
bg='#D78F9A').grid(column=1,
row=7)
# GO measurmeent
Button(ROOT,
text="GO",
padx=20,
pady=5,
font='Raleway',
fg='dark blue',
bg='#C0DEBA',
command=SHOW).grid(column=3,
row=1,
pady=10)
# Plot area
LIVEPLOTTITLE.grid(column=0,
row=4,
columnspan=3)
PLOTCANVAS = FigureCanvasTkAgg(fig1, ROOT)
PLOTCANVAS.draw()
PLOTCANVAS.get_tk_widget().grid(column=0,
row=6,
columnspan=3)
# Output box for print statements
LOGBOX.grid(column=0,
row=9)
TEXTBOX = Text(ROOT,
height=10,
width=50,
pady=5)
SCROLLBAR1.grid(sticky='ns')
SCROLLBAR1.config(command=TEXTBOX.yview)
TEXTBOX.config(yscrollcommand=SCROLLBAR1.set)
TEXTBOX.grid(column=0,
row=10,
columnspan=3)
sys.stdout.write = DECORATOR(sys.stdout.write)
# display
ROOT.mainloop()
And here the error: (and the last measurement I got)
2023-01-24 09:23:49 22.05
Port successfully closed
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\CMerz\Anaconda3\envs\working\Lib\tkinter\__init__.py", line 1948, in __call__
return self.func(*args)
^^^^^^^^^^^^^^^^
File "C:\Users\CMerz\Anaconda3\envs\working\Lib\tkinter\__init__.py", line 861, in callit
func(*args)
File "C:\Users\CMerz\Anaconda3\envs\working\Lib\site-packages\matplotlib\backends\_backend_tk.py", line 141, in _on_timer
super()._on_timer()
File "C:\Users\CMerz\Anaconda3\envs\working\Lib\site-packages\matplotlib\backend_bases.py", line 1193, in _on_timer
ret = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\CMerz\Anaconda3\envs\working\Lib\site-packages\matplotlib\animation.py", line 1405, in _step
still_going = super()._step(*args)
^^^^^^^^^^^^^^^^^^^^
File "C:\Users\CMerz\Anaconda3\envs\working\Lib\site-packages\matplotlib\animation.py", line 1098, in _step
self._draw_next_frame(framedata, self._blit)
File "C:\Users\CMerz\Anaconda3\envs\working\Lib\site-packages\matplotlib\animation.py", line 1117, in _draw_next_frame
self._draw_frame(framedata)
File "C:\Users\CMerz\Anaconda3\envs\working\Lib\site-packages\matplotlib\animation.py", line 1744, in _draw_frame
self._drawn_artists = self._func(framedata, *self._args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "g:\bu process analytics\pa innovation\merz_chantal\buffers and standards\labor\pyproj standards, buffer zert\certiphy\gui.py", line 109, in ANIMATE
almemotempmodule.al_get_data()
File "G:\BU Process Analytics\PA Innovation\Merz_Chantal\Buffers and Standards\Labor\PyProj Standards, Buffer Zert\CertipHy\almemotempmodule.py", line 58, in al_get_data
data = ALMEMOPORT.readline().removeprefix(b'\x03').removesuffix(b'\r\n')
^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\CMerz\Anaconda3\envs\working\Lib\site-packages\serial\serialwin32.py", line 269, in read
raise PortNotOpenError()
serial.serialutil.PortNotOpenError: Attempting to use a port that is not open
So why does the data calling function continue one more time after the port was closed?

Stop a tkinter GUI

I'm taking some real time data through sockets and segregating and plotting a parameter in tkinter GUI. It works fine. I'm threading it so I can create a stop button in parallel to break from an infinite while loop, but it's not working. The GUI opens and as soon as I hit start, it closes. It doesn't even function. How do I manage to create a stop button in such a case where anything else is not hindered?
import socket
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import tkinter as tk
from tkinter import *
from random import randint
from matplotlib.animation import FuncAnimation
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import time
import threading
import datetime as dt
IP = "172.16.22.113"
PORT = 30000
addr = (IP,PORT)
soc = socket.socket()
print("Socket created")
soc.connect(addr)
stop = 0
def function():
if Checkbutton1.get():
Check1 = True
else:
Check1 = False
if Checkbutton2.get():
Check2 = True
else:
Check2 = False
data_points(Check1, Check2)
def start_thread():
global stop
stop=0
t1 = threading.Thread(target=function)
t1.start()
def stop():
global stop
stop=1
def data_points(Check1, Check2):
first_step=0
df=pd.DataFrame(columns=['Chan','Doppler','Code','C_N0','SNR','Track','Stat','Config','X'])
while True:
data = soc.recv(4096).decode()
Chan = []
Doppler = []
Code = []
C_N0 = []
SNR = []
Track = []
Stat = []
Config = []
X_axis = []
xx = []
a = data.split_lines()
for line in a:
if (line=="BEGIN, AUTO CHANSTATE"):
i = a.index(line)
time=i+2
x_as = a[time].split[3]
t=i+3
for p in range(28):
t+=1
Chan.append(int(a[t].split()[1]))
Doppler.append(float(a[t].split()[2]))
Code.append(float(a[t].split()[3]))
C_N0.append(float(a[t].split()[4]))
Track.append(float(a[t].split()[5]))
Stat.append(a[t].split()[6])
Config.append(a[t].split()[7])
xx.append(first_step)
df = pd.DataFrame(list(zip(Chan,Doppler,Code,C_N0,SNR,Track,Stat,Config,xx)), columns=['Chan','Doppler','Code','C_N0','SNR','Track','Stat','Config','X'])
Chan_2 = df[df["Chan"]==2]
first_step=first_step+1
if(Check1==True):
ax1=fig.add_subplot(321)
ax1.cla()
ax1.grid()
ax1=set_xlabel("Time")
ax1=set_ylabel("C_N0")
ax1.plot(Chan_2["X"], Chan_2["C_N0"], marker='o', color='orange')
graph.draw()
if(Check2==True):
ax2=fig.add_subplot(322)
ax2.cla()
ax2.grid()
ax2=set_xlabel("Time")
ax2=set_ylabel("Dopp")
ax2.plot(Chan_2["x"], Chan_2["Doppler"], marker='o', color='orange')
graph.draw()
if stop==1:
break
new_window=' '
def openwindow():
global new_window
global Checkbutton1
global Checkbutton2
global fig
global graph
global start
new_window=Toplevel(root)
new_window.geometry(1000x700)
lbl=Label(new_window, text="Window corresponds to channel")
fig=Figure()
graph = FigureCanvasTkAgg(fig, master=root)
graph.get_tk_widget().pack(side="top",fill='both',expand=True)
C_N0_plot=Checkbutton(new_window, text="C_N0 vs time", variable=Checkbutton1, onvalue=1, offvalue=0, height=2)
C_N0_plot.place(x=700,100)
Dopp_plot=Checkbutton(new_window, text="Doppler vs time", variable=Checkbutton2, onvalue=1, offvalue=0, height=2)
Dopp_plot.place(x=700,100)
start = Button(new_window, text="Start", command=start_thread, bg="red", fg="white")
start.pack()
stop = Button(new_window, text="Stop", command=stop)
stop.pack()
root = Tk()
root.config(background='white')
root.geometry.("500x500")
btn=Button(root,text="Open new window", command=openwindow)
btn.pack()
root.mainloop()

Make interactive graph of count history in tkinter

I have the following code for a counter with two buttons, one to increase the count and the other to decrease it. The count is a label containing a number. The graph that appears with the buttons is supposed to visualise the history of the counted number ie. x axis is the index number of results_table and the y axis is the number that appears in the count. The buttons and the count work but the graph doesn't show with the following code. There is obviously something I am missing to get the graph to update. Here's the code:
import tkinter
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
class App:
def __init__(self, master):
# Create a container
frame = tkinter.Frame(master)
# Create 2 buttons
self.button_left = tkinter.Button(frame,text="-", command=self.decrease, bg = 'red', fg = 'white')
self.button_left.pack(side="left")
self.button_right = tkinter.Button(frame,text="+", command=self.increase, bg = 'green', fg = 'white')
self.button_right.pack(side="right")
self.label_value = tk.Label(frame, text = '0')
self.label_value.pack(side = "bottom")
fig = Figure()
ax = fig.add_subplot(111)
self.line, = ax.plot(0)
self.canvas = FigureCanvasTkAgg(fig,master=master)
self.canvas.draw()
self.canvas.get_tk_widget().pack(side='top', fill='both', expand=1)
frame.pack()
result_table = []
def decrease(self):
value = int(self.label_value["text"])
self.label_value["text"] = f"{value - 1}"
result_table.append(self.label_value['text'])
x, y = self.line.get_data('result_table')
self.canvas.draw()
def increase(self):
value = int(self.label_value["text"])
self.label_value["text"] = f"{value + 1}"
result_table.append(self.label_value['text'])
x, y = self.line.get_data('result_table')
self.canvas.draw()
root = tkinter.Tk()
app = App(root)
root.mainloop()
Any help graetly appreciated.
Matt
so I solved your problem, but it is not a straight forward answer/solution.
First of all you designed your GUI quiet good! I just wouldn't use pack() but I prefer grid().
Next, I had to delete your class, because I never used tkinter with a class. Maybe you will be able to put it back in.
So what I did:
As already mentioned by Matiiss, you don't really use your x and y values to plot your figure. Your result_table on the other hand just works fine and stores all values created!
So you have to plot them as y values and your x values are basically dependent on the length of your result_table len(result_table). For this part I used numpy to always generate an array of the appropriate length.
Furthermore, I created an extra container fig_frame inside the root, where I always display the figure.
This code worked for me:
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import numpy as np
def decrease():
value = int(label_value["text"])
label_value["text"] = f"{value - 1}"
result_table.append(label_value['text'])
x = np.arange(len(result_table))
y = result_table
create_fig(x, y)
def increase():
value = int(label_value["text"])
label_value["text"] = f"{value + 1}"
result_table.append(label_value['text'])
x = np.arange(len(result_table))
y = result_table
create_fig(x, y)
def create_fig(x, y):
fig = Figure()
ax = fig.add_subplot(111)
line, = ax.plot(x, y)
canvas = FigureCanvasTkAgg(fig, fig_frame)
canvas.draw()
canvas.get_tk_widget().grid(row=0, column=0)
root = tk.Tk()
# Create a container
frame = tk.Frame(root)
fig_frame = tk.Canvas(root, height=650, width=650, borderwidth=1, relief='ridge')
fig_frame.pack()
# Create 2 buttons
button_left = tk.Button(frame, text="-", command=decrease, bg='red', fg='white')
button_left.pack(side="left")
button_right = tk.Button(frame, text="+", command=increase, bg='green', fg='white')
button_right.pack(side="right")
label_value = tk.Label(frame, text='0')
label_value.pack(side="bottom")
fig = Figure()
ax = fig.add_subplot(111)
line, = ax.plot(0)
canvas = FigureCanvasTkAgg(fig, fig_frame)
canvas.draw()
canvas.get_tk_widget().grid(row=0, column=0)
frame.pack()
result_table = []
root.mainloop()
Keep going like this! You really already did a great work and even if my code looks different to your code, most parts are just rearranged!

Tkinter Label not being updated with textvariable

In my code I see that the Label is not being updated with the 'textvariable', despite I believe I'm doing it right (probably not!).
varmuTemperature = StringVar(value="default value")
self.Label = Label(Frame2, textvariable = varmuTemperature)
self.Label.pack()
This should show a label with "default value" written on it. The problem is that I don't see anything written.
I have my code posted here.
import matplotlib
import matplotlib.artist as artists
import matplotlib.pyplot as plt
#import matplotlib.mlab as mlab
import scipy.stats
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,
NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
import numpy as np
import statistics
from tkinter import *
from tkinter import ttk
import serial
import time
import itertools
integer=0
xList = []
humidityList = []
humidityListHistogram = []
temperatureList = []
temperatureListHistogram = []
cnt=0
if sys.platform.startswith('win'):
ports = ['COM%s' % (i + 1) for i in range(256)]
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
# this excludes your current terminal "/dev/tty"
ports = glob.glob('/dev/tty[A-Za-z]*')
elif sys.platform.startswith('darwin'):
ports = glob.glob('/dev/tty.*')
else:
raise EnvironmentError('Unsupported platform')
ser = serial.Serial()
style.use("seaborn-whitegrid")
#varmuTemperature = StringVar()
#varmuHumidity = StringVar()
f=plt.figure(0, figsize=(20,10))
humidityGraph = plt.subplot(224)
humidityGraph.set_title("Humidity vs Time")
humidityGraph.set_ylabel("Humidity RAW (Dec)")
humidityGraph.set_xlabel("Sample ()")
temperatureGraph = plt.subplot(223)
temperatureGraph.set_title("Temperature vs Time")
temperatureGraph.set_ylabel("Temperature RAW (Dec)")
temperatureGraph.set_xlabel("Sample ()")
humidityGraphHistogram = plt.subplot(222)
temperatureGraphHistogram = plt.subplot(221)
temperatureGraphHistogramNormal = temperatureGraphHistogram.twinx()
humidityGraphHistogramNormal = humidityGraphHistogram.twinx()
side_text = plt.figtext(0.93, 0.5, 'Text 1'+'\n'+'Text 2', bbox=dict(facecolor='white'))
plt.subplots_adjust(left = 0.05, right = 0.95, bottom = 0.05, top = 0.95, wspace = 0.16, hspace = 0.21)
class make_window():
def __init__(self, *args, **kwargs):
win = Tk()
win.title("Test")
win.state("zoomed")
Frame1 = Frame(win)
Frame1.pack()
self.comboBoxAvailableCOMPort = ttk.Combobox(Frame1, width = 30)
self.comboBoxAvailableCOMPort['values'] = []
self.comboBoxAvailableCOMPort.pack(padx=5, pady=5, side = LEFT)
self.buttonCheckComAvailable = Button(Frame1, text="Check COM Available", command = self.CheckComAvailable)
self.buttonCheckComAvailable.pack(padx=5, pady=10, side = LEFT)
self.buttonOpenCOMPort = Button(Frame1, text="Open COM Port", command = self.OnOpenCom)
self.buttonOpenCOMPort.pack(padx=5, pady=10, side = LEFT)
self.buttonCloseCOMPort = Button(Frame1, text="Close COM Port" , command = self.OnCloseCOM)
self.buttonCloseCOMPort.pack(padx=5, pady=10,side = LEFT)
self.CheckComAvailable()
Frame2 = Frame(win, highlightbackground = "red", highlightcolor = "red", highlightthickness = 1)
Frame2.pack()
varmuTemperature = StringVar(value="default value")
varmuTemperature.set("trerta")
print(varmuTemperature.get())
self.Label = Label(Frame2, textvariable = varmuTemperature)
self.Label.pack()
self.buttonCloseProgram = Button(Frame2, text="Close Program", command = self.OnCloseProgram)
self.buttonCloseProgram.pack(expand=True, fill='x', anchor='s')
Frame3 = Frame(win)
Frame3.pack()
canvas = FigureCanvasTkAgg(f, Frame3)
canvas.get_tk_widget().pack(padx=5, pady=10, side=BOTTOM, expand = True)
toolbar = NavigationToolbar2Tk(canvas, Frame3)
toolbar.update()
canvas._tkcanvas.pack(padx=5, pady=10,side = TOP)
def CheckComAvailable(self):
self.comboBoxAvailableCOMPort['values'] =[]
result = []
for port in ports:
try:
s = serial.Serial(port)
s.close()
result.append(port)
except (OSError, serial.SerialException):
pass
self.comboBoxAvailableCOMPort['values'] += tuple(result)
self.comboBoxAvailableCOMPort.set(result[0])
def OnOpenCom(self):
ser.baudrate = 115200
ser.port = self.comboBoxAvailableCOMPort.get()
try:
ser.open()
ser.readline()
ser.write("#dut,0$\n".encode('utf-8'))
ser.readline()
ser.write("#v,1800,1800$\n".encode('utf-8'))
ser.write("#W,77,08,07$\n".encode('utf-8'))
ser.readline()
except(OSError):
print("COM Port in use")
def OnCloseCOM(self):
global xList
global humidityList
global temperatureList
global humidityListHistogram
global temperatureListHistogram
global integer
integer=0
xList = []
humidityList = []
temperatureList = []
ser.close()
def OnCloseProgram(self):
self.OnCloseCOM()
exit()
## def toggle_geom(self,event):
## geom=self.master.winfo_geometry()
## print(geom,self._geom)
## self.master.geometry(self._geom)
## self._geom=geom
def animate(i):
global integer
global cnt
try:
ser.write(("#R,77,00,03$" + chr(10)).encode('utf-8'))
humidityLine=ser.readline()
inthumidityLine= int(humidityLine,16)
if (inthumidityLine > 8388608):
inthumidityLine = inthumidityLine - 16777216
humidityList.append(inthumidityLine)
humidityListHistogram.append(inthumidityLine)
ser.write(("#R,77,03,03$" + chr(10)).encode('utf-8'))
temperatureLine=ser.readline()
LineHistogram = temperatureLine
inttemperatureLine= int(temperatureLine,16)
if (inttemperatureLine > 8388608):
inttemperatureLine = inttemperatureLine - 16777216
temperatureList.append(inttemperatureLine)
temperatureListHistogram.append(inttemperatureLine)
xList.append(integer)
integer+=1
##################################################################################################################
## Creates the HUMIDITY Graphics
##################################################################################################################
humidityGraph.clear()
humidityGraph.plot(xList,humidityList,'-b*', label = "Humidity RAW")
humidityGraph.legend(loc='upper right', fancybox = True, frameon = True, shadow = True)
humidityGraph.set_title("Humidity vs Time")
humidityGraph.set_ylabel("Humidity RAW (Dec)")
humidityGraph.set_xlabel("Sample ()")
muHumidity = statistics.mean(humidityListHistogram)
#print("Mean = " + str(muHumidity) + " ; N = " + str(len(humidityListHistogram)))
#global varmuHumidity
#varmuHumidity.set("Humidity: ")
if (len(humidityListHistogram) > 1):
sigmaHumidity = statistics.pstdev(humidityListHistogram)
else:
sigmaHumidity = 100
humidityGraphHistogram.clear()
nHumidity, binsHumidity, patchesHumidity = humidityGraphHistogram.hist(humidityListHistogram, 100, density=False, facecolor='blue', alpha=0.75, histtype = 'stepfilled')
normalDistHumidity = scipy.stats.norm.pdf(binsHumidity, muHumidity, sigmaHumidity)
humidityGraphHistogramNormal.clear()
humidityGraphHistogramNormal.plot(binsHumidity, normalDistHumidity, 'r--')
humidityGraphHistogram.set_title("Histogram for Humidity Data")
humidityGraphHistogram.set_ylabel("Humidity RAW Counts (Dec)")
humidityGraphHistogram.set_xlabel("BINS ()")
humidityGraphHistogramNormal.set_ylabel("Normal Distribution")
##################################################################################################################
## Creates the TEMPERATURE Graphics
##################################################################################################################
temperatureGraph.clear()
temperatureGraph.plot(xList,temperatureList,'-r*', label = "Temperature RAW")
temperatureGraph.legend(loc='upper right', fancybox = True, frameon = True, shadow = True)
temperatureGraph.set_title("Temperature vs Time")
temperatureGraph.set_ylabel("Temperature RAW (Dec)")
temperatureGraph.set_xlabel("Sample ()")
muTemperature = statistics.mean(temperatureListHistogram)
#global varmuTemperature
#varmuTemperature.set("Temperature: " )
if (len(temperatureList) > 1):
sigmaTemperature = statistics.pstdev(temperatureListHistogram)
else:
sigmaTemperature = 100
temperatureGraphHistogram.clear()
nTemperature, binsTemperature, patchesTemperature = temperatureGraphHistogram.hist(temperatureListHistogram, 100, density=False, facecolor='red', alpha=0.75, histtype = 'stepfilled')
normalDistTemperature = scipy.stats.norm.pdf(binsTemperature, muTemperature, sigmaTemperature)
temperatureGraphHistogramNormal.clear()
temperatureGraphHistogramNormal.plot(binsTemperature, normalDistTemperature, 'b--')
temperatureGraphHistogram.set_title("Histogram for Temperature Data")
temperatureGraphHistogram.set_ylabel("Temperature RAW Counts (Dec)")
temperatureGraphHistogram.set_xlabel("BINS ()")
temperatureGraphHistogramNormal.set_ylabel("Normal Distribution")
if (cnt > 100):
xList.pop(0)
humidityList.pop(0)
temperatureList.pop(0)
cnt+=1
except(OSError):
bla=0
win = make_window()
ani = animation.FuncAnimation(f, animate, interval = 300)
make_window.mainloop()
Debugging a bit and start commenting lines of code, I see that the problem may come from the
f=plt.figure(0, figsize=(20,10))
Commenting this line (and all dependencies of this) makes the Label to be written.
Can someone help here, please? I don't get why the graphics can interfere in the Label.
Thanks a lot.
The general problem seems to be your management of instance variables and objects. You keep around as self.* variables, things you'll never need to reference again, like buttonCheckComAvailable, but fail to make self.* variables for things, like varmuTemperature, that you will need to reference later.
Object-wise, you do things that don't make sense:
make_window.mainloop()
as make_window is an object class, not an instance, and an instance of the class make_window won't respond to mainloop anyway as it contains a window but isn't one itself.
Here's my MCVE for your example code that makes varmuTemperature an instance variable and, just for demonstration purposes, sets it when the various buttons on the interface are clicked so you can see it's working:
from tkinter import *
from tkinter import ttk
class make_window():
def __init__(self):
self.win = Tk()
self.win.title("Test")
self.win.state("zoomed")
Frame1 = Frame(self.win)
self.comboBoxAvailableCOMPort = ttk.Combobox(Frame1, width=30)
self.comboBoxAvailableCOMPort['values'] = []
self.comboBoxAvailableCOMPort.pack(padx=5, pady=5, side=LEFT)
Button(Frame1, text="Check COM Available", command=self.CheckComAvailable).pack(padx=5, pady=10, side=LEFT)
Button(Frame1, text="Open COM Port", command=self.OnOpenCom).pack(padx=5, pady=10, side=LEFT)
Button(Frame1, text="Close COM Port", command=self.OnCloseCom).pack(padx=5, pady=10, side=LEFT)
Frame1.pack()
Frame2 = Frame(self.win, highlightbackground="red", highlightcolor="red", highlightthickness=1)
self.varmuTemperature = StringVar(value="default value")
Label(Frame2, textvariable=self.varmuTemperature).pack()
Button(Frame2, text="Close Program", command=self.OnCloseProgram).pack(expand=True, fill='x', anchor='s')
Frame2.pack()
def CheckComAvailable(self):
self.varmuTemperature.set("CheckCom")
def OnOpenCom(self):
self.varmuTemperature.set("OpenCom")
def OnCloseCom(self):
self.varmuTemperature.set("CloseCom")
def OnCloseProgram(self):
self.OnCloseCom()
exit()
window = make_window()
window.win.mainloop()

Regarding Tkinter() Based GUI for Arduino

I'm trying to write a python GUI for an arduino, the idea is that a computer will be communicating through serial port with the arduino.
I figured the arduino code itself was too complex to try controlling the thing directly from like matlab, so instead I'd like to control it by sending commands through serial. In addition to that I'm trying to live plot some data.
I'm having a hard time getting the live plot to work without having hard-coded the connection port into the script.
def connectArduino():
global e1
serial_port = e1.get()
baud_rate = 9600
path = "data.txt"
ser = serial.Serial(serial_port, baud_rate, timeout=0, writeTimeout=0)
def setTunings():
global entries
kp = entries[0][1].get()
ki = entries[1][1].get()
kd = entries[2][1].get()
print(kp + ki + kd)
def animate(i):
ser.reset_input_buffer()
data = ser.readline()
data_array = data.split('\t')
xvalue = float(data_array[0])/1000
yvalue = float(data_array[1])
yar.append(yvalue)
xar.append(xvalue)
line.set_data(xar, yar)
ax1.set_xlim(0, i+1)
print(data_array[0] + '\t' + data_array[1])
import serial
from Tkinter import *
from matplotlib import pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
root = Tk()
#root.geometry('1200x700+200+100')
root.title('Temperature Control')
root.config(background='#fafafa')
frame = Frame(root)
frame.pack(side=LEFT)
l1 = Label(frame, text = "Connection Port:", anchor ='w')
l1.pack()
e1 = Entry(frame)
e1.pack()
b1 = Button(frame, text = "Connect", command = connectArduino)
b1.pack()
fields = 'Kp', 'Ki', 'Kd'
entries = []
for field in fields:
row = Frame(frame)
lab = Label(row, width=15, text=field, anchor='w')
ent = Entry(row)
row.pack(side=TOP, fill=X, padx=5, pady=5)
lab.pack(side=LEFT)
ent.pack(side=RIGHT, expand=YES, fill=X)
entries.append((field, ent))
b2 = Button(frame, text = 'Set Tunings')
b2.pack()
b3 = Button(frame, text = 'Start')
b3.pack(side=LEFT, padx=5, pady=5)
#b2.pack(side = LEFT, padx=5, pady=5)
b4 = Button(frame, text = 'Stop')
b4.pack(side=RIGHT, padx=5, pady=5)
#b3.pack(side = LEFT, padx=5, pady=5)
xar = []
yar = []
style.use('ggplot')
fig = plt.figure(figsize=(8, 8), dpi=100)
ax1 = fig.add_subplot(1, 1, 1)
ax1.set_ylim(0, 35)
line, = ax1.plot(xar, yar, 'r', marker='o')
plotcanvas = FigureCanvasTkAgg(fig, root)
plotcanvas.get_tk_widget().pack(side = RIGHT)
ani = animation.FuncAnimation(fig, animate, interval=100, blit=False)
root.mainloop()
I must admit I'm a lot more used to C and cpp than I am python so a lot of this I've gui stuff I've been reading about has been going over my head a little bit.
TLDR: Trying to write/read from serial prompt as well as displaying some data in a live plot all with a GUI and button/text entry user input.
From your description in a comment:
The error is that animate is calling the "ser" object without it being defined…
Right, ser doesn't exist as a global variable, because the only place you define it is as a local variable, inside connectArduino:
def connectArduino():
global e1
serial_port = e1.get()
baud_rate = 9600
path = "data.txt"
ser = serial.Serial(serial_port, baud_rate, timeout=0, writeTimeout=0)
The solution is simple: just as you added global e1 to make e1 a global variable rather than a local one, you need to add global ser to make ser a global variable rather than a local one.
Then, after you call connectArduino, ser will be defined, so animate will be able to use it.

Categories