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.
1. CNC Plotter - instruckcja wykonania pod linkiem:
https://st3am.pl/node/61
2. ESP 32
3. Przewody
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:
- Master (ESP32) wysyła ciąg znaków do urządzenia.
- 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. - 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, T 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:
- Usunąć biblioteki GRBL z folderu Arduino i wkleić biblioteki MIGRBL.
- 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:
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
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"""