''' v0.1 notes --------- Relies on /dev/ttyUSB channel being available to the user. Do it by running the command sudo adduser dialout and logging out, then logging back in. Look at the output of ls /dev/ttyUSB* on a terminal before and after plugging in the USB cable to the PC, to find out which channel to use in the input below. ''' import serial import time import tkinter as tk import ttkbootstrap as ttk from ttkbootstrap.constants import * port_name = '/dev/ttyUSB0' baud_rate = 9600 timeout = 2 commands = { "turn on": "$CMD:SET,CH:8,PAR:ON\n".encode('utf-8'), "turn off": "$CMD:SET,CH:8,PAR:OFF\n".encode('utf-8'), "get name": "$CMD:MON,CH:8,PAR:CRNAME\n".encode('utf-8') } class CrateControl(ttk.Frame): def __init__(self,master): super().__init__(master, padding=20) try: self.ser = serial.Serial(port=port_name,baudrate=baud_rate,timeout=timeout) except serial.SerialException as e: print(f"Error opening serial port: {e}") self.destroy() quit() except Exception as e: print(f"An unexpected error occurred: {e}") self.destroy() quit() print(self.ser) self.freeze_frame=False self.last_return_string="" self.style = ttk.Style() self.style.configure('big.success-round-toggle.TCheckbutton',font=('Verdana',18),indicatorsize=40,padding=10) self.name_of_crate = self.poll(8,"CRNAME","") self.onoff_status = tk.IntVar(value=0) self.status_label = ttk.Label(self, text="Ready",bootstyle="info",justify=LEFT) self.onoff_button = ttk.Checkbutton(self, text=self.name_of_crate, variable=self.onoff_status, onvalue=1, offvalue=0, width=20, #style='success-round-toggle.Checkbutton', command=self.toggle_onoff, bootstyle='big success-round-toggle') self.status_label.pack(expand=YES,fill=X,pady=10) self.onoff_button.pack(expand=YES,fill=X,pady=10) self.refresh_monitors() #print('test') #self.after(200,self.eventloop) #self.refresh_monitors() def poll(self, channel, polstring,replystring=""): #while(self.ser.in_waiting > 0): # self.ser.readline() self.ser.write(str("$CMD:MON,CH:"+str(channel)+",PAR:"+polstring+"\n").encode('utf-8')) retval = self.ser.readline().decode("utf-8").rstrip() #retval = self.read_reply() retval = retval.replace("#CMD:OK,VAL:","") return "%s\t%s\t"%(retval,replystring) #return self.read_reply() def refresh_monitors(self): #fullstatus="name: " fullstatus="name\t\tset1\t\tmon1\t\tmon2\n" for i in range(0,3): reply="" name = self.poll(i,"NAME") setv = self.poll(i,"VSET","V") monv = self.poll(i,"VMON","V") moni = self.poll(i,"IMON","A") #reply = name #.replace("#CMD:OK,VAL:"," ") #reply = reply+"\n" fullstatus += (name+setv+monv+moni+"\n") fullstatus+="\t" fullstatus+=self.poll(8,"FAN1","rpm\n") fullstatus+=self.poll(8,"FAN2","rpm\n") fullstatus+=self.poll(8,"FAN3","rpm\n") fullstatus+=self.poll(8,"FUTEMP","C\n") fullstatus+=self.poll(8,"PSTEMP","C\n") status = self.poll(8,"CRST","\n") fanstat = (int(status)&0x200==512) crstat = int(status)&0x1 fullstatus+=("fan: %d\t crate: %d"%(fanstat,crstat)) if(crstat == 1 and fanstat == 1 and self.onoff_status.get() == 0): self.onoff_status.set(1) self.onoff_button.state(['selected']) while(self.freeze_frame): fullstatus = fullstatus #print(fullstatus,self.freeze_frame) self.status_label.config(text=fullstatus, bootstyle="info") self.after(4000,self.refresh_monitors) def read_reply(self): totresp = "" while(self.ser.in_waiting > 0): response = self.ser.readline().decode('utf-8').rstrip() #rstrip gets rid of trailing whitespace totresp = totresp+response self.last_return_string=totresp return totresp def toggle_onoff(self): self.freeze_frame=True if(self.onoff_status.get()==1): print("Turn ON") self.ser.write(commands['turn on']) time.sleep(0.2) elif(self.onoff_status.get()==0): print("Turn OFF") self.ser.write(commands['turn off']) time.sleep(0.2) #print(self.read_reply()) while(self.ser.in_waiting > 0): self.ser.readline() self.freeze_frame=False if __name__ == "__main__": root = ttk.Window(themename='flatly',title="CrateControl") app = CrateControl(root) #ttk.Window("Crate Control") app.pack(expand=True,fill=BOTH) root.mainloop()