Engraver

Typ_projektu
microPython
Zdjecie główne
Krótki opis projektu

Jest to rozwinięcie poprzedniego projektu, któego opis umieszczony jest na platformie jako "CNC Plotter" (https://st3am.pl/node/61).

Projekt rozszerza funkcjonalność maszyny poprzez dodanie możliwości wysyłania liter, które mają zostać napisane przez Engraver, z telefonu.

Projekt ten jest to jedynie prototyp, który pozwala na zapisywanie maksymalnie trzech liter jednakowej wielkości.

 

Niezbędne elementy

1. CNC Plotter - instruckcja wykonania pod linkiem:

https://st3am.pl/node/61

2. ESP 32

3. Przewody

 

 

 

Opis projektu

Wykonanie projektu składa się z kilku etapów:

1. Komunikacja UART pomiędzy ESP32 i Arduino

Program obsługujący poruszanie silnikami - GRBL, otrzymuje polecenia poprzez port szeregowy UART urządzenia Arduino, na które wgrany jest program. Polecenia są przesyłane w postaci znaków tablicy ASCII i są zapisane w języku GCODE. Proces komunikacji przebiega następująco:

  1. Master (ESP32) wysyła ciąg znaków do urządzenia.
  2. Urządzenie, po uzyskaniu informacji o zakończeniu przesyłania danych, wysyła powiadomienie o ich przyjęciu. Program analizuje przesłany tekst. Jeśli przesłane dane są poleceniem z biblioteki poleceń języka GCODE i maszyna jest w stanie wykonać dane polecenie (przed przesyłaniem komend ruchu należy wysłać komendę Enable - $X, aby maszyna była gotowa do pracy), urządzenie wysyła zwrotną wiadomość "OK" i wykonuje polecenie.
  3. Po otrzymaniu informacji zwrotnej Master przesyła kolejną porcję danych.

Masterem w naszym przypadku jest ESP32, zatem należy podłączyć jego piny zgodnie ze schematem umieszczonym w sekcji "Zdjęcia".

W bibliotece machine znajduje się klasa UART, która umożliwia tego typu komunikację pomiędzy urządzeniami połączonymi z ESP32.

Obsługa tej części projektu odbywa się w funkcji gcode_processing_loop().

2. Zmiana tekstu pisanego na GCODE

Ta część projektu może być zrealizowana na różne sposoby. Należy jednak pamiętać, że wielkość obszaru roboczego jest ograniczona, co wymaga skalowania liter tak, aby się mieściły. Alternatywnie, można poprosić użytkownika o zdefiniowanie wielkości tekstu, na podstawie której określana będzie jego maksymalna długość.

Napisanie programu zamieniającego litery na GCODE i komunikującego się z użytkownikiem jest na tyle dużym zadaniem, że mogłoby stanowić temat pracy inżynierskiej. Dlatego zdecydowałem się na najprostsze rozwiązanie: stworzenie biblioteki liter z wcześniej zdefiniowanymi wielkościami. Ogranicza to długość tekstu do trzech liter. Jednakże, ponieważ zadaniem maszyny jest pisanie inicjałów, ograniczenie to nie jest problematyczne.

Obecnie biblioteka zawiera jedynie litery A, B, C, H, J, L, M,i U. Projektowanie liter jest procesem żmudnym, a sztuczna inteligencja nie radzi sobie z tym zadaniem wystarczająco dobrze. Dlatego dalszy rozwój tej sekcji projektu raczej wiązałby się ze zmianą podejścia do konwersji tekstu na GCODE.

Obsługa tej części projektu odbywa się w funkcjach:

  • letter_spacing
  • break_gcode

Litery w GCODE zapisane są jako zmienne typu string w bibliotece letters.

Stworzono także bibliotekę lettersG0, w której wszystkie ruchy wykonywane są za pomocą komend szybkiego ruchu (G0). Użycie tej biblioteki przyspiesza pracę przy dłuższych tekstach i zwiększa precyzję działania. Jest to szczególnie ważne w przypadku maszyn o słabszych silnikach, które mają problemy z oporami ruchu. W przypadku używania innej maszyny zaleca się korzystanie z ruchów za pomocą komend G1, które zapewniają większą dokładność.

3. Komunikacja z użytkownikiem

Komunikacja z użytkownikiem może być realizowana na kilka sposobów, takich jak: podłączenie klawiatury, ekranu dotykowego, komunikacja przez Bluetooth lub WiFi. Zdecydowałem się na opcję z WiFi, używając płytki ESP32 jako punktu dostępowego (Access Point).

Strona internetowa ma prostą budowę, ponieważ jej celem jest jedynie uzyskanie pliku tekstowego od użytkownika.

Obsługa tej części projektu odbywa się w funkcjach:

  • create_access_point
  • handle_client
  • start_web_server

Program musi jednocześnie obsługiwać funkcje odpowiedzialne za działanie sieci i strony HTTP oraz sprawdzać, czy nie został przesłany tekst. Takie działanie można osiągnąć za pomocą wielowątkowości (multithreading). Jest to szerzej opisane w tym filmie:
https://www.youtube.com/watch?v=5sw9XJokAqw

4. Trzecia oś ruchu

W poprzednim projekcie wspomniano możliwość dodania trzeciej osi ruchu, ale nie została ona wprowadzona. W tej edycji projektu zaimplementowano trzecią oś.

Do jej obsługi należy podłączyć serwomotor:

  • Pin zasilający do 5V na CNC Shield.
  • Pin GND do GND na CNC Shield.
  • Pin PWM do pinu opisanego jako "End Stops Z+".

W programie GRBL piny Arduino odpowiedzialne za sygnały PWM są używane do obsługi czujników krańcowych. Ponieważ oś Z jest sterowana przez serwomotor, czujnik krańcowy nie jest potrzebny, a wolny pin można wykorzystać do sterowania motorem.

Do sterowania urządzeniem należy zmodyfikować program GRBL. Można to zrobić samodzielnie lub skorzystać z programu MIGRBL, dostępnego tutaj:
https://github.com/ikae/MI-GRBL-Z-AXIS-Servo-Controller

Po zainstalowaniu programu należy:

  1. Usunąć biblioteki GRBL z folderu Arduino i wkleić biblioteki MIGRBL.
  2. Uruchomić program grblUpload.ino i wgrać go na Arduino.

Do sterowania serwomotorem używane są następujące komendy:

  • M3 - włącza serwo.
  • M5 - wyłącza serwo.
  • S0 - ustawia serwo w pozycji zerowej.
  • S255 - wykonuje pełen obrót.

Wartości pomiędzy S0 a S255 ustawiają serwo w różnych pozycjach. Po użyciu komendy M5 ostatnia wartość S zostaje zapamiętana.

Przykład:

Zdjęcia
kod programu
  1. import _thread
    import network
    import socket
    import machine
    import time
    import lettersG0
    import re
    
    # Global variables
    is_txt_send = False
    txt = ""
    
    def safemode():
        PIN=0 #Pin D4
        Safemode_pin=machine.Pin(PIN,machine.Pin.IN,machine.Pin.PULL_UP)
        return Safemode_pin.value()==0
        
    def create_access_point(ssid="ENGRAVER", password="12345678"):
        """Set up the ESP32 as an access point."""
        ap = network.WLAN(network.AP_IF)
        ap.active(True)
        ap.config(essid=ssid, password=password)
        while not ap.active():
            pass
        print("Access point active. IP:", ap.ifconfig()[0])
        return ap
    
    def handle_client(client):
        """Handle HTTP requests from the client."""
        global txt, is_txt_send
    
        try:
            # Receive HTTP request
            request = client.recv(1024).decode()
            print("Request:", request)
    
            # Check for input submission in the GET request
            if "/submit?input=" in request:
                start = request.find("/submit?input=") + len("/submit?input=")
                end = request.find(" ", start)
                txt = request[start:end]
                txt = txt.replace("%20", " ")  # Replace encoded spaces
                is_txt_send = True
                print("Received text:", txt)
    
            # HTML response
            response = """\
    HTTP/1.1 200 OK
    Content-Type: text/html
    
    <!DOCTYPE html>
    <html>
    <head>
        <title>Podaj swoje inicjały</title>
        <style>
            body {{ font-family: Arial, sans-serif; margin: 20px; text-align: center; }}
            input[type="text"] {{ width: 300px; padding: 10px; margin: 10px 0; font-size: 16px; }}
            button {{ padding: 10px 20px; font-size: 16px; cursor: pointer; background-color: #007BFF; color: white; border: none; border-radius: 5px; }}
            button:hover {{ background-color: #0056b3; }}
        </style>
    </head>
    <body>
        <h1>Wpisz swoje inicjaly</h1>
        <form action="/submit" method="get">
            <input type="text" name="input" placeholder="Tu wpisz tekst" required>
            <button type="submit">Zatwierdz</button>
        </form>
        <p>Teraz drukuje: {}</p>
    </body>
    </html>
    """.format(txt)
    
            # Send response
            client.sendall(response.encode("utf-8"))
        except Exception as e:
            print(f"Error handling client: {e}")
        finally:
            client.close()
    
    def start_web_server():
        """Start a simple web server."""
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.bind(('', 80))
        server_socket.listen(1)
        print("Web server running...")
    
        while True:
            client, addr = server_socket.accept()
            print("Client connected from", addr)
            handle_client(client)
    
    def letter_spacing(gcode_string, n):
        width = 10 * n  # Letter spacing
    
        def modify_x(match):
            x_value = float(match.group(1))  # Extract X value
            new_x = x_value + width  # Adjust spacing
            return f"X{new_x:.6f}"  # Format X value
    
        modified_gcode = re.sub(r"X([+-]?\d*\.\d+|\d+)", modify_x, gcode_string)
        return modified_gcode + "\nG00 X0 Y0\n\n"
    
    def send_gcode(uart, gcode_commands):
        try:
            print("Connected to GRBL device via UART.")
    
            for command in gcode_commands:
                command = command.strip()
                if command:
                    uart.write((command + '\n').encode('utf-8'))
                    print(f"Sent: {command}")
    
                    time.sleep(1.3)
                    response = b""
                    while uart.any():
                        response += uart.read()
                    if response:
                        print(f"Received: {response.decode('utf-8').strip()}")
                    else:
                        print("No response received.")
    
        except Exception as e:
            print(f"An unexpected error occurred: {e}")
    
    def break_gcode(gcode):
        return [line.strip() for line in gcode.splitlines() if line.strip()]
    
    def gcode_processing_loop():
        """Continuously check if new text is received and send G-code."""
        global is_txt_send, txt
    
        # Configure UART
        UART_NUM = 2
        TX_PIN = 17
        RX_PIN = 16
        BAUD_RATE = 115200
        uart = machine.UART(UART_NUM, baudrate=BAUD_RATE, tx=machine.Pin(TX_PIN), rx=machine.Pin(RX_PIN), timeout=1000)
    
        while True:  
            if is_txt_send:
                txt_list = list(txt)
                code = ""
    
                for i, char in enumerate(txt_list):
                    if hasattr(lettersG0, char):
                        value = getattr(lettersG0, char)
                        value = letter_spacing(value, i)
                        code += value
    
                GCODE = break_gcode(code)
                print("Generated GCODE:", GCODE)
    
                # Send G-code commands
                GCODE_COMMANDS = ["$X"] + GCODE
                send_gcode(uart, GCODE_COMMANDS)
    
                # Reset flag
                is_txt_send = False
            time.sleep(0.5)  # Prevent tight looping
    
    if safemode():
        print('safemode is triggered, skip main program execution -->Ctrl+C')
        time.sleep(30)
        
    else:
        create_access_point()
        _thread.start_new_thread(start_web_server, ())  # Run the web server in a separate thread
        gcode_processing_loop()  # Continuously process G-code
  2. A="""G21
    G90
    M3S120
    G00 X5.034791 Y11.71454
    M3S90
    G00 X4.832278 Y12.55304 F10
    G00 X4.669229 Y11.64511
    G00 X4.471908 Y10.74672
    G00 X4.243432 Y9.85649
    G00 X3.073009 Y5.748372
    G00 X6.713055 Y5.748372
    G00 X5.597674 Y9.627808
    G00 X5.289231 Y10.73991
    G00 X5.034791 Y11.71454
    M3S120
    G00 X5.6496 Y14
    M3S90
    G00 X10 Y0 F0
    G00 X8.390281 Y0
    G00 X7.155468 Y4.242878
    G00 X2.656558 Y4.242878
    G00 X1.498598 Y0
    G00 X0 Y0
    G00 X4.096999 Y14
    G00 X5.6496 Y14
    M3S120"""
    
    B="""G21
    G90
    M3S120
    G00 X7.077596 Y11.74993
    M3S90
    G00 X6.776476 Y11.99222 F10
    G00 X6.414876 Y12.16101
    G00 X5.931026 Y12.26991
    G00 X5.259297 Y12.33525
    G00 X4.402265 Y12.35703
    G00 X1.756531 Y12.35703
    G00 X1.756531 Y8.114146
    G00 X4.618453 Y8.114146
    G00 X5.322352 Y8.131843
    G00 X5.878266 Y8.186291
    G00 X6.288766 Y8.276131
    G00 X6.697979 Y8.447642
    G00 X7.033844 Y8.681769
    G00 X7.296359 Y8.97579
    G00 X7.484236 Y9.329704
    G00 X7.596191 Y9.738065
    G00 X7.634796 Y10.20224
    G00 X7.598765 Y10.65007
    G00 X7.494531 Y11.0598
    G00 X7.318235 Y11.43277
    G00 X7.077596 Y11.74993
    M3S120
    G00 X7.521554 Y5.770151
    M3S90
    G00 X7.170249 Y6.042392 F10
    G00 X6.754601 Y6.237044
    G00 X6.239866 Y6.36772
    G00 X5.593874 Y6.445309
    G00 X4.816626 Y6.471172
    G00 X1.756531 Y6.471172
    G00 X1.756531 Y1.641614
    G00 X5.05469 Y1.641614
    G00 X5.564277 Y1.649781
    G00 X5.961909 Y1.671561
    G00 X6.246301 Y1.709674
    G00 X6.62849 Y1.802236
    G00 X6.965641 Y1.93019
    G00 X7.257753 Y2.092173
    G00 X7.513834 Y2.297715
    G00 X7.736455 Y2.559067
    G00 X7.926908 Y2.873505
    G00 X8.072321 Y3.232863
    G00 X8.159824 Y3.626252
    G00 X8.189423 Y4.056393
    G00 X8.146957 Y4.557317
    G00 X8.01956 Y5.01332
    G00 X7.807232 Y5.421682
    G00 X7.521554 Y5.770151
    M3S120
    G00 X8.444216 Y12.80486
    M3S90
    
    G00 X8.834127 Y12.26582 F10
    G00 X9.122379 Y11.66826
    G00 X9.296101 Y11.05299
    G00 X9.35401 Y10.41867
    G00 X9.302536 Y9.831988
    G00 X9.149403 Y9.268449
    G00 X8.894608 Y8.728051
    G00 X8.536867 Y8.233933
    G00 X8.073606 Y7.807876
    G00 X7.507399 Y7.448518
    G00 X8.24733 Y7.142246
    G00 X8.862438 Y6.718911
    G00 X9.355295 Y6.178513
    G00 X9.713036 Y5.54419
    G00 X9.929224 Y4.837725
    G00 X10 Y4.059115
    G00 X9.956247 Y3.417988
    G00 X9.826277 Y2.808167
    G00 X9.607515 Y2.229655
    G00 X9.324411 Y1.708313
    G00 X9.001415 Y1.274089
    G00 X8.635954 Y0.92562
    G00 X8.217731 Y0.645212
    G00 X7.73517 Y0.413807
    G00 X7.186977 Y0.232766
    G00 X6.561575 Y0.103452
    G00 X5.849955 Y0.025863
    G00 X5.049543 Y0
    G00 X0 Y0
    G00 X0 Y14
    G00 X4.965899 Y14
    G00 X5.910437 Y13.95236
    G00 X6.721142 Y13.81079
    G00 X7.399305 Y13.57394
    G00 X7.965513 Y13.24045
    G00 X8.444216 Y12.80486
    M3S120"""
    C="""G21
    G90
    M3S120
    G00 X8.196739 Y12.96604
    M3S90
    G01 X8.890725 Y12.18233 F10
    G01 X9.428288 Y11.21291
    G01 X9.810531 Y10.05645
    G01 X8.338841 Y9.641547
    G01 X8.037013 Y10.53721
    G01 X7.656973 Y11.25506
    G01 X7.19762 Y11.79377
    G01 X6.655651 Y12.16784
    G01 X6.02776 Y12.39308
    G01 X5.315047 Y12.46684
    G01 X4.49328 Y12.38386
    G01 X3.76735 Y12.13491
    G01 X3.136153 Y11.71869
    G01 X2.609606 Y11.16417
    G01 X2.199824 Y10.49374
    G01 X1.905706 Y9.710039
    G01 X1.708526 Y8.860476
    G01 X1.589557 Y7.993791
    G01 X1.549901 Y7.109982
    G01 X1.597268 Y6.007527
    G01 X1.737167 Y5.002541
    G01 X1.969597 Y4.095023
    G01 X2.301168 Y3.307367
    G01 X2.736285 Y2.665914
    G01 X3.274951 Y2.169348
    G01 X3.881912 Y1.815034
    G01 X4.52082 Y1.601656
    G01 X5.192774 Y1.531847
    G01 X5.987001 Y1.627999
    G01 X6.695307 Y1.917772
    G01 X7.318792 Y2.39985
    G01 X7.836528 Y3.071597
    G01 X8.228684 Y3.930379
    G01 X8.497467 Y4.976197
    G01 X10 Y4.524415
    G01 X9.602335 Y3.169066
    G01 X9.038335 Y2.046853
    G01 X8.306896 Y1.157776
    G01 X7.431152 Y0.513689
    G01 X6.434237 Y0.129081
    G01 X5.316149 Y0
    G01 X4.172725 Y0.098786
    G01 X3.181317 Y0.395145
    G01 X2.341925 Y0.890394
    G01 X1.639127 Y1.572679
    G01 X1.057502 Y2.432779
    G01 X0.598149 Y3.470694
    G01 X0.265477 Y4.625835
    G01 X0.066094 Y5.836298
    G01 X0 Y7.100762
    G01 X0.074906 Y8.450843
    G01 X0.299625 Y9.681062
    G01 X0.675259 Y10.7901
    G01 X1.188588 Y11.75689
    G01 X1.829698 Y12.55377
    G01 X2.597488 Y13.18205
    G01 X3.455607 Y13.63647
    G01 X4.371007 Y13.90912
    G01 X5.341485 Y14
    G01 X6.413307 Y13.88541
    G01 X7.365059 Y13.54031
    G01 X8.196739 Y12.96604
    M3S120"""
    
    H="""G21
    G90
    M3S120
    G00 X10 Y14
    M3S90
    G01 X10 Y0 F10
    G01 X8.304327 Y0
    G01 X8.304327 Y6.588235
    G01 X1.696917 Y6.588235
    G01 X1.696917 Y0
    G01 X0 Y0
    G01 X0 Y14
    G01 X1.696917 Y14
    G01 X1.696917 Y8.231211
    G01 X8.304327 Y8.231211
    G01 X8.304327 Y14
    G01 X10 Y14
    M3S120"""
    
    J="""G21
    G90
    M3S120
    G00 X9.999999 Y14
    M3S90
    G01 X9.999999 Y4.618916 F10
    G01 X9.939769 Y3.553218
    G01 X9.755535 Y2.661566
    G01 X9.449069 Y1.942622
    G01 X9.007971 Y1.360237
    G01 X8.425154 Y0.878263
    G01 X7.698848 Y0.496701
    G01 X6.853852 Y0.220905
    G01 X5.918511 Y0.054891
    G01 X4.889283 Y0
    G01 X3.417183 Y0.116477
    G01 X2.194862 Y0.461892
    G01 X1.222321 Y1.041599
    G01 X0.520815 Y1.848905
    G01 X0.113375 Y2.881132
    G01 X0 Y4.13962
    G01 X2.162976 Y4.364541
    G01 X2.288751 Y3.420675
    G01 X2.549158 Y2.697715
    G01 X2.944198 Y2.198336
    G01 X3.466784 Y1.874343
    G01 X4.10806 Y1.678875
    G01 X4.866253 Y1.614612
    G01 X5.443755 Y1.649421
    G01 X5.96457 Y1.753849
    G01 X6.430469 Y1.929234
    G01 X6.821966 Y2.163527
    G01 X7.124889 Y2.448695
    G01 X7.335695 Y2.784738
    G01 X7.472099 Y3.215837
    G01 X7.555358 Y3.790188
    G01 X7.583702 Y4.509133
    G01 X7.583702 Y14
    G01 X9.999999 Y14
    M3S120"""
    
    L="""G21
    G90
    M3S120
    G00 X2.125837 Y14
    M3S90
    G01 X2.125837 Y1.641614 F10
    G01 X10 Y1.641614
    G01 X10 Y0
    G01 X0 Y0
    G01 X0 Y14
    G01 X2.125837 Y14
    M3S120"""
    
    M="""G21
    G90
    M3S120
    G00 X10 Y14
    M3S90
    G00 X10 Y0 F10
    G00 X8.667822 Y0
    G00 X8.667822 Y11.71181
    G00 X5.606972 Y0
    G00 X4.357354 Y0
    G00 X1.332178 Y11.92688
    G00 X1.332178 Y0
    G00 X0 Y0
    G00 X0 Y14
    G00 X2.080318 Y14
    G00 X4.558149 Y4.089062
    G00 X4.766079 Y3.243753
    G00 X4.933239 Y2.553622
    G00 X5.058608 Y2.017307
    G00 X5.200286 Y2.606709
    G00 X5.387831 Y3.354011
    G00 X5.619203 Y4.260574
    G00 X8.14392 Y14
    G00 X10 Y14
    M3S120"""
    
    T="""G21
    G90
    M3S120
    G00 X10 Y14
    M3S90
    G00 X10 Y12.35703 F10
    G00 X5.837937 Y12.35703
    G00 X5.837937 Y0
    G00 X4.162063 Y0
    G00 X4.162063 Y12.35703
    G00 X0 Y12.35703
    G00 X0 Y14
    G00 X10 Y14
    M3S120"""
    
    U="""G21
    G90
    M3S120
    G00 X10 Y14
    M3S90
    G01 X10 Y6.046094 F10
    G01 X9.951691 Y4.756814
    G01 X9.808002 Y3.658984
    G01 X9.566456 Y2.749928
    G01 X9.199802 Y1.986803
    G01 X8.67831 Y1.325428
    G01 X8.000743 Y0.764464
    G01 X7.168339 Y0.338721
    G01 X6.177382 Y0.085684
    G01 X5.030348 Y0
    G01 X3.911805 Y0.073635
    G01 X2.938189 Y0.29454
    G01 X2.108262 Y0.665392
    G01 X1.423263 Y1.176819
    G01 X0.881952 Y1.819451
    G01 X0.485569 Y2.594626
    G01 X0.216772 Y3.53983
    G01 X0.054503 Y4.689873
    G01 X0 Y6.046094
    G01 X0 Y14
    G01 X1.690821 Y14
    G01 X1.690821 Y6.050111
    G01 X1.725505 Y4.957636
    G01 X1.827078 Y4.076695
    G01 X1.998018 Y3.404609
    G01 X2.251951 Y2.881132
    G01 X2.603741 Y2.444678
    G01 X3.053388 Y2.093909
    G01 X3.589744 Y1.838194
    G01 X4.199183 Y1.684231
    G01 X4.884182 Y1.633356
    G01 X6.000248 Y1.735106
    G01 X6.879723 Y2.039017
    G01 X7.522607 Y2.545089
    G01 X7.959866 Y3.339007
    G01 X8.222469 Y4.507794
    G01 X8.310418 Y6.050111
    G01 X8.310418 Y14
    G01 X10 Y14
    M3S120"""
    
    
Schemat
Tagi
#CNC #CNCploter #microPython #Arduino #CNCShiled
Odnośniki zewnętrzne
https://youtube.com/C-s6MmSz-JA?si=9wuKqtn4UQ5Ge3YK