# Copyright (c) 2019 Esben Rossel # All rights reserved. # # Author: Esben Rossel # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. #python imports from tkinter import messagebox import serial import numpy as np #application imports import config import threading import tkinter as tk import time # The firmware expects 12 bytes from the computer and will not do anything until 12 bytes have been received. # The format is: # byte[1-2]: The characters E and R. Defines where the firmware should start reading in its circular input-buffer. # byte[3-6]: The 4 bytes constituting the 32-bit int holding the SH-period # byte[7-10]: The 4 bytes constituting the 32-bit int holding the ICG-period # byte[11]: Continuous flag: 0 equals one acquisition, 1 equals continuous mode # byte[12]: The number of integrations to average def rxtx(panel, SerQueue, progress_var): if (config.AVGn[0] == 0): threadser = threading.Thread(target=rxtxoncethread, args=(panel, SerQueue, progress_var), daemon=True) elif (config.AVGn[0] == 1): threadser = threading.Thread(target=rxtxcontthread, args=(panel, progress_var), daemon=True) threadser.start() def rxtxoncethread(panel, SerQueue, progress_var): #open serial port try: ser = serial.Serial(config.port, config.baudrate) #share the serial handle with the stop-thread so cancel_read may be called SerQueue.put(ser) #disable controls panelsleep(panel) config.stopsignal = 0 #start the progressbar panel.progress.config(mode="determinate") threadprogress = threading.Thread(target=progressthread, args=(progress_var,), daemon=True) threadprogress.start() #wait to clear the input and output buffers, if they're not empty data is corrupted while (ser.in_waiting > 0): ser.reset_input_buffer() ser.reset_output_buffer() time.sleep(0.01) #Transmit key 'ER' config.txfull[0] = 69 config.txfull[1] = 82 #split 32-bit integers to be sent into 8-bit data config.txfull[2] = (config.SHperiod >> 24) & 0xff config.txfull[3] = (config.SHperiod >> 16) & 0xff config.txfull[4] = (config.SHperiod >> 8) & 0xff config.txfull[5] = config.SHperiod & 0xff config.txfull[6] = (config.ICGperiod >> 24) & 0xff config.txfull[7] = (config.ICGperiod >> 16) & 0xff config.txfull[8] = (config.ICGperiod >> 8) & 0xff config.txfull[9] = config.ICGperiod & 0xff #averages to perfom config.txfull[10] = config.AVGn[0] config.txfull[11] = config.AVGn[1] #transmit everything at once (the USB-firmware does not work if all bytes are not transmitted in one go) ser.write(config.txfull) #wait for the firmware to return data config.rxData8 = ser.read(7388) #close serial port ser.close() #enable all buttons panelwakeup(panel) if (config.stopsignal == 0): #combine received bytes into 16-bit data for rxi in range(3694): config.rxData16[rxi] = (config.rxData8[2*rxi+1] << 8) + config.rxData8[2*rxi] #plot the new data panel.bupdate.invoke() #hold values for saving data to file as the SHperiod and ICGperiod may be updated after acquisition config.SHsent = config.SHperiod config.ICGsent = config.ICGperiod SerQueue.queue.clear() except serial.SerialException: messagebox.showerror("By the great otter!","There's a problem with the specified serial connection.") def rxtxcontthread(panel, progress_var): #open serial port try: ser = serial.Serial(config.port, config.baudrate) #disable controls panelsleep(panel) config.stopsignal = 0 #restart the progressbar for each acquisition panel.progress.config(mode="indeterminate") panel.progress.start(100) # threadprogress = threading.Thread(target=progressthread, args=(progress_var), daemon=True) # threadprogress.start() #wait to clear the input and output buffers, if they're not empty data is corrupted while (ser.in_waiting > 0): ser.reset_input_buffer() ser.reset_output_buffer() time.sleep(0.1) #Transmit key 'ER' config.txfull[0] = 69 config.txfull[1] = 82 #split 32-bit integers to be sent into 8-bit data config.txfull[2] = (config.SHperiod >> 24) & 0xff config.txfull[3] = (config.SHperiod >> 16) & 0xff config.txfull[4] = (config.SHperiod >> 8) & 0xff config.txfull[5] = config.SHperiod & 0xff config.txfull[6] = (config.ICGperiod >> 24) & 0xff config.txfull[7] = (config.ICGperiod >> 16) & 0xff config.txfull[8] = (config.ICGperiod >> 8) & 0xff config.txfull[9] = config.ICGperiod & 0xff #averages to perfom config.txfull[10] = config.AVGn[0] config.txfull[11] = config.AVGn[1] #transmit everything at once (the USB-firmware does not work if all bytes are not transmittet in one go) ser.write(config.txfull) #loop to acquire and plot data continuously while (config.stopsignal == 0): #wait for the firmware to return data config.rxData8 = ser.read(7388) if (config.stopsignal == 0): #combine received bytes into 16-bit data for rxi in range(3694): config.rxData16[rxi] = (config.rxData8[2*rxi+1] << 8) + config.rxData8[2*rxi] #plot the new data panel.bupdate.invoke() #hold values for saving data to file config.SHsent = config.SHperiod config.ICGsent = config.ICGperiod #resend settings with continuous transmission disabled to avoid flooding of the serial port config.txfull[10] = 0 ser.write(config.txfull) #wait until data is received to close the serial port while (ser.out_waiting > 0): time.sleep(0.1) #close serial port ser.close() panelwakeup(panel) panel.progress.stop() except serial.SerialException: messagebox.showerror("By the great otter!","There's a problem with the specified serial connection.") def progressthread(progress_var): progress_var.set(0) for i in range (1,11): progress_var.set(i) #wait 1/10th of the time the acquisition requires before adding to progress bar time.sleep(config.ICGperiod*config.AVGn[1]/config.MCLK/10) def rxtxcancel(SerQueue): config.stopsignal = 1 #Are we stopping one very long measurement, or the continuous real-time view? if (config.AVGn[0]==0): ser = SerQueue.get() ser.cancel_read() def panelsleep(panel): panel.bstop.config(state=tk.NORMAL) panel.bopen.config(state=tk.DISABLED) panel.bsave.config(state=tk.DISABLED) panel.bcollect.config(state=tk.DISABLED) panel.AVGscale.config(state=tk.DISABLED) panel.rcontinuous.config(state=tk.DISABLED) panel.roneshot.config(state=tk.DISABLED) panel.eICG.config(state=tk.DISABLED) panel.eSH.config(state=tk.DISABLED) panel.edevice.config(state=tk.DISABLED) panel.cinvert.config(state=tk.DISABLED) panel.cbalance.config(state=tk.DISABLED) def panelwakeup(panel): panel.bstop.config(state=tk.DISABLED) panel.bopen.config(state=tk.NORMAL) panel.bsave.config(state=tk.NORMAL) panel.bcollect.config(state=tk.NORMAL) panel.AVGscale.config(state=tk.NORMAL) panel.rcontinuous.config(state=tk.NORMAL) panel.roneshot.config(state=tk.NORMAL) panel.eICG.config(state=tk.NORMAL) panel.eSH.config(state=tk.NORMAL) panel.edevice.config(state=tk.NORMAL) panel.cinvert.config(state=tk.NORMAL) if (config.datainvert == 1): panel.cbalance.config(state=tk.NORMAL)