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

    Здравствуйте, уважаемые хаброжители.

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

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


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

    Получается, что ручное сверление отпадает, т.к. ориентироваться просто не по чем (бумажки с картами отверстий давайте не будем предлагать).

    Решено было навесить на имеющийся 3D-принтер вместо Direct-головы — шпиндель, и так и жить. И вот тут появились решения, которыми я хотел бы сегодня поделиться.

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


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

    Результат получился вот таким:









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


    Тут все просто — после длительных попыток создать свой шпиндель, решил прикупить оный на 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 встретится комбинация ${бла-бла-бла}. При этом все, что внутри фигурных скобок, будет передано на вычисление, а весь шаблон заменен результатом.

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

    Пример файла для моего принтера
    <xml>
    	<variables>
    		<var name="ZChangeToolValue" value="30"/>
    		<var name="ZTravelValue" value="10"/>
    		<var name="ZDrillValue" value="0"/>
    	</variables>
    	<functions>
    		<!--
    			predefined function with single parameter:
    			a - source (requested) diameter
    			returns - suggested tool diameter for give requested after halvanic
    			if function nod defined, it assumed return=a
    		-->
    		<plate_increase_dia f="a+0.2"/>
    	</functions>
    	<tools>
    		<!--
    			"tool" node defines a real drill tool for make a hole
    			Depends on your technical process you can set up different
    			tools for plated or not holes or join same holes in single tool.
    			Required parameters for tool are:
    			1. range_min,range_max - diameters range to assign holes for this tool
    			You can joun different diameters (f.ex. 0.31-0.4) to single tool
    			2. plated="yes|no|both" - defines plated property to
    			Other parameters are optional and can be used later in G-Code patterns.
    			For example, you can define tool position or toolbox coords for the tool.
    		-->
    		<tool description="0,3mm" range_min="0" range_max="0.3" plated="both" position="0" />
    		<tool description="0,4mm" range_min="0.3" range_max="0.4" plated="both" position="1" />
    		<tool description="0,5mm" range_min="0.4" range_max="0.5" plated="both" position="2" />
    		<tool description="0,6mm" range_min="0.5" range_max="0.6" plated="both" position="3" />
    		<tool description="0,7mm" range_min="0.6" range_max="0.7" plated="both" position="4" />
    		<tool description="0,8mm" range_min="0.7" range_max="0.8" plated="both" position="5" />
    		<tool description="0,9mm" range_min="0.8" range_max="0.9" plated="both" position="6" />
    		<tool description="1,0mm" range_min="0.9" range_max="1.0" plated="both" position="7" />
    		<tool description="1,1mm" range_min="1.0" range_max="1.1" plated="both" position="8" />
    		<tool description="1,2mm" range_min="1.1" range_max="5" plated="both" position="9" />
    	</tools>
    	<patterns>
    	<!-- in any pattern you can use any variable from context where it's printing
    		Example (used inside 'tool' loop type):
    			Mnnn Please, change tool to ${description} ; message to lcd
    			Mnnn ; pause
    		Note : here ${description} is optional tag defined in <tool> node
    		Use this example outsite the tool loop will cause calculation error.
    	-->
    	<pattern name="start">
    		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}
    	</pattern>
    	<pattern name="finish">
    		G0 Z${ZChangeToolValue}
    		M104 S0 ; disable spindle
    		G0 X0 Y220
    		M117 Drill finished
    		M300 S600 P1
    		; Stats:
    		; Holes : ${holesCount}
    		; Tools : ${toolsCount}
    	</pattern>
    	<pattern name="set_tool">
    		; 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
    	</pattern>
    	<pattern name="go_drill">
    		; 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}
    	</pattern>
    	</patterns>
    	<script>
    	<!--
    		"assign tools". No parameters
    		Just assign all tools declared in
    		DRL-file to tools described in <tools> node.
    		For each DRL-defined tool will be selected FIRST compatible
    		tool from <tools> node. I.e. if range 0.3..0.8 will be defined early,
    		<tool> node for diameter 0.4..0.5 will never be assigned.
    		Except 'plated' property will be different.
    	-->
    	<command verb="assign tools" />
    	<!--
    		"assign tools". No parameters
    		Join all DRL-file tools, assigned to same tool here
    		to one tool (also holes)
    		Just avoid multiply changing physical tool to same
    	-->
    	<command verb="join tools" />
    	<!--
    		"offset".
    		Offset ALL holes by defined values
    		xoffs, yoffs - values to offset. Before offset will be calculated
    		i.e. here you can use global variables.
    	-->
    	<command verb="offset" xoffs="-minX+10" yoffs="-minY+10"/>
    	<!--
    		loop for each DRL-tool (assigned and joined before).
    		Context inside will be filled also with tool's properties and
    		node's parameters
    	-->
    	<command verb="print" pattern="start"/>
    	<loop type="tools">
    		<command verb="print" pattern="set_tool"/>
    		<command verb="print context" line_begin=";"/>
    		<!--
    			loop for each hole inside the tool.
    			Context inside will be filled also with hole's properties(x&y) and
    			node's parameters
    		-->
    		<loop type="toolholes">
    			<command verb="print" pattern="go_drill"/>
    			<!--
    				"print context".
    				Anwhere in script you can use this verb.
    				It inserts all context variables available.
    				Usefull for debug but completely useless for
    				normal work
    			-->
    			<command verb="жprint context" line_begin=";"/>
    		</loop>
    	</loop>
    	<command verb="print" pattern="finish"/>
    </script>
    </xml>
    



    И версия этого же файла после практических испытаний
    <xml>
    	<variables>
                    <var name="ZChangeToolValue" value="10"/>
                    <var name="ZTravelValue" value="2"/>
                    <var name="ZDrillValue" value="-3"/>
                    <var name="FeedHorizontal" value="24000"/>
                    <var name="FeedDown" value="100"/>
                    <var name="FeedFree" value="2000"/>
                    <var name="StartOffsX" value="20"/>
                    <var name="StartOffsY" value="20"/>
                    <var name="ZZeroPosition" value="0.1"/>
                    <var name="first" value="0"/>
            </variables>
    	<functions>
    		<!--
    			predefined function with single parameter:
    			a - source (requested) diameter
    			returns - suggested tool diameter for give requested after halvanic
    			if function nod defined, it assumed return=a
    		-->
                    <plate_increase_dia f="a+0.3"/>
    	</functions>
    	<tools>
    		<!--
    			"tool" node defines a real drill tool for make a hole
    			Depends on your technical process you can set up different
    			tools for plated or not holes or join same holes in single tool.
    			Required parameters for tool are:
    			1. range_min,range_max - diameters range to assign holes for this tool
    			You can joun different diameters (f.ex. 0.31-0.4) to single tool
    			2. plated="yes|no|both" - defines plated property to
    			Other parameters are optional and can be used later in G-Code patterns.
    			For example, you can define tool position or toolbox coords for the tool.
    		-->
    		<tool description="0,3mm" range_min="0" range_max="0.3" plated="both" position="0" />
    		<tool description="0,4mm" range_min="0.3" range_max="0.4" plated="both" position="1" />
    		<tool description="0,5mm" range_min="0.4" range_max="0.5" plated="both" position="2" />
    		<tool description="0,6mm" range_min="0.5" range_max="0.6" plated="both" position="3" />
    		<tool description="0,7mm" range_min="0.6" range_max="0.7" plated="both" position="4" />
    		<tool description="0,8mm" range_min="0.7" range_max="0.8" plated="both" position="5" />
    		<tool description="0,9mm" range_min="0.8" range_max="0.9" plated="both" position="6" />
    		<tool description="1,0mm" range_min="0.9" range_max="1.0" plated="both" position="7" />
    		<tool description="1,1mm" range_min="1.0" range_max="1.1" plated="both" position="8" />
    		<tool description="1,2mm" range_min="1.1" range_max="5" plated="both" position="9" />
    	</tools>
    	<patterns>
    	<!-- in any pattern you can use any variable from context where it's printing
    		Example (used inside 'tool' loop type):
    			Mnnn Please, change tool to ${description} ; message to lcd
    			Mnnn ; pause
    		Note : here ${description} is optional tag defined in <tool> node
    		Use this example outsite the tool loop will cause calculation error.
    	-->
            <pattern name="start1">
            ; Start
            </pattern>
            <pattern name="set_tool1">
            ; Set tool ${description}
            </pattern>
            <pattern name="finish1">
            ; Finish
            </pattern>
            <pattern name="go_drill1">
            ; Drill X${Math.round(x*100)/100} Y${Math.round(y*100)/100}
            </pattern>
            <pattern name="start">
                    ;${var hcnt=holesCount;var tcnt=toolsCount;"Hello"}
    		M117 Homing
                    G28
                    G0 Z0 F${FeedFree}
                    G92 Z1.6
            </pattern>
    	<pattern name="finish">
                    G0 Z${ZChangeToolValue} F${FeedFree}
                    M400
                    M5 ; disable spindle
                    G0 X0 Y220 F${FeedHorizontal}
    		M117 Drill finished
                    M300 S600 P100
    		; Stats:
    		; Holes : ${holesCount}
    		; Tools : ${toolsCount}
    	</pattern>
    	<pattern name="set_tool">
    		; Tools rest: ${tcnt--}
                    G0 Z${ZChangeToolValue} F${FeedFree}
                    M400
                    G0 X100 Y0 F${FeedHorizontal}
                    M117 Stopping spindle
                    M5 ; disable spindle
    		M117 Change tool to ${description}
                    M300 S600 P100
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M25
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    G28 X Y
                    G0 X${StartOffsX-1} Y${StartOffsX-1} Z${ZTravelValue} F${FeedHorizontal}
                    G0 Z${ZZeroPosition} F${FeedFree}
                    M117 Check zero-hole
                    M300 S600 P100
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M25
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
                    G92 Z${ZZeroPosition} F${FeedDown}
                    M117 Starting spindle
                    M3 ; enable spindle
                    G0 Z${ZDrillValue} F${FeedDown/3}
                    G0 Z${ZTravelValue} F${FeedFree}
                    M117 Drilling
                    M117 Starting spindle
                    M3 ; enable spindle
    	</pattern>
    	<pattern name="go_drill">
    		; Holes rest: ${hcnt--}
    		; Percent rest: ${var percent=Math.round(hcnt*100/holesCount); percent}%
    		M73 P${100-percent}
                    M117 Drilling X${Math.round(x*100)/100} Y${Math.round(y*100)/100} Z${ZTravelValue}
                    G0 Z${ZTravelValue} F${FeedFree}
                    G0 X${(Math.round(x*100)/100)-2} Y${(Math.round(y*100)/100)-2}  F${FeedHorizontal}
                    G0 X${Math.round(x*100)/100} Y${Math.round(y*100)/100}  F${FeedHorizontal}
                    M400
                    G0 Z${Math.round((ZZeroPosition+0.2)*100)/100} F${FeedFree}
                    G0 Z${Math.round((ZZeroPosition-0.3)*100)/100} F${FeedDown/10}
                    G0 Z${ZDrillValue} F${FeedDown}
                    M117 Return
                    G0 Z${ZTravelValue} F${FeedFree}
            </pattern>
            <pattern name="second_time">
                ; ${var hcnt=holesCount;var tcnt=toolsCount;"SECOND!!!"}
            </pattern>
            </patterns>
    	<script>
    	<!--
    		"assign tools". No parameters
    		Just assign all tools declared in
    		DRL-file to tools described in <tools> node.
    		For each DRL-defined tool will be selected FIRST compatible
    		tool from <tools> node. I.e. if range 0.3..0.8 will be defined early,
    		<tool> node for diameter 0.4..0.5 will never be assigned.
    		Except 'plated' property will be different.
    	-->
    	<command verb="assign tools" />
    	<!--
    		"assign tools". No parameters
    		Join all DRL-file tools, assigned to same tool here
    		to one tool (also holes)
    		Just avoid multiply changing physical tool to same
    	-->
    	<command verb="join tools" />
    	<!--
    		"offset".
    		Offset ALL holes by defined values
    		xoffs, yoffs - values to offset. Before offset will be calculated
    		i.e. here you can use global variables.
    	-->
            <command verb="offset" xoffs="-minX+StartOffsX" yoffs="-minY+StartOffsY"/>
    	<!--
    		loop for each DRL-tool (assigned and joined before).
    		Context inside will be filled also with tool's properties and
    		node's parameters
    	-->
            <command verb="sort tools"/>
            <command verb="print" pattern="start"/>
            <loop type="tools">
                    <condition content="first++==0">
                        <command verb="print" pattern="set_tool"/>
                    </condition>
    		<command verb=";print context" line_begin=";"/>
    		<!--
    			loop for each hole inside the tool.
    			Context inside will be filled also with hole's properties(x&y) and
    			node's parameters
    		-->
    		<loop type="toolholes">
                            <command verb="print" pattern="go_drill"/>
    			<!--
    				"print context".
    				Anwhere in script you can use this verb.
    				It inserts all context variables available.
    				Usefull for debug but completely useless for
    				normal work
    			-->
    			<command verb=";print context" line_begin=";"/>
    		</loop>
    	</loop>
            <condition content="first=0">
                <command verb=";dummy"/>
            </condition>
            <command verb="print" pattern="second_time"/>
            <loop type="tools">
                <condition content="first++>0">
                    <command verb="print" pattern="set_tool"/>
                    <command verb=";print context" line_begin=";"/>
                    <!--
                            loop for each hole inside the tool.
                            Context inside will be filled also with hole's properties(x&y) and
                            node's parameters
                    -->
                    <loop type="toolholes">
                            <command verb="print" pattern="go_drill"/>
                            <!--
                                    "print context".
                                    Anwhere in script you can use this verb.
                                    It inserts all context variables available.
                                    Usefull for debug but completely useless for
                                    normal work
                            -->
                            <command verb=";print context" line_begin=";"/>
                    </loop>
                </condition>
            </loop>
            <command verb="print" pattern="finish"/>
    </script>
    </xml>
    



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

    	<variables>
    		<var name="Название переменной" value="Значение переменной"/>
    	</variables>
    

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

    	<functions>
    		<plate_increase_dia f="a+0.2"/>
    	</functions>
    

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

    Смысл этой секции — определить функции, которые может использовать конвертер для определенных целей. Эти функции нельзя (пока?) использовать самостоятельно.

    	<tools>
    		<tool description="0,3mm" range_min="0" range_max="0.3" plated="both" position="0" />
    	</tools>
    

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

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

    	<patterns>
    	<pattern name="start">
    		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}
    	</pattern>
    

    А вот теперь оцените всю прелесть скрипт-машины! Шаблоны. Конвертер, как я уже говорил, работает с шаблонами просто: ищет все кусочки вида ${...}, и отправляет в скрипт-машину. А там-то 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 и его имя.

    <script>
    	<command verb="assign tools" />
    

    И основа — секция script


    Внутри секции могут встречаться ноды с именами command и loop.

    Формат ноды command:

    	<command verb="оператор действия" .... параметры для оператора ... />
    

    Оператор действия — один из немногих операторов. Параметры для каждого описаны ниже. Могут быть дополнены любыми другими, которые, как вы уже поняли, можно использовать в шаблонах.

    Формат ноды loop:

    <loop type="тип цикла">
    .....
    </loop>
    

    цикл — это секция, содержимое которой будет исполнено для каждого элемента, определенного типом цикла. Их два (пока):

    tools — цикл выполнится для каджого инструмента, и
    toolholes — цикл выполнится для каждой дырки, предназначенной для сверления этим инструментом. Очевидно, что цикл toolholes может быть только вложенным в tools.
    При этом при выполнении вложенного цикла доступны все переменные для текущего инструмента. Зачем? Не знаю. Просто рассказал.

    Операторы


    assign tools
    Параметры: нет.

    Проводит присвоение каждому сверлу из исходного файла инструмента из XML. Без него большинство других действий не имеют смысла.

    join tools
    Параметры: нет.
    Больше организационный — объединяет все инструменты, которым был присвоен один и тот же из XML-файла. Имеет смысл сразу после assign tools, но я решил дать возможность пользователю сделать свои операции.

    sort tools
    Параметры: нет (пока).
    Сортирует сверла по возрастанию диаметров

    offset
    Параметры:
    xoffs,yoffs — значения смещений. Работает скрипт-машина.
    Смещает все отверстия на указанные значения. Да, часто так бывает, что плата разводится далеко не в начале координат.

    print
    Параметр:
    pattern Название шаблона.
    Печатает шаблон с указанным названием на выходной поток.

    print context
    Параметры:
    line_begin, line_end — начало и конец каждой строки.
    Отладочная штука — позволяет в любом месте вывести на выход все доступные в данный момент переменные и их значения. Каждая переменная выводится отдельной строкой, начало и конец указаны в параметрах

    Предопределенные имена глобальных переменных.


    holesCount, toolsCount — я очень, очень, очень надеюсь, что смысл этих переменных в пояснении не нуждается. Да-да. Это количество инструментов и количество отверстий.
    minX, maxX, minY, maxY — и этих тоже. Нет, ну на всякий случай — это координаты поля сверления. Все дырочки находятся внутри этого прямоугольника. Пересчитывается после команды offset.

    Заключение


    Вот, собственно, постарался вкратце, но максимально полно описать сотворенную тулзу.

    Честно говоря, пока пытался представить сценарии использования, я отчетливо представлял себе, сколько раз проявится татаро-монгольское иго на землях русских (есть мнение, что именно они принесли нам мат).

    Отсюда вопрос: стоит ли заморочится, и сделать простенькую веб-страничку, куда можно вставить вход и скрипт, и получить готовый G-Code, минуя стадии сборки из исходников?

    UPD:
    Спасибо проголосовавшим. Запилил. И… да: я же писал, что веб-страничка будет простенькой? Если кому-нибудь будет не лень привести это все в более эстетический вид — бросайте HTML в личку.

    Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

    Есть ли потребность запилить веб-версию этой утилиты

    • 10,8%Да, буду пользоваться постоянно4
    • 43,2%Да, хотелось бы использовать время от времени16
    • 21,6%Нет, мне проще собрать, и работать локально8
    • 24,3%Нет, этот функционал совершенно не интересен9
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 46

      +2
      Что то просверлить удалось этим принтером?
      Мне кажется, что у принтеров жесткость по осям никакая. Да и стол на пружинках )))
        0

        пока нет: я в процессе прикручивания шпинделя. Но есть мысль, что точность +-0.2 для плат достаточна. А пружинки со стола ч ликвидировал, да. Заменил жесткими стойками. И планирую перейти на винты вместо ремней.

          0
          Но есть мысль, что точность +-0.2 для плат достаточна.
          Это максимальный зазор/проводник, в который я укладываюсь в простых платах. При этом поясок переходных отверстий 0.15-0.2.

          С другой стороны, для условно односторонних (без настоящих переходных отверстий, но с переходом слоев через ноги компонентов) DIP плат неплохое решение в плане соотношения цены и скорости. Кроме того, сдвиг сверловки, скорее всего, будет одинаковый для всех отверстий, и уже по ним можно ± нормально выравнивать млой фольги.
            0
            пожелаю удачи.
            работа по софту не плохая.
            я проходил постройку собственного станка с ЧПУ и скажу, что 8 мм направляющие на 20см не имеют достаточной жесткости.
            Вы будете толкать сверло в текстолит, а вместо контроля глубины будут отгибаться направляющие, причем в каждом месте положения каретки — по разному (максимально по центру).
            Интересно все таки посмотреть результат.
              0

              самому интересно. но этот эффект планирую компенсировать скоростью подачи. Да и шпиндельный мотор почти пол-кило весит

                +1
                мне кажется вес шпинделя принесет дополнительные трудности.
                вибрации при остановке и смене направления.
                неравномерный 0 стола.
                сверление текстолита мелким сверлом больше похож на процесс прокалывания.
                сдается мне не одно свело сгинет пока скорость подберете.
                ждем успехов

                  0
                  я надеюсь, что еще пара-тройка недель, учитывая мой график, и это свершится ) А сверлами помогут братья-китайцы )
                    0
                    Чем меньше диаметр сверла, тем больше оборотов шпинделя необходимо. Стеклотекстолит, фольгированный, который для печатных плат, вообще на оборотах от 10 тысяч в минуту. И свёрла нужны твердосплавные, обычные очень быстро слизываются. Если шпиндель будет на 30 тыс. об. в минуту, то принтер даже не заметит прохода сверла через плату. Думал как-то недорогой оборотистый бесколлекторник с драйвером на Али заказать, ну и патрончик на вал, выбор там большой.
                      0
                      Да, все это учитываю, спс. Правда, начать решил с коллекторника, но суть та же: обороты до 12 тысяч на 48-ми вольтах. Для того, чтобы раздобыть оные, прям рядом с двигом хочу повесить DC/DC 12/48 с того же китая. Стол сделал сменный, на 4-х стойках, и с разъемом. Т.е. теперь можно открутить 4 винта, и сменить стол на другой. С головой планирую проделать ту же операцию, и для питания шпинделя использовать 12В нагрева экструдера. Ну и, опять же, идея в том, что для домашнего очага скорость подачи вторична. Плату с 40-ка отверстиями можно засверлить и с мееедленной подачей сверла в текстолит. Что, по логике, должно снять напряжение на направляющие. Если будет не хватать, то будем городить бесколлекторник. Но сдается мне, что тогда уж проще будет сварганить отдельный фрезер с нормальными направляющими, ШВП и прочими ништяками.
              0
              Согласен с вами, нужно переделать ремни немного для таких целей по осям и заготовку крепить на саму раму стола, дерево точить проблемно наверное, а вот воск можно думаю. Идея классная, тоже озадачивался подобным, но пока руки не доходят
              +1
              Конструкция на фото совсем хлипкая. А для сверловки нужна точность и жесткость.
              То есть фрезер а не принтер.
              А софт фрезера прекрасно понимает drl файлы.

              Другое дело если приделать лазерную голову и выжигать лазером краску для изготовления плат…
              Есть идея попробовать такой способ.
                0

                Есть подозрение, что не так уж она и нужна. Выводные компоненты сегодня — редкость, а смещение переходных на +- 2 десятки… пока проблем не вижу. Быть может, разгляжу в процессе. Ксьати, глядя на то, как люди работали на софте фрезера… я и подумал, что и туда применимо.
                И для лазера, кстати, тож, подойдет )) Получилось гибко.

                  0
                  При поверхностном монтаже ±2 десятки могут унести переходное в соседний пад.
                    0
                    Смещение 2 десятки — многовато. 4 класс это переходные 0,4-0,6 можно запросто промахнуться. Ну или делать платы грубее.
                    Без выводных компонентов даже сейчас никак не обойтись. А еще разъемы и переходные отверстия. Так что сверловка очень актуальна.
                    Знакомые пытаются сделать лазерно-сверлильный станок для плат. Тоже на базе механики принтера. Но чтото у них плохо движется. Так что сейчас делаем прототипы на моем фрезере.

                    А скормить отверстия станку можно многими способами. У моего фрезера софт сам понимает drl из альтиума. А еще можно выгрузить отверстия в обычный dxf и скормить его CAM софту для генерации G кода. Там везде есть «сверловка» да еще и куча стратегий этой сверловки.
                      0
                      4 класс это переходные 0,4-0,6
                      Резонит делает 0.3/0.7 (отверстие/пятачек) по 4 классу.
                        0

                        ребят, идея в том, чтобы использовать это для поделок. Как и детали для печати: тоже печатается далеко не все. Но можно проектировать под технологию. Промах на две десятки компенсируется диаметром площадки. Смещение всего поля не важно, т.к. шаблон потом как раз по отверстиям и совмещать.

                          0
                          Да это понятно, я ж вас и не ругаю) Просто, поделки будут крупнее, чем иногда хочется.
                            0
                            Возможно. Зато будет мотив превращать трассировку в искусство ))) Опять же: да, пусть это будет класс N по всему, кроме точности отверстий. Т.е. дорожки и прочую гадость можно водить по высокому классу, но при этом набор вий и падов делать с большой площадкой (не менее 0,2), а там пусть хоть в край попадает. Все ж лучше, чем ждать месяц из китая или заказывать в Минске втридорого, да еще и непонятно, на каких условиях. И еще при этом умолять производителя, что тебе нужно только 10шт… (хотя по факту — одна)
                              0
                              Это так не работает, к сожалению) Переходные отверстия ужасно жрут эффективную площадь платы.

                              Но вам все равно удачи — раньше сам такое сделать хотел)
                        0

                        согласен, но хотелось отвязаться от производителей и сделать open-src

                    • НЛО прилетело и опубликовало эту надпись здесь
                        0
                        Такой способ тоже рассматривается.
                          +1
                          Засвечивать сканированием долго, шумно. Сейчас бум маленьких фотополимерных принтеров (SparkMaker SLA, Anycubic Photon), где изображение слоя формируется UV-светодиодами просвечивающими через ЖК-матрицу. Так вот, длина волны как раз для фоторезистов подходит- положил на матрицу плату с фоторезистом, вывел изображение дорожек, 1 минута и готово. Сейчас продаются комплекты для апгрейда таких принтеров, которые включают в себя матрицу более высокого разрешения, может на их основе можно сделать такой «моментальный» завсвечивающий принтер?
                          • НЛО прилетело и опубликовало эту надпись здесь
                        0
                        Надо тестировать на разной скорости подачи и с разными фрезами. Вот тут описывается фрезер из ДСП и мебельных рельсов. Фото ничего прецизионного не обещают, но автор гравировал печатные платы с хорошим результатом (там есть видео теста).
                        0

                        А что с металлизацией? Получилось? У меня слой слишком рыхлый получался :(

                          0

                          Только допиливаю. Скоро буду пробовать. Из теории и небольшого опыта: рыхлость от большого тока.

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

                              а я хочу плюнуть на проволочки: достали, сил нет )

                                0
                                Ну тогда придется держать растворчики, ванночки, гальванику. Тот еще геморой.
                                И еще кучу времени на отработку техпроцесса чтоб получить стабильное качество.

                                Я вот заморочился анодированием. Ну никак мне без него. Так установка получилась как гудронный колайдер :) Холодильник, мешалки, электролизер. Все с контроллерами и циферками. Только тогда качественно получилось.
                                  0
                                  жизненной мотивации коммент
                                  (вздохнув): да, да, да… но, с оптимизмом говоря, я не припомню более или менее серьезного навыка, который мне дался сам по себе, без усилий… да и никто ж не торопит. Возюкаюсь помаленечку. А потом, глядь — получилось )
                                0
                                Групповые операции всегда выигрывают при росте количества. Т.е. металлизировать плату на которой 10 или 100 или 1000 отверсий занимает одинаковое время. Для распаивания проволочек время растет очень сильно. Да и нервы не железные.
                                  0
                                  Если 100 или 1000 то такую плату уже наверно проще заказать.
                            0
                            remixoff
                            Всегда интересовал вопрос, можно ли печатать фотошаблон пластиком по фольгированному текстолиту?
                            Что бы пропустить этап нанесения фоторезиста с шаблоном или ЛУТа
                              0
                              А как прямое нанесение шаблона спасет вас от фоторезиста?
                                0
                                Хлорное железо или лимонка вряд ли съедят ABS пластик
                                  0
                                  Протравы будут, тк одной непрерывной линией вы не нанесете его (пластик), а на стыках нет прилипания. Посмотрите на фотки напечатанных деталей «вид снизу» — сами все поймете.
                                0
                                Одна из главных проблем 3D-печати — адгезия модели к столу. Чего только не придумали за время существования RepRap: и каптон клеили, и скотч, и лаком для волос заливали, и АБС-пластик в ацетоне растворяли, чтобы смазывать. Сейчас я нашел для себя пару пластиков, которые прилично клеятся к разогретому до 90 градусов оконному стеклу. Но его надо пренепременно протереть ацетоном — на жирное и пыльное не клеятся.
                                Отсюда закономерные размышления: медь на воздухе мгновенно покрывается слоем окисла. Т.е. адгезии следует ожидать чуть менее, чем нулевой (пластик-то вытекает не жидким, а, скорее, мягким).
                                Впрочем, были технологии рисования по текстолиту маркером и царапания дорожек после полного закрашивания оным. Первая мне не понравилась тем, что маркер не всегда дает равномерную дорожку. Вторая сначала понравилась, но вылез нюанс: царапает оно за несколько проходов. И уловить грань между «не процарапалось» и «уже царапает медь» — почти невозможно. Что плохого в царапине на меди, если ее все равно вытравливать? То, что вторым проходом из-за упомянутой нежесткости конструкции игла стремится свалиться в первую дорожку. Потом вывалится. Получаются зиг-заги… ерунда, короче.
                                  0
                                  Жалко.
                                  Просто когда хобби под настроение, то фоторезист успевает протухнуть, а ждать пару дней с инет-магазинов — как то не охота.
                                    0
                                    Кстати, а сколько ему нужно, чтобы протухнуть? )
                                    А то может и мне перезаказывать пора? )
                                      +1
                                      У меня есть рулон фоторезиста которому лет семь уже. Да, липнет заметно хуже, но это и все что смог заметить. Но т.к. наношу ламинатором на влажную фольгу — прилипает нормально.
                                  +1
                                  Делал такое же для своего фанерного Prusa. Только крепеж сменного инструмента не такой хлипкий.
                                  Да, сверлит, точность достаточная. Даже фрезерует — резал стеклотекстолит и оргстекло. Неоднократно сверлил так двухсторонние платы, отверстия 0,4-0,7. Очень облегчает работу. По жесткости механики проблема есть, но не там где я ее ожидал. А именно: шпиндель достаточно тяжелый и его центр тяжести вынесен от направляющих, следовательно, его вес создает на направляющих скручивающее усилие, из-за чего сверло/фреза незначительно отклоняются от вертикального положения. Проблема заключается в том, что при сверлении действующий вес шпинделя меняется (особенно заметно в момент выхода сверла — он «клюет»), что приводит к смещению. Сверла при таком смещении у меня не ломались, но входное отверстие приобретает каплевидную царапину.
                                  Картинки
                                  Сверление 0,5мм:
                                  image

                                  Результат:
                                  image
                                  Виден небольшой уход координат, но то проблема с печатью фотошаблона.

                                  Отверстия 0,6:
                                  image

                                  Фрезеровка по оргстеклу:
                                  image

                                  … и по стеклотекстолиту, фреза 0,7мм:
                                  image

                                  Софт самописный на сях. При просчете векторов движения нужно учитывать что шпиндель тяжелый; выходить на заданную координату нужно всегда с одного направления (например, х+, у+) — так механика выбирает люфты с хорошей повторяемостью.

                                  Кроме шпинделя и печатающей головки навешивал всякое разное (слабый лазер — для фоторезиста, мощный — для гравировки по пластикам), но то уже отдельная история :)
                                    0
                                    Спасибо за опыт. Отличный коммент. Были мысли, что шпиндель свернет направляющие. Но кронштейн пока в стадии рисования, думаю, учту это размещением крепежных отверстий «ромбиком», чтобы можно было шайбами отрегулировать. (у меня на фланцевой части мотора крепеж «квадратиком»)
                                    Что касается нужного вам захода, моим софтом это реализуется ))) в шаблоне для сверления отверстия (у меня называется go_drill) нужно написать:
                                    G0 X{x-1} Y{y-1}
                                    G0 X{x} Y{y}
                                    G0 Z{ZDrillHeight}
                                    


                                    Ради этой гибкости и выкладывал ))
                                    0
                                    Получается, что ручное сверление отпадает, т.к. ориентироваться просто не по чем (бумажки с картами отверстий давайте не будем предлагать).

                                    Решается элементарно. Вместе с остальными фотошаблонами вывожу и шаблон с одними только отверстиями. Накатываю, проявляю, травлю одну только строну. Вторую сторону, конечно, нужно защитить, я просто скотчем заклеиваю. Таким образом получаются накерненные отверстия. Сверлю не снимая фоторезист, и после нескольких просверленных отверстий можно пыль втереть в еще не просверленные отверстия — белые точки на темно-синем фоторезисте отлично видны.
                                      +1
                                      Как раз сегодня провел похожий эксперимент на своем Анет А8, поставил мотор 775 с цангой ER11.
                                      image
                                      Правда прикрепил прям на скобу (вместо вентилятора обдува), поэтому голова получилась еще тяжелее, а усилие скручивания еще больше.
                                      В целом плату сверлить можно, правда сверло местами как-то странно отгибает. Я даже попытался с помощью этой штуки процарапать плату по контуру, но заготовку оторвало… минус одна фреза.
                                      Вообще как по мне идея приделать сверлилку на принтер прикольная. Но есть пару очевидных и не очень ньюансов:
                                      — акриловый (пластиковый) и скорее всего фанерные принтеры довольно сильно вибрируют от мотора, боюсь так можно похоронить функцию 3д печати -короче мало жесткости.
                                      — пружинная регулировка стола при сверлении и особенно фрезеровании наоборот мешает, нужны какие-то фиксаторы.
                                      — резонанс (известная но не всегда очевидная вещь) у меня почему в диапазоне 11-12 вольт движок начал заметно вибрировать вместе со станком, когда я повысил напряжение до 15 стало все норм.
                                      Короче нужен отдельный бп на двигатель с регулировкой.

                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                      Самое читаемое