Сверлильный станок из 3D-принтера и конвертер карты сверления PCAD в G-Code

Здравствуйте, уважаемые хаброжители.
Сегодня я хочу поделиться небольшой наработкой, призванной конвертировать PCAD-овские карты сверления в G-код. Гибко, просто и open-source. Правда, прости-осспади, на Qt. Писать на нем, конечно, приятно, но вот деплоить и собирать чужие коды…

Часть первая. Механика.

Некоторое время назад я отложил свой проект по голове для приуса, и вот для чего:
Пока ждал микросхемы, пока эксперементировал со схемами, отчетливо понял, что я хочу изготавливать печатные платы дома. Да-да, есть опыт лазерно-утюжной технологии, и даже рисования лаком, но хотелось чего-то настоящего. Решено было использовать пленочный фоторезист и УФ-лампу для ногтей. Естественно, встала проблема сверления и металлизации отверстий. А получается она оттуда, что отверстия нужно металлизировать до того, как на плате протравятся дорожки. Иначе подать ток к каждому отверстию — целая история.
Получается, что ручное сверление отпадает, т.к. ориентироваться просто не по чем (бумажки с картами отверстий давайте не будем предлагать).
Решено было навесить на имеющийся 3D-принтер вместо Direct-головы — шпиндель, и так и жить. И вот тут появились решения, которыми я хотел бы сегодня поделиться.

Сменная головка для RepRap


Для того, чтобы можно было легко превращать принтер в сверлилку и обратно, решено было сделать головку разъемной. Бегунок, прикрученный к ремням — отдельно, а все остальное — съемное. Учитывая, что это не бог весть, какая сложность, отдельной статьей выкладывать смысла нет. Тут просто фото и ссылки на STL-модели, если кто захочет себе точно такую же. В архиве так же присутствуют SLDPRT-исходники, если что подправить. Качается медленно — спасибо ADSL от белтелекома, но лежать должно долго.

Результат получился вот таким:
7dbkeu1syr7kxkvvav4-kfbeyeg.jpeg

vycls3k6iheb70da3vjp-mkr6_4.jpeg

4s78vzislpsksrnupf9dlfi2yzu.jpeg

vrbtxrab-vqidu01ez17ipdzedq.jpeg

Головка-шпиндель


Тут все просто — после длительных попыток создать свой шпиндель, решил прикупить оный на AliExpress, и просто повесить на кронштейне. Фото нет, пока в процессе.

Генератор G-Code


А вот тут начинается самое интересное.
С присущим мне глобализмом я пробежался по имеющимся решениям, и понял, что каждое из них способно не только создать кучу проблем при разворачивании технологии дома, но и доставлять их регулярно и методично, вплоть до пенсии. Что не нравилось? Негибкость. Все они больше под станки, с предопределенными характеристиками шаблонов, и т.п. Да, дело не сложное. Но очень не хотелось однажды столкнуться с ситуацией, когда нужно чуть видоизменить алгоритм, и не иметь возможности это сделать. К примеру, не встречал тулзу, способную повернуть отверстия вокруг оси. А ведь после металлизации плату 1:1 не уложишь. Но это мысли на будущее. Пока мне это не нужно. Но уже можно. В целом, хотелось чего-то простого, легкого, гибкого и… работоспособного. Решил накропать самостоятельно.
В качестве базы были использованы библиотеки Qt 5.11. Приложение написано в консольном стиле. Архитектура приложения выполнена в linux-стиле.
На вход приложению подается файл DRL, выдернутый из PCAD при создании Geber-комплекта. (возможно, придется доработать парсер, если захочется скормить ему что-нибудь из AltiumDesigner. Но лично я для себя решил снести этого Альтиум-монстра от греха подальше. За что теперь он является в страшных снах, и не дает забыть собственное имя)
В качестве параметра указывается файл XML. Описанию формата этого файла будет посвящана вторая половина статьи. Этот файл, по сути, определяет механизм формирования G-Code (а на самом деле — любого текстового файла) для передачи его (G-кода) 3D-принтеру.

Механизм работы приложения


1. Читается и распознается формат DRL (который М48 или Excellon). В результате получаются инструменты, содержащие список дырок, которые этими инструментами сверлятся.
2. С полученными из п.1 данными мы идем в XML, ищем там ноду script, и попросту исполняем все, что там написано. Есть пяток операторов, а большего нам и не нужно.
3. В процессе исполнения п.2 случались операторы print. Результат печатается на выходной поток.

Часть вторая. Формат XML-файла


Для того, чтобы сделать программу максимально гибкой, была использована библиотека ScriptEngine. Сам чуточку ошалел от того, что теперь реально можно сделать при конфигурации. Основной постулат таков: есть много вычисляемых параметров, работа с которыми ведется максимально прозрачно: текст передается модулю ScriptEngine, и используется результат. Та же ситуация происходит, если в шаблоне G-Code встретится комбинация ${бла-бла-бла}. При этом все, что внутри фигурных скобок, будет передано на вычисление, а весь шаблон заменен результатом.

Исходные коды

Пример файла для моего принтера

        
                
                
                
        
        
                
                
        
        
                
                
                
                
                
                
                
                
                
                
                
        
        
        
        
                G90 ;${var hcnt=holesCount;var tcnt=toolsCount;"Hello"}
                M117 Homing
                G28 X Y
                M117 Move Z to travel
                G0 X${minX} Y${minY}
                M76
                G92 Z${ZTravelValue}
        
        
                G0 Z${ZChangeToolValue}
                M104 S0 ; disable spindle
                G0 X0 Y220
                M117 Drill finished
                M300 S600 P1
                ; Stats:
                ; Holes : ${holesCount}
                ; Tools : ${toolsCount}
        
        
                ; Tools rest: ${tcnt--}
                G0 Z${ZChangeToolValue}
                G0 X100 Y0
                M104 S0 ; disable spindle
                M117 Change tool to ${description}
                M300 S600 P1
                M76 ; pause job
                M117 Drilling
                M104 S100 ; enable spindle
                G28 X
        
        
                ; Holes rest: ${hcnt--}
                ; Percent rest: ${var percent=Math.round(hcnt*100/holesCount); percent}%
                M73 P${100-percent}
                G0 Z${ZTravelValue}
                G0 X${Math.round(x*100)/100} Y${Math.round(y*100)/100}
                G0 Z${ZDrillValue}
                G0 Z${ZTravelValue}
        
        
        



На самом деле, ничего сложного нет, если вчитаться. Но давайте разберем посекционно:

 
                
        


В секции variables, как следует из названия, мы можем определить произвольный набор глобальных переменных. Они никак не влияют на работу программы, пока не встретятся в каком-нибудь вычисляемом выражении.

 
                
        

Функции. Ну, точнее, функция. Пока она, предопределенная, одна: вычисление реального диаметра сверла для металлизированных отверстий. Известно, что металлизация крадет диаметр, и это частенько приводит к казусам при попытке просунуть ногу компонента 0,8, которая не лезет в отверстие, заложенное, как 0,9. Чтобы не возиться с этим при проектировании я решил добавить этот функционал.
Смысл этой секции — определить функции, которые может использовать конвертер для определенных целей. Эти функции нельзя (пока?) использовать самостоятельно.

 
                
        

Сверла. Тут нужно сделать отсылку к команде скрипта «align tools», про которую ниже. Каждый элемент этой секции определяет ячейку, в которую будут собраны все инструменты, распознанные во входном файле. Идея такова, что частенько при проектировании случаются дюймовые диаметры, и множество инструментов с их значениями 0,478…0,492… и т.д. Чтобы не возиться с ними, мы задаем обязательные параметры range_min и range_max. Обязателен так же признак металлизации. Ноды XML просматриваются последовательно, и как только очередной инструмент из DRL подходит под определение — нода признается подходящей.
Можно задавать любые другие параметры в ноде. Их значение можно будет позже использовать в шаблонах.
Вы можете задать позицию в пенале или координаты, где захватить сверло, если у вас станок с автосменой инструмента. А можете описать инструмент буквами для вывода на экран принтера, если у вас, как у меня, Marlin и ручная смена сверл.

 
        
                G90 ;${var hcnt=holesCount;var tcnt=toolsCount;"Hello"}
                M117 Homing
                G28 X Y
                M117 Move Z to travel
                G0 X${minX} Y${minY}
                M76
                G92 Z${ZTravelValue}
        

А вот теперь оцените всю прелесть скрипт-машины! Шаблоны. Конвертер, как я уже говорил, работает с шаблонами просто: ищет все кусочки вида ${…}, и отправляет в скрипт-машину. А там-то JS-подобный язык. Поэтому, собственно, можно даже чуточку программировать. В данном примере можно видеть, как при выводе шаблона start мы сначала определили пару переменных, которым присвоили значения глобальных. Ну, а лишь потом написали константу, которая и будет значением выполнения этого куска.

Когда этот шаблон будет выведен в выходной файл, мы увидим:

G90 ;Hello
M117 Homing
G28 X Y
M117 Move Z to travel
G0 X10 Y10
M76
G92 Z10

Ну и не могу ж не похвастаться. Оцените кусочек из шаблона для сверления каждой дырдочки:

         ; Holes rest: ${hcnt--}
                ; Percent rest: ${var percent=Math.round(hcnt*100/holesCount); percent}%
                M73 P${100-percent}


да-да… каждый раз, печатая комментарий Holes rest, мы будем декрементировать значение hcnt. А она, как мы помним, была определена, пока мы печатали start, а, стало быть, находится контекстом выше. А потом будем вычислять переменную percent, чтобы после использовать ее в другом куске — при передаче ее в команду M73 (эта команда заставляет марлин подвинуть полоску прогресса). G-Код, сгенерированный этим фрагментом:

; Holes rest: 6
; Percent rest: 13%
M73 P87

кстати, toolsCount, minX — это предопределенные имена глобальных переменных.
Отмечу, что имена шаблонов не предопределены, т.е. вы можете использовать любые. Шаблон будет распечатан, когда в скрипте встретится команда print и его имя.