Hello stackoverflow members,
I have built a script in Python that gathers temperature data from three thermocouples with an arduino and plots the data in real-time using FuncAnimation. However, I am having trouble saving the data to a csv file. It seems as if Python runs through the entire script before starting the animation, causing the csv file to be empty, as no data has been recorded when it was called.
My question is, how can I call the FuncAnimation function such that I can
- Initialize Arduino connection
- Gather data and plot it in real-time using a user-inputted duration
- Save that data to a csv after the animation closes
- Perform later functions, such as turning LEDs on and off
My current code has been provided below:
import serial
import time
import matplotlib.pyplot as plt
import datetime as dt
import matplotlib.animation as animation
from matplotlib.ticker import MaxNLocator
import csv
# Accept user inputs
title = input("Please enter the trial title: ")
duration = float(input("Next, please enter trial duration in seconds: " ))
date = dt.datetime.now()
today_date = date.strftime("%Y-%m-%d %H:%M:%S")
# Make sure the 'COM#' is set according the Windows Device Manager
ser = serial.Serial('COM3', 115200, timeout=1)
time.sleep(2)
input("Finally, press 'Enter' to begin the trial")
# Create a figure for plotting
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
t = []
T1 = []
T2 = []
T3 = []
time_resolution = 100 # miliseconds
# Duration while-loop and CSV file generation
def update_time():
f = 0
elapsed = 0
time_start = time.time()
while elapsed < duration:
time_now = time.time()
elapsed = time_now - time_start
f += 1
yield f
ani.pause()
ser.close()
# This function is called periodically from FuncAnimation
def animate(i, t, T1, T2, T3):
#Converts byte data to float number for temperature
line = ser.readline()
string = line.decode()
stripped_string = string.strip()
split_string = stripped_string.split()
temps = [float(T) for T in split_string]
temp1 = temps[0]
temp2 = temps[1]
temp3 = temps[2]
# Add x and y to lists
time_current = time.time()
time_passed = time_current - time_start_run
t.append(time_passed)
T1.append(temp1)
T2.append(temp2)
T3.append(temp3)
# Draw x and y lists
ax.clear()
ax.plot(t, T1)
ax.plot(t, T2)
ax.plot(t, T3)
# Format plot
plt.xlim([min(t), max(t)])
ax.xaxis.set_major_locator(MaxNLocator(10))
plt.xticks(rotation=45, ha='right')
plt.subplots_adjust(bottom=0.30)
plt.title('Temperature Data')
plt.ylabel('Temperature (C)')
plt.xlabel('Trial Time (s)')
ax.legend(['T1', 'T2', 'T3'], loc="lower right")
# Set up plot to call animate() function periodically
time_start_run = time.time()
ani = animation.FuncAnimation(fig, animate, fargs=(t, T1, T2, T3), frames = update_time, interval = time_resolution, repeat = True)
ani.running = True
plt.show()
#Thanks you for your time
print("\nThank you, enjoy your data!")
# Saves gathered data as csv file
with open(title + '.csv', mode='w', newline='') as trial_run_data:
header = ["Trial Time (s)","T1 (C)", "T2 (C)", "T3 (C)", today_date]
trial_writer = csv.writer(trial_run_data, delimiter=',', quotechar='"', quoting=csv.QUOTE_NONNUMERIC)
trial_writer.writerow(header)
rcount = 0
for row in t:
trial_writer.writerow((t[rcount], T1[rcount], T2[rcount], T3[rcount]))
rcount = rcount +1
trial_run_data.close()