value -= 1 << bits
return value
+def is_integer(n):
+ try:
+ float(n)
+ except ValueError:
+ return False
+ else:
+ return float(n).is_integer()
+
+def select_all(event):
+ event.widget.select_range(0, tk.END)
+ event.widget.icursor(tk.END)
+ return "break"
class SerialGUI:
def __init__(self, root):
self.root = root
self.root.title("Step Motor Controller")
- self.root.columnconfigure(1, weight=1)
- self.root.columnconfigure(2, weight=1)
-
+ # self.root.columnconfigure(1, weight=1)
+ # self.root.columnconfigure(2, weight=1)
+ self.root.columnconfigure(3, weight=1)
labels = [
- "Position [steps]", "Position [um]", "Goto [steps]", "Goto [um]",
- "Move by [steps]", "Move by [um]", "Stepsize", "Speed"]
+ "Position [steps]", "Position [µm]", "Goto [steps]", "Goto [µm]",
+ "Move by [steps]", "Move by [µm]", "Stepsize", "Speed"]
names = ["PS","PU","GS","GU","MS","MU","SS","SP"]
self.entries = {}
label = ttk.Label(root, text=label_text)
label.grid(row=row, column=0, padx=8, pady=5, sticky="w")
- entry1 = ttk.Entry(root)
+ entry1 = ttk.Entry(root, width=20)
+ for b in ["<Return>","<KP_Enter>"]: entry1.bind(b, lambda event, sn=names[row]+"X": self.valuechanged(sn))
+ for b in ["<Shift-Return>","<Shift-KP_Enter>"]: entry1.bind(b, lambda event, sn=names[row]+"X": self.valuechanged(sn,1))
+ for b in ["<Control-a>","<Double-Button-1>"]: entry1.bind(b,select_all)
entry1.grid(row=row, column=1, padx=5, pady=5, sticky="ew")
if row < 6:
- entry2 = ttk.Entry(root)
+ entry2 = ttk.Entry(root, width=20)
+ for b in ["<Return>","<KP_Enter>"]: entry2.bind(b, lambda event, sn=names[row]+"Y": self.valuechanged(sn))
+ for b in ["<Shift-Return>","<Shift-KP_Enter>"]: entry2.bind(b, lambda event, sn=names[row]+"Y": self.valuechanged(sn,1))
+ for b in ["<Control-a>","<Double-Button-1>"]: entry2.bind(b,select_all)
entry2.grid(row=row, column=2, padx=5, pady=5, sticky="ew")
if row < 6:
colname = ["Active","X+","X-","Y+","Y-"];
color_frame = tk.Frame(root)
color_frame.grid(
- row=8, column=0, columnspan=3,
+ row=8, column=0, columnspan=4,
padx=8, pady=12, sticky="ew"
)
fill="black", # Text color
font=("Arial", 10, "bold"),
)
+ if col==0:
+ rectangle.bind("<Button-1>", lambda event: self.activate())
rectangle.grid(row=0, column=col, padx=5)
self.entries[colname[col]] = rectangle
+ ttk.Button(
+ color_frame,
+ text="Clear",
+ style="Tiny.TButton",
+ command=lambda: self.clearcoordinates()
+ ).grid(row=0, column=5)
+
ttk.Button(
root,
text="Store",
style="Tiny.TButton",
command=lambda: self.storeposition()
).grid(row=9, column=0)
+ ttk.Button(
+ root,
+ text="Goto",
+ style="Tiny.TButton",
+ command=lambda: self.gotoline()
+ ).grid(row=10, column=0)
ttk.Button(
root,
text="Save",
style="Tiny.TButton",
command=lambda: self.savelog()
-
- ).grid(row=10, column=0)
+ ).grid(row=11, column=0)
text_box = tk.Text(
root,
height=10,
- width=50,
+ width=80,
wrap="word", # Wrap long lines
- font=("Courier", 11)
+ font=("Courier", 11),
)
- text_box.grid(row=9, column=1, columnspan=2, rowspan=2)
+ text_box.bind("<Control-g>", lambda event: self.gotoline())
+ text_box.grid(row=9, column=1, columnspan=3, rowspan=3)
self.entries["store"] = text_box
+ self.entries["store"].insert("end","X (step) Y (step) X (µm) Y (µm) Notes\n")
- label = ttk.Label(root, text="Raw Input")
- label.grid(row=11, column=0, padx=8, pady=5, sticky="w")
- entry1 = ttk.Entry(root)
- entry1.grid(row=11, column=1, columnspan=2, padx=5, pady=5, sticky="ew")
+ # label = ttk.Label(root, text="Raw Input")
+ # label.grid(row=11, column=0, padx=8, pady=5, sticky="w")
+ # entry1 = ttk.Entry(root)
+ # entry1.grid(row=11, column=1, columnspan=2, padx=5, pady=5, sticky="ew")
- self.entries["raw"] = entry1
+ # self.entries["raw"] = entry1
# --- Serial Setup ---
try:
self.ser = serial.Serial(PORT, BAUD_RATE, timeout=1)
self.display_var.set(f"Error: Could not open {PORT}\n{e}")
return
- # Start a background thread to read serial data without freezing the GUI
self.thread = threading.Thread(target=self.read_serial, daemon=True)
self.thread.start()
- def storeposition(self):
- xs = self.entries["PSX"].get()
- xu = self.entries["PUX"].get()
- ys = self.entries["PSY"].get()
- yu = self.entries["PUY"].get()
- self.entries["store"].insert("end",f"{xs} {ys}\t{xu} {yu}\n")
+ def valuechanged(self, field, invert=0):
+ if field == "SSX":
+ value = self.entries["SS"].get()
+ if is_integer(value):
+ value = int(value) & 0x7fffffff
+ self.send_data("W03%08x\n" % value)
+ if field == "SPX":
+ value = self.entries["SP"].get()
+ if is_integer(value):
+ value = int(value) & 0x7
+ self.send_data("W04%08x\n" % value)
+ if field == "GSX":
+ value = self.entries["GSX"].get()
+ if is_integer(value):
+ value = int(value) & 0xffffffff
+ self.send_data("W05%08x\n" % value)
+ if field == "GSY":
+ value = self.entries["GSY"].get()
+ if is_integer(value):
+ value = int(value) & 0xffffffff
+ self.send_data("W06%08x\n" % value)
+ if field == "GUX":
+ value = self.entries["GUX"].get()
+ if is_integer(value):
+ value = round((float(value))/STEP_SIZE/CORRECTION_X) & 0xffffffff
+ self.send_data("W05%08x\n" % value)
+ if field == "GUY":
+ value = self.entries["GUY"].get()
+ if is_integer(value):
+ value = round((float(value))/STEP_SIZE/CORRECTION_Y) & 0xffffffff
+ self.send_data("W06%08x\n" % value)
+ if field == "MSX":
+ value = self.entries["MSX"].get()
+ if is_integer(value):
+ value = int(value)
+ if invert: value = -value
+ value = value & 0xffffffff
+ self.send_data("W07%08x\n" % value)
+ if field == "MSY":
+ value = self.entries["MSY"].get()
+ if is_integer(value):
+ value = int(value)
+ if invert: value = -value
+ value = value & 0xffffffff
+ self.send_data("W08%08x\n" % value)
+ if field == "MUX":
+ value = self.entries["MUX"].get()
+ if is_integer(value):
+ value = float(value)
+ if invert: value = -value
+ value = round(value/STEP_SIZE/CORRECTION_X) & 0xffffffff
+ self.send_data("W07%08x\n" % value)
+ if field == "MUY":
+ value = self.entries["MUY"].get()
+ if is_integer(value):
+ value = float(value)
+ if invert: value = -value
+ value = round(value/STEP_SIZE/CORRECTION_Y) & 0xffffffff
+ self.send_data("W08%08x\n" % value)
+
+ def clearcoordinates(self):
+ xs = int(self.entries["PSX"].get())
+ ys = int(self.entries["PSY"].get())
+
+ self.send_data("W0A00000000\n")
+ self.entries["store"].see(tk.END)
+
+
+ def activate(self):
+ if self.entries["Active"].cget("bg") == "green":
+ self.send_data("W0900000000\n")
+ else:
+ self.send_data("W0900000001\n")
+
+ def gotoline(self):
+ line = self.entries["store"].get("insert linestart", "insert lineend")
+ m = re.search(r"^\s*([\-0-9]+)\s*([\-0-9]+)",line)
+ if m:
+ value = int(m.group(1)) & 0xffffffff
+ self.send_data("W05%08x\n" % value)
+ value = int(m.group(2)) & 0xffffffff
+ self.send_data("W06%08x\n" % value)
+
+ def storeposition(self, text=""):
+ xs = int(self.entries["PSX"].get())
+ xu = float(self.entries["PUX"].get())
+ ys = int(self.entries["PSY"].get())
+ yu = float(self.entries["PUY"].get())
+ s = ""
+ if text != "":
+ s = " # " + text
+ # self.entries["store"].insert("end",f"{xs} {ys}\t{xu} {yu}\n")
+ self.entries["store"].insert("end","%8i %8i %11.2f %11.2f %s\n" %(xs,ys,xu,yu,s))
+ self.entries["store"].see(tk.END)
def savelog(self):
filename = filedialog.asksaveasfilename(
break
if inputline is not None:
# print(inputline)
- self.setbox("raw",inputline)
- m = re.search(r"P([0-9a-fA-F]+),([0-9a-fA-F]+)",inputline)
+ # self.setbox("raw",inputline)
+ m = re.search(r"P([0-9a-fA-F]+),([0-9a-fA-F]+)([SR]?)",inputline)
if m:
xs = twos_complement(m.group(1),32)
ys = twos_complement(m.group(2),32)
self.setbox("PSY",ys)
self.setbox("PUX",xs * STEP_SIZE * CORRECTION_X, 2)
self.setbox("PUY",ys * STEP_SIZE * CORRECTION_Y, 2)
+ if m.group(3) == 'S':
+ self.storeposition()
+ if m.group(3) == 'R':
+ self.storeposition("# reset")
m = re.search(r"S([01])([01])([01])([01])([01])",inputline)
if m:
if m.group(1) == '1':
except Exception:
pass
- # def send_data(self):
- # data = self.entry.get()
- # if data:
- # try:
- # # Add newline character and send as bytes
- # self.ser.write((data + '\n').encode('utf-8'))
- # self.entry.delete(0, tk.END) # Clear input field
- # except Exception as e:
- # print(f"Send Error: {e}")
+ def send_data(self, data):
+ if data:
+ try:
+ # Add newline character and send as bytes
+ self.ser.write((data + '\n').encode('utf-8'))
+ except Exception as e:
+ print(f"Send Error: {e}")
+
+
def close_window():
stop_event.set()
root.destroy()
uint8_t rxcnt = 0, txpoint = 0;
uint8_t rxbuf[15];
uint8_t txbuf[15];
-uint8_t buffer[24];
+uint8_t buffer[26];
color_t colors[16];
color_t topback;
volatile uint8_t keypad[16] = { 0 };
}
sei();
}
+
+void MoveRelativeX(int32_t pos) {
+ cli();
+ if (motor_active) {
+ if (pos > 0) {
+ DIRECTION_FORWARD_M1();
+ M1_DIR = 1;
+ M1_STEPS = pos;
+ }
+ else{
+ DIRECTION_BACKWARD_M1();
+ M1_DIR = 0;
+ M1_STEPS = -pos;
+ }
+ }
+ sei();
+}
+
+void MoveRelativeY(int32_t pos) {
+ cli();
+ if (motor_active) {
+ if (pos > 0) {
+ DIRECTION_FORWARD_M2();
+ M2_DIR = 1;
+ M2_STEPS = pos;
+ }
+ else{
+ DIRECTION_BACKWARD_M2();
+ M2_DIR = 0;
+ M2_STEPS = -pos;
+ }
+ }
+ sei();
+}
+
+void activate(uint8_t state) {
+ if (state == 0) {
+ motor_active = 0;
+ DISABLE_M1();
+ DISABLE_M2();
+ DISABLE_POWER();
+ M1_STEPS = 0;
+ M2_STEPS = 0;
+ lcd_use_foreground(colors[5]);
+ lcd_putstr_xy_P(PSTR("off"), 16, 280);
+ lcd_use_foreground(colors[0]);
+ } else {
+ motor_active = 1;
+ ENABLE_M1();
+ ENABLE_M2();
+ ENABLE_POWER();
+ lcd_use_foreground(colors[3]);
+ lcd_putstr_xy_P(PSTR("ON "), 16, 280);
+ lcd_use_foreground(colors[0]);
+ }
+}
+
+void sendPosition(uint8_t c) {
+ strncpy((char*)buffer, " ", 24);
+ buffer[0]='P';
+ ltoa(M1_position, (char*)buffer+1, 16);
+ buffer[11]=',';
+ ltoa(M2_position, (char*)buffer+12, 16);
+ buffer[22]=c;
+ buffer[23]='\n';
+ buffer[24] = 0;
+ send_string(buffer,25);
+}
+
+
+void clearcoordinates(void) {
+ cli();
+ M1_position = 0;
+ M2_position = 0;
+ sendPosition('R');
+ sei();
+}
+
+
/*****************************************************************
* Communication
*****************************************************************/
MoveAbsoluteY(pos);
send_answer_hex(&rxbuf[0], pos);
}
-
+ if (regnumber == 7) { // Register 7: Move Relative X W0700000064
+ int32_t pos = hex_to_int32(&rxbuf[3]);
+ MoveRelativeX(pos);
+ send_answer_hex(&rxbuf[0], pos);
+ }
+ if (regnumber == 8) { // Register 8: Move Relative Y W08FFFFFF95
+ int32_t pos = hex_to_int32(&rxbuf[3]);
+ MoveRelativeY(pos);
+ send_answer_hex(&rxbuf[0], pos);
+ }
+ if (regnumber == 9) { // Register 9: Activate W0900000001 W0900000000
+ uint8_t t = hex_to_int(rxbuf[10]) & 1;
+ activate(t);
+ send_answer_hex(&rxbuf[0], t);
+ }
+ if (regnumber == 0xa) { // Register 10: Clear W0A00000000
+ clearcoordinates();
+ send_answer_hex(&rxbuf[0], 1);
+ }
}
}
}
lcd_set_font(FONT_PROP_16, SPACING);
if (keypad[12] && keypad[12] != 255) { // A
- if (motor_active) {
- motor_active = 0;
- DISABLE_M1();
- DISABLE_M2();
- DISABLE_POWER();
- M1_STEPS = 0;
- M2_STEPS = 0;
- lcd_use_foreground(colors[5]);
- lcd_putstr_xy_P(PSTR("off"), 16, 280);
- lcd_use_foreground(colors[0]);
- } else {
- motor_active = 1;
- ENABLE_M1();
- ENABLE_M2();
- ENABLE_POWER();
- lcd_use_foreground(colors[3]);
- lcd_putstr_xy_P(PSTR("ON "), 16, 280);
- lcd_use_foreground(colors[0]);
- }
+ if (motor_active)
+ activate(0);
+ else
+ activate(1);
keypad[12] = 255;
}
if (keypad[13] && keypad[13] != 255) { // B
// Cursor Keys
if (!numlock) {
if (keypad[14] && keypad[14] != 255) { // C
- cli();
- M1_position = 0;
- M2_position = 0;
- sei();
+ clearcoordinates();
keypad[14] = 255;
}
if (keypad[15] && keypad[15] != 255) { // D
M1_STEPS = 0;
M2_STEPS = 0;
sei();
- keypad[14] = 255;
+ keypad[15] = 255;
}
if (keypad[4]) { // 2
if (!(keystate & (1 << 4)))
keypad[4] = 255;
}
- if (keypad[5]) { // 5, toggle single step mode
+ if (keypad[5] && keypad[5] != 255) { // 5, toggle single step mode
cli();
if (stepsize != 1) {
stored_stepsize = stepsize;
keypad[5] = 255;
}
+ if (keypad[7] && keypad[7] != 255) { // 0, send position
+ cli();
+ sendPosition('S');
+ sei();
+ keypad[7] = 255;
+ }
if (keypad[6]) { // 8
if (keypad[6] < 20 || keypad[6] > 40) {
cli();
// }
//output position to USB
// if ((time & 0x7) == 0) {
- strncpy((char*)buffer, " ", 24);
- buffer[0]='P';
- ltoa(M1_position, (char*)buffer+1, 16);
- buffer[11]=',';
- ltoa(M2_position, (char*)buffer+12, 16);
- buffer[22]='\n';
- buffer[23] = 0;
- send_string(buffer,24);
+ sendPosition(' ');
strncpy((char*)buffer, " ", 24);
buffer[0]='S';