Skip to content

Instantly share code, notes, and snippets.

@FilipDominec
Created November 13, 2024 14:20
Show Gist options
  • Save FilipDominec/322046a23f5fa9449ba1a185c0d44a00 to your computer and use it in GitHub Desktop.
Save FilipDominec/322046a23f5fa9449ba1a185c0d44a00 to your computer and use it in GitHub Desktop.
Control software for the SEM/CL setup at FZU in the A44 lab - updated for rp2daq
vpos_panchro = 4230
vpos_spectro = 1180
vpos_monochro = 110
calib1_nm = 27.1
calib1_stepper = 0
calib2_nm = 653.3
calib2_stepper = 100000
min_limit_nm = 100
max_limit_nm = 1000
#!/usr/bin/python3
#-*- coding: utf-8 -*-
"""
Update after Win11 transition of the "black" computer in the A/44 room. 2024-11-11
Module dependencies: tkinter, serial, struct
"""
# TODO keyboard shortcuts;
# TODO saving recent few
# big optional TODO PS/2 keyboard sniffer
# TODO TEST NEW FEATURES 2021-01-08
import sys, os, time, serial, struct
import tkinter as tk
from tkinter import messagebox
## == error handling with a graphical message box ==
def myerr(exc_type, exc_value, tb):
import traceback
message = '\r'.join(traceback.format_exception(exc_type, exc_value, tb))
print(message)
messagebox.showerror(title=exc_value, message=message)
sys.excepthook = myerr
## == settings loading == {{{
def load_settings_from_file(infile='a44_config.txt'):
#infile = os.path.realpath(__file__)
#print("DEBUG: infile = ", infile)
settings = {}
with open(infile) as f:
for l in f.readlines():
l = l.split('#')[0] # ignore comments
k,v = [s.strip() for s in l.split('=', 1)] # split at first '=' sign
settings[k] = v
return settings
settings = load_settings_from_file()# }}}
## == Serial port & stepper init == {{{
import rp2daq
rp = rp2daq.Rp2daq()
zero_pos = [0,0]
zero_pos[0] = rp.stepper_init(0, dir_gpio=12, step_gpio=13, endswitch_gpio=19, inertia=400)["initial_nanopos"]
zero_pos[1] = rp.stepper_init(1, dir_gpio=10, step_gpio=11, endswitch_gpio=18, inertia=400)["initial_nanopos"]
NANOSTEP_PER_MICROSTEP = 256
VERT_MINIMUM_POS = -10000
MONO_MINIMUM_POS = -1000000
MONO_MOTOR_ID = 0
VERT_MOTOR_ID = 1
## == Hardware-communication routines =={{{
def vmotor(choice):
print('VMOTOR', choice)
settings = load_settings_from_file()
## send the message to the hardware
if choice == VERT_MINIMUM_POS:
target_micropos = -10000
else:
target_micropos = int(settings['vpos_'+choice])
rp.stepper_move(VERT_MOTOR_ID,
to=zero_pos[VERT_MOTOR_ID] + 2*target_micropos*NANOSTEP_PER_MICROSTEP,
speed=64,
reset_nanopos_at_endswitch=1)
## visual GUI feedback (todo indicate that the movement was really finished)
for k,v in button_dict.items(): v.config(relief=tk.SUNKEN if k == choice else tk.RAISED)
for k,v in frame_dict.items(): v.config(bg='green2' if k == choice else BGCOLOR)
lbl_wavelength.config(bg='green2' if choice=='monochro' else BGCOLOR)
#ent_wavelength.config(state=tk.NORMAL if choice=='monochro' else tk.DISABLED) (why not move monochromator any time...)
#btn_wavelength.config(state=tk.NORMAL if choice=='monochro' else tk.DISABLED)
def mmotor(target_micropos):
rp.stepper_move(MONO_MOTOR_ID,
to=zero_pos[MONO_MOTOR_ID] + 2*target_micropos*NANOSTEP_PER_MICROSTEP,
speed=64*2,
reset_nanopos_at_endswitch=1)
def mmotor_by_wl(target_wavelength_nm):
## (re)load the monochromator calib
settings = load_settings_from_file()
nm1, nm2 = float(settings['calib1_nm']), float(settings['calib2_nm']),
s1, s2 = float(settings['calib1_stepper']), float(settings['calib2_stepper'])
target_micropos = int((target_wavelength_nm-nm1)/(nm2-nm1)*(s2-s1)+s1)
#print("DEBUG: target_micropos = ", target_micropos)
mmotor(target_micropos)
def mmotor_by_entry(event):
try:
target_wavelength_nm = float(ent_wavelength.get())
ent_wavelength.config(bg='#ffffff')
except:
ent_wavelength.config(bg='#ff8800')
print("DEBUG: target_wavelength_nm = ", target_wavelength_nm)
print( settings['min_limit_nm'] + settings['max_limit_nm'])
target_wavelength_nm = min(max(target_wavelength_nm, float(settings['min_limit_nm'])), float(settings['max_limit_nm']))
txt_wavelength.set('{:6.0f}'.format(target_wavelength_nm))
mmotor_by_wl(target_wavelength_nm)
# }}}
## == GUI init ==
root = tk.Tk()
root.geometry("300x330+300+300")
root.wm_title("CL control")
#root.pack_propagate(0) # don't shrink
btnkwarg = {'padx':100, 'pady':20}
frm_panchro = tk.Frame(root, height=32, width=32); frm_panchro.pack(padx=5, pady=5, side=tk.TOP)
btn_panchro = tk.Button(frm_panchro, text='Panchromatic imaging', command=lambda:vmotor('panchro'), **btnkwarg) # fill=tk.X, ipady=10,
btn_panchro.pack(padx=5, pady=5, side=tk.TOP)
BGCOLOR = btn_panchro.cget('bg')
frm_spectro = tk.Frame(root, height=32, width=32); frm_spectro.pack(padx=5, pady=5, side=tk.TOP)
btn_spectro = tk.Button(frm_spectro, text='Spectrometer', command=lambda:vmotor('spectro'), **btnkwarg)
btn_spectro.pack(padx=5, pady=5, side=tk.TOP)
frm_monochro = tk.Frame(root, height=32, width=32); frm_monochro.pack(padx=5, pady=5, side=tk.TOP)
btn_monochro = tk.Button(frm_monochro, text='Monochromator imaging', command=lambda:vmotor('monochro'), **btnkwarg)
btn_monochro.pack(padx=5, pady=5, side=tk.TOP)
lbl_wavelength = tk.Label(text='Wavelength (nm)', master=frm_monochro ); lbl_wavelength.pack(padx=5, pady=5, side=tk.LEFT)
txt_wavelength = tk.StringVar()
ent_wavelength = tk.Entry(frm_monochro, text='nm', textvariable=txt_wavelength, width=10);
ent_wavelength.pack(padx=5, pady=5, side=tk.LEFT)
ent_wavelength.bind('<Return>', mmotor_by_entry)
btn_wavelength = tk.Button(frm_monochro, text='set!', command=lambda:mmotor_by_entry('mouse')); btn_wavelength.pack(padx=5, pady=5, side=tk.LEFT)
frm_endstop = tk.Frame(root, height=22); frm_endstop.pack(padx=5, pady=5, side=tk.TOP)
btn_mgotovend = tk.Button(frm_endstop, text='Vertical zero', command=lambda:[vmotor(VERT_MINIMUM_POS)], bg='#eebbbb', padx=10, pady=2) # fill=tk.X, ipady=10,
btn_mgotovend.pack(padx=5, pady=5, side=tk.LEFT)
btn_mgotomend = tk.Button(frm_endstop, text='Monochromator zero', command=lambda:[mmotor(MONO_MINIMUM_POS)], bg='#eebbbb', padx=10, pady=2) # fill=tk.X, ipady=10,
btn_mgotomend.pack(padx=5, pady=5, side=tk.LEFT)
frame_dict = {'panchro':frm_panchro, 'spectro':frm_spectro, 'monochro':frm_monochro}
button_dict = {'panchro':btn_panchro, 'spectro':btn_spectro, 'monochro':btn_monochro}
## == responsive GUI loop and termination ==
tk.mainloop()
## TODO try py2exe https://stackoverflow.com/questions/13894083/py2exe-window-disappears-immediately-after-running
@FilipDominec
Copy link
Author

This is how the GUI looks like.
screen-2024-11-13-15-45-11

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment