]> jspc29.x-matter.uni-frankfurt.de Git - avr.git/commitdiff
add USB to step motor controller, GUI can show status
authorJan Michel <michel@physik.uni-frankfurt.de>
Fri, 16 Oct 2026 13:34:07 +0000 (15:34 +0200)
committerJan Michel <michel@physik.uni-frankfurt.de>
Fri, 16 Oct 2026 13:34:07 +0000 (15:34 +0200)
atmega32u4/stepmotorcontroller/gui.py [new file with mode: 0755]
atmega32u4/stepmotorcontroller/main.c

diff --git a/atmega32u4/stepmotorcontroller/gui.py b/atmega32u4/stepmotorcontroller/gui.py
new file mode 100755 (executable)
index 0000000..b13a802
--- /dev/null
@@ -0,0 +1,232 @@
+#!/usr/bin/python3
+
+import tkinter as tk
+from tkinter import ttk
+import serial
+import threading
+from pprint import pprint
+import queue
+import re
+from tkinter import filedialog
+
+
+# Configuration
+PORT = '/dev/Xmatter_SMC'
+BAUD_RATE = 9600
+input_queue = queue.Queue()
+stop_event = threading.Event()
+
+STEP_SIZE = 0.3125
+CORRECTION_X = 1.001
+CORRECTION_Y = 1.001
+
+def twos_complement(hexstr, bits):
+    value = int(hexstr, 16)
+    if value & (1 << (bits - 1)):
+        value -= 1 << bits
+    return value
+
+
+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)
+
+        labels = [
+            "Position [steps]", "Position [um]", "Goto [steps]", "Goto [um]",
+            "Move by [steps]", "Move by [um]", "Stepsize", "Speed"]
+        names = ["PS","PU","GS","GU","MS","MU","SS","SP"]
+        self.entries = {}
+
+        for row, label_text in enumerate(labels):
+            label = ttk.Label(root, text=label_text)
+            label.grid(row=row, column=0, padx=8, pady=5, sticky="w")
+
+            entry1 = ttk.Entry(root)
+            entry1.grid(row=row, column=1, padx=5, pady=5, sticky="ew")
+
+            if row < 6:
+              entry2 = ttk.Entry(root)
+              entry2.grid(row=row, column=2, padx=5, pady=5, sticky="ew")
+
+            if row < 6:
+              self.entries[names[row]+"X"] = entry1
+              self.entries[names[row]+"Y"] = entry2
+            else:
+              self.entries[names[row]] = entry1
+
+        colors = ["red", "green", "green", "green", "green"]
+        colname = ["Active","X+","X-","Y+","Y-"];
+        color_frame = tk.Frame(root)
+        color_frame.grid(
+            row=8, column=0, columnspan=3,
+            padx=8, pady=12, sticky="ew"
+        )
+
+        for col, color in enumerate(colors):
+            rectangle = tk.Canvas(
+                color_frame,
+                width=80,
+                height=30,
+                bg=color,
+                highlightthickness=0,
+            )
+            rectangle.create_text(
+                    40, 15,                 # Center of 80 x 30 rectangle
+                    text=colname[col],
+                    fill="black",           # Text color
+                    font=("Arial", 10, "bold"),
+                )
+            rectangle.grid(row=0, column=col, padx=5)
+            self.entries[colname[col]] = rectangle
+
+        ttk.Button(
+          root,
+          text="Store",
+          style="Tiny.TButton",
+          command=lambda: self.storeposition()
+          ).grid(row=9, column=0)
+        ttk.Button(
+          root,
+          text="Save",
+          style="Tiny.TButton",
+          command=lambda: self.savelog()
+
+          ).grid(row=10, column=0)
+
+        text_box = tk.Text(
+            root,
+            height=10,
+            width=50,
+            wrap="word",       # Wrap long lines
+            font=("Courier", 11)
+            )
+        text_box.grid(row=9, column=1, columnspan=2, rowspan=2)
+        self.entries["store"] = text_box
+
+        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
+        # --- Serial Setup ---
+        try:
+            self.ser = serial.Serial(PORT, BAUD_RATE, timeout=1)
+        except Exception as e:
+            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 savelog(self):
+        filename = filedialog.asksaveasfilename(
+            title="Save text",
+            defaultextension=".txt",
+            filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
+            )
+        if filename:
+            with open(filename, "w", encoding="utf-8") as file:
+                file.write(self.entries["store"].get("1.0", "end-1c"))
+
+
+    def setbox(self, name, value, digits=4):
+        # if value is (int, float):
+        try:
+            if not value.is_integer():
+                value = str(round(float(value),digits))
+        except:
+            pass
+            # value = value
+        self.entries[name].delete(0, tk.END)
+        self.entries[name].insert(0,value)
+
+    def update_display(self):
+        inputline = None
+        while True:
+            try:
+                inputline = input_queue.get_nowait()
+            except queue.Empty:
+                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)
+                if m:
+                    xs = twos_complement(m.group(1),32)
+                    ys = twos_complement(m.group(2),32)
+                    self.setbox("PSX",xs)
+                    self.setbox("PSY",ys)
+                    self.setbox("PUX",xs * STEP_SIZE * CORRECTION_X, 2)
+                    self.setbox("PUY",ys * STEP_SIZE * CORRECTION_Y, 2)
+                m = re.search(r"S([01])([01])([01])([01])([01])",inputline)
+                if m:
+                    if m.group(1) == '1':
+                        self.entries["X-"].configure(bg="red")
+                    else:
+                        self.entries["X-"].configure(bg="green")
+                    if m.group(2) == '1':
+                        self.entries["X+"].configure(bg="red")
+                    else:
+                        self.entries["X+"].configure(bg="green")
+                    if m.group(3) == '1':
+                        self.entries["Y-"].configure(bg="red")
+                    else:
+                        self.entries["Y-"].configure(bg="green")
+                    if m.group(4) == '1':
+                        self.entries["Y+"].configure(bg="red")
+                    else:
+                        self.entries["Y+"].configure(bg="green")
+                    if m.group(5) == '1':
+                        self.entries["Active"].configure(bg="green")
+                    else:
+                        self.entries["Active"].configure(bg="red")
+
+
+        if not stop_event.is_set():
+            root.after(50, self.update_display)
+
+
+    def read_serial(self):
+        while not stop_event.is_set():
+            while self.ser.in_waiting > 0:
+                try:
+                    # Read line and decode bytes to string
+                    line = self.ser.readline().decode('utf-8').strip("\r\n").replace("\0","").replace(" ","")
+                    if line:
+                        # Update the UI from the thread safely
+                        input_queue.put(line)
+                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 close_window():
+    stop_event.set()
+    root.destroy()
+
+if __name__ == "__main__":
+    root = tk.Tk()
+    root.protocol("WM_DELETE_WINDOW", close_window)
+    app = SerialGUI(root)
+    root.after(50, SerialGUI.update_display(app))
+    root.mainloop()
+
index 5aff4af498df2d3358d25cc66b37e3b8a3ec0043..78543cd004d4a4767ee37062df387bc058729421 100644 (file)
@@ -3,12 +3,13 @@
 // #include <avr/eeprom.h>
 #include "lcdlib/font.h"
 #include "lcdlib/lcd-color-graphic.h"
-#include "twi.h"
+// #include "twi.h"
 #include <avr/interrupt.h>
 #include <avr/io.h>
 #include <string.h>
 #include <usb_serial.h>
 #include <util/delay.h>
+#include <stdio.h>
 
 void getdata(uint8_t buf);
 
@@ -74,6 +75,7 @@ uint16_t time;
 uint8_t rxcnt = 0, txpoint = 0;
 uint8_t rxbuf[15];
 uint8_t txbuf[15];
+uint8_t buffer[24];
 color_t colors[16];
 color_t topback;
 volatile uint8_t keypad[16] = { 0 };
@@ -130,9 +132,17 @@ uint32_t hex_to_int32(uint8_t* buf)
     return value;
 }
 
-void send_answer_buf(uint8_t* b) { UCSR1B |= (1 << UDRIE1); }
+// void send_answer_buf(uint8_t* b) { UCSR1B |= (1 << UDRIE1); }
 // activate transmit interupt "ISR(USART1_UDRE_vect)"
 
+
+
+void send_string(uint8_t* buf, uint8_t len)
+{
+    usb_serial_write(buf, len);
+    usb_serial_flush_output();
+}
+
 // compose response string
 void send_answer_hex(uint8_t* rxbuf, uint32_t v)
 {
@@ -149,11 +159,13 @@ void send_answer_hex(uint8_t* rxbuf, uint32_t v)
     txbuf[10] = nib_to_hex(v, 0);
     txbuf[11] = '\n';
     txbuf[12] = 0;
-    usb_serial_write(txbuf, 12);
-    usb_serial_flush_output();
+    send_string(txbuf,12);
+    // usb_serial_write(txbuf, 12);
+    // usb_serial_flush_output();
     // send_answer_buf(txbuf);
 }
 
+
 // R0100000000
 /*****************************************************************
  * Setting functions
@@ -296,6 +308,56 @@ void init_spi_lcd()
 {
 }
 
+
+void updateSpeed(uint8_t speed) {
+    cli();
+    OCR1A = speeds[speed];
+    TCNT1 = 0;
+    sei();
+    lcd_set_font(FONT_PROP_16, SPACING);
+    lcd_set_area_xy(280, 320, 48, 64);
+    LCD_MOVE_TO(48, 280);
+    lcd_putstr_P(PSTR("Sp "));
+    lcd_put_short(speed);
+}
+
+void updateStepsize(void) {
+    lcd_set_font(FONT_DIGITS_32, SPACING);
+    LCD_MOVE_TO(200, 16);
+    lcd_set_area_xy(16, 240, 200, 232);
+    lcd_put_long_align32_7(stepsize);
+    lcd_set_font(FONT_PROP_16, SPACING);
+}
+
+void MoveAbsoluteX(int32_t pos) {
+    cli();
+    M1_STEPS = 0;
+    if (M1_position < pos) {
+        DIRECTION_FORWARD_M1();
+        M1_DIR = 1;
+        M1_STEPS = pos - M1_position;
+    } else {
+        DIRECTION_BACKWARD_M1();
+        M1_DIR = 0;
+        M1_STEPS = M1_position - pos;
+    }
+    sei();
+}
+
+void MoveAbsoluteY(int32_t pos) {
+    cli();
+    M2_STEPS = 0;
+    if (M2_position < pos) {
+        DIRECTION_FORWARD_M2();
+        M2_DIR = 1;
+        M2_STEPS = pos - M2_position;
+    } else {
+        DIRECTION_BACKWARD_M2();
+        M2_DIR = 0;
+        M2_STEPS = M2_position - pos;
+    }
+    sei();
+}
 /*****************************************************************
  * Communication
  *****************************************************************/
@@ -313,12 +375,50 @@ void getdata(uint8_t buf)
                 if (regnumber == 1) { // Register 1: PIN
                     send_answer_hex(&rxbuf[0], 0xabcd1234);
                 }
+                if (regnumber == 2) { // Register 2: Switches  (0,0,speed,act,yh,yl,xh,xl)
+                    send_answer_hex(&rxbuf[0], M1_SWITCH_LOW + (M1_SWITCH_HIGH<<4) + (M2_SWITCH_LOW << 8) + (M2_SWITCH_HIGH << 12) + ((uint32_t)motor_active << 16));
+                }
+                if (regnumber == 3) { // Register 3: Step size
+                    send_answer_hex(&rxbuf[0], stepsize);
+                }
+                if (regnumber == 4) { // Register 4: Speed
+                    send_answer_hex(&rxbuf[0], speed);
+                }
+                if (regnumber == 5) { // Register 5: Position X
+                    send_answer_hex(&rxbuf[0], M1_position);
+                }
+                if (regnumber == 6) { // Register 6: Position Y
+                    send_answer_hex(&rxbuf[0], M2_position);
+                }
+
             }
             if (rxbuf[0] == 'W') {
                 uint32_t value = hex_to_int32(&rxbuf[3]);
                 if (regnumber == 0) {
                     send_answer_hex(&rxbuf[0], value);
                 }
+                if (regnumber == 3) { // Register 3: Stepsize W0300000190
+                    stepsize = hex_to_int32(&rxbuf[3]);
+                    updateStepsize();
+                    send_answer_hex(&rxbuf[0], stepsize);
+                }
+
+                if (regnumber == 4) { // Register 4: Speed   W0400000007
+                    speed = hex_to_int(rxbuf[10]) & 0x7;
+                    updateSpeed( speed );
+                    send_answer_hex(&rxbuf[0], speed);
+                }
+                if (regnumber == 5) { // Register 5: Move Absolute X   W0500007895
+                    int32_t pos = hex_to_int32(&rxbuf[3]);
+                    MoveAbsoluteX(pos);
+                    send_answer_hex(&rxbuf[0], pos);
+                }
+                if (regnumber == 6) { // Register 6: Move Absolute Y   W06FFFF7895
+                    int32_t pos = hex_to_int32(&rxbuf[3]);
+                    MoveAbsoluteY(pos);
+                    send_answer_hex(&rxbuf[0], pos);
+                }
+
             }
         }
     }
@@ -327,7 +427,8 @@ void getdata(uint8_t buf)
         rxcnt = 0;
     }
 }
-
+// R0200000000
+// R0300000000
 // R0000000000
 /*****************************************************************
  * Main function
@@ -434,11 +535,7 @@ __attribute__((naked)) void main(void)
     lcd_use_foreground(colors[0]);
     lcd_putstr_xy_P(PSTR("<-->"), 32, 280);
     lcd_putstr_xy_P(PSTR("Sp 4"), 48, 280);
-    lcd_set_font(FONT_DIGITS_32, SPACING);
-    LCD_MOVE_TO(200, 46);
-    lcd_put_long(stepsize);
-    lcd_set_font(FONT_PROP_16, SPACING);
-
+    updateStepsize();
     uint16_t lasttime = 0;
 
     for (uint8_t i = 0; i < 16; i++)
@@ -545,21 +642,15 @@ __attribute__((naked)) void main(void)
                     }
                     if (keypad[5]) { // 5, toggle single step mode
                         cli();
-                        if(stepsize != 1) {
-                          stored_stepsize = stepsize;
-                          stepsize = 1;
-                          }
-                        else {
-                          stepsize = stored_stepsize;
-                          }
+                        if (stepsize != 1) {
+                            stored_stepsize = stepsize;
+                            stepsize = 1;
+                        } else {
+                            stepsize = stored_stepsize;
+                        }
                         sei();
+                        updateStepsize();
 
-                        lcd_set_font(FONT_DIGITS_32, SPACING);
-                        LCD_MOVE_TO(200, 16);
-                        lcd_set_area_xy(16, 240, 200, 232);
-                        lcd_put_long_align32_7(stepsize);
-                        lcd_set_font(FONT_PROP_16, SPACING);
-                        
                         keypad[5] = 255;
                     }
                     if (keypad[6]) { // 8
@@ -676,67 +767,34 @@ __attribute__((naked)) void main(void)
                         if (stepsize > 100000)
                             stepsize = 100000;
                         keypad[11] = 255;
-                        lcd_set_font(FONT_DIGITS_32, SPACING);
-                        LCD_MOVE_TO(200, 16);
-                        lcd_set_area_xy(16, 240, 200, 232);
-                        lcd_put_long_align32_7(stepsize);
-                        lcd_set_font(FONT_PROP_16, SPACING);
+                        updateStepsize();
                     }
                     if (keypad[3] && keypad[3] != 255) { // *
                         speed = inputbuffer & 0x7;
                         inputbuffer = 0;
                         negativeinput = 0;
-                        cli();
-                        OCR1A = speeds[speed];
-                        TCNT1 = 0;
-                        sei();
                         keypad[3] = 255;
-                        lcd_set_font(FONT_PROP_16, SPACING);
-                        lcd_set_area_xy(280, 320, 48, 64);
-                        LCD_MOVE_TO(48, 280);
-                        lcd_putstr_P(PSTR("Sp "));
-                        lcd_put_short(speed);
+                        updateSpeed(speed);
                     }
                     if (keypad[14] && keypad[14] != 255) { // C
                         if (motor_active) {
-                            cli();
-                            M1_STEPS = 0;
                             if (negativeinput)
                                 inputbuffer *= -1;
-                            if (M1_position < inputbuffer) {
-                                DIRECTION_FORWARD_M1();
-                                M1_DIR = 1;
-                                M1_STEPS = inputbuffer - M1_position;
-                            } else {
-                                DIRECTION_BACKWARD_M1();
-                                M1_DIR = 0;
-                                M1_STEPS = M1_position - inputbuffer;
-                            }
+                            MoveAbsoluteX(inputbuffer);
                             inputbuffer = 0;
                             negativeinput = 0;
-                            sei();
                         }
                         keypad[14] = 255;
                     }
                     if (keypad[15] && keypad[15] != 255) { // D
                         if (motor_active) {
-                            cli();
-                            M2_STEPS = 0;
                             if (negativeinput)
                                 inputbuffer *= -1;
-                            if (M2_position < inputbuffer) {
-                                DIRECTION_FORWARD_M2();
-                                M2_DIR = 1;
-                                M2_STEPS = inputbuffer - M2_position;
-                            } else {
-                                DIRECTION_BACKWARD_M2();
-                                M2_DIR = 0;
-                                M2_STEPS = M2_position - inputbuffer;
-                            }
+                            MoveAbsoluteY(inputbuffer);
                             inputbuffer = 0;
                             negativeinput = 0;
-                            sei();
                         }
+
                         keypad[15] = 255;
                     }
                     if (inputbuffer > 1000000)
@@ -769,7 +827,39 @@ __attribute__((naked)) void main(void)
 
                 lcd_set_font(FONT_PROP_16, SPACING);
                 LCD_MOVE_TO(24, 0);
+            // }
+            //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);
+
+                strncpy((char*)buffer, "                        ", 24);
+                buffer[0]='S';
+                buffer[1]=M1_SWITCH_LOW?'1':'0';
+                buffer[2]=M1_SWITCH_HIGH?'1':'0';
+                buffer[3]=M2_SWITCH_LOW?'1':'0';
+                buffer[4]=M2_SWITCH_HIGH?'1':'0';
+                buffer[5]=motor_active?'1':'0';
+                buffer[6]='\n';
+                buffer[7] = 0;
+                send_string(buffer,8);
+            }
+        }
 
+        lasttime = time;
+    }
+}
+// 1 shunt voltage/
+// 2 bus voltage
+// 3 power
+// 4 current
+// 5 calib 29789 -> 1/8mA
                 // i2cTx[0]=1; //switch to voltage register
                 // twi_writeTo(0x47, i2cTx, 1, 1, 1);
                 // twi_readFrom(0x47, i2cRx, 2, 1);
@@ -792,14 +882,5 @@ __attribute__((naked)) void main(void)
                 // twi_writeTo(0x47, i2cTx, 1, 1, 1);
                 // twi_readFrom(0x47, i2cRx, 2, 1);
                 // lcd_put_int(((i2cRx[0]<<8)+i2cRx[1])/8);
-            }
-        }
 
-        lasttime = time;
-    }
-}
-// 1 shunt voltage
-// 2 bus voltage
-// 3 power
-// 4 current
-// 5 calib 29789 -> 1/8mA
+