print('Mini Fluorimeter Program is Running') """ Written by Jackson Coole (jbc7@rice.edu) for the article "Open-Source Miniature Fluorimeter to Monitor Real-Time Isothermal Nucleic Acid Amplification Reactions in Resource-Limited Settings" submitted to Journal of Visualized Experiments. Date: December 16th, 2020 """ #----- Import Libraries---- import serial import csv import time from datetime import datetime import numpy as np import tkinter as tk import matplotlib import matplotlib.pyplot as plt import matplotlib.animation as animation from matplotlib.figure import Figure from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import string import pandas as pd import sys from tkinter import * import tkinter.font as font #---- User Inputs ------- measurement_time = int(1) # Amount of time program will log measurements outputFilepath = '20201216_LAMP_Test.xlsx' # Name of output data file serialPort = 'COM4' #----- global variables------ start_time = datetime.now() # Gets the current time PD1_data = np.array([]) PD2_data = np.array([]) Time = np.array([]) cond = True status = 'go' #------ plot data functions----- def plot_data(): global cond, PD1_data, PD2_data, Time, start_time if ser.in_waiting > 0 and status != 'stop': cond = True else: cond = False if cond: # Read a line from the Arduino and convert into a form that can be read in and processed ser_bytes = ser.readline() # Reads a line from the Arduino string_n = ser_bytes.decode('latin-1') output = string_n.strip() # Remove \n and \r from the decoded byte valid_characters = string.printable output = ''.join(i for i in output if i in valid_characters) # Check to see if the line from the Arduino is a flux data point # if so the format will be 'OPT3002_P#: [flux value] [unit] if 'OPT3002_P1:' in output: print(output) # Get flux and unit of flux and append to array of all flux values for a sample flux1 = float(output.split( )[1]) # Get the flux value for this entry if nonzero unit1 = output.split( )[2] # Get the unit of the flux PD1_data = np.append(PD1_data,flux1) # Append latest flux value Time = np.append(Time,((datetime.now() - start_time).total_seconds()/60)) # Get the time this measurement was taken # We want to show all of the data over time, so we will consistently update the xlimits of the graph lines1.set_xdata(Time) lines1.set_ydata(PD1_data) # Write the data to an excel spreadsheet df = pd.DataFrame({"Time":[Time[-1]],"Flux":[flux1],"Unit":[unit1]}) df.to_excel(writer,sheet_name='Photodiode_1', startrow=len(PD1_data), index = False,header= False) elif 'OPT3002_P2:' in output: print(output) # Get flux and unit of flux and append to array of all flux values for a sample flux2 = float(output.split( )[1]) # Get the flux value for this entry unit2 = output.split( )[2] # Get the unit of the flux PD2_data = np.append(PD2_data,flux2) # Append latest flux value # We want to show all of the data over time, so we will consistently update the xlimits of the graph lines2.set_xdata(Time) lines2.set_ydata(PD2_data) # Write the data to an excel spreadsheet df = pd.DataFrame({"Time":[Time[-1]],"Flux":[flux2],"Unit":[unit2]}) df.to_excel(writer,sheet_name='Photodiode_2', startrow=len(PD1_data), index = False,header= False) root.after(1,plot_data) # Read from serial again after 1 ms # Update the graph plot when data is not being read in to speed up the overall program else: if len(PD1_data) > 0: ax.set_xlim(0,(np.amax(Time) + 5)) # Set x limit to be length of data points ax.set_ylim(0,(max(np.amax(PD1_data),np.amax(PD2_data)) + 1000))# Set y limit to be max value of either diode + 1000 canvas.draw() # Update the graph on the GUI # If there are no data entries, the LED is in its interval of no exposure so only check again every 100 ms if ((datetime.now() - start_time).total_seconds()/60) > measurement_time or status == 'stop': print("Acquisition Finished") time.sleep(1) writer.save() # Saves the output data file root.destroy() # Closes the user interface ser.close() # Closes the connection to the Arduino sys.exit() # Exits the program root.after(800,plot_data) #---------- Create Functions for GUI --------- def stopProgram(): # Notifies the program the user is finished with acquisition once they hit the stop button global status global cond status = 'stop' cond = False #------Initialize Tkinter GUI Settings-------- root = tk.Tk() root.attributes('-fullscreen', True) root.title("real Time Plot") width = root.winfo_screenwidth() height = root.winfo_screenheight() root.configure(background = 'light blue') # Set the background color root.geometry(f'{width}x{height}') # Set the window size #------Add Figure Canvas--------------- fig = Figure() ax = fig.add_subplot(1,1,1) ax.set_title("Fluorescence vs Time",fontsize = 30 ) ax.set_xlabel("Time (minutes)",fontsize = 30) ax.set_ylabel("Fluorescenct Intensity (RFU)",fontsize = 30) ax.tick_params(labelsize = 20) ax.set_xlim(0,100) ax.set_ylim(0,60000) lines1 = ax.plot([],[])[0] lines2 = ax.plot([],[])[0] ax.legend((lines1, lines2), ('Photodiode 1', 'Photodiode 2')) btn = Button(root, text = 'Stop Acquisition', bd = '5', command = stopProgram, height = 5, width =20) btn.place(x=width*.90, y=200) # Designates where the button should be located myFont = font.Font(size=10) # define font btn['font'] = myFont canvas = FigureCanvasTkAgg(fig,master=root) canvas.get_tk_widget().place(x=50,y=50,width=width*.85,height=height*.85) canvas.draw() #---------Establish Serial Connection--------- print('Establishing connection to the Arduino...') # Comment out whichever interface you are not using ser = serial.Serial(serialPort, 9600) # This can be used for a Raspberry Pi or a laptop computer ser.flushInput() print('Connection Established') #--------- Initialize Excel Document------ writer = pd.ExcelWriter(outputFilepath, engine='xlsxwriter') df = pd.DataFrame({"Time":[0],"Flux":[0],"Unit":[0]}) df.to_excel(writer,sheet_name='Photodiode_1', startrow=0, index = False,header= True) df.to_excel(writer,sheet_name='Photodiode_2', startrow=0, index = False,header= True) root.after(1,plot_data) root.mainloop()