├── LICENSE ├── Makefile ├── README.md ├── candle_heightmap_adjust.py ├── check_gcode.py ├── check_pcb_common.py ├── check_pcb_cu.py ├── check_pcb_drill.py ├── check_pcb_edge.py ├── check_pcb_stencil.py ├── examples └── breadboard_buttons │ ├── breadboard_buttons.kicad_pcb │ ├── breadboard_buttons.kicad_prl │ ├── breadboard_buttons.kicad_pro │ ├── breadboard_buttons.kicad_sch │ ├── breadboard_buttons.net │ ├── export │ ├── Project_20220814_140748.FlatPrj │ ├── breadboard_buttons-Edge_Cuts.gbr │ ├── breadboard_buttons-Edge_Cuts.gbr_cutout_cnc.nc │ ├── breadboard_buttons-F_Cu.gbr │ ├── breadboard_buttons-F_Cu.gbr_iso_combined_cnc.nc │ ├── breadboard_buttons-NPTH.drl │ ├── breadboard_buttons-PTH.drl │ ├── breadboard_buttons-PTH.drl_cnc.nc │ ├── breadboard_buttons-job.gbrjob │ └── test.nc │ └── fp-info-cache ├── images ├── 555_smd.jpg ├── board_cutout.png ├── breadboard.jpg ├── button_6x3.jpg ├── button_cnc_board.jpg ├── button_cnc_complete.jpg ├── button_pcb.png ├── button_schematic.png ├── ca_on_tape.png ├── candle_height_map_button.png ├── candle_probe_zero.png ├── cnc_probes.jpg ├── design_constraints.png ├── drill_bit.jpg ├── drilling_holes.png ├── end_mill_bit.jpg ├── end_mill_drill_bit.jpg ├── epaper_clock.jpg ├── epaper_clock2.jpg ├── epaper_clock3.jpg ├── epaper_clock4.jpg ├── epaper_clock5.jpg ├── fast_edge_generator.jpg ├── fast_edge_generator2.jpg ├── finishing_up.jpg ├── flatcam_copper_cnc_object.png ├── flatcam_copper_gcode.png ├── flatcam_copper_properties.png ├── flatcam_cutout_tool.png ├── flatcam_cutout_tool2.png ├── flatcam_delete_geometry.png ├── flatcam_drill_cnc.png ├── flatcam_drill_icon.png ├── flatcam_drill_open.png ├── flatcam_drill_properties.png ├── flatcam_drill_properties2.png ├── flatcam_drilling_tool.png ├── flatcam_edge_cnc.png ├── flatcam_edge_geometry.png ├── flatcam_edge_properties.png ├── flatcam_geometry_properties.png ├── flatcam_gerber_icon.png ├── flatcam_gerber_open.png ├── flatcam_isolation_2.png ├── flatcam_isolation_3.png ├── flatcam_isolation_tool.png ├── flatcam_loaded.png ├── flatcam_save.png ├── flatcam_select_copper.png ├── flatcam_set_origin.png ├── flatcam_start.png ├── footprint_editor.png ├── generate_drill_files.png ├── gerber_plot.png ├── height_map_autosize.png ├── loaded_design_and_heightmap.png ├── meld_heightmap_compare.png ├── painters_tape.png ├── painters_tape_on_pcb.png ├── pc_monitor.jpg ├── pc_monitor2.jpg ├── pin_header.png ├── press_together.png ├── probe_finished.png ├── proto_board.jpg ├── proto_board2.jpg ├── settings_dialog.png ├── spray_tape.png ├── spring_probe.jpg ├── test_cut.png ├── test_heightmap.png ├── test_loaded_in_candle.png ├── tinning.jpg ├── use_heightmap.png ├── v_bit_20_degrees.jpg ├── v_bit_20_degrees_fluted.jpg ├── v_bit_20_degrees_fluted_output.jpg ├── v_bit_20_degrees_output.jpg ├── v_bit_30_degrees.jpg ├── v_bit_30_degrees_output.jpg ├── v_bit_45_degrees_fluted.jpg ├── v_bit_45_degrees_fluted_output.jpg ├── validation.png ├── voltage_cutoff.jpg └── voltage_cutoff2.jpg ├── pylintrc ├── test_template.txt └── tests ├── check_pcb_cu └── breadboard_buttons-F_Cu.gbr_iso_combined_cnc.nc ├── check_pcb_drill └── breadboard_buttons-PTH.drl_cnc.nc └── check_pcb_edge ├── breadboard_buttons-Edge_Cuts.gbr_cutout_cnc.nc └── test.nc /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Note: this file is only for running tests. No make is needed to use the software 2 | 3 | PYTHON_FILES=$(foreach f,$(wildcard *.py),$f.force) 4 | CHECK_PCB_CU_FILES=$(foreach f,$(wildcard tests/check_pcb_cu/*.nc),$f.force) 5 | CHECK_PCB_EDGE_FILES=$(foreach f,$(wildcard tests/check_pcb_edge/*.nc),$f.force) 6 | CHECK_PCB_DRILL_FILES=$(foreach f,$(wildcard tests/check_pcb_drill/*.nc),$f.force) 7 | 8 | test: lint check 9 | 10 | lint: $(PYTHON_FILES) 11 | check: check_pcb_cu check_pcb_edge check_pcb_drill 12 | check_pcb_cu: $(CHECK_PCB_CU_FILES) 13 | check_pcb_drill: $(CHECK_PCB_DRILL_FILES) 14 | check_pcb_edge: $(CHECK_PCB_EDGE_FILES) 15 | 16 | $(PYTHON_FILES): %.force: 17 | pylint $(subst .force,,$@) 18 | 19 | $(CHECK_PCB_CU_FILES): %.force: 20 | python3 check_pcb_cu.py $(subst .force,,$@) 21 | 22 | $(CHECK_PCB_DRILL_FILES): %.force: 23 | python3 check_pcb_drill.py $(subst .force,,$@) 24 | 25 | $(CHECK_PCB_EDGE_FILES): %.force: 26 | python3 check_pcb_edge.py $(subst .force,,$@) 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CNC PCB Tools 2 | 3 | This document discusses an entire end-to-end process of milling PCBs with 4 | a "cheap" 3018 CNC machine (although other machines could also apply). 5 | 6 | Part of that process, verifying the design and calibrating the machine, are 7 | helped along with simple Python scripts that I created and am giving away 8 | for free. This document explains how to to use these optional tools in 9 | the appropriate places in the process. 10 | 11 | Even if you do not use the tools, I think there this document contains 12 | helpful information, both for beginners and more experienced individuals 13 | looking to compare notes. 14 | 15 | ### Effort Estimate 16 | 17 | This ended up being a long document. Does that mean that the process takes 18 | a long time? I don't think it does. Here are some time estimates: 19 | 20 | | Step | Beginner / Small Project | Experienced / Small Project | Experienced / Medium Project | 21 | |--------------|--------------------------|-----------------------------|------------------------------| 22 | | Design PCB | 3 hours | 1 hour | 4 hours | 23 | | Create GCode | 1 hour | 10 minutes | 15 minutes | 24 | | CNC Setup | 1 hour | 15 minutes | 15 minutes | 25 | | CNC Cutting | 15 minutes | 15 minutes | 1 hour | 26 | | Learning | 1 hour | - | - | 27 | | **Total** | **~6 hours** | **~2 hours** | **~ 6 hours** | 28 | 29 | Of course there are many variables. For example, if you use someone else's 30 | PCB layout, you can cut that out of the time needed. 31 | 32 | ## Disclaimer 33 | 34 | I have tested these tools and process and they work well for me. That said, I 35 | accept no responsibility for any damage caused to either person or equipment 36 | that occurs from following advice in this document or running any of the 37 | provided or mentioned software. 38 | 39 | If you are a beginner to CNC, I have special "Tips" sidebars throughout the 40 | document the CNC section that should be helpful. 41 | 42 | ## Gallery 43 | 44 | Let start by looking at some example results. 45 | 46 | First, a fast rise edge generator, as explained in 47 | [this video](https://www.youtube.com/watch?v=9cP6w2odGUc). 48 | This one was created for through-hole parts. 49 | 50 | ![fast edge generator](images/fast_edge_generator.jpg) 51 | ![fast edge generator 2](images/fast_edge_generator2.jpg) 52 | 53 | Here is a simple SMD board that uses a 555 and counter chip to make a simple 54 | LED light show. This one is still on the CNC machine: 55 | 56 | ![555 SMD](images/555_smd.jpg) 57 | 58 | Here a simple voltage cutoff circuit I made that combines an ATTiny85, 59 | MOSFET and a fuse for good measure. 60 | 61 | ![voltage cutoff](images/voltage_cutoff.jpg) 62 | ![voltage cutoff 2](images/voltage_cutoff2.jpg) 63 | 64 | Here is an [e-paper based clock](https://github.com/mattwach/epaper_clock) 65 | that I designed earlier this year. I created 66 | the first copy using a CNC machine, then made 4 more using boards from 67 | a fab. The first CNC copy was very useful in helping me discover 68 | most of changes I wanted to make to the design (mostly layout) before 69 | submitting the PCB. 70 | 71 | ![e-paper clock](images/epaper_clock.jpg) 72 | ![e-paper clock 2](images/epaper_clock2.jpg) 73 | ![e-paper clock 3](images/epaper_clock3.jpg) 74 | 75 | ## Why CNC a PCB? 76 | 77 | I've tried the following methods: 78 | 79 | * Breadboard 80 | * Prototype Boards 81 | * CNC (this doc) 82 | * Ordering a PCB from a fab, such as [oshpark.com](http://oshpark.com) or [www.pcbway.com](http://www.pcbway.com) 83 | 84 | I feel that all of these methods have their place - it just depends on the 85 | project. Below I'll give my opinion on each one while trying not to go 86 | overboard: 87 | 88 | ### Breadboard 89 | 90 | Breadboards are great for trying something out in a short time. Parts are 91 | also not "consumed" and can be reused. Using prebuilt modules as components 92 | makes wiring simple and there are many cheap ones available. 93 | 94 | However, any interconnects in your design will be performance limited in terms 95 | of frequency, stray capacitance, unintended resistance and lower reliability. 96 | It's not all bad since you get to exercise some troubleshooting skills and 97 | things seems to work fine most of the time. 98 | 99 | Also the method is not suitable for anything permanent. 100 | 101 | ![breadboard](images/breadboard.jpg) 102 | 103 | ### Prototype Boards 104 | 105 | Like a breadboard but with better reliability and more permanence. Making 106 | one of these is a bit labor intensive though and there are few economies of 107 | scale when making multiple copies. Frequency is also usually limited 108 | due to the lack of careful layout, stray capacitances and the lack of 109 | a ground plane. 110 | 111 | ![proto board](images/proto_board.jpg) 112 | ![proto board 2](images/proto_board2.jpg) 113 | 114 | ## CNC 115 | 116 | Simple double sided boards are easy (often just a few patch wires are required 117 | on the back side and these can be executed by drilling holes and hand routing 118 | wires on the back). Complex double sided is possible but requires alignment 119 | techniques that are out-of-scope for this guide. 120 | 121 | Performance is excellent. Both SMD and through-hole components are supported. 122 | 123 | Soldering is less convenient than a manufactured PCB due to lack of a solder 124 | mask. Lack of a silk screen (labeling) means that you may need a PCB printout 125 | to guide component placement. 126 | 127 | Requires some learning to be successful - that's the purpose of this document! 128 | 129 | ![pc monitor](images/pc_monitor.jpg) 130 | 131 | ### Chemical Etch 132 | 133 | I have not tried this one yet (and am meaning to soon) but have read and 134 | watched videos on the process. Overall, it seems similar to CNC - perhaps a 135 | bit easier to dial in the process and get good results. There are some clear 136 | downsides however. One is having to work with some nasty chemicals that 137 | literally dissolve metal. A second drawback is that drilling holes and cutting 138 | out the board are not handled by the process so you'll have to come up with 139 | something. 140 | 141 | ## Ordering a PCB 142 | 143 | If you need a lot of copies, > 2 layers, or have a complex design, this is your 144 | route. You also get a solder mask for easier soldering and a silkscreen to 145 | label component and provide other notes. 146 | 147 | Turn around latency is around a week. Cost is in the $20-$60 range assuming 148 | that your board design is perfect. If not, you'll consume additional weeks and 149 | $$$ on iterations (or you can try to hack in workarounds). 150 | 151 | ![epaper clock 3](images/epaper_clock3.jpg) 152 | 153 | ## CNC Trip Points 154 | 155 | I think a reason that many people avoid CNC is the upfront learning 156 | curve. Breaking a bit is easy and the risk of doing so increases 157 | for any of the following reasons: 158 | 159 | 1. You use the wrong parameters (feed rate, cut depth) either because you 160 | don't know or accidently type in the wrong number somewhere. 161 | 2. Your GCode file (which the CNC takes as an input) neglected to command 162 | the motor to spin up. 163 | 3. You don't zero the machine properly. 164 | 4. You don't build a height map or improperly set one up 165 | 5. Your copper clad is bowed due to improper clamping 166 | 6. You are using unnecessarily aggressive design constraints, such as a 167 | 0.2mm track width when 0.4mm could work for your project. 168 | 169 | I'm going to talk about how to mitigate each of the problems about so your 170 | success rate and quality can be quite high. 171 | 172 | ## Process Overview 173 | 174 | Here is an overview of the steps I will cover. But first, let's acknowledge 175 | that everyone's situation, available tools, and skill sets are different. The 176 | process I describe below is what works for me, your ideal process will likely 177 | end up being different, but it's good know what works for others. 178 | 179 | Also, there could be better or more efficient ways of doing the things I 180 | describe below. If you think that is the case, let me know! 181 | 182 | An overview of the steps: 183 | 184 | * **Design the PCB** (printed circuit board). Here you create (or get) a 185 | schematic and layout a PCB. The PCB you layout can be used for many 186 | purposes. For example, you could take the same PCB design, checmical etch 187 | it, CNC it and send it to a fab. That said, an "optimal" layout will take 188 | the final manufacturing process into consideration. 189 | * **Plot the PCB** design in a universal format know as "Gerber". Gerber format 190 | is one that PCB manufacturers will accept as input when then make boards 191 | for you. If you are into 3D printing, you can think of Gerber as "STL 192 | for circuits." 193 | * **Convert the "Gerber" file to "GCode"**. GCode is a text file of instructions 194 | for a specific model of automated machine (CNC machines, 3D printers and 195 | others). The GCode file tells the machine where to cut, how fast to cut, 196 | how deep to cuts, etc. None of this information is in the Gerber file - 197 | the conversion step adds it in. 198 | * **Setup the machine, execute the `.gcode`**. 199 | * **Finishing work**. Just a little bit of post work for CNC and you are ready for 200 | soldering. 201 | 202 | ## Designing your PCB 203 | 204 | Some PCB's needs very fine traces and spacing to allow for placement of tiny 205 | SMD components. These are possible with CNC but not an ideal first project. 206 | While learning, a more forgiving project will help you be 207 | successful as you learn. Thus my suggested starter project is a simple SMD 208 | button adapter for breadboards. 209 | 210 | ![button cnc](images/button_cnc_complete.jpg) 211 | 212 | It's nice because: 213 | 214 | * Simple: No getting sidetracked on how the circuit works 215 | * Useful: A lot of buttons work so-so when fit in a breadboard. This one 216 | give a very solid and reliable connection. 217 | * Small: It's fast and cheap to iterate and experiment with different 218 | settings. 219 | * Forgiving design: Thick traces (1mm) and large clearances (0.4mm) mean 220 | that the result can be usable even if the settings still need tweaking. 221 | 222 | I'm going to use KiCAD. I used to use Eagle but many agree that KiCAD has 223 | surpassed it. Eagle and many others will still work fine, 224 | however, as they can all export Gerber plots. Here is the schematic: 225 | 226 | ![breadboard schematic](images/button_schematic.png) 227 | 228 | This design has two pin headers and 4 buttons. For the Pin header PCB 229 | footprint, I'm just using a pin header built into the standard library: 230 | 231 | ![pin header](images/pin_header.png) 232 | 233 | If you are just starting with KiCAD, one way to set the footprint is to click 234 | on it in the schematic, then type "e" to bring up its dialog, where you can 235 | select the footprint: 236 | 237 | ![settings dialog](images/settings_dialog.png) 238 | 239 | If this is new information for you, then I suggest watching some of 240 | [John's Basement KiCAD tutorial](https://www.youtube.com/playlist?list=PL3by7evD3F51fKkyrUbH-PCdwPCWc9F8a) 241 | (or similar). 242 | 243 | Here are the SMD buttons I chose. They are 6x3.5mm: 244 | 245 | ![button 6x3](images/button_6x3.jpg) 246 | 247 | I could not find any footprints for these in the KiCAD library. The 248 | files probably exist somewhere on the internet, but it's really quite 249 | fast and easy to make a custom one in KiCAD (using the Footprint Editor), 250 | and that is what I did: 251 | 252 | ![Footprint Editor](images/footprint_editor.png) 253 | 254 | [John's Basement, video #12](https://www.youtube.com/watch?v=Rdm5lS_sYsc&list=PL3by7evD3F51fKkyrUbH-PCdwPCWc9F8a&index=14) 255 | demonstrates how to do this. 256 | 257 | Of course, you can just use the one I already placed in the `examples/` 258 | folder if you are going with the same button and want to focus more on the 259 | CNC steps. 260 | 261 | Here is the completed/routed PCB. 262 | 263 | > **Beginner Note** this circuit doesn't have a ground fill place 264 | because the schematic has no ground, which is a bit unusual. For a design 265 | with a ground, you'll want to Google search how to do a "ground fill" in 266 | KiCAD as it can simplify wiring and help reduce electrical noise in your 267 | circuit. 268 | 269 | ![button pcb](images/button_pcb.png) 270 | 271 | One more thing about KiCAD PCB design. If you choose `File -> Board Setup...` 272 | in the PCB editor, you'll should see a dialog of settings, one of which is 273 | `Design Rules: Constraints`: 274 | 275 | ![design constraints](images/design_constraints.png) 276 | 277 | Here, there is a "minimum clearance" and "minimum track width" setting that you 278 | can use to make your CNC life easier (in trade for limiting the ability to 279 | route tiny parts). I think 0.4mm for both settings is a good setting because: 280 | 281 | * You can use bits with a thicker point of contact (e.g. 0.3mm vs 0.1mm). 282 | Thicker contact points will be harder to break. 283 | * If you cut a but deeper (or rougher) than expected, the conservative 284 | minimum track width setting will increase the chance there is still some 285 | metal there to complete the connection. 286 | 287 | of course, this is *just for less-complex designs that can support it*. Once 288 | you have a dialed-in process, you can push these number as small as you want to 289 | try for. 290 | 291 | ## Making Gerber & Drill Files 292 | 293 | Regardless of what tool I used to design the PCB, I'll need to export the 294 | design in a universal format that the next phase of the process can understand. 295 | These files are known as "Gerber" files and are like a "PDF" or "STL" for 296 | circuits meaning that they capture only the shapes of the PCB layout and not 297 | other information, such as the associated schematic. 298 | 299 | In KiCAD, you create Gerber files using the `File -> Plot`. Here is the dialog 300 | with the settings I use: 301 | 302 | ![Gerber plot](images/gerber_plot.png) 303 | 304 | Note that I'm only exporting the F.CU and Edge.Cuts layers. If you were 305 | sending these files to a Fab, they would want other layers, like the silkscreen 306 | layer, but the CNC machine can not directly make use of these (at least not in 307 | the process I describe here). 308 | 309 | This particular design needs holes drilled for the pin headers and 310 | that data needs to be exported too. "Generate Drill Files..." in the dialog 311 | above will bring up this one. 312 | 313 | ![generate drill files](images/generate_drill_files.png) 314 | 315 | Output are "PTH" and "NPTH" files. Those stand for "plated hole" and "non 316 | plated hole". This design only has plated holes but, if you have mounting 317 | holes, then both files might have some data in them. 318 | 319 | ## Gerber to GCode (using Flatcam) 320 | 321 | There are a number of tools to do this as well. One that I like is 322 | [Cambam](www.cambam.info), which is a great all-around CNC program that can 323 | import the files above. 324 | 325 | But Cambam is not a free program so we are going to go with Flatcam, which is 326 | free. Flatcam is a bit more complex than it needs to be and is not bug-free 327 | but it get's the job done once you find a navigation path through it's large 328 | set of options. 329 | 330 | Let's start the software: 331 | 332 | ![flatcam start](images/flatcam_start.png) 333 | 334 | > **Tip** The very first thing I recommend doing is saving your project. 335 | I also suggest adding the project file to version control. 336 | 337 | ![flatcam save](images/flatcam_save.png) 338 | 339 | Let's load up all of the Gerber files 340 | 341 | ![flatcam Gerber icon](images/flatcam_gerber_icon.png) 342 | ![flatcam gerber open](images/flatcam_gerber_open.png) 343 | 344 | and drill files 345 | 346 | ![flatcam drill icon](images/flatcam_drill_icon.png) 347 | ![flatcam drill open](images/flatcam_drill_open.png) 348 | 349 | and here is the design loaded: 350 | 351 | ![flatcam loaded](images/flatcam_loaded.png) 352 | 353 | Note the horizontal and vertical red lines. These will be the [0, 0] origin of 354 | the CNC machine. It's not in a good spot above because, I forgot to check "Use 355 | drill/place origin" in the KiCAD Gerber plot dialog. I can go back to KiCAD and 356 | replot but flatcam has a tool as well: 357 | 358 | ![flatcam set origin](images/flatcam_set_origin.png) 359 | 360 | You can put the origin wherever you want and may wonder why I didn't choose the 361 | bottom left corner (as the bottom left aligns better with CNC coordinates). 362 | The reason is because of where I solder my heighmap probe. I cover the 363 | details in the CNC section. 364 | 365 | Now is a good time to save the project. 366 | 367 | ### Copper Isolation GCode 368 | 369 | Now we are going to start making GCode, starting (arbitrarily) with the 370 | copper layer. Select the copper layer, then "properties": 371 | 372 | ![flatcam select copper](images/flatcam_select_copper.png) 373 | 374 | The properties dialog is a launchpad for doing a number of things. For the 375 | copper layer, we want to choose "Isolation routing" 376 | 377 | ![flatcam copper properties](images/flatcam_copper_properties.png) 378 | 379 | Now we are here: 380 | 381 | ![flatcam copper properties](images/flatcam_isolation_tool.png) 382 | 383 | #### Step 1 384 | 385 | * Here we choose the bit type and properties. These settings are complexity 386 | overkill, in my opinion. The intent is that the tool wants to help 387 | you calculate the needed depth for V bits to get the isolation width 388 | you want. The reality is that the tool assumes your machine and process 389 | have perfect tolerances (e.g. no bit runout, perfect cuts, perfect 390 | positioning). If any of these are untrue (and the probably are on a 391 | 3018), then you just end up putting in adjusted numbers anyway to get the 392 | final result to come out. 393 | 394 | * To keep it simple, I directly choose the final cut width. I do this 395 | by choosing a "C1" bit type and typing in the expected cut width. 396 | I'm essentially telling the tool "trust me, the cuts will be 0.39mm wide". 397 | If the real cuts are a little wider or narrower, it's usually not a problem 398 | unless the real cuts are so much wider that you lose copper traces. 399 | 400 | * I chose 0.39mm because I chose 0.4mm isolation widths in KiCAD. See 401 | The KiCAD Design section above for a refresher on how I did this. 402 | **You need to choose a smaller number in flatcam than kicad** or flatcam 403 | will silently omit traces where it can't fit a line. 404 | 405 | * Keeping it simple makes the "Add from DB" section irrelevant so we can 406 | skip it. 407 | 408 | #### Step 2 409 | 410 | * The main options here are "Passes" and "overlap" 411 | 412 | * Passes are the number of cut lines to make around each trace. 413 | 414 | * Overlap is how much to overlap the passes. Too little overlap will lead 415 | to small bit of copper remaining between each pass, which usually isn't a 416 | big deal, but doesn't look as nice. 417 | 418 | * "Follow" and "Isolation Type" can be kept at default settings. 419 | 420 | #### Step 3 421 | 422 | * Create the geometry traces for out next step. You can actually click 423 | this button multiple times (presumably with different settings) to 424 | create multiple geometries - then ignore, hide or delete the 425 | ones you are not planning to use. 426 | 427 | #### Here are results with 3 passes and 25% overlap 428 | 429 | ![flatcam isolation 3](images/flatcam_isolation_3.png) 430 | 431 | #### and 2 passes and 10% overlap 432 | 433 | ![flatcam isolation 3](images/flatcam_isolation_2.png) 434 | 435 | Note that both are included in the project at this point. I 436 | like the three pass version better so I'm going to right click 437 | on the 2 pass version and delete it. 438 | 439 | ![flatcam delete geometry](images/flatcam_delete_geometry.png) 440 | 441 | > **Tip** Now is a good time to save (and possible version control snapshot) 442 | 443 | One more step. At this point we need to create the final gcode file. To do 444 | this flatcam needs more information. Click on the geometry object, then properties: 445 | 446 | ![flatcam geometry properties](images/flatcam_geometry_properties.png) 447 | 448 | and we are onto this dialog: 449 | 450 | ![flatcam copper gcode](images/flatcam_copper_gcode.png) 451 | 452 | #### Step 1 453 | 454 | * This section helps you calculate your cut depth if you are using a V 455 | bit. As I said earlier, I find it easier to choose the "C1" bit type 456 | and put in the depth myself. Thus if you are following my path, 457 | **you can simply ignore this section**. 458 | 459 | #### Step 2 460 | 461 | * Cut Z: How deep to cut. A very important parameter. With a V bit, it also 462 | will determine your cut width (deeper cuts wider due to the V shape). Cut 463 | depth also has a big effect on how likely you are to break a bit. **When 464 | in doubt, choose a small number here, like -0.03** - the penalty of cutting 465 | too shallow is that you need to cut again deeper which is very quick to do 466 | with the tools I talk about later (candle_heightmaap_adjust.py). 467 | * Multidepth. This setting allows, you to split your cut into multiple passes. 468 | Using my `candle_heightmap_ajust.py` tool, I can do this "at the machine" as 469 | needed, thus **I don't use this option for copper isolation**. 470 | * Travel Z: How high to hover the bit over the board when not cutting. 471 | **Defaults are fine** but your job will finish quicker on a complex board if 472 | you set it lower. 473 | * Feedrate X-Y: How fast to cut in the XY direction. I use settings in the 474 | 120 to 200 range and found through controlled experiments that the quality 475 | was similar. There is surely too high of a setting where quality and bit 476 | health will suffer and of course that limit will depend on which bit you 477 | choose. 478 | * Feedrate Z: How fast to plunge. I find the **default setting of 60 to be ok** 479 | and have not experimented further. 480 | * Spindle Speed: RPM of the motor. With a stock 3018, this number is not 481 | accurate becuase these machines have no RPM sensor. 482 | **10000 basically means "full power"** which will be ~7500 RPM in with a 483 | typical 3018 stock motor. 484 | 485 | #### Step 3 486 | 487 | * End Move X,Y,Z: Where you want the bit to end up when the job is done. 488 | **I find ending at a x,y of 0,0 is nice for changing to the next bit**. 489 | * Preprocessor: Used to tailor the GCode output to a type of machine. If 490 | you have a 3018 machine, you probably want a GRBL variant. You can also 491 | make your own template from an existing one - something to consider 492 | after you get more experience with the process. 493 | 494 | #### Step 4 495 | 496 | * Click "Generate CNC object" 497 | 498 | #### Now you have a "simulated" CNC plot and an option to save it. 499 | 500 | ![flatcam copper cnc object](images/flatcam_copper_cnc_object.png) 501 | 502 | Here is a quick look at what was saved: 503 | 504 | 505 | (G-CODE GENERATED BY FLATCAM v8.994 - www.flatcam.org - Version Date: 2020/11/7) 506 | 507 | (Name: breadboard_buttons-F_Cu.gbr_iso_combined_cnc) 508 | (Type: G-code from Geometry) 509 | (Units: MM) 510 | 511 | (Created on Saturday, 27 August 2022 at 10:30) 512 | 513 | (This preprocessor is used with a motion controller loaded with GRBL firmware.) 514 | (It is configured to be compatible with almost any version of GRBL firmware.) 515 | 516 | (... MORE COMMENTS REMOVED ...) 517 | 518 | G21 519 | G90 520 | G17 521 | G94 522 | 523 | G01 F120.00 524 | 525 | M5 526 | G00 Z15.0000 527 | G00 X0.0000 Y0.0000 528 | ( --- LOOK HERE --- ) 529 | T1 530 | (MSG, Change to Tool Dia = 0.3900) 531 | M0 532 | G00 Z15.0000 533 | 534 | M03 S10000.0 535 | G01 F120.00 536 | G00 X2.0236 Y-7.7442 537 | G01 F60.00 538 | G01 Z-0.0500 539 | G01 F120.00 540 | G01 X2.0370 Y-7.7495 F120.00 541 | G01 X2.0923 Y-7.7691 F120.00 542 | G01 X2.1584 Y-7.7856 F120.00 543 | G01 X2.2259 Y-7.7957 F120.00 544 | (... MORE COMMANDS ...) 545 | 546 | 547 | Check out the `LOOK HERE` section above. My particular machine does not like 548 | the `T1` (tool change) code. I can just delete it and move on but this is an 549 | extra step. Creating a custom template (beyond the scope of this document but not 550 | hard), can allow the generate file to never add the code. 551 | The takeaway for now is that the CNC "dry run", explained later, 552 | might produce errors and these errors might require you to change the gcode file a 553 | bit. 554 | 555 | Another thing to look for is the `M03 S10000.0` line. If you forgot to set your 556 | RPM (left it at zero), this line will be missing and you will be sad when the 557 | not-spinning bit breaks on the copper plate. Checking cut depth is also advised. 558 | 559 | Wouldn't it be nice if the computer did all of this checking for you? 560 | I the tools I have included do exactly that (`check_pcb_cu.py `) 561 | Computers are way better than me at checking everything so I just run the 562 | check tool every time to help find unintentional "typo" settings I put into 563 | flatcam. For example, I once put -0.5mm instead of -0.05mm for my cut depth 564 | and broke 3 bits before disovering it! (which led to my creating the check 565 | tool to begin with). More details on that later... 566 | 567 | Anyway, lets move on to the other cuts. 568 | 569 | ### Drills 570 | 571 | I'll arbitrarily work on the drills next. 572 | First I disable (not delete) the isolation geometry and CNC cuts to clean up 573 | the plot a bit: 574 | 575 | ![flatcam drill properties](images/flatcam_drill_properties.png) 576 | 577 | Taking a look at the dialog 578 | 579 | ![flatcam drill properties2](images/flatcam_drill_properties2.png) 580 | 581 | Number 1 above is the "Excellon Editor". For this project we have 8 holes 582 | to drill and they are all the same size so **we don't need this option**. 583 | But, this option *is* often useful as it is often the case where you have 584 | several drill sizes (like 0.85, 0.9, 0.95) and you want to combine 585 | them all into a single size (all 0.9 for example) to make your drilling 586 | process easier. 587 | 588 | Number 2 is the drilling tool. Lets click it: 589 | 590 | ![flatcam drilling tool](images/flatcam_drilling_tool.png) 591 | 592 | 1. The drills you want to include in this file. We only 593 | have one type in this example, so nothing futher to do here. 594 | 2. How deep and fast you want to drill. 595 | * Cut Z: should be the same as your board thickness, maybe plus 0.1mm or so. 596 | * Multidepth: Should not be needed for drilling 597 | * Travel Z: hover height. default is fine. 598 | * Feedrate Z: cut speed. Depends on the bit you use and the 599 | diameter of the hole. For small holes, the 300 setting is fine. 600 | * Spindle Speed: Explained in the previous (copper) section 601 | * Offset Z: It's just added to Cut Z for tapered bit. Or you 602 | could have just changed Cut Z directly so I'm not sure why this 603 | exists. 604 | 3. It's all the same as the copper section (common parameters) 605 | 4. Click here to make your drills 606 | 607 | Now the drill paths are made and it's time to save the gcode file: 608 | 609 | ![flatcam drill cnc](images/flatcam_drill_cnc.png) 610 | 611 | Let's move onto the final cut, the edge cuts (cutting out the board outline). 612 | Again, I disable unneeded plots to clean up the display a bit. 613 | 614 | ### Edge Properties 615 | 616 | ![flatcam edge properties](images/flatcam_edge_properties.png) 617 | 618 | ![flatcam cutout tool](images/flatcam_cutout_tool.png) 619 | 620 | ![flatcam cutout tool](images/flatcam_cutout_tool2.png) 621 | 622 | 1. Set this to the diameter of your cutout bit. I usually do with 623 | something in the 2-3mm range here (and all have worked fine) 624 | 2. The defaults as-shown work fine for me. Set Cut-Z to the thickness of 625 | your board plus 0.1mm. Here we do want "multidepth" 626 | selected so that the bit takes several passes to do the cut as trying to 627 | cut everything in one pass would put a lot of load on the cutting bit. 628 | 3. I have "Bridge Gaps" set to none. If you set it to something else 629 | (like 4), then the CNC machine will leave small tabs that connect the 630 | board to the copper clad. This is important or not important depending 631 | on how you have your copper clad secured. I use the "double sided tape" 632 | method (described in the CNC section) so I do not need any tabs. 633 | 634 | When you are all set, click on "Generate Geometry" (top one). On my version of 635 | flatcam, I had to manually select the "properties" tab after doing this but 636 | you might not need to. 637 | 638 | ![flatcam edge geometry](images/flatcam_edge_geometry.png) 639 | 640 | 1. Verify this is the width of your cutting bit 641 | 2. The parameters are similar to the copper section except that 642 | we are going with multidepth for this cut. Make sure spindle 643 | RPM is not zero. 644 | 3. The common section is the same as the copper section. Make 645 | sure that the Preprocessor is set as-described in the copper section. 646 | Note that changing the Preprocessor can mess with other settings so 647 | check them over after making any changes. 648 | 4. Generate the tool paths 649 | 650 | All that is left to do is export the gcode: 651 | 652 | ![flatcam edge cnc](images/flatcam_edge_cnc.png) 653 | 654 | And we are done! It may have seemed like a lot of work but it goes pretty 655 | quick (a few minutes) after you've done it a few times. The bigger issue is 656 | accidentally putting in a wrong number somewhere. I have a solution that I 657 | describe in the next section. 658 | 659 | I suggest saving your project and check it into version control. I also like 660 | to commit all of my gcode files to version control so I can look at them later 661 | if need-be. 662 | 663 | ### CGode Validation (Optional) 664 | 665 | Sometimes when driving the creation tools (in this case KiCAD and Flatcam), I 666 | accidentally enter an incorrect value in one of the parameter dialogs. The 667 | example I'll share is that I once entered -0.45mm for a z-cut depth into the 668 | copper - what I meant to enter was -0.045mm. The result what that I broke a 669 | $15 bit and two more $2 bits before finding the problem. 670 | 671 | The solution I came up with is "validation" scripts. These are simple [Python](python.org) 672 | scripts that you feed your final GCode into. They read in the code and look 673 | for problems. The problem I mentioned in the previous paragraph will be caught 674 | by the checking program along with many others. Which problems exactly? Well that is 675 | ultimately up to you. I provide my own configuration and I tried to make it 676 | easy to modify it for whatever things you want to check - you can make it as 677 | rigid or flexible as you want. 678 | 679 | Here is the actual output of running the three gcode files I generated earlier in 680 | Flatcam. 681 | 682 | First the copper check. 683 | 684 | python3 check_pcb_cu.py breadboard_buttons-F_Cu.gbr_iso_combined_cnc.nc 685 | 686 | Properties: 687 | lines: 5063 688 | codes: 5029 689 | units: {'MM'} 690 | xrange: 0.0 .. 19.814 691 | yrange: -19.664 .. 0.0 692 | zrange: -0.05 .. 15.0 693 | max_plunge_feed_z: 60.0 694 | max_cut_feed_xy: 120.0 695 | 696 | Spinning in material: OK 697 | min_x: 0.0 > -1.0: OK 698 | max_x: 19.814 < 60.0: OK 699 | min_y: -19.664 > -40.0: OK 700 | max_y: 0.0 < 1.0: OK 701 | min_z: -0.05 > -0.1: OK 702 | max_z: 15.0 < 15.1: OK 703 | max_z_plunge: 0.05 <= 0.05: OK 704 | max_plunge_feed_z: 60.0 <= 61.0: OK 705 | max_cut_feed_xy: 120.0 <= 180.0: OK 706 | T1 not present: OK 707 | 708 | Let's dig into this one a bit. The first thing the script does is read the 709 | file. Then some basic properties are printed. These are just informational. 710 | Finally the checks. Now let's look at the `check_pcb_cu.py` script that we ran: 711 | 712 | #!/usr/bin/env python3 713 | 714 | import check_gcode 715 | 716 | check = check_gcode.GCodeChecker() 717 | check.dump_properties() 718 | check.assert_spinning() 719 | check.assert_gt('min_x', check.min_x(), -1.0) 720 | check.assert_lt('max_x', check.max_x(), 60.0) 721 | check.assert_gt('min_y', check.min_y(), -40.0) 722 | check.assert_lt('max_y', check.max_y(), 1.0) 723 | check.assert_gt('min_z', check.min_z(), -0.1) 724 | check.assert_lt('max_z', check.max_z(), 15.1) 725 | check.assert_lte('max_z_plunge', check.max_z_plunge(), 0.05) 726 | check.assert_lte('max_plunge_feed_z', check.max_plunge_feed_z(), 61.0) 727 | check.assert_lte('max_cut_feed_xy', check.max_cut_feed_xy(), 180.0) 728 | # T1 may not be supported by your CNC machine 729 | check.assert_false('T1 not present', check.has_code('T01')) 730 | 731 | This script is a simple set of rules. Note how the checks in `check_pcb_cu.py` 732 | match right up with the output printed when the tool is run. 733 | 734 | You can definitely change rules, add your own or remove rules to mold the 735 | checks to work with your setup. Here is a basic list of *aggregation* 736 | functions that you can choose from (all coded up in the 737 | relatively-straightforward `check_gcode.py` file): 738 | 739 | > Note: An aggregation takes a set of values and returns a single one. 740 | 741 | * `min_x(), min_y(), min_z()`: These represent the minimum bit coordinates 742 | present in the entire gcode file. Checking these allows us to tell if we 743 | put the origin in an unexpected spot, accidently chose too big of a design 744 | to cut, or accidently set a final cut depth too deep. 745 | * `max_x(), max_y(), max_z()`: These represent the maximum bit coordinates 746 | present in the entire gcode file. Checking these allows us to assert 747 | the design is not too big and that the final z position is not too high, 748 | where it would be in danger of striking hitting it's upper limit. 749 | * `max_z_plunge()`: This represents the maximum single-step change in z 750 | while cutting. Changing Z too much in a single pass can break a bit. 751 | * `max_plunge_feed_z()`: This represents how fast the bit plunges into 752 | the PCB. 753 | * `max_cut_feed_xy()`: This represents the maximum cut speed in XY 754 | plane (across the surface of the PCB). 755 | * `has_code()`: This returns True if a gcode (such as `T01`) is found anywhere 756 | in the file. This is useful for detecting codes that your machine does 757 | not know how to process. 758 | 759 | Below are some asserts that you can wrap around the aggregations above. 760 | An assert is like an "if" statement, where failing the assert will lead to the 761 | program returning an error when it exits: 762 | 763 | * `assert_true(message, condition)`: Fails with `message` if `condition` is 764 | false. 765 | * `assert_false(message, condition)`: Fails with `message` if `condition` is 766 | true. 767 | * `assert_gt(message, value, reference)`: Fails with `message` if `value` is <= `reference`. 768 | * `assert_lt(message, value, reference)`: Fails with `message` if `value` is >= `reference`. 769 | * `assert_gte(message, value, reference)`: Fails with `message` if `value` is < `reference`. 770 | * `assert_lte(message, value, reference)`: Fails with `message` if `value` is > `reference`. 771 | * `assert_spinning()`: Fails if the bit is ever not spinning when `z < 0.0` 772 | 773 | See the existing files for real-world examples. Feel free to add your own 774 | checks or functions as needed. 775 | 776 | Back to the example, here is example output from the drill checker: 777 | 778 | python3 check_pcb_drill.py breadboard_buttons-PTH.drl_cnc.nc 779 | Properties: 780 | lines: 95 781 | codes: 47 782 | units: {'MM'} 783 | xrange: 0.0 .. 14.224 784 | yrange: -18.034 .. 0.0 785 | zrange: -1.7 .. 15.0 786 | max_plunge_feed_z: 300.0 787 | max_cut_feed_xy: 0.0 788 | 789 | Spinning in material: OK 790 | min_x: 0.0 > -2.0: OK 791 | max_x: 14.224 < 60.0: OK 792 | min_y: -18.034 > -40.0: OK 793 | max_y: 0.0 < 2.0: OK 794 | min_z: -1.7 > -1.8: OK 795 | max_z: 15.0 < 15.1: OK 796 | max_z_plunge: 1.7 < 1.8: OK 797 | max_cut_feed_xy: 0.0 <= 0.0: OK 798 | T1 not present: OK 799 | 800 | and finally the edge checker: 801 | 802 | python3 check_pcb_edge.py breadboard_buttons-Edge_Cuts.gbr_cutout_cnc.nc 803 | Properties: 804 | lines: 280 805 | codes: 245 806 | units: {'MM'} 807 | xrange: -1.096 .. 21.924 808 | yrange: -21.924 .. 1.096 809 | zrange: -1.7 .. 15.0 810 | max_plunge_feed_z: 60.0 811 | max_cut_feed_xy: 120.0 812 | 813 | Spinning in material: OK 814 | min_x: -1.096 > -2.0: OK 815 | max_x: 21.924 < 60.0: OK 816 | min_y: -21.924 > -40.0: OK 817 | max_y: 1.096 < 2.0: OK 818 | min_z: -1.7 > -1.8: OK 819 | max_z: 15.0 < 15.1: OK 820 | max_z_plunge: 0.6 <= 0.61: OK 821 | max_cut_feed_xy: 120.0 <= 120.0: OK 822 | T1 not present: OK 823 | Wrote /home/mattwach/git/kicad/breadboard_buttons/export/test.nc 824 | 825 | Look at that last line. The edge checker wrote out a gcode file because the 826 | bottom if `check_pcb_edge.py` has this line: 827 | 828 | # the -1.0 padding account for an assumed 2.0 (or more) thick edge cut 829 | check.create_test_gcode('test_template.txt', 'test.nc', -1.0) 830 | 831 | What is this new gcode file? It is a rectangle that outlines your design with 832 | a 1mm border and a z depth of 0.0. What is that good for? It is very useful 833 | for machine calibration and verification. More on that in the CNC section. 834 | 835 | ### CNC Setup 836 | 837 | #### Machine Properties 838 | 839 | The machine I'm working with is a 3018 Sainsmart. I've also done this process 840 | on a friend's differently-branded 3018 machine with success. Many machines can 841 | be made to work here but clearly some process adaptation will be needed. 842 | 843 | #### Bit Choices 844 | 845 | I did extensive and isolated testing on many different types and sizes of bits 846 | to determine which ones I would use. Below are the "winners" of this process, 847 | along with sample images from the tests. 848 | 849 | > Note: The result images are "before sanding". When doing the tests, I thought 850 | that knowing how clean the cut was would be useful but I now think that post-sanding 851 | results would have been more useful. 852 | 853 | ##### Isolation routing 854 | 855 | The first option is the 20 degree bits that come with many CNC machines. These 856 | bits can produce some fine traces and nice detail. They are also quite cheap. 857 | 858 | On the downside, they are not the designed for cutting PCBs and the associated 859 | cutting stress exposes inconsistencies in quality. I found that success with 860 | these bits is a combination of cutting very slowly, not cutting too deeply and 861 | getting a little lucky. 862 | 863 | v bit 20 degreesv bit 20 degrees output 864 | 865 | Many go for the 30 degree bits as an alternative. These bits are a little 866 | more robust than the 20 degree bits. The trade-off is that cutting deeper 867 | results in a wider track so you will not be able to get as 868 | narrow of a track width as 20 degree if it matters (and for many projects 869 | it does not). 870 | 871 | Also I still think these are not an ideal choice verses the next two I will 872 | describe. 873 | 874 | v bit 30 degreesv bit 30 degrees output 875 | 876 | Next up are 20 degree bits with an actual cutting flutes. I find these 877 | to be much more robust than the previously-mentioned bits. They are 878 | forgiving to a wider range of cutting speeds and depths. 879 | 880 | The downside is the minimum track width - around 0.4mm. This may 881 | be a non-problem or a deal-breaker depending on your project. If you 882 | are just starting I recommend a project that can accept this width. 883 | 884 | v bit 20 degrees flutedv bit 20 degrees fluted output 885 | 886 | I final one I'll mention is a bit that is designed for isolation routing. 887 | I find this bit give the best overall results. But it is $15 a bit. These 888 | are also a bit more fragile than the 20 degree fluted bits and you need to 889 | watch your cut depth more closely (because they are 45 degree bits) both 890 | in terms of cut width and avoiding a breakage. 891 | 892 | I feel this bit is one to "graduate to" after you are confident in your 893 | process. 894 | 895 | v bit 45 degrees flutedv bit 45 degrees fluted output 896 | 897 | ##### Hole drilling 898 | 899 | For hole drilling, you can use special purpose drill bits: 900 | 901 | drill bit 902 | 903 | or repurpose an end-mill bit for drilling 904 | 905 | end mill drill bit 906 | 907 | A specialized drill bit makes cleaner cuts but I find the end-mill to be 908 | "good enough" for those cases where you don't have a correctly-sized 909 | drill bit handy. 910 | 911 | ##### Board cutout 912 | 913 | Here I find a standard end-mill bit in the 2-3mm range works well. 914 | 915 | end mill bits 916 | 917 | #### Dry Run 918 | 919 | I highly recommend doing a dry run before doing your first real cuts. After 920 | you get a couple sucessful project behind you, you can skip the dry run. 921 | 922 | ![validation](images/validation.png) 923 | 924 | To do this, first connect the Candle software to your machine and start it. 925 | Next follow these steps. 926 | 927 | 1. Remove any bit that is in the machine 928 | 2. Use the jogging controls to place the motor head in a safe region. If 929 | you are cuttiong someting small (as recommended), the middle of the machine 930 | and maybe 2cm (or so) away from the board is a good spot. 931 | 3. Click Zero XY 932 | 4. Click Zero Z 933 | 5. Load one of your gcode files into candle (like the edge cut file, for example) 934 | 6. Click the "check mode" option. 935 | 7. Click "send". This sends each gcode to the machine in a special 936 | mode where the machine will give errors if it gets a code it doesn't like but 937 | will do nothing if everything is accepted. 938 | 8. If all is good, unclick "check mode" and click "send" again. Now the machine will 939 | start spinning up and moving - it thinks it's cutting but there is no bit loaded 940 | and plenty of clearance so you can just watch it for unexpected behavior. Be ready to 941 | click "Abort" if anything looks like it is going wrong. 942 | 943 | If the machine appears to be doing the right thing, repeat steps 5-8 for each remaning 944 | gcode files (copper isolation, drills, test file) 945 | 946 | You might see an unrecognized gcode error in step 7. Often these are "tool 947 | change" codes (M6, T1) that you can delete from the gcode file with a text 948 | editor with no ill effects. If in doubt, look up the gcode on Google. For 949 | future projects, you can change the "Preprocessor" in Flatcam (or make a custom 950 | one) that permanently omits these codes. 951 | 952 | #### PCB Preparation 953 | 954 | ##### Painters Tape Method 955 | 956 | A lot of CNC projects shows the part being clamped down on the edges. Besides 957 | creating an obstacle that your CNC bit will need to avoid, this creates a 958 | bowing issue where the pressure of the clamp can cause the middle of the PCB to 959 | bow up. The precision needed for a good quality cut is around 0.02mm so this 960 | bowing is a problem. 961 | 962 | The solution is to use the following process. 963 | 964 | Take a piece of blue painters tape and put in on your machine. I like 965 | to use a small roller to make sure it's flat. 966 | 967 | ![painters tape](images/painters_tape.png) 968 | 969 | Put another piece of painters tape on the back of your PCB. I use a roller 970 | here too. 971 | 972 | ![painters tape on pcb](images/painters_tape_on_pcb.png) 973 | 974 | Add some CA to the painters tape on the PCB. Try not to get the glue so close 975 | to the edge that it makes a mess. 976 | 977 | ![ca on tape](images/ca_on_tape.png) 978 | 979 | Optional: Add CA fixing spray to the tape on the machine (a mist of water also 980 | works). If you are willing to wait 20ish minutes for the CA to dry, this 981 | step can be skipped. 982 | 983 | ![spray tape](images/spray_tape.png) 984 | 985 | Press the two pieces together firmly 986 | 987 | ![press together](images/press_together.png) 988 | 989 | ##### Probe Preparation 990 | 991 | If you are going to run a height map, which I recommend doing, you'll need to 992 | get probes setup. If you are unfamiliar, your CNC should have a couple of 993 | terminals where you can attach wires. On mine and many 994 | controllers, this is at port A5 on the controller. When these wires short together, the CNC 995 | firmware receives a signal which it an interpret as "contact" between the bit 996 | and the material. Because the bit and copper PCB are metal, they can 997 | participate in this electrical connection. 998 | 999 | So you simply connect one probe to the bit and one to the board. 1000 | To connect to the board, I take a little piece of wire that I clipped of from a 1001 | resistor/capacitor/whatever of an old project and solder it to some upper 1002 | corner of the board (I go upper left). 1003 | 1004 | ![pcb probe](images/cnc_probes.jpg) 1005 | 1006 | When everything is connected, you should be able to test the "zero" feature of 1007 | your CNC software (I'm using candle) 1008 | 1009 | ![candle probe zero](images/candle_probe_zero.png) 1010 | 1011 | > Tip: Doing a probe like this is a bit unnerving the first time as it will be the 1012 | first end-to-end test of your setup. If you have an appropriate mechanical 1013 | spring lying around, you can use it as your "bit" for testing purposes with 1014 | your hand at the abort button in case the probe fails and the spring starts to 1015 | compress. 1016 | 1017 | spring probe 1018 | 1019 | #### Loading the design 1020 | 1021 | I start by loading the "test.nc" file I generated by the checking software in 1022 | the verification step. 1023 | 1024 | > Tip: If you did not produce this file, you can use the edge cuts file instead. 1025 | 1026 | ![test loaded in candle](images/test_loaded_in_candle.png) 1027 | 1028 | #### Zeroing the machine 1029 | 1030 | If you followed my method of putting the zero point in the upper left corner, 1031 | you can zero the XY of the CNC machine just a bit under the soldered probe pad - 1032 | or whever you want so long as the design will fit. 1033 | 1034 | #### Generating a height map 1035 | 1036 | The z tolerance for a quality copper cut is tight. I'd say around 0.02 mm if you are 1037 | going for 0.4mm wide traces and even tighter if you are going smaller. 1038 | 1039 | To get this kind of precision, a height map will help immensely. The way a 1040 | heightmap works is that it probes your board in a grid-like fashion, then 1041 | alters the commands that will be sent to the CNC machine to conform to the 1042 | collected data. The result is that you will see much better depth consistency. 1043 | 1044 | To generate a height map in Candle, you first click on the "Create" button under the 1045 | "height map" section on the right: 1046 | 1047 | ![candle height map button](images/candle_height_map_button.png) 1048 | 1049 | And the bottom of the window changes to a setup UI: 1050 | 1051 | ![height map autosize](images/height_map_autosize.png) 1052 | 1053 | 1. Click the autosize button to resize the map 1054 | 2. Pick a row and column count. More will result in a better height map in 1055 | trade for more probing time. I think that a grid size of ~1cm is 1056 | acceptable so I simply take the mm dimension / 10 + 1 and round up. In this 1057 | case, the design is 21x21mm so I'll go with (21 / 10 + 1 = 3). Thus a 3x3 grid to 1058 | get an acceptably-detailed grid. If you need more precision (due to an 1059 | uneven PCB or finer cuts), use larger numbers (e.g. 6x6). 1060 | 3. There are also "interpolation settings". I always set these to the same 1061 | values as the grid ones. 1062 | 4. Now look everything over one final time and click "probe" when you are 1063 | ready to commit. and the machine will start to collect data. When it 1064 | is done, you will see something like this: 1065 | 1066 | ![probe finished](images/probe_finished.png) 1067 | 1068 | 1. Go to the File menu and chose "save". I name my file `height.map`. 1069 | 2. Click the "Edit" button to get back out. 1070 | 1071 | ![use heightmap](images/use_heightmap.png) 1072 | 1073 | 1. To actually use a heightmap click "Open" 1074 | 2. Make sure that the "use heightmap" option is checked. As you check and 1075 | uncheck the option you should see the design slightly change, indicating 1076 | that your heightmap is being applied. 1077 | 1078 | #### Adjusting the height map 1079 | 1080 | I found that for my 3018 machine, the height map process is precise but 1081 | not accurate. Putting it another way, I'll see errors in the height map results 1082 | of 0-0.15mm BUT this error is consistent across every point so simply shifting 1083 | all of the points leads to a good result. 1084 | 1085 | Here are two approaches for dealing with this. 1086 | 1087 | The way I recommend is to generate a modified version of the heightmap. The tool, 1088 | `candle_heightmap_adjust.py` is included in this project. Here is how I use it: 1089 | 1090 | $ python3 candle_heightmap_adjust.py --input height.map --offset 0.1 1091 | Wrote height_offset0.1.map 1092 | 1093 | This creates an new heightmap file with the requested offset applied. Let's 1094 | compare the raw data from the two files: 1095 | 1096 | ![meld heightmap compare](images/meld_heightmap_compare.png) 1097 | 1098 | As you can see, the number were all simply shifted the requested offset. 1099 | 1100 | > Tip: An *alternate* approach is to manually rezero the machine slightly. 1101 | Get the machine to z=0 (maybe take xy off the PCB surface to be safe), then 1102 | click the "0.1mm" or "0.01mm" resolution and click z arrows the appropriate 1103 | number of times in the desired direction. Then click "zero Z". 1104 | 1105 | ![test heightmap](images/test_heightmap.png) 1106 | 1107 | Now we can use the loaded test file to validate our depth. 1108 | 1109 | 1. Load the `heightmap_offset0.1.map` file into candle and make sure "use heightmap" is checked. 1110 | 2. Click "Send" 1111 | 1112 | The machine will cut a rectangle outline which should only take a minute. 1113 | 1114 | ![test cut](images/test_cut.png) 1115 | 1116 | You resulting cut may not have cut anything at all. That's because you told it 1117 | to cut at a height of 0.1mm (due to the heightmap file) above the board. If 1118 | that is the case, try repeating with a `heightmap_offset0.05.map` file and keep 1119 | repeating in 0.05mm steps until you finally see the bit make contact. 1120 | 1121 | **Or** 0.1mm might be your perfect number, in which case you'll see the 1122 | lightest of contact with the board, scratching the copper but not making it 1123 | through. 1124 | 1125 | **Or** 0.1mm might be cutting through to the copper. In this case, you'll want 1126 | to make a 0.15mm file and probably go with it assuming the 0.1mm cuts were no 1127 | too deep. The example image represents this case. 1128 | 1129 | You might also find that the number you choose is inconsistent in places on the 1130 | board (sometimes cutting sometimes not). This can happen if the board is of a 1131 | lower quality and it not actually flat enough for your chosen grid points to 1132 | capture. If this is the case, you'll probably need to cut in multiple passes, 1133 | each 0.05mm deeper, until all of your traces are good. This will keep your 1134 | bit from breaking (as a result of cutting too deeply). If it's really off, 1135 | you can start over with more heightmap points, on a different part of the 1136 | copper clad. 1137 | 1138 | #### Cutting the copper 1139 | 1140 | At this point, you should have a nicely-calibrated heightmap file (be it the 1141 | original, or one with an offset applied) and you are ready to cut for real. 1142 | Load your copper gcode and *reload the heightmap* (which Candle clears out on 1143 | a load). Choose the heightmap file that gave you good results: 1144 | 1145 | ![load design and heightmap](images/loaded_design_and_heightmap.png) 1146 | 1147 | When your cut is finished, vacuum the cut material away and inspect the board. 1148 | 1149 | It may be obvious that the bit did not cut deep enough everywhere (likely due 1150 | to a lower quality PCB). If this is the case, you can simply rerun the 1151 | `candle_heightmap_adjust.py` file with a height 0.05mm lower, load it into 1152 | candle and rerun the cut (alternately, use the manual rezero trick described 1153 | above). There is no need to fully rezero the machine and, in fact, doing 1154 | so will cause more harm than good. In really bad cases, you might need to 1155 | recut incrementally more than once. 1156 | 1157 | After you think the cuts are deep enough, you can sand the board. I use 400 1158 | grit first, then 1500 grit. It's not a long process, just a few seconds with 1159 | each. I then use a paper towel with some isopropyl alcohol on it for a final 1160 | clean. 1161 | 1162 | Now I get out my multimeter and start poking around on the board. The main 1163 | thing to look for is unexpected shorts. I find this usually goes pretty well, 1164 | but the boost in confidence is worth the time. 1165 | 1166 | #### Drilling the holes 1167 | 1168 | Drilling holes is easy compare to the copper. You just load a bit and 1169 | rezero the Z axis for the new bit. *Do not rezero X or Y* as this will mess up the drill 1170 | locations. I zero Z using the "paper" method where I get the bit within 2 mm 1171 | of the surface, then set the Z increment to 0.1mm. I then move a piece of 1172 | paper between the bit and PCB while moving it down in 0.1mm increments. When 1173 | the paper sticks, I click the Z-zero button in Candle. 1174 | 1175 | > **Note** You might think to use the probes to zero the drill bit. If you do 1176 | so, make sure there is a conductive path to the bit as the isolation routing 1177 | process may have isolated your board from the probe. I once broke a drill bit 1178 | because I didn't think about that! 1179 | 1180 | You *can* use the heightmap file here but it's not necessary as the 1181 | need-for-z-precision is lower. 1182 | 1183 | Watching the machine drill the holes so efficiently and precisely is one of the 1184 | highlights of the process for me. 1185 | 1186 | ![drilling holes](images/drilling_holes.png) 1187 | 1188 | You may have multiple drill sizes (and thus multiple gcode files). Just repeat 1189 | the process above for each size. 1190 | 1191 | #### Board cutout 1192 | 1193 | A *relatively* straight-forward task. Load up your endmill bit and z-zero it using the 1194 | same paper technique as described in the drilling section. 1195 | 1196 | For this part, I'd consider wearing a mask as it creates the most material. My 1197 | machine does not kick the material into the air or anything, but it's still 1198 | there. 1199 | 1200 | ![board cutout](images/board_cutout.png) 1201 | 1202 | ### Finishing Up 1203 | 1204 | Now that everything is done, you just need to dislodge the board. I get if off 1205 | the surface by wedging in a flathead screwdriver and work at it on a side until 1206 | it comes loose. I usually leave the rest of the PCB on the machine so I 1207 | (might) have fewer steps to do on the next board I cut. 1208 | 1209 | ![finishing up](images/finishing_up.jpg) 1210 | 1211 | #### Optional: Tinning 1212 | 1213 | Copper oxides over time and may not be the easiest thing to solder. You can 1214 | buy "liquid tin" to address these "problems". I use quotes because I don't see 1215 | signs of oxidation on 1 year+ old boards I created and because I don't find 1216 | soldering to plain copper to be too difficult. I still like to tin my more 1217 | complex designs though. 1218 | 1219 | The process is simple but you'll want rubber gloves the entire process at a 1220 | minimum for protection: 1221 | 1222 | 1. Find a plastic container. One that is barely big enough to hold your 1223 | board is ideal. 1224 | 2. Put your board in the container and pour the liquid tin on it. Use as 1225 | little as you can. 1226 | 3. Wait 7-10 minutes. Gentle agitation of the container is probably helpful. 1227 | 4. Take out your board and rinse it in warm water. 1228 | 1229 | tinning 1230 | 1231 | 1232 | 1233 | -------------------------------------------------------------------------------- /candle_heightmap_adjust.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """Adjusts a Candle CNC heightmap by the requested offset. 4 | 5 | Example usage: 6 | python3 candle_heightmap_adjust --input height.map --offset 0.1 7 | """ 8 | 9 | from typing import List 10 | 11 | import argparse 12 | import pathlib 13 | import sys 14 | 15 | class Error(Exception): 16 | pass 17 | 18 | class FileTooShortError(Error): 19 | pass 20 | 21 | class HeightmapFileNotFound(Error): 22 | pass 23 | 24 | class UnexpectedColumnCountError(Error): 25 | pass 26 | 27 | class UnexpectedTokenCountError(Error): 28 | pass 29 | 30 | def parse_args() -> argparse.Namespace: 31 | """Parses program arguments.""" 32 | parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__) 33 | parser.add_argument('--input', required=True, help='Input height.map path') 34 | parser.add_argument('--offset', required=True, type=float, help='Z offset to apply.') 35 | return parser.parse_args() 36 | 37 | def check_token_count(lines: List[str], index: int, expected_count: int): 38 | """Asserts that a given line has the expected token count.""" 39 | actual_count = len(lines[index].split(';')) 40 | if actual_count != expected_count: 41 | raise UnexpectedTokenCountError( 42 | 'Line %d has an unexpected number of tokens. Expected %d, got %d' % ( 43 | index + 1, expected_count, actual_count)) 44 | 45 | def candle_heightmap_adjust() -> None: 46 | """Main logic.""" 47 | args = parse_args() 48 | input_path = pathlib.Path(args.input) 49 | if not input_path.exists(): 50 | raise HeightmapFileNotFound('%s does not exist.' % input_path) 51 | 52 | with input_path.open(encoding='utf8') as fin: 53 | lines = [l.strip() for l in fin if l.strip()] 54 | 55 | if len(lines) < 4: 56 | raise FileTooShortError('Heightmap file is too short to be valid') 57 | 58 | # Validate file format expectations. 59 | check_token_count(lines, 0, 4) 60 | check_token_count(lines, 1, 4) 61 | check_token_count(lines, 2, 3) 62 | rows_and_cols = lines[1].split(';') 63 | cols = int(rows_and_cols[0]) 64 | rows = int(rows_and_cols[1]) 65 | 66 | if len(lines) != (rows + 3): 67 | raise FileTooShortError( 68 | 'With %d rows, expected %d lines in the file. ' 69 | 'Found %d lines instead' % (rows, rows + 3, len(lines))) 70 | 71 | new_lines = lines[:3] 72 | for line in lines[3:]: 73 | tokens = line.split(';') 74 | if len(tokens) != cols: 75 | raise UnexpectedColumnCountError( 76 | 'Expected %d columns, found %d' % (cols, len(tokens))) 77 | new_lines.append(';'.join(str(float(t) + args.offset) for t in tokens)) 78 | 79 | # create an output file in the same directory. 80 | output_path = pathlib.Path( 81 | input_path.parents[0], 82 | '%s_offset%s%s' % (input_path.stem, args.offset, input_path.suffix)) 83 | output_path.write_text('\n'.join(new_lines), encoding='utf8') 84 | print('Wrote %s' % output_path) 85 | 86 | def main() -> None: 87 | """Program entry.""" 88 | try: 89 | candle_heightmap_adjust() 90 | except Error as e: 91 | sys.exit(e) 92 | 93 | if __name__ == '__main__': 94 | main() 95 | -------------------------------------------------------------------------------- /check_gcode.py: -------------------------------------------------------------------------------- 1 | """Gcode checker library""" 2 | 3 | import atexit 4 | import argparse 5 | import math 6 | import os 7 | import pathlib 8 | import sys 9 | from typing import List, Optional 10 | import __main__ 11 | 12 | #pylint:disable=global-statement 13 | #pylint:disable=invalid-name 14 | #pylint:disable=missing-function-docstring 15 | 16 | def parse_args() -> argparse.Namespace: 17 | parser = argparse.ArgumentParser(description='Load and check a GCode file.') 18 | parser.add_argument('filename', type=str, 19 | help='GCode filename') 20 | return parser.parse_args() 21 | 22 | class Error(Exception): 23 | pass 24 | 25 | class BadTokenError(Error): 26 | pass 27 | 28 | class MissingKeyError(Error): 29 | pass 30 | 31 | class NotAllPassingError(Error): 32 | pass 33 | 34 | class TemplatePathNotFoundError(Error): 35 | pass 36 | 37 | class TooManyTokensError(Error): 38 | pass 39 | 40 | class UnknownGCodeError(Error): 41 | pass 42 | 43 | class UnexpectedUnitsError(Error): 44 | pass 45 | 46 | class UnknownParameterError(Error): 47 | pass 48 | 49 | all_passing = True 50 | 51 | def check_all_passing(): 52 | if not all_passing: 53 | print() 54 | raise NotAllPassingError('Some tests failed. (see above)') 55 | 56 | atexit.register(check_all_passing) 57 | 58 | 59 | class GCode: 60 | """Holds parsed information about a GCode line.""" 61 | def __init__(self): 62 | self.code = None 63 | self.x = 0.0 64 | self.y = 0.0 65 | self.z = 0.0 66 | self.feed = 0.0 67 | self.rpm = 0.0 68 | self.line_number = 0 69 | self.units = '' 70 | self.is_rapid = False 71 | 72 | self.x_delta = 0 73 | self.y_delta = 0 74 | self.z_delta = 0 75 | self.feed_delta = 0 76 | self.rpm_delta = 0 77 | 78 | def copy(self): 79 | c = GCode() 80 | c.x = self.x 81 | c.y = self.y 82 | c.z = self.z 83 | c.feed = self.feed 84 | c.rpm = self.rpm 85 | c.units = self.units 86 | return c 87 | 88 | 89 | class GCodeChecker: 90 | """GCodeChecker is the object a user instantiates to load the gcode file and check it.""" 91 | def __init__(self): 92 | args = parse_args() 93 | self.codes = load_gcode(args.filename) 94 | self.dir = pathlib.Path(args.filename).absolute().parents[0] 95 | 96 | def dump_properties(self): 97 | print('Properties:') 98 | print(f' lines: {self.codes[-1].line_number}') 99 | print(f' codes: {len(self.codes)}') 100 | print(f' units: {self.units()}') 101 | print(f' xrange: {self.min_x()} .. {self.max_x()}') 102 | print(f' yrange: {self.min_y()} .. {self.max_y()}') 103 | print(f' zrange: {self.min_z()} .. {self.max_z()}') 104 | print(f' max_plunge_feed_z: {self.max_plunge_feed_z()}') 105 | print(f' max_cut_feed_xy: {self.max_cut_feed_xy()}') 106 | 107 | print() 108 | 109 | def aggregate(self, getter, combiner): 110 | values = [getter(code) for code in self.codes] 111 | return combiner(values) 112 | 113 | def min_x(self): 114 | return self.aggregate(lambda code: code.x, min) 115 | 116 | def max_x(self): 117 | return self.aggregate(lambda code: code.x, max) 118 | 119 | def min_y(self): 120 | return self.aggregate(lambda code: code.y, min) 121 | 122 | def max_y(self): 123 | return self.aggregate(lambda code: code.y, max) 124 | 125 | def min_z(self): 126 | return self.aggregate(lambda code: code.z, min) 127 | 128 | def max_z(self): 129 | return self.aggregate(lambda code: code.z, max) 130 | 131 | def max_rpm(self): 132 | return self.aggregate(lambda code: code.rpm, max) 133 | 134 | def max_z_plunge(self): 135 | def check(code): 136 | if code.z >= 0.0: 137 | return 0.0 138 | if code.z_delta >= 0.0: 139 | return 0.0 140 | if code.z - code.z_delta > 0: 141 | return -code.z 142 | return -code.z_delta 143 | return self.aggregate(check, max) 144 | 145 | def max_plunge_feed_z(self): 146 | def check_z(code): 147 | if code.rpm == 0.0: 148 | return 0.0 149 | if code.z_delta >= 0.0: 150 | return 0.0 151 | return code.feed 152 | return self.aggregate(check_z, max) 153 | 154 | def max_cut_feed_xy(self): 155 | def check(code): 156 | if code.z >= 0.0: 157 | return 0.0 158 | if code.x_delta == 0.0 and code.y_delta == 0.0: 159 | return 0.0 160 | return code.feed 161 | return self.aggregate(check, max) 162 | 163 | def has_code(self, code): 164 | if len(code) == 2: 165 | code = code[0] + '0' + code[1] 166 | for c in self.codes: 167 | if c.code == code: 168 | return True 169 | return False 170 | 171 | def units(self): 172 | return set((code.units for code in self.codes)) 173 | 174 | # Assertions 175 | def assert_true(self, msg: str, cond: bool) -> None: 176 | if cond: 177 | print(f'{msg}: OK') 178 | else: 179 | print(f'{msg}: FAIL') 180 | global all_passing 181 | all_passing = False 182 | 183 | def assert_false(self, msg: str, cond: bool) -> None: 184 | self.assert_true(msg, not cond) 185 | 186 | def assert_spinning(self): 187 | spin_ok = True 188 | for code in self.codes: 189 | if code.z < 0.0 and code.rpm == 0.0: 190 | spin_ok = False 191 | self.assert_true('Spinning in material', spin_ok) 192 | 193 | def assert_gt(self, msg, val, ref): 194 | msg = f'{msg}: {val} > {ref}' 195 | self.assert_true(msg, val > ref) 196 | 197 | def assert_lt(self, msg, val, ref): 198 | msg = f'{msg}: {val} < {ref}' 199 | self.assert_true(msg, val < ref) 200 | 201 | def assert_gte(self, msg, val, ref): 202 | msg = f'{msg}: {val} >= {ref}' 203 | self.assert_true(msg, val >= ref) 204 | 205 | def assert_lte(self, msg, val, ref): 206 | msg = f'{msg}: {val} <= {ref}' 207 | self.assert_true(msg, val <= ref) 208 | 209 | # Test file creation 210 | 211 | def create_test_gcode( 212 | self, 213 | template_filename: str, 214 | output_filename: str, 215 | padding: float) -> None: 216 | if not all_passing: 217 | print('Skipping test gcode output (tests are failing)') 218 | if self.units() != set(['MM']): 219 | raise UnexpectedUnitsError('Only MM units are supported for test files.') 220 | template_path = pathlib.Path(template_filename) 221 | if not template_path.exists(): 222 | template_path = pathlib.Path( 223 | os.path.join(os.path.dirname(__main__.__file__), template_path)) 224 | 225 | if not template_path.exists(): 226 | raise TemplatePathNotFoundError( 227 | 'Did not find template path: %s' % template_path) 228 | 229 | x_start = math.floor(self.min_x() - padding) 230 | x_end = math.ceil(self.max_x() + padding) 231 | y_start = math.ceil(self.max_y() + padding) 232 | y_end = math.floor(self.min_y() - padding) 233 | 234 | template_data = { 235 | 'spindle_speed': self.max_rpm(), 236 | 'unit_code': 21, 237 | 'units': 'MM', 238 | 'x_end': x_end, 239 | 'x_start': x_start, 240 | 'xy_feedrate': self.max_cut_feed_xy(), 241 | 'y_end': y_end, 242 | 'y_start': y_start, 243 | 'z_dwell_feedrate': 200, 244 | 'z_initial_height': 10, 245 | 'z_plunge_feedrate': 60, 246 | } 247 | 248 | template = template_path.read_text(encoding='utf8') 249 | try: 250 | output = template.format(**template_data) 251 | except KeyError as e: 252 | raise MissingKeyError('Missing template key: %s' % e) from e 253 | 254 | output_path = self.dir / output_filename 255 | with open(output_path, 'w', encoding='utf8') as fout: 256 | fout.write(output) 257 | 258 | print('Wrote %s' % output_path) 259 | 260 | 261 | def load_gcode(filename: str) -> List[str]: 262 | lines = [] 263 | with open(filename, 'r', encoding='utf8') as fin: 264 | previous_code = None 265 | for line_number0, line in enumerate(fin): 266 | line_number = line_number0 + 1 267 | try: 268 | code = process_line(previous_code, line, line_number) 269 | except Error as e: 270 | sys.stderr.write(f'Line {line_number}\n') 271 | sys.stderr.write(f'{line}') 272 | sys.stderr.write(f'{e}\n') 273 | sys.exit(1) 274 | if code: 275 | lines.append(code) 276 | previous_code = code 277 | return lines 278 | 279 | 280 | def process_line( 281 | previous_code: GCode, line: str, line_number: int) -> Optional[GCode]: 282 | line = line.strip().upper().split('(')[0] 283 | if not line: 284 | return None 285 | if previous_code: 286 | code = previous_code.copy() 287 | else: 288 | code = GCode() 289 | code.line_number = line_number 290 | tokens = make_tokens(line) 291 | code.code = tokens[0] 292 | if code.code not in CODES: 293 | raise UnknownGCodeError('Unknown GCode') 294 | CODES[code.code](code, tokens) 295 | return code 296 | 297 | def make_tokens(line: str) -> List[str]: 298 | tokens = [] 299 | t = [] 300 | for c in line.strip().upper(): 301 | if c == ';': 302 | #start of a comment 303 | break 304 | if t: 305 | if c in '-0123456789.': 306 | t.append(c) 307 | continue 308 | 309 | tokens.append(''.join(t)) 310 | t = [] 311 | 312 | if c in ' \t': 313 | continue 314 | 315 | if not t: 316 | if c in ' \t': 317 | continue 318 | if 'A' <= c <= 'Z': 319 | t = [c] 320 | else: 321 | raise BadTokenError('Bad token') 322 | 323 | if t: 324 | tokens.append(''.join(t)) 325 | 326 | if tokens: 327 | if len(tokens[0]) == 2: 328 | # G1 -> G01 329 | tokens[0] = f'{tokens[0][0]}0{tokens[0][1]}' 330 | 331 | return tokens 332 | 333 | # 334 | # GCODE handlers below this point 335 | # 336 | 337 | def linear_move(code: GCode, tokens: List[str]): 338 | for t in tokens[1:]: 339 | parm = t[0] 340 | val = float(t[1:]) 341 | if parm == 'F': 342 | code.feed_delta = val - code.feed 343 | code.feed = val 344 | elif parm == 'X': 345 | code.x_delta = val - code.x 346 | code.x = val 347 | elif parm == 'Y': 348 | code.y_delta = val - code.y 349 | code.y = val 350 | elif parm == 'Z': 351 | code.z_delta = val - code.z 352 | code.z = val 353 | else: 354 | raise UnknownParameterError(f'Unknown parameter: {parm}') 355 | 356 | def ignore(unused_code: GCode, unused_tokens: List[str]): 357 | pass 358 | 359 | def rapid_move(code: GCode, tokens: List[str]): 360 | code.is_rapid = True 361 | linear_move(code, tokens) 362 | 363 | def set_units_to_mm(code: GCode, tokens: List[str]): 364 | if len(tokens) > 1: 365 | raise TooManyTokensError('Too many tokens') 366 | code.units = 'MM' 367 | 368 | def start_spindle(code: GCode, tokens: List[str]): 369 | for t in tokens[1:]: 370 | parm = t[0] 371 | val = float(t[1:]) 372 | if parm == 'S': 373 | code.rpm_delta = val - code.rpm 374 | code.rpm = val 375 | else: 376 | raise UnknownParameterError(f'Unknown parameter: {parm}') 377 | 378 | def stop_spindle(code: GCode, tokens: List[str]): 379 | if len(tokens) > 1: 380 | raise TooManyTokensError('Too many tokens') 381 | code.rpm = 0 382 | 383 | 384 | CODES = { 385 | 'G00': rapid_move, 386 | 'G01': linear_move, 387 | 'G17': ignore, # set plane to XY (assumed to be true) 388 | 'G21': set_units_to_mm, 389 | 'G90': ignore, # absolute positioning 390 | 'G94': ignore, # feed per minute 391 | 'M00': ignore, # unconditional stop 392 | 'M03': start_spindle, 393 | 'M05': stop_spindle, 394 | 'T01': ignore, # change to tool 1 395 | } 396 | -------------------------------------------------------------------------------- /check_pcb_common.py: -------------------------------------------------------------------------------- 1 | """Common checks that would go in every file.""" 2 | 3 | def check_common(check, maxx=100.0, miny=-70.0): 4 | """Common checks that would otherwise have to be repeated in every file.""" 5 | check.dump_properties() 6 | check.assert_spinning() 7 | # Assumes a 7x10cm board with the origin at the upper-left 8 | check.assert_gt('min_x', check.min_x(), -2.0) 9 | check.assert_lt('max_x', check.max_x(), maxx) 10 | check.assert_gt('min_y', check.min_y(), miny) 11 | check.assert_lt('max_y', check.max_y(), 2.0) 12 | 13 | # maximum allowed z 14 | check.assert_lt('max_z', check.max_z(), 15.1) 15 | -------------------------------------------------------------------------------- /check_pcb_cu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Checks a copper isolation gcode file.""" 3 | 4 | import check_gcode 5 | import check_pcb_common 6 | 7 | check = check_gcode.GCodeChecker() 8 | # checks that are common for all files (board dimensions, assert spinning, etc) 9 | check_pcb_common.check_common(check) 10 | check.assert_gt('min_z', check.min_z(), -0.1) 11 | check.assert_lte('max_z_plunge', check.max_z_plunge(), 0.05) 12 | check.assert_lte('max_plunge_feed_z', check.max_plunge_feed_z(), 61.0) 13 | check.assert_lte('max_cut_feed_xy', check.max_cut_feed_xy(), 180.0) 14 | -------------------------------------------------------------------------------- /check_pcb_drill.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Checks a drill gcode file.""" 3 | 4 | import check_gcode 5 | import check_pcb_common 6 | 7 | check = check_gcode.GCodeChecker() 8 | # checks that are common for all files (board dimensions, assert spinning, etc) 9 | check_pcb_common.check_common(check) 10 | check.assert_gt('min_z', check.min_z(), -1.8) 11 | check.assert_lt('max_z_plunge', check.max_z_plunge(), 1.8) 12 | # don't move the drill in the XY direction while cutting 13 | check.assert_lte('max_cut_feed_xy', check.max_cut_feed_xy(), 0.0) 14 | -------------------------------------------------------------------------------- /check_pcb_edge.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Checks a edge gcode file. 3 | 4 | You should edit this file as needed to conform to your particular machine and process. 5 | """ 6 | 7 | import check_gcode 8 | import check_pcb_common 9 | 10 | check = check_gcode.GCodeChecker() 11 | # checks that are common for all files (board dimensions, assert spinning, etc) 12 | check_pcb_common.check_common(check) 13 | check.assert_gt('min_z', check.min_z(), -1.8) 14 | check.assert_lte('max_z_plunge', check.max_z_plunge(), 0.61) 15 | check.assert_lte('max_cut_feed_xy', check.max_cut_feed_xy(), 120.0) 16 | 17 | # the -1.0 padding account for an assumed 2.0 (or more) thick edge cut 18 | check.create_test_gcode('test_template.txt', 'test.nc', -1.0) 19 | -------------------------------------------------------------------------------- /check_pcb_stencil.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Checks a copper isolation gcode file.""" 3 | 4 | import check_gcode 5 | import check_pcb_common 6 | 7 | check = check_gcode.GCodeChecker() 8 | # checks that are common for all files (board dimensions, assert spinning, etc) 9 | check_pcb_common.check_common(check, maxx=200.0, miny=-120.0) 10 | check.assert_gt('min_z', check.min_z(), -0.21) 11 | check.assert_lte('max_z_plunge', check.max_z_plunge(), 0.2) 12 | check.assert_lte('max_plunge_feed_z', check.max_plunge_feed_z(), 101.0) 13 | check.assert_lte('max_cut_feed_xy', check.max_cut_feed_xy(), 300.0) 14 | -------------------------------------------------------------------------------- /examples/breadboard_buttons/breadboard_buttons.kicad_pcb: -------------------------------------------------------------------------------- 1 | (kicad_pcb (version 20211014) (generator pcbnew) 2 | 3 | (general 4 | (thickness 1.6) 5 | ) 6 | 7 | (paper "A4") 8 | (layers 9 | (0 "F.Cu" signal) 10 | (31 "B.Cu" signal) 11 | (32 "B.Adhes" user "B.Adhesive") 12 | (33 "F.Adhes" user "F.Adhesive") 13 | (34 "B.Paste" user) 14 | (35 "F.Paste" user) 15 | (36 "B.SilkS" user "B.Silkscreen") 16 | (37 "F.SilkS" user "F.Silkscreen") 17 | (38 "B.Mask" user) 18 | (39 "F.Mask" user) 19 | (40 "Dwgs.User" user "User.Drawings") 20 | (41 "Cmts.User" user "User.Comments") 21 | (42 "Eco1.User" user "User.Eco1") 22 | (43 "Eco2.User" user "User.Eco2") 23 | (44 "Edge.Cuts" user) 24 | (45 "Margin" user) 25 | (46 "B.CrtYd" user "B.Courtyard") 26 | (47 "F.CrtYd" user "F.Courtyard") 27 | (48 "B.Fab" user) 28 | (49 "F.Fab" user) 29 | (50 "User.1" user) 30 | (51 "User.2" user) 31 | (52 "User.3" user) 32 | (53 "User.4" user) 33 | (54 "User.5" user) 34 | (55 "User.6" user) 35 | (56 "User.7" user) 36 | (57 "User.8" user) 37 | (58 "User.9" user) 38 | ) 39 | 40 | (setup 41 | (stackup 42 | (layer "F.SilkS" (type "Top Silk Screen")) 43 | (layer "F.Paste" (type "Top Solder Paste")) 44 | (layer "F.Mask" (type "Top Solder Mask") (thickness 0.01)) 45 | (layer "F.Cu" (type "copper") (thickness 0.035)) 46 | (layer "dielectric 1" (type "core") (thickness 1.51) (material "FR4") (epsilon_r 4.5) (loss_tangent 0.02)) 47 | (layer "B.Cu" (type "copper") (thickness 0.035)) 48 | (layer "B.Mask" (type "Bottom Solder Mask") (thickness 0.01)) 49 | (layer "B.Paste" (type "Bottom Solder Paste")) 50 | (layer "B.SilkS" (type "Bottom Silk Screen")) 51 | (copper_finish "None") 52 | (dielectric_constraints no) 53 | ) 54 | (pad_to_mask_clearance 0) 55 | (aux_axis_origin 138.43 91.44) 56 | (pcbplotparams 57 | (layerselection 0x0001000_7fffffff) 58 | (disableapertmacros false) 59 | (usegerberextensions false) 60 | (usegerberattributes true) 61 | (usegerberadvancedattributes true) 62 | (creategerberjobfile true) 63 | (svguseinch false) 64 | (svgprecision 6) 65 | (excludeedgelayer true) 66 | (plotframeref false) 67 | (viasonmask false) 68 | (mode 1) 69 | (useauxorigin false) 70 | (hpglpennumber 1) 71 | (hpglpenspeed 20) 72 | (hpglpendiameter 15.000000) 73 | (dxfpolygonmode true) 74 | (dxfimperialunits true) 75 | (dxfusepcbnewfont true) 76 | (psnegative false) 77 | (psa4output false) 78 | (plotreference true) 79 | (plotvalue true) 80 | (plotinvisibletext false) 81 | (sketchpadsonfab false) 82 | (subtractmaskfromsilk false) 83 | (outputformat 1) 84 | (mirror false) 85 | (drillshape 0) 86 | (scaleselection 1) 87 | (outputdirectory "export/") 88 | ) 89 | ) 90 | 91 | (net 0 "") 92 | (net 1 "Net-(J1-Pad1)") 93 | (net 2 "Net-(J2-Pad4)") 94 | (net 3 "Net-(J1-Pad2)") 95 | (net 4 "Net-(J2-Pad3)") 96 | (net 5 "Net-(J1-Pad3)") 97 | (net 6 "Net-(J2-Pad2)") 98 | (net 7 "Net-(J1-Pad4)") 99 | (net 8 "Net-(J2-Pad1)") 100 | 101 | (footprint "Matt:SMD_Button_6mm_3_5mm" (layer "F.Cu") 102 | (tedit 0) (tstamp 3054d448-903a-40b6-98c5-4c0c1e334d8d) 103 | (at 151.13 105.79) 104 | (property "Sheetfile" "breadboard_buttons.kicad_sch") 105 | (property "Sheetname" "") 106 | (path "/30e3de30-6f75-46f0-be4f-7ecf2cdf7b40") 107 | (attr smd) 108 | (fp_text reference "SW2" (at 0 -4.19 unlocked) (layer "F.SilkS") 109 | (effects (font (size 1 1) (thickness 0.15))) 110 | (tstamp 66dc70c5-ee0a-4358-8ea0-9699c2bd53ab) 111 | ) 112 | (fp_text value "SW_Push" (at 1.27 1.271 unlocked) (layer "F.Fab") 113 | (effects (font (size 0.2 0.2) (thickness 0.05))) 114 | (tstamp e274ab69-927c-4b03-85de-603ff12ace73) 115 | ) 116 | (fp_line (start -2 1) (end 2 1) (layer "F.CrtYd") (width 0.05) (tstamp 28acf318-4ef4-40bf-9881-631a3a2cce0c)) 117 | (fp_line (start -2 -9) (end -2 1) (layer "F.CrtYd") (width 0.05) (tstamp 45cb7379-4f9a-4c0d-a412-5bd9e0112b7a)) 118 | (fp_line (start 2 -9) (end -2 -9) (layer "F.CrtYd") (width 0.05) (tstamp 620649e4-1d91-4242-981c-2a419ca149e5)) 119 | (fp_line (start 2 1) (end 2 -9) (layer "F.CrtYd") (width 0.05) (tstamp 6b3e9b94-1ed0-46a8-8ec8-291158fdb768)) 120 | (fp_line (start 0.75 -5.5) (end -0.75 -5.5) (layer "F.Fab") (width 0.1) (tstamp 00f97587-5246-4fa4-8f47-130d23214070)) 121 | (fp_line (start -1.75 -1) (end 1.75 -1) (layer "F.Fab") (width 0.1) (tstamp 3f5f547d-3556-4a68-bb9c-636bd5afa3b0)) 122 | (fp_line (start 1.75 -1) (end 1.75 -7) (layer "F.Fab") (width 0.1) (tstamp 5887f481-28ad-47ba-9ed0-420cbc3ebd7e)) 123 | (fp_line (start -0.75 -2.5) (end 0.75 -2.5) (layer "F.Fab") (width 0.1) (tstamp 938e7da4-e5c4-42fa-a06e-ca56659b4c9a)) 124 | (fp_line (start 1.75 -7) (end -1.75 -7) (layer "F.Fab") (width 0.1) (tstamp 9e95574b-4d1a-4294-ae33-d18db933cd31)) 125 | (fp_line (start -0.75 -5.5) (end -0.75 -2.5) (layer "F.Fab") (width 0.1) (tstamp afdd72cb-fef8-4740-b09d-1d2e5f613496)) 126 | (fp_line (start -1.75 -7) (end -1.75 -1) (layer "F.Fab") (width 0.1) (tstamp d6bd501c-359b-49ae-bf34-347ec888ee19)) 127 | (fp_line (start 0.75 -2.5) (end 0.75 -5.5) (layer "F.Fab") (width 0.1) (tstamp e18271f1-9a54-43b8-a40b-000bc905c18b)) 128 | (pad "1" smd roundrect (at 0 0) (size 2 2) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25) 129 | (net 3 "Net-(J1-Pad2)") (pinfunction "1") (pintype "passive") (tstamp 6581881c-d6a1-4170-9bd9-8a5ed125ff62)) 130 | (pad "2" smd roundrect (at 0 -8) (size 2 2) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25) 131 | (net 4 "Net-(J2-Pad3)") (pinfunction "2") (pintype "passive") (tstamp a57e17ec-a351-417f-986e-ecad4e65f9f5)) 132 | ) 133 | 134 | (footprint "Matt:SMD_Button_6mm_3_5mm" (layer "F.Cu") 135 | (tedit 0) (tstamp 700ba280-2523-42dc-9704-5615462badf5) 136 | (at 156.21 105.79) 137 | (property "Sheetfile" "breadboard_buttons.kicad_sch") 138 | (property "Sheetname" "") 139 | (path "/37fe36f4-c986-4cd4-b196-5c5cc58f6ceb") 140 | (attr smd) 141 | (fp_text reference "SW1" (at 0 -4.19 unlocked) (layer "F.SilkS") 142 | (effects (font (size 1 1) (thickness 0.15))) 143 | (tstamp f4e1ed47-4185-4438-8261-7752e8fd2f5e) 144 | ) 145 | (fp_text value "SW_Push" (at 1.143 1.525 unlocked) (layer "F.Fab") 146 | (effects (font (size 0.2 0.2) (thickness 0.05))) 147 | (tstamp 66ce6bb3-0fa2-4529-803f-a01810669061) 148 | ) 149 | (fp_line (start 2 -9) (end -2 -9) (layer "F.CrtYd") (width 0.05) (tstamp 11da0898-3491-48a5-bec5-a8e35dfebf25)) 150 | (fp_line (start 2 1) (end 2 -9) (layer "F.CrtYd") (width 0.05) (tstamp 4662c426-d128-4182-9efb-d62677ac4aef)) 151 | (fp_line (start -2 1) (end 2 1) (layer "F.CrtYd") (width 0.05) (tstamp 5c7c00eb-2848-4bf4-bfc3-db1deba86464)) 152 | (fp_line (start -2 -9) (end -2 1) (layer "F.CrtYd") (width 0.05) (tstamp 79f491be-9819-4b6b-af79-643789f29fa0)) 153 | (fp_line (start -1.75 -1) (end 1.75 -1) (layer "F.Fab") (width 0.1) (tstamp 33de1d51-fb43-45ac-9b6f-c809ad20330d)) 154 | (fp_line (start -1.75 -7) (end -1.75 -1) (layer "F.Fab") (width 0.1) (tstamp 3addd48c-c5b8-4559-9f7a-0b66ed63beee)) 155 | (fp_line (start 0.75 -2.5) (end 0.75 -5.5) (layer "F.Fab") (width 0.1) (tstamp 3d7d3bef-a5ea-404c-8178-dca8bf5ff632)) 156 | (fp_line (start 0.75 -5.5) (end -0.75 -5.5) (layer "F.Fab") (width 0.1) (tstamp 76a8dfbe-b351-4342-aec3-cde1c5107a40)) 157 | (fp_line (start -0.75 -2.5) (end 0.75 -2.5) (layer "F.Fab") (width 0.1) (tstamp a5f3c3a4-f905-41ec-8fac-08135299f38e)) 158 | (fp_line (start 1.75 -7) (end -1.75 -7) (layer "F.Fab") (width 0.1) (tstamp b277b6bb-2841-4920-ae64-3c0ef2cb5364)) 159 | (fp_line (start -0.75 -5.5) (end -0.75 -2.5) (layer "F.Fab") (width 0.1) (tstamp c301e20a-afe0-4562-8b17-8a02e71490dc)) 160 | (fp_line (start 1.75 -1) (end 1.75 -7) (layer "F.Fab") (width 0.1) (tstamp f273a572-cc83-4ed4-aae1-4b2c9cd4c64a)) 161 | (pad "1" smd roundrect (at 0 0) (size 2 2) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25) 162 | (net 1 "Net-(J1-Pad1)") (pinfunction "1") (pintype "passive") (tstamp 6439e955-8476-43f4-a594-a960f91f8f23)) 163 | (pad "2" smd roundrect (at 0 -8) (size 2 2) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25) 164 | (net 2 "Net-(J2-Pad4)") (pinfunction "2") (pintype "passive") (tstamp af81156b-9e69-4662-8628-3ad728e75d7f)) 165 | ) 166 | 167 | (footprint "Matt:SMD_Button_6mm_3_5mm" (layer "F.Cu") 168 | (tedit 0) (tstamp 7dd731c7-0db3-46f5-af02-560cbecbba14) 169 | (at 146.11 105.79) 170 | (property "Sheetfile" "breadboard_buttons.kicad_sch") 171 | (property "Sheetname" "") 172 | (path "/2f9c4dba-7a05-4b41-b54f-6083f79e001e") 173 | (attr smd) 174 | (fp_text reference "SW3" (at -0.06 -4.19 unlocked) (layer "F.SilkS") 175 | (effects (font (size 1 1) (thickness 0.15))) 176 | (tstamp 12faf36d-aa66-4d59-bc0c-56d53d9a56b4) 177 | ) 178 | (fp_text value "SW_Push" (at -1.203 1.398 unlocked) (layer "F.Fab") 179 | (effects (font (size 0.2 0.2) (thickness 0.05))) 180 | (tstamp a5ca09bb-b63c-4cfc-b978-0f615ea2d653) 181 | ) 182 | (fp_line (start -2 -9) (end -2 1) (layer "F.CrtYd") (width 0.05) (tstamp 023ed00a-8de1-4492-acc7-b19ab2da3090)) 183 | (fp_line (start 2 -9) (end -2 -9) (layer "F.CrtYd") (width 0.05) (tstamp 0c6db845-a82d-488f-966e-4d28db28e35c)) 184 | (fp_line (start 2 1) (end 2 -9) (layer "F.CrtYd") (width 0.05) (tstamp 97aec12a-7dec-46fa-a05c-4a0d4454e191)) 185 | (fp_line (start -2 1) (end 2 1) (layer "F.CrtYd") (width 0.05) (tstamp eb97ed44-1edf-40e2-ab95-dcb817234bd1)) 186 | (fp_line (start -1.75 -1) (end 1.75 -1) (layer "F.Fab") (width 0.1) (tstamp 0d41ddba-23f1-43fd-802a-44d6ef8900d4)) 187 | (fp_line (start -0.75 -5.5) (end -0.75 -2.5) (layer "F.Fab") (width 0.1) (tstamp 2b29381b-d8aa-4af5-b2e6-fed6a09d8162)) 188 | (fp_line (start -1.75 -7) (end -1.75 -1) (layer "F.Fab") (width 0.1) (tstamp 571cea0d-7569-4f1d-8c2a-a437ed7e0cd2)) 189 | (fp_line (start 1.75 -7) (end -1.75 -7) (layer "F.Fab") (width 0.1) (tstamp 67a3f4a5-c5cc-4b60-acb3-fba21f246b1d)) 190 | (fp_line (start -0.75 -2.5) (end 0.75 -2.5) (layer "F.Fab") (width 0.1) (tstamp 713d3407-2edd-4d43-bb26-74b8953ae456)) 191 | (fp_line (start 0.75 -5.5) (end -0.75 -5.5) (layer "F.Fab") (width 0.1) (tstamp 7ea012d1-256b-4189-909d-4a4f46737532)) 192 | (fp_line (start 1.75 -1) (end 1.75 -7) (layer "F.Fab") (width 0.1) (tstamp c728b65e-bdeb-4cac-b04d-a7df0245d1b1)) 193 | (fp_line (start 0.75 -2.5) (end 0.75 -5.5) (layer "F.Fab") (width 0.1) (tstamp dade2f91-95b4-4ed5-98a0-b3f4b9887dad)) 194 | (pad "1" smd roundrect (at 0 0) (size 2 2) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25) 195 | (net 5 "Net-(J1-Pad3)") (pinfunction "1") (pintype "passive") (tstamp a1a426f5-7b93-4a7e-b786-0e8f789affb4)) 196 | (pad "2" smd roundrect (at 0 -8) (size 2 2) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25) 197 | (net 6 "Net-(J2-Pad2)") (pinfunction "2") (pintype "passive") (tstamp 4ce900a0-e023-4e5d-a201-7c5ab5b4ff3f)) 198 | ) 199 | 200 | (footprint "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical" (layer "F.Cu") 201 | (tedit 59FED5CC) (tstamp bdd7e85d-337d-4541-b7ea-cd55ac367b3e) 202 | (at 144.78 93.98 90) 203 | (descr "Through hole straight pin header, 1x04, 2.54mm pitch, single row") 204 | (tags "Through hole pin header THT 1x04 2.54mm single row") 205 | (property "Sheetfile" "breadboard_buttons.kicad_sch") 206 | (property "Sheetname" "") 207 | (path "/8818971a-834b-4ae8-9f69-2d947edae811") 208 | (attr through_hole) 209 | (fp_text reference "J2" (at 0 -2.33 90) (layer "F.SilkS") 210 | (effects (font (size 1 1) (thickness 0.15))) 211 | (tstamp fc0ef859-59dd-4937-9980-56d58fdedff8) 212 | ) 213 | (fp_text value "Conn_01x04_Male" (at 0 9.95 90) (layer "F.Fab") 214 | (effects (font (size 0.2 0.2) (thickness 0.05))) 215 | (tstamp 613fc88d-ed20-411c-a32a-e1293269f861) 216 | ) 217 | (fp_text user "${REFERENCE}" (at 0 3.81) (layer "F.Fab") 218 | (effects (font (size 1 1) (thickness 0.15))) 219 | (tstamp 4bb7dc7b-2c5c-4b6e-91a0-ac8dfbf2e387) 220 | ) 221 | (fp_line (start -1.33 1.27) (end 1.33 1.27) (layer "F.SilkS") (width 0.12) (tstamp 1c30fb04-c991-4a34-916d-3c7576c647da)) 222 | (fp_line (start -1.33 8.95) (end 1.33 8.95) (layer "F.SilkS") (width 0.12) (tstamp 7585f722-b97a-4d6e-8c49-8fee5e345b4d)) 223 | (fp_line (start -1.33 0) (end -1.33 -1.33) (layer "F.SilkS") (width 0.12) (tstamp bbf283bd-71ba-4b54-af90-c4694602f399)) 224 | (fp_line (start -1.33 1.27) (end -1.33 8.95) (layer "F.SilkS") (width 0.12) (tstamp e025741c-0d9c-4e5a-bc52-cc92b64c2883)) 225 | (fp_line (start 1.33 1.27) (end 1.33 8.95) (layer "F.SilkS") (width 0.12) (tstamp ea7b21d3-c1a2-4cbc-a5c7-3aa81059ad64)) 226 | (fp_line (start -1.33 -1.33) (end 0 -1.33) (layer "F.SilkS") (width 0.12) (tstamp fefd1349-26f9-4ccf-b67f-30c020027728)) 227 | (fp_line (start -1.8 -1.8) (end -1.8 9.4) (layer "F.CrtYd") (width 0.05) (tstamp 1f5dcf59-5e8d-4a57-8951-c83a8356238e)) 228 | (fp_line (start -1.8 9.4) (end 1.8 9.4) (layer "F.CrtYd") (width 0.05) (tstamp 399ad86a-ac1f-49e4-b25a-c28dc3c9dc2b)) 229 | (fp_line (start 1.8 9.4) (end 1.8 -1.8) (layer "F.CrtYd") (width 0.05) (tstamp b2c9d6fb-1b49-4f02-906e-0b36986eb8bd)) 230 | (fp_line (start 1.8 -1.8) (end -1.8 -1.8) (layer "F.CrtYd") (width 0.05) (tstamp d0ee4397-5b9f-45ab-a240-cef8320771ec)) 231 | (fp_line (start 1.27 8.89) (end -1.27 8.89) (layer "F.Fab") (width 0.1) (tstamp 02e55101-3d7b-40bb-b479-16f8f1d1dc76)) 232 | (fp_line (start -1.27 -0.635) (end -0.635 -1.27) (layer "F.Fab") (width 0.1) (tstamp a680237b-3daf-4817-ad3a-7527ecf8d761)) 233 | (fp_line (start -1.27 8.89) (end -1.27 -0.635) (layer "F.Fab") (width 0.1) (tstamp ab2ce259-caef-482f-a7fc-bc78ede88101)) 234 | (fp_line (start 1.27 -1.27) (end 1.27 8.89) (layer "F.Fab") (width 0.1) (tstamp dea9c27e-d05e-4dbd-84f6-558e636c65b7)) 235 | (fp_line (start -0.635 -1.27) (end 1.27 -1.27) (layer "F.Fab") (width 0.1) (tstamp ec6bf876-8173-47fc-9a91-b3bf04f3de24)) 236 | (pad "1" thru_hole rect (at 0 0 90) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask) 237 | (net 8 "Net-(J2-Pad1)") (pinfunction "Pin_1") (pintype "passive") (tstamp 78b9aa1a-6972-40a1-9fb4-2705896c2c96)) 238 | (pad "2" thru_hole oval (at 0 2.54 90) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask) 239 | (net 6 "Net-(J2-Pad2)") (pinfunction "Pin_2") (pintype "passive") (tstamp c22d9972-f5b8-4b8a-9432-6abd18020496)) 240 | (pad "3" thru_hole oval (at 0 5.08 90) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask) 241 | (net 4 "Net-(J2-Pad3)") (pinfunction "Pin_3") (pintype "passive") (tstamp bd12f3bc-5121-4c7c-959f-2e9bf6180bf1)) 242 | (pad "4" thru_hole oval (at 0 7.62 90) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask) 243 | (net 2 "Net-(J2-Pad4)") (pinfunction "Pin_4") (pintype "passive") (tstamp 03b04a5c-d552-48ad-9fc4-c106751abc3d)) 244 | (model "${KICAD6_3DMODEL_DIR}/Connector_PinHeader_2.54mm.3dshapes/PinHeader_1x04_P2.54mm_Vertical.wrl" 245 | (offset (xyz 0 0 0)) 246 | (scale (xyz 1 1 1)) 247 | (rotate (xyz 0 0 0)) 248 | ) 249 | ) 250 | 251 | (footprint "Matt:SMD_Button_6mm_3_5mm" (layer "F.Cu") 252 | (tedit 0) (tstamp d60e1349-1584-4bd3-a444-3e6d787ec397) 253 | (at 140.97 105.79) 254 | (property "Sheetfile" "breadboard_buttons.kicad_sch") 255 | (property "Sheetname" "") 256 | (path "/b1de6aec-c95c-496c-ad69-fb5b6db6dd5d") 257 | (attr smd) 258 | (fp_text reference "SW4" (at 0 -4.19 unlocked) (layer "F.SilkS") 259 | (effects (font (size 1 1) (thickness 0.15))) 260 | (tstamp f67d6170-d211-4c52-9d0d-8f31c23426e4) 261 | ) 262 | (fp_text value "SW_Push" (at -1.27 1.525 unlocked) (layer "F.Fab") 263 | (effects (font (size 0.2 0.2) (thickness 0.05))) 264 | (tstamp dbde4b80-34c7-481e-b964-bfef0ac7f9a2) 265 | ) 266 | (fp_line (start -2 1) (end 2 1) (layer "F.CrtYd") (width 0.05) (tstamp 3ea81e68-c50e-431e-baf3-26a095f3434f)) 267 | (fp_line (start 2 1) (end 2 -9) (layer "F.CrtYd") (width 0.05) (tstamp 497769ca-2143-467e-a660-e937f6b44d22)) 268 | (fp_line (start -2 -9) (end -2 1) (layer "F.CrtYd") (width 0.05) (tstamp 60256079-aeee-4574-9453-93a6969ba6fd)) 269 | (fp_line (start 2 -9) (end -2 -9) (layer "F.CrtYd") (width 0.05) (tstamp b5dc8190-c32a-42a7-a3e7-1f0b261cffe1)) 270 | (fp_line (start 0.75 -2.5) (end 0.75 -5.5) (layer "F.Fab") (width 0.1) (tstamp 0e8e401e-93aa-442c-9b6c-a995c583122b)) 271 | (fp_line (start -1.75 -1) (end 1.75 -1) (layer "F.Fab") (width 0.1) (tstamp 6181d5b5-446f-431a-a8b7-db925b08fc4e)) 272 | (fp_line (start 0.75 -5.5) (end -0.75 -5.5) (layer "F.Fab") (width 0.1) (tstamp 648eba6c-2fb2-4606-8cd5-d2d760cf9af1)) 273 | (fp_line (start -0.75 -5.5) (end -0.75 -2.5) (layer "F.Fab") (width 0.1) (tstamp 8d18f2d6-c6ee-462b-828e-666447b75feb)) 274 | (fp_line (start 1.75 -1) (end 1.75 -7) (layer "F.Fab") (width 0.1) (tstamp 948538ad-2bdb-407d-8387-4b985035ca68)) 275 | (fp_line (start 1.75 -7) (end -1.75 -7) (layer "F.Fab") (width 0.1) (tstamp c6f54069-82d6-45fc-a4b4-8c979ea5e034)) 276 | (fp_line (start -1.75 -7) (end -1.75 -1) (layer "F.Fab") (width 0.1) (tstamp d85974f6-b270-4538-ab08-f9f92dd795e8)) 277 | (fp_line (start -0.75 -2.5) (end 0.75 -2.5) (layer "F.Fab") (width 0.1) (tstamp e5c0d798-f576-4db7-8248-4eea9f3f5f79)) 278 | (pad "1" smd roundrect (at 0 0) (size 2 2) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25) 279 | (net 7 "Net-(J1-Pad4)") (pinfunction "1") (pintype "passive") (tstamp 51d19ca8-5db7-4546-8443-76b62db08b92)) 280 | (pad "2" smd roundrect (at 0 -8) (size 2 2) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25) 281 | (net 8 "Net-(J2-Pad1)") (pinfunction "2") (pintype "passive") (tstamp 0a006b79-ebd3-411d-863d-c8c9a79658f6)) 282 | ) 283 | 284 | (footprint "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical" (layer "F.Cu") 285 | (tedit 59FED5CC) (tstamp f111a510-4d94-492c-8326-daffae497f0a) 286 | (at 152.38 109.22 -90) 287 | (descr "Through hole straight pin header, 1x04, 2.54mm pitch, single row") 288 | (tags "Through hole pin header THT 1x04 2.54mm single row") 289 | (property "Sheetfile" "breadboard_buttons.kicad_sch") 290 | (property "Sheetname" "") 291 | (path "/892259d1-2e0b-4e45-803c-a4ead1cf370f") 292 | (attr through_hole) 293 | (fp_text reference "J1" (at 0 -2.33 90) (layer "F.SilkS") 294 | (effects (font (size 1 1) (thickness 0.15))) 295 | (tstamp 67c06c0a-bb5c-4044-a81e-9d8172e1c347) 296 | ) 297 | (fp_text value "Conn_01x04_Male" (at 0 9.95 90) (layer "F.Fab") 298 | (effects (font (size 0.2 0.2) (thickness 0.05))) 299 | (tstamp e524098b-d92f-49c3-afac-175d5b012016) 300 | ) 301 | (fp_text user "${REFERENCE}" (at 0 3.81) (layer "F.Fab") 302 | (effects (font (size 1 1) (thickness 0.15))) 303 | (tstamp 8b0c59e3-8525-48da-8dc6-1d9fbdcb2abc) 304 | ) 305 | (fp_line (start -1.33 -1.33) (end 0 -1.33) (layer "F.SilkS") (width 0.12) (tstamp 1dc45dd5-980a-4b93-b829-3e4d3d7dd98c)) 306 | (fp_line (start -1.33 8.95) (end 1.33 8.95) (layer "F.SilkS") (width 0.12) (tstamp 451dd6d7-6a85-4b9e-995f-f245b107a4ea)) 307 | (fp_line (start -1.33 1.27) (end -1.33 8.95) (layer "F.SilkS") (width 0.12) (tstamp 8bed9bc3-8265-4672-b176-685f997eab14)) 308 | (fp_line (start -1.33 1.27) (end 1.33 1.27) (layer "F.SilkS") (width 0.12) (tstamp aa229899-79fa-420c-abc3-6513cac95d6d)) 309 | (fp_line (start -1.33 0) (end -1.33 -1.33) (layer "F.SilkS") (width 0.12) (tstamp b47c0495-d951-4a7c-b70b-b42be605510f)) 310 | (fp_line (start 1.33 1.27) (end 1.33 8.95) (layer "F.SilkS") (width 0.12) (tstamp caa0294d-0e0f-4460-a257-88bdc303dc24)) 311 | (fp_line (start 1.8 9.4) (end 1.8 -1.8) (layer "F.CrtYd") (width 0.05) (tstamp 602ac1b3-b47b-422d-af7a-3dd1747b695e)) 312 | (fp_line (start -1.8 -1.8) (end -1.8 9.4) (layer "F.CrtYd") (width 0.05) (tstamp 71bda1ac-3570-4f3c-960d-89a19659e51d)) 313 | (fp_line (start -1.8 9.4) (end 1.8 9.4) (layer "F.CrtYd") (width 0.05) (tstamp 74053bda-0e52-4fec-a1b9-bd7a68335bcf)) 314 | (fp_line (start 1.8 -1.8) (end -1.8 -1.8) (layer "F.CrtYd") (width 0.05) (tstamp f20cb92b-e4e9-4cf6-8174-53b506592e99)) 315 | (fp_line (start 1.27 8.89) (end -1.27 8.89) (layer "F.Fab") (width 0.1) (tstamp 338b03c1-06ef-4237-bb2c-01dab27b591c)) 316 | (fp_line (start -0.635 -1.27) (end 1.27 -1.27) (layer "F.Fab") (width 0.1) (tstamp 38bf7133-4aee-4fcd-ac0b-07a23251b80f)) 317 | (fp_line (start 1.27 -1.27) (end 1.27 8.89) (layer "F.Fab") (width 0.1) (tstamp 3cccb7f0-e5a9-4684-99cd-7a71b7b6c355)) 318 | (fp_line (start -1.27 -0.635) (end -0.635 -1.27) (layer "F.Fab") (width 0.1) (tstamp 5099afdc-681d-43b8-998b-6016f87417d0)) 319 | (fp_line (start -1.27 8.89) (end -1.27 -0.635) (layer "F.Fab") (width 0.1) (tstamp c8a7262c-00c2-4aa7-b668-d32443b87dca)) 320 | (pad "1" thru_hole rect (at 0 0 270) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask) 321 | (net 1 "Net-(J1-Pad1)") (pinfunction "Pin_1") (pintype "passive") (tstamp af2aca14-42c2-442a-836c-3f405480f9ad)) 322 | (pad "2" thru_hole oval (at 0 2.54 270) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask) 323 | (net 3 "Net-(J1-Pad2)") (pinfunction "Pin_2") (pintype "passive") (tstamp cc853b36-b0b4-4cf9-aa89-8064b7335770)) 324 | (pad "3" thru_hole oval (at 0 5.08 270) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask) 325 | (net 5 "Net-(J1-Pad3)") (pinfunction "Pin_3") (pintype "passive") (tstamp 8533edb2-8864-42f0-bc99-88da8f744af9)) 326 | (pad "4" thru_hole oval (at 0 7.62 270) (size 1.7 1.7) (drill 1) (layers *.Cu *.Mask) 327 | (net 7 "Net-(J1-Pad4)") (pinfunction "Pin_4") (pintype "passive") (tstamp 93c5a2b2-a1fb-4a6d-852f-9d91d98fb0f1)) 328 | (model "${KICAD6_3DMODEL_DIR}/Connector_PinHeader_2.54mm.3dshapes/PinHeader_1x04_P2.54mm_Vertical.wrl" 329 | (offset (xyz 0 0 0)) 330 | (scale (xyz 1 1 1)) 331 | (rotate (xyz 0 0 0)) 332 | ) 333 | ) 334 | 335 | (gr_line (start 158.75 91.44) (end 138.43 91.44) (layer "Edge.Cuts") (width 0.1) (tstamp 137fb6be-feb6-4c66-b869-7e5718a53a99)) 336 | (gr_line (start 158.75 111.76) (end 158.75 91.44) (layer "Edge.Cuts") (width 0.1) (tstamp 240ecb66-d408-44c5-a370-d8bd6ce34697)) 337 | (gr_line (start 138.43 91.44) (end 138.43 111.76) (layer "Edge.Cuts") (width 0.1) (tstamp bb8d4151-3861-4ae0-8359-f3f83ba82d2e)) 338 | (gr_line (start 138.43 111.76) (end 158.75 111.76) (layer "Edge.Cuts") (width 0.1) (tstamp e8a43ac6-a356-4201-92e9-c369db2e1cc8)) 339 | 340 | (segment (start 152.78 109.22) (end 156.21 105.79) (width 1) (layer "F.Cu") (net 1) (tstamp 2d98314c-3536-4c25-a732-9249213226a5)) 341 | (segment (start 152.38 109.22) (end 152.78 109.22) (width 1) (layer "F.Cu") (net 1) (tstamp f80943cd-a760-4f9d-bf9c-36b8be157167)) 342 | (segment (start 152.4 93.98) (end 156.21 97.79) (width 1) (layer "F.Cu") (net 2) (tstamp c541a110-69dd-4c69-8722-7621e5d8a71c)) 343 | (segment (start 149.84 109.22) (end 149.84 107.08) (width 1) (layer "F.Cu") (net 3) (tstamp 0a69d6bd-1449-49f0-b743-05e25e0f9093)) 344 | (segment (start 149.84 107.08) (end 151.13 105.79) (width 1) (layer "F.Cu") (net 3) (tstamp 4a39d29f-9375-4892-8dba-3f995a210d87)) 345 | (segment (start 149.86 93.98) (end 149.86 96.52) (width 1) (layer "F.Cu") (net 4) (tstamp 59329f42-6e02-41f5-8bc2-17ab6f3556ed)) 346 | (segment (start 149.86 96.52) (end 151.13 97.79) (width 1) (layer "F.Cu") (net 4) (tstamp 8c94d589-6475-4252-9684-123108114cfb)) 347 | (segment (start 147.3 106.98) (end 146.11 105.79) (width 1) (layer "F.Cu") (net 5) (tstamp 03e6b2a1-b480-475d-ac96-3b061fdb44ec)) 348 | (segment (start 147.3 109.22) (end 147.3 106.98) (width 1) (layer "F.Cu") (net 5) (tstamp f13f163d-26f3-4538-b015-0709387e77b4)) 349 | (segment (start 147.32 96.58) (end 147.32 93.98) (width 1) (layer "F.Cu") (net 6) (tstamp 2cc0a15c-fdfe-4d59-991a-3ba2fe4f15df)) 350 | (segment (start 146.11 97.79) (end 147.32 96.58) (width 1) (layer "F.Cu") (net 6) (tstamp f70a8990-7aae-4d98-8e81-3931b90e2aae)) 351 | (segment (start 144.4 109.22) (end 140.97 105.79) (width 1) (layer "F.Cu") (net 7) (tstamp 98ea7e38-db39-42ad-a627-cc182ce37259)) 352 | (segment (start 144.76 109.22) (end 144.4 109.22) (width 1) (layer "F.Cu") (net 7) (tstamp a1b19ede-314b-49e4-a9ff-86c287a80010)) 353 | (segment (start 140.97 97.79) (end 144.78 93.98) (width 1) (layer "F.Cu") (net 8) (tstamp 49221e2f-29c8-441c-9a03-4df459e553b1)) 354 | 355 | ) 356 | -------------------------------------------------------------------------------- /examples/breadboard_buttons/breadboard_buttons.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 44, 4 | "active_layer_preset": "", 5 | "auto_track_width": true, 6 | "hidden_nets": [], 7 | "high_contrast_mode": 0, 8 | "net_color_mode": 1, 9 | "opacity": { 10 | "pads": 1.0, 11 | "tracks": 1.0, 12 | "vias": 1.0, 13 | "zones": 0.6 14 | }, 15 | "ratsnest_display_mode": 0, 16 | "selection_filter": { 17 | "dimensions": true, 18 | "footprints": true, 19 | "graphics": true, 20 | "keepouts": true, 21 | "lockedItems": true, 22 | "otherItems": true, 23 | "pads": true, 24 | "text": true, 25 | "tracks": true, 26 | "vias": true, 27 | "zones": true 28 | }, 29 | "visible_items": [ 30 | 0, 31 | 1, 32 | 2, 33 | 3, 34 | 4, 35 | 5, 36 | 8, 37 | 9, 38 | 10, 39 | 11, 40 | 12, 41 | 13, 42 | 14, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36 64 | ], 65 | "visible_layers": "7ffffff_80000001", 66 | "zone_display_mode": 0 67 | }, 68 | "meta": { 69 | "filename": "breadboard_buttons.kicad_prl", 70 | "version": 3 71 | }, 72 | "project": { 73 | "files": [] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /examples/breadboard_buttons/breadboard_buttons.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "design_settings": { 4 | "defaults": { 5 | "board_outline_line_width": 0.09999999999999999, 6 | "copper_line_width": 0.19999999999999998, 7 | "copper_text_italic": false, 8 | "copper_text_size_h": 1.5, 9 | "copper_text_size_v": 1.5, 10 | "copper_text_thickness": 0.3, 11 | "copper_text_upright": false, 12 | "courtyard_line_width": 0.049999999999999996, 13 | "dimension_precision": 4, 14 | "dimension_units": 3, 15 | "dimensions": { 16 | "arrow_length": 1270000, 17 | "extension_offset": 500000, 18 | "keep_text_aligned": true, 19 | "suppress_zeroes": false, 20 | "text_position": 0, 21 | "units_format": 1 22 | }, 23 | "fab_line_width": 0.09999999999999999, 24 | "fab_text_italic": false, 25 | "fab_text_size_h": 1.0, 26 | "fab_text_size_v": 1.0, 27 | "fab_text_thickness": 0.15, 28 | "fab_text_upright": false, 29 | "other_line_width": 0.15, 30 | "other_text_italic": false, 31 | "other_text_size_h": 1.0, 32 | "other_text_size_v": 1.0, 33 | "other_text_thickness": 0.15, 34 | "other_text_upright": false, 35 | "pads": { 36 | "drill": 0.762, 37 | "height": 1.524, 38 | "width": 1.524 39 | }, 40 | "silk_line_width": 0.15, 41 | "silk_text_italic": false, 42 | "silk_text_size_h": 1.0, 43 | "silk_text_size_v": 1.0, 44 | "silk_text_thickness": 0.15, 45 | "silk_text_upright": false, 46 | "zones": { 47 | "45_degree_only": false, 48 | "min_clearance": 0.508 49 | } 50 | }, 51 | "diff_pair_dimensions": [ 52 | { 53 | "gap": 0.0, 54 | "via_gap": 0.0, 55 | "width": 0.0 56 | } 57 | ], 58 | "drc_exclusions": [], 59 | "meta": { 60 | "version": 2 61 | }, 62 | "rule_severities": { 63 | "annular_width": "error", 64 | "clearance": "error", 65 | "copper_edge_clearance": "error", 66 | "courtyards_overlap": "error", 67 | "diff_pair_gap_out_of_range": "error", 68 | "diff_pair_uncoupled_length_too_long": "error", 69 | "drill_out_of_range": "error", 70 | "duplicate_footprints": "warning", 71 | "extra_footprint": "warning", 72 | "footprint_type_mismatch": "error", 73 | "hole_clearance": "error", 74 | "hole_near_hole": "error", 75 | "invalid_outline": "error", 76 | "item_on_disabled_layer": "error", 77 | "items_not_allowed": "error", 78 | "length_out_of_range": "error", 79 | "malformed_courtyard": "error", 80 | "microvia_drill_out_of_range": "error", 81 | "missing_courtyard": "ignore", 82 | "missing_footprint": "warning", 83 | "net_conflict": "warning", 84 | "npth_inside_courtyard": "ignore", 85 | "padstack": "error", 86 | "pth_inside_courtyard": "ignore", 87 | "shorting_items": "error", 88 | "silk_over_copper": "warning", 89 | "silk_overlap": "warning", 90 | "skew_out_of_range": "error", 91 | "through_hole_pad_without_hole": "error", 92 | "too_many_vias": "error", 93 | "track_dangling": "warning", 94 | "track_width": "error", 95 | "tracks_crossing": "error", 96 | "unconnected_items": "error", 97 | "unresolved_variable": "error", 98 | "via_dangling": "warning", 99 | "zone_has_empty_net": "error", 100 | "zones_intersect": "error" 101 | }, 102 | "rules": { 103 | "allow_blind_buried_vias": false, 104 | "allow_microvias": false, 105 | "max_error": 0.005, 106 | "min_clearance": 0.39999999999999997, 107 | "min_copper_edge_clearance": 0.0, 108 | "min_hole_clearance": 0.25, 109 | "min_hole_to_hole": 0.25, 110 | "min_microvia_diameter": 0.19999999999999998, 111 | "min_microvia_drill": 0.09999999999999999, 112 | "min_silk_clearance": 0.0, 113 | "min_through_hole_diameter": 0.3, 114 | "min_track_width": 0.39999999999999997, 115 | "min_via_annular_width": 0.049999999999999996, 116 | "min_via_diameter": 0.39999999999999997, 117 | "solder_mask_clearance": 0.0, 118 | "solder_mask_min_width": 0.0, 119 | "use_height_for_length_calcs": true 120 | }, 121 | "track_widths": [ 122 | 0.0, 123 | 1.0 124 | ], 125 | "via_dimensions": [ 126 | { 127 | "diameter": 0.0, 128 | "drill": 0.0 129 | } 130 | ], 131 | "zones_allow_external_fillets": false, 132 | "zones_use_no_outline": true 133 | }, 134 | "layer_presets": [] 135 | }, 136 | "boards": [], 137 | "cvpcb": { 138 | "equivalence_files": [] 139 | }, 140 | "erc": { 141 | "erc_exclusions": [], 142 | "meta": { 143 | "version": 0 144 | }, 145 | "pin_map": [ 146 | [ 147 | 0, 148 | 0, 149 | 0, 150 | 0, 151 | 0, 152 | 0, 153 | 1, 154 | 0, 155 | 0, 156 | 0, 157 | 0, 158 | 2 159 | ], 160 | [ 161 | 0, 162 | 2, 163 | 0, 164 | 1, 165 | 0, 166 | 0, 167 | 1, 168 | 0, 169 | 2, 170 | 2, 171 | 2, 172 | 2 173 | ], 174 | [ 175 | 0, 176 | 0, 177 | 0, 178 | 0, 179 | 0, 180 | 0, 181 | 1, 182 | 0, 183 | 1, 184 | 0, 185 | 1, 186 | 2 187 | ], 188 | [ 189 | 0, 190 | 1, 191 | 0, 192 | 0, 193 | 0, 194 | 0, 195 | 1, 196 | 1, 197 | 2, 198 | 1, 199 | 1, 200 | 2 201 | ], 202 | [ 203 | 0, 204 | 0, 205 | 0, 206 | 0, 207 | 0, 208 | 0, 209 | 1, 210 | 0, 211 | 0, 212 | 0, 213 | 0, 214 | 2 215 | ], 216 | [ 217 | 0, 218 | 0, 219 | 0, 220 | 0, 221 | 0, 222 | 0, 223 | 0, 224 | 0, 225 | 0, 226 | 0, 227 | 0, 228 | 2 229 | ], 230 | [ 231 | 1, 232 | 1, 233 | 1, 234 | 1, 235 | 1, 236 | 0, 237 | 1, 238 | 1, 239 | 1, 240 | 1, 241 | 1, 242 | 2 243 | ], 244 | [ 245 | 0, 246 | 0, 247 | 0, 248 | 1, 249 | 0, 250 | 0, 251 | 1, 252 | 0, 253 | 0, 254 | 0, 255 | 0, 256 | 2 257 | ], 258 | [ 259 | 0, 260 | 2, 261 | 1, 262 | 2, 263 | 0, 264 | 0, 265 | 1, 266 | 0, 267 | 2, 268 | 2, 269 | 2, 270 | 2 271 | ], 272 | [ 273 | 0, 274 | 2, 275 | 0, 276 | 1, 277 | 0, 278 | 0, 279 | 1, 280 | 0, 281 | 2, 282 | 0, 283 | 0, 284 | 2 285 | ], 286 | [ 287 | 0, 288 | 2, 289 | 1, 290 | 1, 291 | 0, 292 | 0, 293 | 1, 294 | 0, 295 | 2, 296 | 0, 297 | 0, 298 | 2 299 | ], 300 | [ 301 | 2, 302 | 2, 303 | 2, 304 | 2, 305 | 2, 306 | 2, 307 | 2, 308 | 2, 309 | 2, 310 | 2, 311 | 2, 312 | 2 313 | ] 314 | ], 315 | "rule_severities": { 316 | "bus_definition_conflict": "error", 317 | "bus_entry_needed": "error", 318 | "bus_label_syntax": "error", 319 | "bus_to_bus_conflict": "error", 320 | "bus_to_net_conflict": "error", 321 | "different_unit_footprint": "error", 322 | "different_unit_net": "error", 323 | "duplicate_reference": "error", 324 | "duplicate_sheet_names": "error", 325 | "extra_units": "error", 326 | "global_label_dangling": "warning", 327 | "hier_label_mismatch": "error", 328 | "label_dangling": "error", 329 | "lib_symbol_issues": "warning", 330 | "multiple_net_names": "warning", 331 | "net_not_bus_member": "warning", 332 | "no_connect_connected": "warning", 333 | "no_connect_dangling": "warning", 334 | "pin_not_connected": "error", 335 | "pin_not_driven": "error", 336 | "pin_to_pin": "warning", 337 | "power_pin_not_driven": "error", 338 | "similar_labels": "warning", 339 | "unannotated": "error", 340 | "unit_value_mismatch": "error", 341 | "unresolved_variable": "error", 342 | "wire_dangling": "error" 343 | } 344 | }, 345 | "libraries": { 346 | "pinned_footprint_libs": [], 347 | "pinned_symbol_libs": [] 348 | }, 349 | "meta": { 350 | "filename": "breadboard_buttons.kicad_pro", 351 | "version": 1 352 | }, 353 | "net_settings": { 354 | "classes": [ 355 | { 356 | "bus_width": 12.0, 357 | "clearance": 0.2, 358 | "diff_pair_gap": 0.25, 359 | "diff_pair_via_gap": 0.25, 360 | "diff_pair_width": 0.2, 361 | "line_style": 0, 362 | "microvia_diameter": 0.3, 363 | "microvia_drill": 0.1, 364 | "name": "Default", 365 | "pcb_color": "rgba(0, 0, 0, 0.000)", 366 | "schematic_color": "rgba(0, 0, 0, 0.000)", 367 | "track_width": 0.25, 368 | "via_diameter": 0.8, 369 | "via_drill": 0.4, 370 | "wire_width": 6.0 371 | } 372 | ], 373 | "meta": { 374 | "version": 2 375 | }, 376 | "net_colors": null 377 | }, 378 | "pcbnew": { 379 | "last_paths": { 380 | "gencad": "", 381 | "idf": "", 382 | "netlist": "breadboard_buttons.net", 383 | "specctra_dsn": "", 384 | "step": "", 385 | "vrml": "" 386 | }, 387 | "page_layout_descr_file": "" 388 | }, 389 | "schematic": { 390 | "annotate_start_num": 0, 391 | "drawing": { 392 | "default_line_thickness": 6.0, 393 | "default_text_size": 50.0, 394 | "field_names": [], 395 | "intersheets_ref_own_page": false, 396 | "intersheets_ref_prefix": "", 397 | "intersheets_ref_short": false, 398 | "intersheets_ref_show": false, 399 | "intersheets_ref_suffix": "", 400 | "junction_size_choice": 3, 401 | "label_size_ratio": 0.375, 402 | "pin_symbol_size": 25.0, 403 | "text_offset_ratio": 0.15 404 | }, 405 | "legacy_lib_dir": "", 406 | "legacy_lib_list": [], 407 | "meta": { 408 | "version": 1 409 | }, 410 | "net_format_name": "KiCad", 411 | "ngspice": { 412 | "fix_include_paths": true, 413 | "fix_passive_vals": false, 414 | "meta": { 415 | "version": 0 416 | }, 417 | "model_mode": 0, 418 | "workbook_filename": "" 419 | }, 420 | "page_layout_descr_file": "", 421 | "plot_directory": "", 422 | "spice_adjust_passive_values": false, 423 | "spice_external_command": "spice \"%I\"", 424 | "subpart_first_id": 65, 425 | "subpart_id_separator": 0 426 | }, 427 | "sheets": [ 428 | [ 429 | "c1fb9cd6-221d-4158-8929-57d8a2bc2047", 430 | "" 431 | ] 432 | ], 433 | "text_variables": {} 434 | } 435 | -------------------------------------------------------------------------------- /examples/breadboard_buttons/breadboard_buttons.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch (version 20211123) (generator eeschema) 2 | 3 | (uuid c1fb9cd6-221d-4158-8929-57d8a2bc2047) 4 | 5 | (paper "A4") 6 | 7 | (lib_symbols 8 | (symbol "Connector:Conn_01x04_Male" (pin_names (offset 1.016) hide) (in_bom yes) (on_board yes) 9 | (property "Reference" "J" (id 0) (at 0 5.08 0) 10 | (effects (font (size 1.27 1.27))) 11 | ) 12 | (property "Value" "Conn_01x04_Male" (id 1) (at 0 -7.62 0) 13 | (effects (font (size 1.27 1.27))) 14 | ) 15 | (property "Footprint" "" (id 2) (at 0 0 0) 16 | (effects (font (size 1.27 1.27)) hide) 17 | ) 18 | (property "Datasheet" "~" (id 3) (at 0 0 0) 19 | (effects (font (size 1.27 1.27)) hide) 20 | ) 21 | (property "ki_keywords" "connector" (id 4) (at 0 0 0) 22 | (effects (font (size 1.27 1.27)) hide) 23 | ) 24 | (property "ki_description" "Generic connector, single row, 01x04, script generated (kicad-library-utils/schlib/autogen/connector/)" (id 5) (at 0 0 0) 25 | (effects (font (size 1.27 1.27)) hide) 26 | ) 27 | (property "ki_fp_filters" "Connector*:*_1x??_*" (id 6) (at 0 0 0) 28 | (effects (font (size 1.27 1.27)) hide) 29 | ) 30 | (symbol "Conn_01x04_Male_1_1" 31 | (polyline 32 | (pts 33 | (xy 1.27 -5.08) 34 | (xy 0.8636 -5.08) 35 | ) 36 | (stroke (width 0.1524) (type default) (color 0 0 0 0)) 37 | (fill (type none)) 38 | ) 39 | (polyline 40 | (pts 41 | (xy 1.27 -2.54) 42 | (xy 0.8636 -2.54) 43 | ) 44 | (stroke (width 0.1524) (type default) (color 0 0 0 0)) 45 | (fill (type none)) 46 | ) 47 | (polyline 48 | (pts 49 | (xy 1.27 0) 50 | (xy 0.8636 0) 51 | ) 52 | (stroke (width 0.1524) (type default) (color 0 0 0 0)) 53 | (fill (type none)) 54 | ) 55 | (polyline 56 | (pts 57 | (xy 1.27 2.54) 58 | (xy 0.8636 2.54) 59 | ) 60 | (stroke (width 0.1524) (type default) (color 0 0 0 0)) 61 | (fill (type none)) 62 | ) 63 | (rectangle (start 0.8636 -4.953) (end 0 -5.207) 64 | (stroke (width 0.1524) (type default) (color 0 0 0 0)) 65 | (fill (type outline)) 66 | ) 67 | (rectangle (start 0.8636 -2.413) (end 0 -2.667) 68 | (stroke (width 0.1524) (type default) (color 0 0 0 0)) 69 | (fill (type outline)) 70 | ) 71 | (rectangle (start 0.8636 0.127) (end 0 -0.127) 72 | (stroke (width 0.1524) (type default) (color 0 0 0 0)) 73 | (fill (type outline)) 74 | ) 75 | (rectangle (start 0.8636 2.667) (end 0 2.413) 76 | (stroke (width 0.1524) (type default) (color 0 0 0 0)) 77 | (fill (type outline)) 78 | ) 79 | (pin passive line (at 5.08 2.54 180) (length 3.81) 80 | (name "Pin_1" (effects (font (size 1.27 1.27)))) 81 | (number "1" (effects (font (size 1.27 1.27)))) 82 | ) 83 | (pin passive line (at 5.08 0 180) (length 3.81) 84 | (name "Pin_2" (effects (font (size 1.27 1.27)))) 85 | (number "2" (effects (font (size 1.27 1.27)))) 86 | ) 87 | (pin passive line (at 5.08 -2.54 180) (length 3.81) 88 | (name "Pin_3" (effects (font (size 1.27 1.27)))) 89 | (number "3" (effects (font (size 1.27 1.27)))) 90 | ) 91 | (pin passive line (at 5.08 -5.08 180) (length 3.81) 92 | (name "Pin_4" (effects (font (size 1.27 1.27)))) 93 | (number "4" (effects (font (size 1.27 1.27)))) 94 | ) 95 | ) 96 | ) 97 | (symbol "Switch:SW_Push" (pin_numbers hide) (pin_names (offset 1.016) hide) (in_bom yes) (on_board yes) 98 | (property "Reference" "SW" (id 0) (at 1.27 2.54 0) 99 | (effects (font (size 1.27 1.27)) (justify left)) 100 | ) 101 | (property "Value" "SW_Push" (id 1) (at 0 -1.524 0) 102 | (effects (font (size 1.27 1.27))) 103 | ) 104 | (property "Footprint" "" (id 2) (at 0 5.08 0) 105 | (effects (font (size 1.27 1.27)) hide) 106 | ) 107 | (property "Datasheet" "~" (id 3) (at 0 5.08 0) 108 | (effects (font (size 1.27 1.27)) hide) 109 | ) 110 | (property "ki_keywords" "switch normally-open pushbutton push-button" (id 4) (at 0 0 0) 111 | (effects (font (size 1.27 1.27)) hide) 112 | ) 113 | (property "ki_description" "Push button switch, generic, two pins" (id 5) (at 0 0 0) 114 | (effects (font (size 1.27 1.27)) hide) 115 | ) 116 | (symbol "SW_Push_0_1" 117 | (circle (center -2.032 0) (radius 0.508) 118 | (stroke (width 0) (type default) (color 0 0 0 0)) 119 | (fill (type none)) 120 | ) 121 | (polyline 122 | (pts 123 | (xy 0 1.27) 124 | (xy 0 3.048) 125 | ) 126 | (stroke (width 0) (type default) (color 0 0 0 0)) 127 | (fill (type none)) 128 | ) 129 | (polyline 130 | (pts 131 | (xy 2.54 1.27) 132 | (xy -2.54 1.27) 133 | ) 134 | (stroke (width 0) (type default) (color 0 0 0 0)) 135 | (fill (type none)) 136 | ) 137 | (circle (center 2.032 0) (radius 0.508) 138 | (stroke (width 0) (type default) (color 0 0 0 0)) 139 | (fill (type none)) 140 | ) 141 | (pin passive line (at -5.08 0 0) (length 2.54) 142 | (name "1" (effects (font (size 1.27 1.27)))) 143 | (number "1" (effects (font (size 1.27 1.27)))) 144 | ) 145 | (pin passive line (at 5.08 0 180) (length 2.54) 146 | (name "2" (effects (font (size 1.27 1.27)))) 147 | (number "2" (effects (font (size 1.27 1.27)))) 148 | ) 149 | ) 150 | ) 151 | ) 152 | 153 | 154 | (wire (pts (xy 111.76 115.57) (xy 116.84 115.57)) 155 | (stroke (width 0) (type default) (color 0 0 0 0)) 156 | (uuid 01a34557-c98e-4b2a-a4f3-a24bc6af53e2) 157 | ) 158 | (wire (pts (xy 128.27 113.03) (xy 128.27 115.57)) 159 | (stroke (width 0) (type default) (color 0 0 0 0)) 160 | (uuid 178c6091-67f8-4f85-b3a3-5ef6ad680dcf) 161 | ) 162 | (wire (pts (xy 134.62 113.03) (xy 128.27 113.03)) 163 | (stroke (width 0) (type default) (color 0 0 0 0)) 164 | (uuid 2dbb53fe-eee2-48c8-8b20-ee95eeb145fa) 165 | ) 166 | (wire (pts (xy 129.54 107.95) (xy 129.54 101.6)) 167 | (stroke (width 0) (type default) (color 0 0 0 0)) 168 | (uuid 3651d177-75dd-4e71-b028-fad05d3defa2) 169 | ) 170 | (wire (pts (xy 111.76 113.03) (xy 118.11 113.03)) 171 | (stroke (width 0) (type default) (color 0 0 0 0)) 172 | (uuid 481b0064-a15c-414b-8284-f77f8d573113) 173 | ) 174 | (wire (pts (xy 116.84 115.57) (xy 116.84 121.92)) 175 | (stroke (width 0) (type default) (color 0 0 0 0)) 176 | (uuid 56437a73-1a5f-47d3-b3d2-fa104f78f83e) 177 | ) 178 | (wire (pts (xy 111.76 107.95) (xy 116.84 107.95)) 179 | (stroke (width 0) (type default) (color 0 0 0 0)) 180 | (uuid 5fbd57b0-77e3-47ba-b08a-3e42bba384a8) 181 | ) 182 | (wire (pts (xy 118.11 110.49) (xy 118.11 107.95)) 183 | (stroke (width 0) (type default) (color 0 0 0 0)) 184 | (uuid 672fd6b6-f769-47a4-a344-7c68541cc705) 185 | ) 186 | (wire (pts (xy 129.54 121.92) (xy 128.27 121.92)) 187 | (stroke (width 0) (type default) (color 0 0 0 0)) 188 | (uuid 8c1a0559-ee21-45cf-883e-fa2ea222b3ae) 189 | ) 190 | (wire (pts (xy 118.11 113.03) (xy 118.11 115.57)) 191 | (stroke (width 0) (type default) (color 0 0 0 0)) 192 | (uuid 8ee018d7-8fbf-46c3-a755-2a0723c52f51) 193 | ) 194 | (wire (pts (xy 129.54 115.57) (xy 129.54 121.92)) 195 | (stroke (width 0) (type default) (color 0 0 0 0)) 196 | (uuid a7e088d1-5d22-4661-8fdb-d109065825bf) 197 | ) 198 | (wire (pts (xy 116.84 101.6) (xy 118.11 101.6)) 199 | (stroke (width 0) (type default) (color 0 0 0 0)) 200 | (uuid b3033ac4-e20e-4242-a204-2610e2141114) 201 | ) 202 | (wire (pts (xy 116.84 107.95) (xy 116.84 101.6)) 203 | (stroke (width 0) (type default) (color 0 0 0 0)) 204 | (uuid b98bf3e9-92da-4ef0-95d9-02fb59f22a6d) 205 | ) 206 | (wire (pts (xy 129.54 101.6) (xy 128.27 101.6)) 207 | (stroke (width 0) (type default) (color 0 0 0 0)) 208 | (uuid b98e4771-0249-40ec-be31-3f46d90a7355) 209 | ) 210 | (wire (pts (xy 134.62 115.57) (xy 129.54 115.57)) 211 | (stroke (width 0) (type default) (color 0 0 0 0)) 212 | (uuid ca0f962b-a275-4b2c-8789-d456b1445a5e) 213 | ) 214 | (wire (pts (xy 111.76 110.49) (xy 118.11 110.49)) 215 | (stroke (width 0) (type default) (color 0 0 0 0)) 216 | (uuid d13f366d-30d7-4faa-8669-a78f92ae8d8c) 217 | ) 218 | (wire (pts (xy 128.27 110.49) (xy 128.27 107.95)) 219 | (stroke (width 0) (type default) (color 0 0 0 0)) 220 | (uuid d6bd5e45-74f5-4e92-9a71-c6fdbb6af072) 221 | ) 222 | (wire (pts (xy 116.84 121.92) (xy 118.11 121.92)) 223 | (stroke (width 0) (type default) (color 0 0 0 0)) 224 | (uuid dc723963-fabc-4d6c-af72-b3fc639cb409) 225 | ) 226 | (wire (pts (xy 134.62 107.95) (xy 129.54 107.95)) 227 | (stroke (width 0) (type default) (color 0 0 0 0)) 228 | (uuid ec795703-9aef-4970-bf1e-0d771a25188f) 229 | ) 230 | (wire (pts (xy 134.62 110.49) (xy 128.27 110.49)) 231 | (stroke (width 0) (type default) (color 0 0 0 0)) 232 | (uuid f03343b0-be58-4f60-ab49-867aa59575e2) 233 | ) 234 | 235 | (symbol (lib_id "Switch:SW_Push") (at 123.19 115.57 0) (unit 1) 236 | (in_bom yes) (on_board yes) 237 | (uuid 2f9c4dba-7a05-4b41-b54f-6083f79e001e) 238 | (property "Reference" "SW3" (id 0) (at 123.19 113.03 0)) 239 | (property "Value" "SW_Push" (id 1) (at 123.19 116.84 0)) 240 | (property "Footprint" "Matt:SMD_Button_6mm_3_5mm" (id 2) (at 123.19 110.49 0) 241 | (effects (font (size 1.27 1.27)) hide) 242 | ) 243 | (property "Datasheet" "~" (id 3) (at 123.19 110.49 0) 244 | (effects (font (size 1.27 1.27)) hide) 245 | ) 246 | (pin "1" (uuid 06a6d994-62ec-4eb9-8c04-8143d394e382)) 247 | (pin "2" (uuid e4bf58e2-c467-432d-a061-426cf5f156ac)) 248 | ) 249 | 250 | (symbol (lib_id "Switch:SW_Push") (at 123.19 107.95 0) (unit 1) 251 | (in_bom yes) (on_board yes) 252 | (uuid 30e3de30-6f75-46f0-be4f-7ecf2cdf7b40) 253 | (property "Reference" "SW2" (id 0) (at 123.19 105.41 0)) 254 | (property "Value" "SW_Push" (id 1) (at 123.19 109.22 0)) 255 | (property "Footprint" "Matt:SMD_Button_6mm_3_5mm" (id 2) (at 123.19 102.87 0) 256 | (effects (font (size 1.27 1.27)) hide) 257 | ) 258 | (property "Datasheet" "~" (id 3) (at 123.19 102.87 0) 259 | (effects (font (size 1.27 1.27)) hide) 260 | ) 261 | (pin "1" (uuid 99895a9b-ec3c-4e33-88bf-59268dca7e77)) 262 | (pin "2" (uuid 847d3e70-3369-4ed4-9651-9871d72f2d2c)) 263 | ) 264 | 265 | (symbol (lib_id "Switch:SW_Push") (at 123.19 101.6 0) (unit 1) 266 | (in_bom yes) (on_board yes) 267 | (uuid 37fe36f4-c986-4cd4-b196-5c5cc58f6ceb) 268 | (property "Reference" "SW1" (id 0) (at 123.19 99.06 0)) 269 | (property "Value" "SW_Push" (id 1) (at 123.19 102.87 0)) 270 | (property "Footprint" "Matt:SMD_Button_6mm_3_5mm" (id 2) (at 123.19 96.52 0) 271 | (effects (font (size 1.27 1.27)) hide) 272 | ) 273 | (property "Datasheet" "~" (id 3) (at 123.19 96.52 0) 274 | (effects (font (size 1.27 1.27)) hide) 275 | ) 276 | (pin "1" (uuid 49f4309e-5c5b-4518-8746-983a6191671a)) 277 | (pin "2" (uuid 4e07fbcc-f322-46a4-b780-659259e69c5b)) 278 | ) 279 | 280 | (symbol (lib_id "Connector:Conn_01x04_Male") (at 139.7 113.03 180) (unit 1) 281 | (in_bom yes) (on_board yes) (fields_autoplaced) 282 | (uuid 8818971a-834b-4ae8-9f69-2d947edae811) 283 | (property "Reference" "J2" (id 0) (at 140.97 110.4899 0) 284 | (effects (font (size 1.27 1.27)) (justify right)) 285 | ) 286 | (property "Value" "Conn_01x04_Male" (id 1) (at 140.97 113.0299 0) 287 | (effects (font (size 1.27 1.27)) (justify right)) 288 | ) 289 | (property "Footprint" "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical" (id 2) (at 139.7 113.03 0) 290 | (effects (font (size 1.27 1.27)) hide) 291 | ) 292 | (property "Datasheet" "~" (id 3) (at 139.7 113.03 0) 293 | (effects (font (size 1.27 1.27)) hide) 294 | ) 295 | (pin "1" (uuid 29fac569-14c4-4eb2-ab19-6ecd52df66c5)) 296 | (pin "2" (uuid 0824d822-6b9f-4bfe-afa2-75ada956df1e)) 297 | (pin "3" (uuid 3661ed60-8bca-40a4-9bcc-63b5a6c5c012)) 298 | (pin "4" (uuid df925065-8058-41bb-b943-04d8c5ceab65)) 299 | ) 300 | 301 | (symbol (lib_id "Connector:Conn_01x04_Male") (at 106.68 110.49 0) (unit 1) 302 | (in_bom yes) (on_board yes) (fields_autoplaced) 303 | (uuid 892259d1-2e0b-4e45-803c-a4ead1cf370f) 304 | (property "Reference" "J1" (id 0) (at 107.315 102.87 0)) 305 | (property "Value" "Conn_01x04_Male" (id 1) (at 107.315 105.41 0)) 306 | (property "Footprint" "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical" (id 2) (at 106.68 110.49 0) 307 | (effects (font (size 1.27 1.27)) hide) 308 | ) 309 | (property "Datasheet" "~" (id 3) (at 106.68 110.49 0) 310 | (effects (font (size 1.27 1.27)) hide) 311 | ) 312 | (pin "1" (uuid 88e7c368-0e4c-433e-819d-9899ad34f115)) 313 | (pin "2" (uuid f1bbcfea-f3c8-4717-b1f0-f73822b84755)) 314 | (pin "3" (uuid 52b9620f-0edb-4ba2-91ba-38212922247b)) 315 | (pin "4" (uuid 00c8320a-139a-4672-8582-55aee175e6ec)) 316 | ) 317 | 318 | (symbol (lib_id "Switch:SW_Push") (at 123.19 121.92 0) (unit 1) 319 | (in_bom yes) (on_board yes) 320 | (uuid b1de6aec-c95c-496c-ad69-fb5b6db6dd5d) 321 | (property "Reference" "SW4" (id 0) (at 123.19 119.38 0)) 322 | (property "Value" "SW_Push" (id 1) (at 123.19 123.19 0)) 323 | (property "Footprint" "Matt:SMD_Button_6mm_3_5mm" (id 2) (at 123.19 116.84 0) 324 | (effects (font (size 1.27 1.27)) hide) 325 | ) 326 | (property "Datasheet" "~" (id 3) (at 123.19 116.84 0) 327 | (effects (font (size 1.27 1.27)) hide) 328 | ) 329 | (pin "1" (uuid be352a51-c62e-41e4-80e6-e32b8f9cc7a8)) 330 | (pin "2" (uuid 7cc9d864-1d89-4bfb-bb9e-8a2aa34fb896)) 331 | ) 332 | 333 | (sheet_instances 334 | (path "/" (page "1")) 335 | ) 336 | 337 | (symbol_instances 338 | (path "/892259d1-2e0b-4e45-803c-a4ead1cf370f" 339 | (reference "J1") (unit 1) (value "Conn_01x04_Male") (footprint "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical") 340 | ) 341 | (path "/8818971a-834b-4ae8-9f69-2d947edae811" 342 | (reference "J2") (unit 1) (value "Conn_01x04_Male") (footprint "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical") 343 | ) 344 | (path "/37fe36f4-c986-4cd4-b196-5c5cc58f6ceb" 345 | (reference "SW1") (unit 1) (value "SW_Push") (footprint "Matt:SMD_Button_6mm_3_5mm") 346 | ) 347 | (path "/30e3de30-6f75-46f0-be4f-7ecf2cdf7b40" 348 | (reference "SW2") (unit 1) (value "SW_Push") (footprint "Matt:SMD_Button_6mm_3_5mm") 349 | ) 350 | (path "/2f9c4dba-7a05-4b41-b54f-6083f79e001e" 351 | (reference "SW3") (unit 1) (value "SW_Push") (footprint "Matt:SMD_Button_6mm_3_5mm") 352 | ) 353 | (path "/b1de6aec-c95c-496c-ad69-fb5b6db6dd5d" 354 | (reference "SW4") (unit 1) (value "SW_Push") (footprint "Matt:SMD_Button_6mm_3_5mm") 355 | ) 356 | ) 357 | ) 358 | -------------------------------------------------------------------------------- /examples/breadboard_buttons/breadboard_buttons.net: -------------------------------------------------------------------------------- 1 | (export (version "E") 2 | (design 3 | (source "/home/mattwach/git/kicad/breadboard_buttons/breadboard_buttons.kicad_sch") 4 | (date "Sun 14 Aug 2022 12:22:57 PM PDT") 5 | (tool "Eeschema 6.0.5+dfsg-1") 6 | (sheet (number "1") (name "/") (tstamps "/") 7 | (title_block 8 | (title) 9 | (company) 10 | (rev) 11 | (date) 12 | (source "breadboard_buttons.kicad_sch") 13 | (comment (number "1") (value "")) 14 | (comment (number "2") (value "")) 15 | (comment (number "3") (value "")) 16 | (comment (number "4") (value "")) 17 | (comment (number "5") (value "")) 18 | (comment (number "6") (value "")) 19 | (comment (number "7") (value "")) 20 | (comment (number "8") (value "")) 21 | (comment (number "9") (value ""))))) 22 | (components 23 | (comp (ref "J1") 24 | (value "Conn_01x04_Male") 25 | (footprint "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical") 26 | (libsource (lib "Connector") (part "Conn_01x04_Male") (description "Generic connector, single row, 01x04, script generated (kicad-library-utils/schlib/autogen/connector/)")) 27 | (property (name "Sheetname") (value "")) 28 | (property (name "Sheetfile") (value "breadboard_buttons.kicad_sch")) 29 | (sheetpath (names "/") (tstamps "/")) 30 | (tstamps "892259d1-2e0b-4e45-803c-a4ead1cf370f")) 31 | (comp (ref "J2") 32 | (value "Conn_01x04_Male") 33 | (footprint "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical") 34 | (libsource (lib "Connector") (part "Conn_01x04_Male") (description "Generic connector, single row, 01x04, script generated (kicad-library-utils/schlib/autogen/connector/)")) 35 | (property (name "Sheetname") (value "")) 36 | (property (name "Sheetfile") (value "breadboard_buttons.kicad_sch")) 37 | (sheetpath (names "/") (tstamps "/")) 38 | (tstamps "8818971a-834b-4ae8-9f69-2d947edae811")) 39 | (comp (ref "SW1") 40 | (value "SW_Push") 41 | (footprint "Matt:SMD_Button_6mm_3_5mm") 42 | (libsource (lib "Switch") (part "SW_Push") (description "Push button switch, generic, two pins")) 43 | (property (name "Sheetname") (value "")) 44 | (property (name "Sheetfile") (value "breadboard_buttons.kicad_sch")) 45 | (sheetpath (names "/") (tstamps "/")) 46 | (tstamps "37fe36f4-c986-4cd4-b196-5c5cc58f6ceb")) 47 | (comp (ref "SW2") 48 | (value "SW_Push") 49 | (footprint "Matt:SMD_Button_6mm_3_5mm") 50 | (libsource (lib "Switch") (part "SW_Push") (description "Push button switch, generic, two pins")) 51 | (property (name "Sheetname") (value "")) 52 | (property (name "Sheetfile") (value "breadboard_buttons.kicad_sch")) 53 | (sheetpath (names "/") (tstamps "/")) 54 | (tstamps "30e3de30-6f75-46f0-be4f-7ecf2cdf7b40")) 55 | (comp (ref "SW3") 56 | (value "SW_Push") 57 | (footprint "Matt:SMD_Button_6mm_3_5mm") 58 | (libsource (lib "Switch") (part "SW_Push") (description "Push button switch, generic, two pins")) 59 | (property (name "Sheetname") (value "")) 60 | (property (name "Sheetfile") (value "breadboard_buttons.kicad_sch")) 61 | (sheetpath (names "/") (tstamps "/")) 62 | (tstamps "2f9c4dba-7a05-4b41-b54f-6083f79e001e")) 63 | (comp (ref "SW4") 64 | (value "SW_Push") 65 | (footprint "Matt:SMD_Button_6mm_3_5mm") 66 | (libsource (lib "Switch") (part "SW_Push") (description "Push button switch, generic, two pins")) 67 | (property (name "Sheetname") (value "")) 68 | (property (name "Sheetfile") (value "breadboard_buttons.kicad_sch")) 69 | (sheetpath (names "/") (tstamps "/")) 70 | (tstamps "b1de6aec-c95c-496c-ad69-fb5b6db6dd5d"))) 71 | (libparts 72 | (libpart (lib "Connector") (part "Conn_01x04_Male") 73 | (description "Generic connector, single row, 01x04, script generated (kicad-library-utils/schlib/autogen/connector/)") 74 | (docs "~") 75 | (footprints 76 | (fp "Connector*:*_1x??_*")) 77 | (fields 78 | (field (name "Reference") "J") 79 | (field (name "Value") "Conn_01x04_Male") 80 | (field (name "Datasheet") "~")) 81 | (pins 82 | (pin (num "1") (name "Pin_1") (type "passive")) 83 | (pin (num "2") (name "Pin_2") (type "passive")) 84 | (pin (num "3") (name "Pin_3") (type "passive")) 85 | (pin (num "4") (name "Pin_4") (type "passive")))) 86 | (libpart (lib "Switch") (part "SW_Push") 87 | (description "Push button switch, generic, two pins") 88 | (docs "~") 89 | (fields 90 | (field (name "Reference") "SW") 91 | (field (name "Value") "SW_Push") 92 | (field (name "Datasheet") "~")) 93 | (pins 94 | (pin (num "1") (name "1") (type "passive")) 95 | (pin (num "2") (name "2") (type "passive"))))) 96 | (libraries 97 | (library (logical "Connector") 98 | (uri "/usr/share/kicad/symbols//Connector.kicad_sym")) 99 | (library (logical "Switch") 100 | (uri "/usr/share/kicad/symbols//Switch.kicad_sym"))) 101 | (nets 102 | (net (code "1") (name "Net-(J1-Pad1)") 103 | (node (ref "J1") (pin "1") (pinfunction "Pin_1") (pintype "passive")) 104 | (node (ref "SW1") (pin "1") (pinfunction "1") (pintype "passive"))) 105 | (net (code "2") (name "Net-(J1-Pad2)") 106 | (node (ref "J1") (pin "2") (pinfunction "Pin_2") (pintype "passive")) 107 | (node (ref "SW2") (pin "1") (pinfunction "1") (pintype "passive"))) 108 | (net (code "3") (name "Net-(J1-Pad3)") 109 | (node (ref "J1") (pin "3") (pinfunction "Pin_3") (pintype "passive")) 110 | (node (ref "SW3") (pin "1") (pinfunction "1") (pintype "passive"))) 111 | (net (code "4") (name "Net-(J1-Pad4)") 112 | (node (ref "J1") (pin "4") (pinfunction "Pin_4") (pintype "passive")) 113 | (node (ref "SW4") (pin "1") (pinfunction "1") (pintype "passive"))) 114 | (net (code "5") (name "Net-(J2-Pad1)") 115 | (node (ref "J2") (pin "1") (pinfunction "Pin_1") (pintype "passive")) 116 | (node (ref "SW4") (pin "2") (pinfunction "2") (pintype "passive"))) 117 | (net (code "6") (name "Net-(J2-Pad2)") 118 | (node (ref "J2") (pin "2") (pinfunction "Pin_2") (pintype "passive")) 119 | (node (ref "SW3") (pin "2") (pinfunction "2") (pintype "passive"))) 120 | (net (code "7") (name "Net-(J2-Pad3)") 121 | (node (ref "J2") (pin "3") (pinfunction "Pin_3") (pintype "passive")) 122 | (node (ref "SW2") (pin "2") (pinfunction "2") (pintype "passive"))) 123 | (net (code "8") (name "Net-(J2-Pad4)") 124 | (node (ref "J2") (pin "4") (pinfunction "Pin_4") (pintype "passive")) 125 | (node (ref "SW1") (pin "2") (pinfunction "2") (pintype "passive"))))) -------------------------------------------------------------------------------- /examples/breadboard_buttons/export/Project_20220814_140748.FlatPrj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/examples/breadboard_buttons/export/Project_20220814_140748.FlatPrj -------------------------------------------------------------------------------- /examples/breadboard_buttons/export/breadboard_buttons-Edge_Cuts.gbr: -------------------------------------------------------------------------------- 1 | %TF.GenerationSoftware,KiCad,Pcbnew,6.0.5+dfsg-1*% 2 | %TF.CreationDate,2022-08-14T13:17:23-07:00*% 3 | %TF.ProjectId,breadboard_buttons,62726561-6462-46f6-9172-645f62757474,rev?*% 4 | %TF.SameCoordinates,Original*% 5 | %TF.FileFunction,Profile,NP*% 6 | %FSLAX46Y46*% 7 | G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* 8 | G04 Created by KiCad (PCBNEW 6.0.5+dfsg-1) date 2022-08-14 13:17:23* 9 | %MOMM*% 10 | %LPD*% 11 | G01* 12 | G04 APERTURE LIST* 13 | %TA.AperFunction,Profile*% 14 | %ADD10C,0.100000*% 15 | %TD*% 16 | G04 APERTURE END LIST* 17 | D10* 18 | X138430000Y-91440000D02* 19 | X138430000Y-111760000D01* 20 | X158750000Y-91440000D02* 21 | X138430000Y-91440000D01* 22 | X158750000Y-111760000D02* 23 | X158750000Y-91440000D01* 24 | X138430000Y-111760000D02* 25 | X158750000Y-111760000D01* 26 | M02* 27 | -------------------------------------------------------------------------------- /examples/breadboard_buttons/export/breadboard_buttons-Edge_Cuts.gbr_cutout_cnc.nc: -------------------------------------------------------------------------------- 1 | (G-CODE GENERATED BY FLATCAM v8.994 - www.flatcam.org - Version Date: 2020/11/7) 2 | 3 | (Name: breadboard_buttons-Edge_Cuts.gbr_cutout_cnc) 4 | (Type: G-code from Geometry) 5 | (Units: MM) 6 | 7 | (Created on Sunday, 14 August 2022 at 14:13) 8 | 9 | (This preprocessor is the default preprocessor used by FlatCAM.) 10 | (It is made to work with MACH3 compatible motion controllers.) 11 | 12 | (TOOL DIAMETER: 2.0 mm) 13 | (Feedrate_XY: 120.0 mm/min) 14 | (Feedrate_Z: 60.0 mm/min) 15 | (Feedrate rapids 1500.0 mm/min) 16 | 17 | (Z_Cut: -1.7 mm) 18 | (DepthPerCut: 0.6 mm <=>3 passes) 19 | (Z_Move: 2.0 mm) 20 | (Z Start: None mm) 21 | (Z End: 10.0 mm) 22 | (X,Y End: 0.0000, 0.0000 mm) 23 | (Steps per circle: 64) 24 | (Preprocessor Geometry: mattwach) 25 | 26 | (X range: -1.1500 ... 21.4700 mm) 27 | (Y range: -21.4700 ... 1.1500 mm) 28 | 29 | (Spindle Speed: 10000.0 RPM) 30 | G21 31 | G90 32 | G94 33 | 34 | G01 F120.00 35 | (mattwach: Tool change disabled) 36 | M03 S10000.0 37 | G01 F120.00 38 | G00 X-1.1487 Y-20.3740 39 | G01 F60.00 40 | G01 Z-0.6000 41 | G01 F120.00 42 | G01 X-1.1500 Y-20.3200 43 | G01 X-1.1500 Y-0.0000 44 | G01 X-1.1485 Y0.0589 45 | G01 X-1.1379 Y0.1663 46 | G01 X-1.1161 Y0.2770 47 | G01 X-1.0836 Y0.3851 48 | G01 X-1.0406 Y0.4895 49 | G01 X-0.9876 Y0.5891 50 | G01 X-0.9251 Y0.6831 51 | G01 X-0.8537 Y0.7705 52 | G01 X-0.7741 Y0.8504 53 | G01 X-0.6870 Y0.9222 54 | G01 X-0.5933 Y0.9851 55 | G01 X-0.4939 Y1.0385 56 | G01 X-0.3897 Y1.0819 57 | G01 X-0.2818 Y1.1149 58 | G01 X-0.1712 Y1.1372 59 | G01 X-0.0589 Y1.1485 60 | G01 X0.0000 Y1.1500 61 | G01 X20.3200 Y1.1500 62 | G01 X20.3789 Y1.1485 63 | G01 X20.4863 Y1.1379 64 | G01 X20.5970 Y1.1161 65 | G01 X20.7051 Y1.0836 66 | G01 X20.8095 Y1.0406 67 | G01 X20.9091 Y0.9876 68 | G01 X21.0031 Y0.9251 69 | G01 X21.0905 Y0.8537 70 | G01 X21.1704 Y0.7741 71 | G01 X21.2422 Y0.6870 72 | G01 X21.3051 Y0.5933 73 | G01 X21.3585 Y0.4939 74 | G01 X21.4019 Y0.3897 75 | G01 X21.4349 Y0.2818 76 | G01 X21.4572 Y0.1712 77 | G01 X21.4685 Y0.0589 78 | G01 X21.4700 Y-0.0000 79 | G01 X21.4700 Y-20.3200 80 | G01 X21.4685 Y-20.3789 81 | G01 X21.4579 Y-20.4863 82 | G01 X21.4361 Y-20.5970 83 | G01 X21.4036 Y-20.7051 84 | G01 X21.3606 Y-20.8095 85 | G01 X21.3076 Y-20.9091 86 | G01 X21.2451 Y-21.0031 87 | G01 X21.1737 Y-21.0905 88 | G01 X21.0941 Y-21.1704 89 | G01 X21.0070 Y-21.2422 90 | G01 X20.9133 Y-21.3051 91 | G01 X20.8139 Y-21.3585 92 | G01 X20.7097 Y-21.4019 93 | G01 X20.6018 Y-21.4349 94 | G01 X20.4912 Y-21.4572 95 | G01 X20.3789 Y-21.4685 96 | G01 X20.3200 Y-21.4700 97 | G01 X0.0000 Y-21.4700 98 | G01 X-0.0540 Y-21.4687 99 | G01 X-0.1663 Y-21.4579 100 | G01 X-0.2770 Y-21.4361 101 | G01 X-0.3851 Y-21.4036 102 | G01 X-0.4895 Y-21.3606 103 | G01 X-0.5891 Y-21.3076 104 | G01 X-0.6831 Y-21.2451 105 | G01 X-0.7705 Y-21.1737 106 | G01 X-0.8504 Y-21.0941 107 | G01 X-0.9222 Y-21.0070 108 | G01 X-0.9851 Y-20.9133 109 | G01 X-1.0385 Y-20.8139 110 | G01 X-1.0819 Y-20.7097 111 | G01 X-1.1149 Y-20.6018 112 | G01 X-1.1372 Y-20.4912 113 | G01 X-1.1487 Y-20.3740 114 | G00 X-1.1487 Y-20.3740 115 | G01 F60.00 116 | G01 Z-1.2000 117 | G01 F120.00 118 | G01 X-1.1372 Y-20.4912 119 | G01 X-1.1149 Y-20.6018 120 | G01 X-1.0819 Y-20.7097 121 | G01 X-1.0385 Y-20.8139 122 | G01 X-0.9851 Y-20.9133 123 | G01 X-0.9222 Y-21.0070 124 | G01 X-0.8504 Y-21.0941 125 | G01 X-0.7705 Y-21.1737 126 | G01 X-0.6831 Y-21.2451 127 | G01 X-0.5891 Y-21.3076 128 | G01 X-0.4895 Y-21.3606 129 | G01 X-0.3851 Y-21.4036 130 | G01 X-0.2770 Y-21.4361 131 | G01 X-0.1663 Y-21.4579 132 | G01 X-0.0540 Y-21.4687 133 | G01 X0.0000 Y-21.4700 134 | G01 X20.3200 Y-21.4700 135 | G01 X20.3789 Y-21.4685 136 | G01 X20.4912 Y-21.4572 137 | G01 X20.6018 Y-21.4349 138 | G01 X20.7097 Y-21.4019 139 | G01 X20.8139 Y-21.3585 140 | G01 X20.9133 Y-21.3051 141 | G01 X21.0070 Y-21.2422 142 | G01 X21.0941 Y-21.1704 143 | G01 X21.1737 Y-21.0905 144 | G01 X21.2451 Y-21.0031 145 | G01 X21.3076 Y-20.9091 146 | G01 X21.3606 Y-20.8095 147 | G01 X21.4036 Y-20.7051 148 | G01 X21.4361 Y-20.5970 149 | G01 X21.4579 Y-20.4863 150 | G01 X21.4685 Y-20.3789 151 | G01 X21.4700 Y-20.3200 152 | G01 X21.4700 Y-0.0000 153 | G01 X21.4685 Y0.0589 154 | G01 X21.4572 Y0.1712 155 | G01 X21.4349 Y0.2818 156 | G01 X21.4019 Y0.3897 157 | G01 X21.3585 Y0.4939 158 | G01 X21.3051 Y0.5933 159 | G01 X21.2422 Y0.6870 160 | G01 X21.1704 Y0.7741 161 | G01 X21.0905 Y0.8537 162 | G01 X21.0031 Y0.9251 163 | G01 X20.9091 Y0.9876 164 | G01 X20.8095 Y1.0406 165 | G01 X20.7051 Y1.0836 166 | G01 X20.5970 Y1.1161 167 | G01 X20.4863 Y1.1379 168 | G01 X20.3789 Y1.1485 169 | G01 X20.3200 Y1.1500 170 | G01 X0.0000 Y1.1500 171 | G01 X-0.0589 Y1.1485 172 | G01 X-0.1712 Y1.1372 173 | G01 X-0.2818 Y1.1149 174 | G01 X-0.3897 Y1.0819 175 | G01 X-0.4939 Y1.0385 176 | G01 X-0.5933 Y0.9851 177 | G01 X-0.6870 Y0.9222 178 | G01 X-0.7741 Y0.8504 179 | G01 X-0.8537 Y0.7705 180 | G01 X-0.9251 Y0.6831 181 | G01 X-0.9876 Y0.5891 182 | G01 X-1.0406 Y0.4895 183 | G01 X-1.0836 Y0.3851 184 | G01 X-1.1161 Y0.2770 185 | G01 X-1.1379 Y0.1663 186 | G01 X-1.1485 Y0.0589 187 | G01 X-1.1500 Y-0.0000 188 | G01 X-1.1500 Y-20.3200 189 | G01 X-1.1487 Y-20.3740 190 | G00 X-1.1487 Y-20.3740 191 | G01 F60.00 192 | G01 Z-1.7000 193 | G01 F120.00 194 | G01 X-1.1500 Y-20.3200 195 | G01 X-1.1500 Y-0.0000 196 | G01 X-1.1485 Y0.0589 197 | G01 X-1.1379 Y0.1663 198 | G01 X-1.1161 Y0.2770 199 | G01 X-1.0836 Y0.3851 200 | G01 X-1.0406 Y0.4895 201 | G01 X-0.9876 Y0.5891 202 | G01 X-0.9251 Y0.6831 203 | G01 X-0.8537 Y0.7705 204 | G01 X-0.7741 Y0.8504 205 | G01 X-0.6870 Y0.9222 206 | G01 X-0.5933 Y0.9851 207 | G01 X-0.4939 Y1.0385 208 | G01 X-0.3897 Y1.0819 209 | G01 X-0.2818 Y1.1149 210 | G01 X-0.1712 Y1.1372 211 | G01 X-0.0589 Y1.1485 212 | G01 X0.0000 Y1.1500 213 | G01 X20.3200 Y1.1500 214 | G01 X20.3789 Y1.1485 215 | G01 X20.4863 Y1.1379 216 | G01 X20.5970 Y1.1161 217 | G01 X20.7051 Y1.0836 218 | G01 X20.8095 Y1.0406 219 | G01 X20.9091 Y0.9876 220 | G01 X21.0031 Y0.9251 221 | G01 X21.0905 Y0.8537 222 | G01 X21.1704 Y0.7741 223 | G01 X21.2422 Y0.6870 224 | G01 X21.3051 Y0.5933 225 | G01 X21.3585 Y0.4939 226 | G01 X21.4019 Y0.3897 227 | G01 X21.4349 Y0.2818 228 | G01 X21.4572 Y0.1712 229 | G01 X21.4685 Y0.0589 230 | G01 X21.4700 Y-0.0000 231 | G01 X21.4700 Y-20.3200 232 | G01 X21.4685 Y-20.3789 233 | G01 X21.4579 Y-20.4863 234 | G01 X21.4361 Y-20.5970 235 | G01 X21.4036 Y-20.7051 236 | G01 X21.3606 Y-20.8095 237 | G01 X21.3076 Y-20.9091 238 | G01 X21.2451 Y-21.0031 239 | G01 X21.1737 Y-21.0905 240 | G01 X21.0941 Y-21.1704 241 | G01 X21.0070 Y-21.2422 242 | G01 X20.9133 Y-21.3051 243 | G01 X20.8139 Y-21.3585 244 | G01 X20.7097 Y-21.4019 245 | G01 X20.6018 Y-21.4349 246 | G01 X20.4912 Y-21.4572 247 | G01 X20.3789 Y-21.4685 248 | G01 X20.3200 Y-21.4700 249 | G01 X0.0000 Y-21.4700 250 | G01 X-0.0540 Y-21.4687 251 | G01 X-0.1663 Y-21.4579 252 | G01 X-0.2770 Y-21.4361 253 | G01 X-0.3851 Y-21.4036 254 | G01 X-0.4895 Y-21.3606 255 | G01 X-0.5891 Y-21.3076 256 | G01 X-0.6831 Y-21.2451 257 | G01 X-0.7705 Y-21.1737 258 | G01 X-0.8504 Y-21.0941 259 | G01 X-0.9222 Y-21.0070 260 | G01 X-0.9851 Y-20.9133 261 | G01 X-1.0385 Y-20.8139 262 | G01 X-1.0819 Y-20.7097 263 | G01 X-1.1149 Y-20.6018 264 | G01 X-1.1372 Y-20.4912 265 | G01 X-1.1487 Y-20.3740 266 | G00 Z2.0000 267 | M05 268 | G00 Z2.0000 269 | G00 Z10.00 270 | G00 X0.0 Y0.0 271 | 272 | 273 | -------------------------------------------------------------------------------- /examples/breadboard_buttons/export/breadboard_buttons-F_Cu.gbr: -------------------------------------------------------------------------------- 1 | %TF.GenerationSoftware,KiCad,Pcbnew,6.0.5+dfsg-1*% 2 | %TF.CreationDate,2022-08-14T13:17:23-07:00*% 3 | %TF.ProjectId,breadboard_buttons,62726561-6462-46f6-9172-645f62757474,rev?*% 4 | %TF.SameCoordinates,Original*% 5 | %TF.FileFunction,Copper,L1,Top*% 6 | %TF.FilePolarity,Positive*% 7 | %FSLAX46Y46*% 8 | G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* 9 | G04 Created by KiCad (PCBNEW 6.0.5+dfsg-1) date 2022-08-14 13:17:23* 10 | %MOMM*% 11 | %LPD*% 12 | G01* 13 | G04 APERTURE LIST* 14 | G04 Aperture macros list* 15 | %AMRoundRect* 16 | 0 Rectangle with rounded corners* 17 | 0 $1 Rounding radius* 18 | 0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners* 19 | 0 Add a 4 corners polygon primitive as box body* 20 | 4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0* 21 | 0 Add four circle primitives for the rounded corners* 22 | 1,1,$1+$1,$2,$3* 23 | 1,1,$1+$1,$4,$5* 24 | 1,1,$1+$1,$6,$7* 25 | 1,1,$1+$1,$8,$9* 26 | 0 Add four rect primitives between the rounded corners* 27 | 20,1,$1+$1,$2,$3,$4,$5,0* 28 | 20,1,$1+$1,$4,$5,$6,$7,0* 29 | 20,1,$1+$1,$6,$7,$8,$9,0* 30 | 20,1,$1+$1,$8,$9,$2,$3,0*% 31 | G04 Aperture macros list end* 32 | %TA.AperFunction,ComponentPad*% 33 | %ADD10O,1.700000X1.700000*% 34 | %TD*% 35 | %TA.AperFunction,ComponentPad*% 36 | %ADD11R,1.700000X1.700000*% 37 | %TD*% 38 | %TA.AperFunction,SMDPad,CuDef*% 39 | %ADD12RoundRect,0.500000X-0.500000X-0.500000X0.500000X-0.500000X0.500000X0.500000X-0.500000X0.500000X0*% 40 | %TD*% 41 | %TA.AperFunction,Conductor*% 42 | %ADD13C,1.000000*% 43 | %TD*% 44 | G04 APERTURE END LIST* 45 | D10* 46 | %TO.P,J2,4,Pin_4*% 47 | %TO.N,Net-(J2-Pad4)*% 48 | X152400000Y-93980000D03* 49 | %TO.P,J2,3,Pin_3*% 50 | %TO.N,Net-(J2-Pad3)*% 51 | X149860000Y-93980000D03* 52 | %TO.P,J2,2,Pin_2*% 53 | %TO.N,Net-(J2-Pad2)*% 54 | X147320000Y-93980000D03* 55 | D11* 56 | %TO.P,J2,1,Pin_1*% 57 | %TO.N,Net-(J2-Pad1)*% 58 | X144780000Y-93980000D03* 59 | %TD*% 60 | %TO.P,J1,1,Pin_1*% 61 | %TO.N,Net-(J1-Pad1)*% 62 | X152380000Y-109220000D03* 63 | D10* 64 | %TO.P,J1,2,Pin_2*% 65 | %TO.N,Net-(J1-Pad2)*% 66 | X149840000Y-109220000D03* 67 | %TO.P,J1,3,Pin_3*% 68 | %TO.N,Net-(J1-Pad3)*% 69 | X147300000Y-109220000D03* 70 | %TO.P,J1,4,Pin_4*% 71 | %TO.N,Net-(J1-Pad4)*% 72 | X144760000Y-109220000D03* 73 | %TD*% 74 | D12* 75 | %TO.P,SW4,1,1*% 76 | %TO.N,Net-(J1-Pad4)*% 77 | X140970000Y-105790000D03* 78 | %TO.P,SW4,2,2*% 79 | %TO.N,Net-(J2-Pad1)*% 80 | X140970000Y-97790000D03* 81 | %TD*% 82 | %TO.P,SW3,2,2*% 83 | %TO.N,Net-(J2-Pad2)*% 84 | X146110000Y-97790000D03* 85 | %TO.P,SW3,1,1*% 86 | %TO.N,Net-(J1-Pad3)*% 87 | X146110000Y-105790000D03* 88 | %TD*% 89 | %TO.P,SW2,2,2*% 90 | %TO.N,Net-(J2-Pad3)*% 91 | X151130000Y-97790000D03* 92 | %TO.P,SW2,1,1*% 93 | %TO.N,Net-(J1-Pad2)*% 94 | X151130000Y-105790000D03* 95 | %TD*% 96 | %TO.P,SW1,1,1*% 97 | %TO.N,Net-(J1-Pad1)*% 98 | X156210000Y-105790000D03* 99 | %TO.P,SW1,2,2*% 100 | %TO.N,Net-(J2-Pad4)*% 101 | X156210000Y-97790000D03* 102 | %TD*% 103 | D13* 104 | %TO.N,Net-(J1-Pad3)*% 105 | X147300000Y-109220000D02* 106 | X147300000Y-106980000D01* 107 | X147300000Y-106980000D02* 108 | X146110000Y-105790000D01* 109 | %TO.N,Net-(J1-Pad2)*% 110 | X149840000Y-109220000D02* 111 | X149840000Y-107080000D01* 112 | X149840000Y-107080000D02* 113 | X151130000Y-105790000D01* 114 | %TO.N,Net-(J1-Pad1)*% 115 | X152380000Y-109220000D02* 116 | X152780000Y-109220000D01* 117 | X152780000Y-109220000D02* 118 | X156210000Y-105790000D01* 119 | %TO.N,Net-(J1-Pad4)*% 120 | X144760000Y-109220000D02* 121 | X144400000Y-109220000D01* 122 | X144400000Y-109220000D02* 123 | X140970000Y-105790000D01* 124 | %TO.N,Net-(J2-Pad4)*% 125 | X152400000Y-93980000D02* 126 | X156210000Y-97790000D01* 127 | %TO.N,Net-(J2-Pad3)*% 128 | X149860000Y-93980000D02* 129 | X149860000Y-96520000D01* 130 | X149860000Y-96520000D02* 131 | X151130000Y-97790000D01* 132 | %TO.N,Net-(J2-Pad2)*% 133 | X146110000Y-97790000D02* 134 | X147320000Y-96580000D01* 135 | X147320000Y-96580000D02* 136 | X147320000Y-93980000D01* 137 | %TO.N,Net-(J2-Pad1)*% 138 | X140970000Y-97790000D02* 139 | X144780000Y-93980000D01* 140 | %TD*% 141 | M02* 142 | -------------------------------------------------------------------------------- /examples/breadboard_buttons/export/breadboard_buttons-NPTH.drl: -------------------------------------------------------------------------------- 1 | M48 2 | ; DRILL file {KiCad 6.0.5+dfsg-1} date Sun 14 Aug 2022 01:17:48 PM PDT 3 | ; FORMAT={-:-/ absolute / inch / decimal} 4 | ; #@! TF.CreationDate,2022-08-14T13:17:48-07:00 5 | ; #@! TF.GenerationSoftware,Kicad,Pcbnew,6.0.5+dfsg-1 6 | ; #@! TF.FileFunction,NonPlated,1,2,NPTH 7 | FMAT,2 8 | INCH 9 | % 10 | G90 11 | G05 12 | T0 13 | M30 14 | -------------------------------------------------------------------------------- /examples/breadboard_buttons/export/breadboard_buttons-PTH.drl: -------------------------------------------------------------------------------- 1 | M48 2 | ; DRILL file {KiCad 6.0.5+dfsg-1} date Sun 14 Aug 2022 01:17:48 PM PDT 3 | ; FORMAT={-:-/ absolute / inch / decimal} 4 | ; #@! TF.CreationDate,2022-08-14T13:17:48-07:00 5 | ; #@! TF.GenerationSoftware,Kicad,Pcbnew,6.0.5+dfsg-1 6 | ; #@! TF.FileFunction,Plated,1,2,PTH 7 | FMAT,2 8 | INCH 9 | ; #@! TA.AperFunction,Plated,PTH,ComponentDrill 10 | T1C0.0394 11 | % 12 | G90 13 | G05 14 | T1 15 | X5.6992Y-4.3 16 | X5.7Y-3.7 17 | X5.7992Y-4.3 18 | X5.8Y-3.7 19 | X5.8992Y-4.3 20 | X5.9Y-3.7 21 | X5.9992Y-4.3 22 | X6.0Y-3.7 23 | T0 24 | M30 25 | -------------------------------------------------------------------------------- /examples/breadboard_buttons/export/breadboard_buttons-PTH.drl_cnc.nc: -------------------------------------------------------------------------------- 1 | (G-CODE GENERATED BY FLATCAM v8.994 - www.flatcam.org - Version Date: 2020/11/7) 2 | 3 | (Name: breadboard_buttons-PTH.drl_cnc) 4 | (Type: G-code from Geometry) 5 | (Units: MM) 6 | 7 | (Created on Sunday, 14 August 2022 at 14:19) 8 | 9 | (This preprocessor is the default preprocessor used by FlatCAM.) 10 | (It is made to work with MACH3 compatible motion controllers.) 11 | 12 | 13 | (TOOLS DIAMETER: ) 14 | (Tool: 1 -> Dia: 1.0007599999999999) 15 | 16 | (FEEDRATE Z: ) 17 | (Tool: 1 -> Feedrate: 300) 18 | 19 | (FEEDRATE RAPIDS: ) 20 | (Tool: 1 -> Feedrate Rapids: 1500) 21 | 22 | (Z_CUT: ) 23 | (Tool: 1 -> Z_Cut: -1.7) 24 | 25 | (Tools Offset: ) 26 | (Tool: 1 -> Offset Z: 0.0) 27 | 28 | (Z_MOVE: ) 29 | (Tool: 1 -> Z_Move: 2) 30 | 31 | (Z Toolchange: 10.0 mm) 32 | (X,Y Toolchange: 0.0000, 0.0000 mm) 33 | (Z Start: None mm) 34 | (Z End: 10.0 mm) 35 | (X,Y End: 0.0000, 0.0000 mm) 36 | (Steps per circle: 64) 37 | (Preprocessor Excellon: mattwach) 38 | 39 | (X range: 5.8293 ... 14.4704 mm) 40 | (Y range: -18.2804 ... -2.0396 mm) 41 | 42 | (Spindle Speed: 10000 RPM) 43 | G21 44 | G90 45 | G94 46 | 47 | G01 F300.00 48 | (mattwach: Tool change disabled) 49 | G01 F300.00 50 | M03 S10000 51 | G00 X6.3297 Y-17.7800 52 | G01 Z-1.7000 53 | G01 Z0 54 | G00 Z2.0000 55 | G00 X8.8697 Y-17.7800 56 | G01 Z-1.7000 57 | G01 Z0 58 | G00 Z2.0000 59 | G00 X11.4097 Y-17.7800 60 | G01 Z-1.7000 61 | G01 Z0 62 | G00 Z2.0000 63 | G00 X13.9497 Y-17.7800 64 | G01 Z-1.7000 65 | G01 Z0 66 | G00 Z2.0000 67 | G00 X13.9700 Y-2.5400 68 | G01 Z-1.7000 69 | G01 Z0 70 | G00 Z2.0000 71 | G00 X11.4300 Y-2.5400 72 | G01 Z-1.7000 73 | G01 Z0 74 | G00 Z2.0000 75 | G00 X8.8900 Y-2.5400 76 | G01 Z-1.7000 77 | G01 Z0 78 | G00 Z2.0000 79 | G00 X6.3500 Y-2.5400 80 | G01 Z-1.7000 81 | G01 Z0 82 | G00 Z2.0000 83 | M05 84 | G00 Z10.00 85 | G00 X0.0 Y0.0 86 | 87 | 88 | -------------------------------------------------------------------------------- /examples/breadboard_buttons/export/breadboard_buttons-job.gbrjob: -------------------------------------------------------------------------------- 1 | { 2 | "Header": { 3 | "GenerationSoftware": { 4 | "Vendor": "KiCad", 5 | "Application": "Pcbnew", 6 | "Version": "6.0.5+dfsg-1" 7 | }, 8 | "CreationDate": "2022-08-14T13:17:23-07:00" 9 | }, 10 | "GeneralSpecs": { 11 | "ProjectId": { 12 | "Name": "breadboard_buttons", 13 | "GUID": "62726561-6462-46f6-9172-645f62757474", 14 | "Revision": "rev?" 15 | }, 16 | "Size": { 17 | "X": 20.42, 18 | "Y": 20.42 19 | }, 20 | "LayerNumber": 2, 21 | "BoardThickness": 1.6, 22 | "Finish": "None" 23 | }, 24 | "DesignRules": [ 25 | { 26 | "Layers": "Outer", 27 | "PadToPad": 0.2, 28 | "PadToTrack": 0.2, 29 | "TrackToTrack": 0.2, 30 | "MinLineWidth": 1.0 31 | } 32 | ], 33 | "FilesAttributes": [ 34 | { 35 | "Path": "breadboard_buttons-F_Cu.gbr", 36 | "FileFunction": "Copper,L1,Top", 37 | "FilePolarity": "Positive" 38 | }, 39 | { 40 | "Path": "breadboard_buttons-Edge_Cuts.gbr", 41 | "FileFunction": "Profile", 42 | "FilePolarity": "Positive" 43 | } 44 | ], 45 | "MaterialStackup": [ 46 | { 47 | "Type": "Legend", 48 | "Name": "Top Silk Screen" 49 | }, 50 | { 51 | "Type": "SolderPaste", 52 | "Name": "Top Solder Paste" 53 | }, 54 | { 55 | "Type": "SolderMask", 56 | "Thickness": 0.01, 57 | "Name": "Top Solder Mask" 58 | }, 59 | { 60 | "Type": "Copper", 61 | "Thickness": 0.035, 62 | "Name": "F.Cu" 63 | }, 64 | { 65 | "Type": "Dielectric", 66 | "Thickness": 1.51, 67 | "Material": "FR4", 68 | "Name": "F.Cu/B.Cu", 69 | "Notes": "Type: dielectric layer 1 (from F.Cu to B.Cu)" 70 | }, 71 | { 72 | "Type": "Copper", 73 | "Thickness": 0.035, 74 | "Name": "B.Cu" 75 | }, 76 | { 77 | "Type": "SolderMask", 78 | "Thickness": 0.01, 79 | "Name": "Bottom Solder Mask" 80 | }, 81 | { 82 | "Type": "SolderPaste", 83 | "Name": "Bottom Solder Paste" 84 | }, 85 | { 86 | "Type": "Legend", 87 | "Name": "Bottom Silk Screen" 88 | } 89 | ] 90 | } 91 | -------------------------------------------------------------------------------- /examples/breadboard_buttons/export/test.nc: -------------------------------------------------------------------------------- 1 | (CNC Regtangle Test File) 2 | 3 | G21 (Units in MM) 4 | G90 (Absolute positioning) 5 | G94 (Units per minute feed rate mode) 6 | 7 | G01 F200 (Move to starting position) 8 | G01 Z10 9 | G00 X-1 Y1 10 | M03 S10000.0 (Start motor) 11 | G01 Z1.0 (Move to dwell height) 12 | 13 | G01 F60 (Approach material) 14 | G01 Z0.0 15 | G01 F120.0 (Draw rectangle) 16 | G01 X21 17 | G01 Y-21 18 | G01 X-1 19 | G01 Y1 20 | 21 | G01 F200 22 | G01 Z10 (Exit material) 23 | M05 (Stop motor) 24 | (Test complete, EOF) 25 | 26 | -------------------------------------------------------------------------------- /images/555_smd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/555_smd.jpg -------------------------------------------------------------------------------- /images/board_cutout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/board_cutout.png -------------------------------------------------------------------------------- /images/breadboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/breadboard.jpg -------------------------------------------------------------------------------- /images/button_6x3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/button_6x3.jpg -------------------------------------------------------------------------------- /images/button_cnc_board.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/button_cnc_board.jpg -------------------------------------------------------------------------------- /images/button_cnc_complete.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/button_cnc_complete.jpg -------------------------------------------------------------------------------- /images/button_pcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/button_pcb.png -------------------------------------------------------------------------------- /images/button_schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/button_schematic.png -------------------------------------------------------------------------------- /images/ca_on_tape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/ca_on_tape.png -------------------------------------------------------------------------------- /images/candle_height_map_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/candle_height_map_button.png -------------------------------------------------------------------------------- /images/candle_probe_zero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/candle_probe_zero.png -------------------------------------------------------------------------------- /images/cnc_probes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/cnc_probes.jpg -------------------------------------------------------------------------------- /images/design_constraints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/design_constraints.png -------------------------------------------------------------------------------- /images/drill_bit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/drill_bit.jpg -------------------------------------------------------------------------------- /images/drilling_holes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/drilling_holes.png -------------------------------------------------------------------------------- /images/end_mill_bit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/end_mill_bit.jpg -------------------------------------------------------------------------------- /images/end_mill_drill_bit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/end_mill_drill_bit.jpg -------------------------------------------------------------------------------- /images/epaper_clock.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/epaper_clock.jpg -------------------------------------------------------------------------------- /images/epaper_clock2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/epaper_clock2.jpg -------------------------------------------------------------------------------- /images/epaper_clock3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/epaper_clock3.jpg -------------------------------------------------------------------------------- /images/epaper_clock4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/epaper_clock4.jpg -------------------------------------------------------------------------------- /images/epaper_clock5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/epaper_clock5.jpg -------------------------------------------------------------------------------- /images/fast_edge_generator.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/fast_edge_generator.jpg -------------------------------------------------------------------------------- /images/fast_edge_generator2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/fast_edge_generator2.jpg -------------------------------------------------------------------------------- /images/finishing_up.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/finishing_up.jpg -------------------------------------------------------------------------------- /images/flatcam_copper_cnc_object.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_copper_cnc_object.png -------------------------------------------------------------------------------- /images/flatcam_copper_gcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_copper_gcode.png -------------------------------------------------------------------------------- /images/flatcam_copper_properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_copper_properties.png -------------------------------------------------------------------------------- /images/flatcam_cutout_tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_cutout_tool.png -------------------------------------------------------------------------------- /images/flatcam_cutout_tool2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_cutout_tool2.png -------------------------------------------------------------------------------- /images/flatcam_delete_geometry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_delete_geometry.png -------------------------------------------------------------------------------- /images/flatcam_drill_cnc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_drill_cnc.png -------------------------------------------------------------------------------- /images/flatcam_drill_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_drill_icon.png -------------------------------------------------------------------------------- /images/flatcam_drill_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_drill_open.png -------------------------------------------------------------------------------- /images/flatcam_drill_properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_drill_properties.png -------------------------------------------------------------------------------- /images/flatcam_drill_properties2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_drill_properties2.png -------------------------------------------------------------------------------- /images/flatcam_drilling_tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_drilling_tool.png -------------------------------------------------------------------------------- /images/flatcam_edge_cnc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_edge_cnc.png -------------------------------------------------------------------------------- /images/flatcam_edge_geometry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_edge_geometry.png -------------------------------------------------------------------------------- /images/flatcam_edge_properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_edge_properties.png -------------------------------------------------------------------------------- /images/flatcam_geometry_properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_geometry_properties.png -------------------------------------------------------------------------------- /images/flatcam_gerber_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_gerber_icon.png -------------------------------------------------------------------------------- /images/flatcam_gerber_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_gerber_open.png -------------------------------------------------------------------------------- /images/flatcam_isolation_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_isolation_2.png -------------------------------------------------------------------------------- /images/flatcam_isolation_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_isolation_3.png -------------------------------------------------------------------------------- /images/flatcam_isolation_tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_isolation_tool.png -------------------------------------------------------------------------------- /images/flatcam_loaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_loaded.png -------------------------------------------------------------------------------- /images/flatcam_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_save.png -------------------------------------------------------------------------------- /images/flatcam_select_copper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_select_copper.png -------------------------------------------------------------------------------- /images/flatcam_set_origin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_set_origin.png -------------------------------------------------------------------------------- /images/flatcam_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/flatcam_start.png -------------------------------------------------------------------------------- /images/footprint_editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/footprint_editor.png -------------------------------------------------------------------------------- /images/generate_drill_files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/generate_drill_files.png -------------------------------------------------------------------------------- /images/gerber_plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/gerber_plot.png -------------------------------------------------------------------------------- /images/height_map_autosize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/height_map_autosize.png -------------------------------------------------------------------------------- /images/loaded_design_and_heightmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/loaded_design_and_heightmap.png -------------------------------------------------------------------------------- /images/meld_heightmap_compare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/meld_heightmap_compare.png -------------------------------------------------------------------------------- /images/painters_tape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/painters_tape.png -------------------------------------------------------------------------------- /images/painters_tape_on_pcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/painters_tape_on_pcb.png -------------------------------------------------------------------------------- /images/pc_monitor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/pc_monitor.jpg -------------------------------------------------------------------------------- /images/pc_monitor2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/pc_monitor2.jpg -------------------------------------------------------------------------------- /images/pin_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/pin_header.png -------------------------------------------------------------------------------- /images/press_together.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/press_together.png -------------------------------------------------------------------------------- /images/probe_finished.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/probe_finished.png -------------------------------------------------------------------------------- /images/proto_board.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/proto_board.jpg -------------------------------------------------------------------------------- /images/proto_board2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/proto_board2.jpg -------------------------------------------------------------------------------- /images/settings_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/settings_dialog.png -------------------------------------------------------------------------------- /images/spray_tape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/spray_tape.png -------------------------------------------------------------------------------- /images/spring_probe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/spring_probe.jpg -------------------------------------------------------------------------------- /images/test_cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/test_cut.png -------------------------------------------------------------------------------- /images/test_heightmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/test_heightmap.png -------------------------------------------------------------------------------- /images/test_loaded_in_candle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/test_loaded_in_candle.png -------------------------------------------------------------------------------- /images/tinning.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/tinning.jpg -------------------------------------------------------------------------------- /images/use_heightmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/use_heightmap.png -------------------------------------------------------------------------------- /images/v_bit_20_degrees.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/v_bit_20_degrees.jpg -------------------------------------------------------------------------------- /images/v_bit_20_degrees_fluted.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/v_bit_20_degrees_fluted.jpg -------------------------------------------------------------------------------- /images/v_bit_20_degrees_fluted_output.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/v_bit_20_degrees_fluted_output.jpg -------------------------------------------------------------------------------- /images/v_bit_20_degrees_output.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/v_bit_20_degrees_output.jpg -------------------------------------------------------------------------------- /images/v_bit_30_degrees.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/v_bit_30_degrees.jpg -------------------------------------------------------------------------------- /images/v_bit_30_degrees_output.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/v_bit_30_degrees_output.jpg -------------------------------------------------------------------------------- /images/v_bit_45_degrees_fluted.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/v_bit_45_degrees_fluted.jpg -------------------------------------------------------------------------------- /images/v_bit_45_degrees_fluted_output.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/v_bit_45_degrees_fluted_output.jpg -------------------------------------------------------------------------------- /images/validation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/validation.png -------------------------------------------------------------------------------- /images/voltage_cutoff.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/voltage_cutoff.jpg -------------------------------------------------------------------------------- /images/voltage_cutoff2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattwach/cnc_pcb_tools/e7a78eb9d0b1b2af105fcd3c5315644ca4b43ea0/images/voltage_cutoff2.jpg -------------------------------------------------------------------------------- /pylintrc: -------------------------------------------------------------------------------- 1 | [MAIN] 2 | 3 | # Analyse import fallback blocks. This can be used to support both Python 2 and 4 | # 3 compatible code, which means that the block might have code that exists 5 | # only in one or another interpreter, leading to false positives when analysed. 6 | analyse-fallback-blocks=no 7 | 8 | # Load and enable all available extensions. Use --list-extensions to see a list 9 | # all available extensions. 10 | #enable-all-extensions= 11 | 12 | # In error mode, messages with a category besides ERROR or FATAL are 13 | # suppressed, and no reports are done by default. Error mode is compatible with 14 | # disabling specific errors. 15 | #errors-only= 16 | 17 | # Always return a 0 (non-error) status code, even if lint errors are found. 18 | # This is primarily useful in continuous integration scripts. 19 | #exit-zero= 20 | 21 | # A comma-separated list of package or module names from where C extensions may 22 | # be loaded. Extensions are loading into the active Python interpreter and may 23 | # run arbitrary code. 24 | extension-pkg-allow-list= 25 | 26 | # A comma-separated list of package or module names from where C extensions may 27 | # be loaded. Extensions are loading into the active Python interpreter and may 28 | # run arbitrary code. (This is an alternative name to extension-pkg-allow-list 29 | # for backward compatibility.) 30 | extension-pkg-whitelist= 31 | 32 | # Return non-zero exit code if any of these messages/categories are detected, 33 | # even if score is above --fail-under value. Syntax same as enable. Messages 34 | # specified are enabled, while categories only check already-enabled messages. 35 | fail-on= 36 | 37 | # Specify a score threshold under which the program will exit with error. 38 | fail-under=10 39 | 40 | # Interpret the stdin as a python script, whose filename needs to be passed as 41 | # the module_or_package argument. 42 | #from-stdin= 43 | 44 | # Files or directories to be skipped. They should be base names, not paths. 45 | ignore=CVS 46 | 47 | # Add files or directories matching the regular expressions patterns to the 48 | # ignore-list. The regex matches against paths and can be in Posix or Windows 49 | # format. Because '\' represents the directory delimiter on Windows systems, it 50 | # can't be used as an escape character. 51 | ignore-paths= 52 | 53 | # Files or directories matching the regular expression patterns are skipped. 54 | # The regex matches against base names, not paths. The default value ignores 55 | # Emacs file locks 56 | ignore-patterns=^\.# 57 | 58 | # List of module names for which member attributes should not be checked 59 | # (useful for modules/projects where namespaces are manipulated during runtime 60 | # and thus existing member attributes cannot be deduced by static analysis). It 61 | # supports qualified module names, as well as Unix pattern matching. 62 | ignored-modules= 63 | 64 | # Python code to execute, usually for sys.path manipulation such as 65 | # pygtk.require(). 66 | #init-hook= 67 | 68 | # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the 69 | # number of processors available to use, and will cap the count on Windows to 70 | # avoid hangs. 71 | jobs=1 72 | 73 | # Control the amount of potential inferred values when inferring a single 74 | # object. This can help the performance when dealing with large functions or 75 | # complex, nested conditions. 76 | limit-inference-results=100 77 | 78 | # List of plugins (as comma separated values of python module names) to load, 79 | # usually to register additional checkers. 80 | load-plugins= 81 | 82 | # Pickle collected data for later comparisons. 83 | persistent=yes 84 | 85 | # Minimum Python version to use for version dependent checks. Will default to 86 | # the version used to run pylint. 87 | py-version=3.10 88 | 89 | # Discover python modules and packages in the file system subtree. 90 | recursive=no 91 | 92 | # When enabled, pylint would attempt to guess common misconfiguration and emit 93 | # user-friendly hints instead of false-positive error messages. 94 | suggestion-mode=yes 95 | 96 | # Allow loading of arbitrary C extensions. Extensions are imported into the 97 | # active Python interpreter and may run arbitrary code. 98 | unsafe-load-any-extension=no 99 | 100 | # In verbose mode, extra non-checker-related info will be displayed. 101 | #verbose= 102 | 103 | 104 | [REPORTS] 105 | 106 | # Python expression which should return a score less than or equal to 10. You 107 | # have access to the variables 'fatal', 'error', 'warning', 'refactor', 108 | # 'convention', and 'info' which contain the number of messages in each 109 | # category, as well as 'statement' which is the total number of statements 110 | # analyzed. This score is used by the global evaluation report (RP0004). 111 | #evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) 112 | 113 | # Template used to display messages. This is a python new-style format string 114 | # used to format the message information. See doc for all details. 115 | msg-template= 116 | 117 | # Set the output format. Available formats are text, parseable, colorized, json 118 | # and msvs (visual studio). You can also give a reporter class, e.g. 119 | # mypackage.mymodule.MyReporterClass. 120 | #output-format= 121 | 122 | # Tells whether to display a full report or only the messages. 123 | reports=no 124 | 125 | # Activate the evaluation score. 126 | score=yes 127 | 128 | 129 | [MESSAGES CONTROL] 130 | 131 | # Only show warnings with the listed confidence levels. Leave empty to show 132 | # all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, 133 | # UNDEFINED. 134 | confidence=HIGH, 135 | INFERENCE, 136 | INFERENCE_FAILURE, 137 | UNDEFINED 138 | 139 | # Disable the message, report, category or checker with the given id(s). You 140 | # can either give multiple identifiers separated by comma (,) or put this 141 | # option multiple times (only on the command line, not in the configuration 142 | # file where it should appear only once). You can also use "--disable=all" to 143 | # disable everything first and then re-enable specific checks. For example, if 144 | # you want to run only the similarities checker, you can use "--disable=all 145 | # --enable=similarities". If you want to run only the classes checker, but have 146 | # no Warning level messages displayed, use "--disable=all --enable=classes 147 | # --disable=W". 148 | disable=raw-checker-failed, 149 | bad-inline-option, 150 | consider-using-f-string, 151 | locally-disabled, 152 | file-ignored, 153 | suppressed-message, 154 | useless-suppression, 155 | deprecated-pragma, 156 | use-symbolic-message-instead, 157 | no-self-use 158 | 159 | # Enable the message, report, category or checker with the given id(s). You can 160 | # either give multiple identifier separated by comma (,) or put this option 161 | # multiple time (only on the command line, not in the configuration file where 162 | # it should appear only once). See also the "--disable" option for examples. 163 | enable=c-extension-no-member 164 | 165 | 166 | [CLASSES] 167 | 168 | # Warn about protected attribute access inside special methods 169 | check-protected-access-in-special-methods=no 170 | 171 | # List of method names used to declare (i.e. assign) instance attributes. 172 | defining-attr-methods=__init__, 173 | __new__, 174 | setUp, 175 | __post_init__ 176 | 177 | # List of member names, which should be excluded from the protected access 178 | # warning. 179 | exclude-protected=_asdict, 180 | _fields, 181 | _replace, 182 | _source, 183 | _make 184 | 185 | # List of valid names for the first argument in a class method. 186 | valid-classmethod-first-arg=cls 187 | 188 | # List of valid names for the first argument in a metaclass class method. 189 | valid-metaclass-classmethod-first-arg=cls 190 | 191 | 192 | [FORMAT] 193 | 194 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 195 | expected-line-ending-format= 196 | 197 | # Regexp for a line that is allowed to be longer than the limit. 198 | ignore-long-lines=^\s*(# )??$ 199 | 200 | # Number of spaces of indent required inside a hanging or continued line. 201 | indent-after-paren=4 202 | 203 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 204 | # tab). 205 | indent-string=' ' 206 | 207 | # Maximum number of characters on a single line. 208 | max-line-length=100 209 | 210 | # Maximum number of lines in a module. 211 | max-module-lines=1000 212 | 213 | # Allow the body of a class to be on the same line as the declaration if body 214 | # contains single statement. 215 | single-line-class-stmt=no 216 | 217 | # Allow the body of an if to be on the same line as the test if there is no 218 | # else. 219 | single-line-if-stmt=no 220 | 221 | 222 | [MISCELLANEOUS] 223 | 224 | # List of note tags to take in consideration, separated by a comma. 225 | notes=FIXME, 226 | XXX, 227 | TODO 228 | 229 | # Regular expression of note tags to take in consideration. 230 | notes-rgx= 231 | 232 | 233 | [METHOD_ARGS] 234 | 235 | # List of qualified names (i.e., library.method) which require a timeout 236 | # parameter e.g. 'requests.api.get,requests.api.post' 237 | timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request 238 | 239 | 240 | [LOGGING] 241 | 242 | # The type of string formatting that logging methods do. `old` means using % 243 | # formatting, `new` is for `{}` formatting. 244 | logging-format-style=old 245 | 246 | # Logging modules to check that the string format arguments are in logging 247 | # function parameter format. 248 | logging-modules=logging 249 | 250 | 251 | [DESIGN] 252 | 253 | # List of regular expressions of class ancestor names to ignore when counting 254 | # public methods (see R0903) 255 | exclude-too-few-public-methods= 256 | 257 | # List of qualified class names to ignore when counting class parents (see 258 | # R0901) 259 | ignored-parents= 260 | 261 | # Maximum number of arguments for function / method. 262 | max-args=5 263 | 264 | # Maximum number of attributes for a class (see R0902). 265 | max-attributes=50 266 | 267 | # Maximum number of boolean expressions in an if statement (see R0916). 268 | max-bool-expr=5 269 | 270 | # Maximum number of branch for function / method body. 271 | max-branches=12 272 | 273 | # Maximum number of locals for function / method body. 274 | max-locals=15 275 | 276 | # Maximum number of parents for a class (see R0901). 277 | max-parents=7 278 | 279 | # Maximum number of public methods for a class (see R0904). 280 | max-public-methods=30 281 | 282 | # Maximum number of return / yield for function / method body. 283 | max-returns=6 284 | 285 | # Maximum number of statements in function / method body. 286 | max-statements=50 287 | 288 | # Minimum number of public methods for a class (see R0903). 289 | min-public-methods=0 290 | 291 | 292 | [TYPECHECK] 293 | 294 | # List of decorators that produce context managers, such as 295 | # contextlib.contextmanager. Add to this list to register other decorators that 296 | # produce valid context managers. 297 | contextmanager-decorators=contextlib.contextmanager 298 | 299 | # List of members which are set dynamically and missed by pylint inference 300 | # system, and so shouldn't trigger E1101 when accessed. Python regular 301 | # expressions are accepted. 302 | generated-members= 303 | 304 | # Tells whether to warn about missing members when the owner of the attribute 305 | # is inferred to be None. 306 | ignore-none=yes 307 | 308 | # This flag controls whether pylint should warn about no-member and similar 309 | # checks whenever an opaque object is returned when inferring. The inference 310 | # can return multiple potential results while evaluating a Python object, but 311 | # some branches might not be evaluated, which results in partial inference. In 312 | # that case, it might be useful to still emit no-member and other checks for 313 | # the rest of the inferred objects. 314 | ignore-on-opaque-inference=yes 315 | 316 | # List of symbolic message names to ignore for Mixin members. 317 | ignored-checks-for-mixins=no-member, 318 | not-async-context-manager, 319 | not-context-manager, 320 | attribute-defined-outside-init 321 | 322 | # List of class names for which member attributes should not be checked (useful 323 | # for classes with dynamically set attributes). This supports the use of 324 | # qualified names. 325 | ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace 326 | 327 | # Show a hint with possible names when a member name was not found. The aspect 328 | # of finding the hint is based on edit distance. 329 | missing-member-hint=yes 330 | 331 | # The minimum edit distance a name should have in order to be considered a 332 | # similar match for a missing member name. 333 | missing-member-hint-distance=1 334 | 335 | # The total number of similar names that should be taken in consideration when 336 | # showing a hint for a missing member. 337 | missing-member-max-choices=1 338 | 339 | # Regex pattern to define which classes are considered mixins. 340 | mixin-class-rgx=.*[Mm]ixin 341 | 342 | # List of decorators that change the signature of a decorated function. 343 | signature-mutators= 344 | 345 | 346 | [SIMILARITIES] 347 | 348 | # Comments are removed from the similarity computation 349 | ignore-comments=yes 350 | 351 | # Docstrings are removed from the similarity computation 352 | ignore-docstrings=yes 353 | 354 | # Imports are removed from the similarity computation 355 | ignore-imports=yes 356 | 357 | # Signatures are removed from the similarity computation 358 | ignore-signatures=yes 359 | 360 | # Minimum lines number of a similarity. 361 | min-similarity-lines=4 362 | 363 | 364 | [SPELLING] 365 | 366 | # Limits count of emitted suggestions for spelling mistakes. 367 | max-spelling-suggestions=4 368 | 369 | # Spelling dictionary name. Available dictionaries: none. To make it work, 370 | # install the 'python-enchant' package. 371 | spelling-dict= 372 | 373 | # List of comma separated words that should be considered directives if they 374 | # appear at the beginning of a comment and should not be checked. 375 | spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: 376 | 377 | # List of comma separated words that should not be checked. 378 | spelling-ignore-words= 379 | 380 | # A path to a file that contains the private dictionary; one word per line. 381 | spelling-private-dict-file= 382 | 383 | # Tells whether to store unknown words to the private dictionary (see the 384 | # --spelling-private-dict-file option) instead of raising a message. 385 | spelling-store-unknown-words=no 386 | 387 | 388 | [IMPORTS] 389 | 390 | # List of modules that can be imported at any level, not just the top level 391 | # one. 392 | allow-any-import-level= 393 | 394 | # Allow wildcard imports from modules that define __all__. 395 | allow-wildcard-with-all=no 396 | 397 | # Deprecated modules which should not be used, separated by a comma. 398 | deprecated-modules= 399 | 400 | # Output a graph (.gv or any supported image format) of external dependencies 401 | # to the given file (report RP0402 must not be disabled). 402 | ext-import-graph= 403 | 404 | # Output a graph (.gv or any supported image format) of all (i.e. internal and 405 | # external) dependencies to the given file (report RP0402 must not be 406 | # disabled). 407 | import-graph= 408 | 409 | # Output a graph (.gv or any supported image format) of internal dependencies 410 | # to the given file (report RP0402 must not be disabled). 411 | int-import-graph= 412 | 413 | # Force import order to recognize a module as part of the standard 414 | # compatibility libraries. 415 | known-standard-library= 416 | 417 | # Force import order to recognize a module as part of a third party library. 418 | known-third-party=enchant 419 | 420 | # Couples of modules and preferred modules, separated by a comma. 421 | preferred-modules= 422 | 423 | 424 | [EXCEPTIONS] 425 | 426 | # Exceptions that will emit a warning when caught. 427 | overgeneral-exceptions=BaseException, 428 | Exception 429 | 430 | 431 | [REFACTORING] 432 | 433 | # Maximum number of nested blocks for function / method body 434 | max-nested-blocks=5 435 | 436 | # Complete name of functions that never returns. When checking for 437 | # inconsistent-return-statements if a never returning function is called then 438 | # it will be considered as an explicit return statement and no message will be 439 | # printed. 440 | never-returning-functions=sys.exit,argparse.parse_error 441 | 442 | 443 | [STRING] 444 | 445 | # This flag controls whether inconsistent-quotes generates a warning when the 446 | # character used as a quote delimiter is used inconsistently within a module. 447 | check-quote-consistency=no 448 | 449 | # This flag controls whether the implicit-str-concat should generate a warning 450 | # on implicit string concatenation in sequences defined over several lines. 451 | check-str-concat-over-line-jumps=no 452 | 453 | 454 | [BASIC] 455 | 456 | # Naming style matching correct argument names. 457 | argument-naming-style=snake_case 458 | 459 | # Regular expression matching correct argument names. Overrides argument- 460 | # naming-style. If left empty, argument names will be checked with the set 461 | # naming style. 462 | #argument-rgx= 463 | 464 | # Naming style matching correct attribute names. 465 | attr-naming-style=snake_case 466 | 467 | # Regular expression matching correct attribute names. Overrides attr-naming- 468 | # style. If left empty, attribute names will be checked with the set naming 469 | # style. 470 | #attr-rgx= 471 | 472 | # Bad variable names which should always be refused, separated by a comma. 473 | bad-names=foo, 474 | bar, 475 | baz, 476 | toto, 477 | tutu, 478 | tata 479 | 480 | # Bad variable names regexes, separated by a comma. If names match any regex, 481 | # they will always be refused 482 | bad-names-rgxs= 483 | 484 | # Naming style matching correct class attribute names. 485 | class-attribute-naming-style=any 486 | 487 | # Regular expression matching correct class attribute names. Overrides class- 488 | # attribute-naming-style. If left empty, class attribute names will be checked 489 | # with the set naming style. 490 | #class-attribute-rgx= 491 | 492 | # Naming style matching correct class constant names. 493 | class-const-naming-style=UPPER_CASE 494 | 495 | # Regular expression matching correct class constant names. Overrides class- 496 | # const-naming-style. If left empty, class constant names will be checked with 497 | # the set naming style. 498 | #class-const-rgx= 499 | 500 | # Naming style matching correct class names. 501 | class-naming-style=PascalCase 502 | 503 | # Regular expression matching correct class names. Overrides class-naming- 504 | # style. If left empty, class names will be checked with the set naming style. 505 | #class-rgx= 506 | 507 | # Naming style matching correct constant names. 508 | const-naming-style=UPPER_CASE 509 | 510 | # Regular expression matching correct constant names. Overrides const-naming- 511 | # style. If left empty, constant names will be checked with the set naming 512 | # style. 513 | #const-rgx= 514 | 515 | # Minimum line length for functions/classes that require docstrings, shorter 516 | # ones are exempt. 517 | docstring-min-length=4 518 | 519 | # Naming style matching correct function names. 520 | function-naming-style=snake_case 521 | 522 | # Regular expression matching correct function names. Overrides function- 523 | # naming-style. If left empty, function names will be checked with the set 524 | # naming style. 525 | #function-rgx= 526 | 527 | # Good variable names which should always be accepted, separated by a comma. 528 | good-names=c, 529 | e, 530 | i, 531 | j, 532 | k, 533 | t, 534 | ex, 535 | Run, 536 | _ 537 | 538 | # Good variable names regexes, separated by a comma. If names match any regex, 539 | # they will always be accepted 540 | good-names-rgxs= 541 | 542 | # Include a hint for the correct naming format with invalid-name. 543 | include-naming-hint=no 544 | 545 | # Naming style matching correct inline iteration names. 546 | inlinevar-naming-style=any 547 | 548 | # Regular expression matching correct inline iteration names. Overrides 549 | # inlinevar-naming-style. If left empty, inline iteration names will be checked 550 | # with the set naming style. 551 | #inlinevar-rgx= 552 | 553 | # Naming style matching correct method names. 554 | method-naming-style=snake_case 555 | 556 | # Regular expression matching correct method names. Overrides method-naming- 557 | # style. If left empty, method names will be checked with the set naming style. 558 | #method-rgx= 559 | 560 | # Naming style matching correct module names. 561 | module-naming-style=snake_case 562 | 563 | # Regular expression matching correct module names. Overrides module-naming- 564 | # style. If left empty, module names will be checked with the set naming style. 565 | #module-rgx= 566 | 567 | # Colon-delimited sets of names that determine each other's naming style when 568 | # the name regexes allow several styles. 569 | name-group= 570 | 571 | # Regular expression which should only match function or class names that do 572 | # not require a docstring. 573 | no-docstring-rgx=^_ 574 | 575 | # List of decorators that produce properties, such as abc.abstractproperty. Add 576 | # to this list to register other decorators that produce valid properties. 577 | # These decorators are taken in consideration only for invalid-name. 578 | property-classes=abc.abstractproperty 579 | 580 | # Regular expression matching correct type variable names. If left empty, type 581 | # variable names will be checked with the set naming style. 582 | #typevar-rgx= 583 | 584 | # Naming style matching correct variable names. 585 | variable-naming-style=snake_case 586 | 587 | # Regular expression matching correct variable names. Overrides variable- 588 | # naming-style. If left empty, variable names will be checked with the set 589 | # naming style. 590 | #variable-rgx= 591 | 592 | 593 | [VARIABLES] 594 | 595 | # List of additional names supposed to be defined in builtins. Remember that 596 | # you should avoid defining new builtins when possible. 597 | additional-builtins= 598 | 599 | # Tells whether unused global variables should be treated as a violation. 600 | allow-global-unused-variables=yes 601 | 602 | # List of names allowed to shadow builtins 603 | allowed-redefined-builtins= 604 | 605 | # List of strings which can identify a callback function by name. A callback 606 | # name must start or end with one of those strings. 607 | callbacks=cb_, 608 | _cb 609 | 610 | # A regular expression matching the name of dummy variables (i.e. expected to 611 | # not be used). 612 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 613 | 614 | # Argument names that match this expression will be ignored. 615 | ignored-argument-names=_.*|^ignored_|^unused_ 616 | 617 | # Tells whether we should check for unused import in __init__ files. 618 | init-import=no 619 | 620 | # List of qualified module names which can have objects that can redefine 621 | # builtins. 622 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io 623 | -------------------------------------------------------------------------------- /test_template.txt: -------------------------------------------------------------------------------- 1 | (CNC Regtangle Test File) 2 | 3 | G{unit_code} (Units in {units}) 4 | G90 (Absolute positioning) 5 | G94 (Units per minute feed rate mode) 6 | 7 | G01 F{z_dwell_feedrate} (Move to starting position) 8 | G01 Z{z_initial_height} 9 | G00 X{x_start} Y{y_start} 10 | M03 S{spindle_speed} (Start motor) 11 | G01 Z1.0 (Move to dwell height) 12 | 13 | G01 F{z_plunge_feedrate} (Approach material) 14 | G01 Z0.0 15 | G01 F{xy_feedrate} (Draw rectangle) 16 | G01 X{x_end} 17 | G01 Y{y_end} 18 | G01 X{x_start} 19 | G01 Y{y_start} 20 | 21 | G01 F{z_dwell_feedrate} 22 | G01 Z{z_initial_height} (Exit material) 23 | M05 (Stop motor) 24 | (Test complete, EOF) 25 | 26 | -------------------------------------------------------------------------------- /tests/check_pcb_drill/breadboard_buttons-PTH.drl_cnc.nc: -------------------------------------------------------------------------------- 1 | (G-CODE GENERATED BY FLATCAM v8.994 - www.flatcam.org - Version Date: 2020/11/7) 2 | 3 | (Name: breadboard_buttons-PTH.drl_cnc) 4 | (Type: G-code from Geometry) 5 | (Units: MM) 6 | 7 | (Created on Sunday, 14 August 2022 at 14:19) 8 | 9 | (This preprocessor is the default preprocessor used by FlatCAM.) 10 | (It is made to work with MACH3 compatible motion controllers.) 11 | 12 | 13 | (TOOLS DIAMETER: ) 14 | (Tool: 1 -> Dia: 1.0007599999999999) 15 | 16 | (FEEDRATE Z: ) 17 | (Tool: 1 -> Feedrate: 300) 18 | 19 | (FEEDRATE RAPIDS: ) 20 | (Tool: 1 -> Feedrate Rapids: 1500) 21 | 22 | (Z_CUT: ) 23 | (Tool: 1 -> Z_Cut: -1.7) 24 | 25 | (Tools Offset: ) 26 | (Tool: 1 -> Offset Z: 0.0) 27 | 28 | (Z_MOVE: ) 29 | (Tool: 1 -> Z_Move: 2) 30 | 31 | (Z Toolchange: 10.0 mm) 32 | (X,Y Toolchange: 0.0000, 0.0000 mm) 33 | (Z Start: None mm) 34 | (Z End: 10.0 mm) 35 | (X,Y End: 0.0000, 0.0000 mm) 36 | (Steps per circle: 64) 37 | (Preprocessor Excellon: mattwach) 38 | 39 | (X range: 5.8293 ... 14.4704 mm) 40 | (Y range: -18.2804 ... -2.0396 mm) 41 | 42 | (Spindle Speed: 10000 RPM) 43 | G21 44 | G90 45 | G94 46 | 47 | G01 F300.00 48 | (mattwach: Tool change disabled) 49 | G01 F300.00 50 | M03 S10000 51 | G00 X6.3297 Y-17.7800 52 | G01 Z-1.7000 53 | G01 Z0 54 | G00 Z2.0000 55 | G00 X8.8697 Y-17.7800 56 | G01 Z-1.7000 57 | G01 Z0 58 | G00 Z2.0000 59 | G00 X11.4097 Y-17.7800 60 | G01 Z-1.7000 61 | G01 Z0 62 | G00 Z2.0000 63 | G00 X13.9497 Y-17.7800 64 | G01 Z-1.7000 65 | G01 Z0 66 | G00 Z2.0000 67 | G00 X13.9700 Y-2.5400 68 | G01 Z-1.7000 69 | G01 Z0 70 | G00 Z2.0000 71 | G00 X11.4300 Y-2.5400 72 | G01 Z-1.7000 73 | G01 Z0 74 | G00 Z2.0000 75 | G00 X8.8900 Y-2.5400 76 | G01 Z-1.7000 77 | G01 Z0 78 | G00 Z2.0000 79 | G00 X6.3500 Y-2.5400 80 | G01 Z-1.7000 81 | G01 Z0 82 | G00 Z2.0000 83 | M05 84 | G00 Z10.00 85 | G00 X0.0 Y0.0 86 | 87 | 88 | -------------------------------------------------------------------------------- /tests/check_pcb_edge/breadboard_buttons-Edge_Cuts.gbr_cutout_cnc.nc: -------------------------------------------------------------------------------- 1 | (G-CODE GENERATED BY FLATCAM v8.994 - www.flatcam.org - Version Date: 2020/11/7) 2 | 3 | (Name: breadboard_buttons-Edge_Cuts.gbr_cutout_cnc) 4 | (Type: G-code from Geometry) 5 | (Units: MM) 6 | 7 | (Created on Sunday, 14 August 2022 at 14:13) 8 | 9 | (This preprocessor is the default preprocessor used by FlatCAM.) 10 | (It is made to work with MACH3 compatible motion controllers.) 11 | 12 | (TOOL DIAMETER: 2.0 mm) 13 | (Feedrate_XY: 120.0 mm/min) 14 | (Feedrate_Z: 60.0 mm/min) 15 | (Feedrate rapids 1500.0 mm/min) 16 | 17 | (Z_Cut: -1.7 mm) 18 | (DepthPerCut: 0.6 mm <=>3 passes) 19 | (Z_Move: 2.0 mm) 20 | (Z Start: None mm) 21 | (Z End: 10.0 mm) 22 | (X,Y End: 0.0000, 0.0000 mm) 23 | (Steps per circle: 64) 24 | (Preprocessor Geometry: mattwach) 25 | 26 | (X range: -1.1500 ... 21.4700 mm) 27 | (Y range: -21.4700 ... 1.1500 mm) 28 | 29 | (Spindle Speed: 10000.0 RPM) 30 | G21 31 | G90 32 | G94 33 | 34 | G01 F120.00 35 | (mattwach: Tool change disabled) 36 | M03 S10000.0 37 | G01 F120.00 38 | G00 X-1.1487 Y-20.3740 39 | G01 F60.00 40 | G01 Z-0.6000 41 | G01 F120.00 42 | G01 X-1.1500 Y-20.3200 43 | G01 X-1.1500 Y-0.0000 44 | G01 X-1.1485 Y0.0589 45 | G01 X-1.1379 Y0.1663 46 | G01 X-1.1161 Y0.2770 47 | G01 X-1.0836 Y0.3851 48 | G01 X-1.0406 Y0.4895 49 | G01 X-0.9876 Y0.5891 50 | G01 X-0.9251 Y0.6831 51 | G01 X-0.8537 Y0.7705 52 | G01 X-0.7741 Y0.8504 53 | G01 X-0.6870 Y0.9222 54 | G01 X-0.5933 Y0.9851 55 | G01 X-0.4939 Y1.0385 56 | G01 X-0.3897 Y1.0819 57 | G01 X-0.2818 Y1.1149 58 | G01 X-0.1712 Y1.1372 59 | G01 X-0.0589 Y1.1485 60 | G01 X0.0000 Y1.1500 61 | G01 X20.3200 Y1.1500 62 | G01 X20.3789 Y1.1485 63 | G01 X20.4863 Y1.1379 64 | G01 X20.5970 Y1.1161 65 | G01 X20.7051 Y1.0836 66 | G01 X20.8095 Y1.0406 67 | G01 X20.9091 Y0.9876 68 | G01 X21.0031 Y0.9251 69 | G01 X21.0905 Y0.8537 70 | G01 X21.1704 Y0.7741 71 | G01 X21.2422 Y0.6870 72 | G01 X21.3051 Y0.5933 73 | G01 X21.3585 Y0.4939 74 | G01 X21.4019 Y0.3897 75 | G01 X21.4349 Y0.2818 76 | G01 X21.4572 Y0.1712 77 | G01 X21.4685 Y0.0589 78 | G01 X21.4700 Y-0.0000 79 | G01 X21.4700 Y-20.3200 80 | G01 X21.4685 Y-20.3789 81 | G01 X21.4579 Y-20.4863 82 | G01 X21.4361 Y-20.5970 83 | G01 X21.4036 Y-20.7051 84 | G01 X21.3606 Y-20.8095 85 | G01 X21.3076 Y-20.9091 86 | G01 X21.2451 Y-21.0031 87 | G01 X21.1737 Y-21.0905 88 | G01 X21.0941 Y-21.1704 89 | G01 X21.0070 Y-21.2422 90 | G01 X20.9133 Y-21.3051 91 | G01 X20.8139 Y-21.3585 92 | G01 X20.7097 Y-21.4019 93 | G01 X20.6018 Y-21.4349 94 | G01 X20.4912 Y-21.4572 95 | G01 X20.3789 Y-21.4685 96 | G01 X20.3200 Y-21.4700 97 | G01 X0.0000 Y-21.4700 98 | G01 X-0.0540 Y-21.4687 99 | G01 X-0.1663 Y-21.4579 100 | G01 X-0.2770 Y-21.4361 101 | G01 X-0.3851 Y-21.4036 102 | G01 X-0.4895 Y-21.3606 103 | G01 X-0.5891 Y-21.3076 104 | G01 X-0.6831 Y-21.2451 105 | G01 X-0.7705 Y-21.1737 106 | G01 X-0.8504 Y-21.0941 107 | G01 X-0.9222 Y-21.0070 108 | G01 X-0.9851 Y-20.9133 109 | G01 X-1.0385 Y-20.8139 110 | G01 X-1.0819 Y-20.7097 111 | G01 X-1.1149 Y-20.6018 112 | G01 X-1.1372 Y-20.4912 113 | G01 X-1.1487 Y-20.3740 114 | G00 X-1.1487 Y-20.3740 115 | G01 F60.00 116 | G01 Z-1.2000 117 | G01 F120.00 118 | G01 X-1.1372 Y-20.4912 119 | G01 X-1.1149 Y-20.6018 120 | G01 X-1.0819 Y-20.7097 121 | G01 X-1.0385 Y-20.8139 122 | G01 X-0.9851 Y-20.9133 123 | G01 X-0.9222 Y-21.0070 124 | G01 X-0.8504 Y-21.0941 125 | G01 X-0.7705 Y-21.1737 126 | G01 X-0.6831 Y-21.2451 127 | G01 X-0.5891 Y-21.3076 128 | G01 X-0.4895 Y-21.3606 129 | G01 X-0.3851 Y-21.4036 130 | G01 X-0.2770 Y-21.4361 131 | G01 X-0.1663 Y-21.4579 132 | G01 X-0.0540 Y-21.4687 133 | G01 X0.0000 Y-21.4700 134 | G01 X20.3200 Y-21.4700 135 | G01 X20.3789 Y-21.4685 136 | G01 X20.4912 Y-21.4572 137 | G01 X20.6018 Y-21.4349 138 | G01 X20.7097 Y-21.4019 139 | G01 X20.8139 Y-21.3585 140 | G01 X20.9133 Y-21.3051 141 | G01 X21.0070 Y-21.2422 142 | G01 X21.0941 Y-21.1704 143 | G01 X21.1737 Y-21.0905 144 | G01 X21.2451 Y-21.0031 145 | G01 X21.3076 Y-20.9091 146 | G01 X21.3606 Y-20.8095 147 | G01 X21.4036 Y-20.7051 148 | G01 X21.4361 Y-20.5970 149 | G01 X21.4579 Y-20.4863 150 | G01 X21.4685 Y-20.3789 151 | G01 X21.4700 Y-20.3200 152 | G01 X21.4700 Y-0.0000 153 | G01 X21.4685 Y0.0589 154 | G01 X21.4572 Y0.1712 155 | G01 X21.4349 Y0.2818 156 | G01 X21.4019 Y0.3897 157 | G01 X21.3585 Y0.4939 158 | G01 X21.3051 Y0.5933 159 | G01 X21.2422 Y0.6870 160 | G01 X21.1704 Y0.7741 161 | G01 X21.0905 Y0.8537 162 | G01 X21.0031 Y0.9251 163 | G01 X20.9091 Y0.9876 164 | G01 X20.8095 Y1.0406 165 | G01 X20.7051 Y1.0836 166 | G01 X20.5970 Y1.1161 167 | G01 X20.4863 Y1.1379 168 | G01 X20.3789 Y1.1485 169 | G01 X20.3200 Y1.1500 170 | G01 X0.0000 Y1.1500 171 | G01 X-0.0589 Y1.1485 172 | G01 X-0.1712 Y1.1372 173 | G01 X-0.2818 Y1.1149 174 | G01 X-0.3897 Y1.0819 175 | G01 X-0.4939 Y1.0385 176 | G01 X-0.5933 Y0.9851 177 | G01 X-0.6870 Y0.9222 178 | G01 X-0.7741 Y0.8504 179 | G01 X-0.8537 Y0.7705 180 | G01 X-0.9251 Y0.6831 181 | G01 X-0.9876 Y0.5891 182 | G01 X-1.0406 Y0.4895 183 | G01 X-1.0836 Y0.3851 184 | G01 X-1.1161 Y0.2770 185 | G01 X-1.1379 Y0.1663 186 | G01 X-1.1485 Y0.0589 187 | G01 X-1.1500 Y-0.0000 188 | G01 X-1.1500 Y-20.3200 189 | G01 X-1.1487 Y-20.3740 190 | G00 X-1.1487 Y-20.3740 191 | G01 F60.00 192 | G01 Z-1.7000 193 | G01 F120.00 194 | G01 X-1.1500 Y-20.3200 195 | G01 X-1.1500 Y-0.0000 196 | G01 X-1.1485 Y0.0589 197 | G01 X-1.1379 Y0.1663 198 | G01 X-1.1161 Y0.2770 199 | G01 X-1.0836 Y0.3851 200 | G01 X-1.0406 Y0.4895 201 | G01 X-0.9876 Y0.5891 202 | G01 X-0.9251 Y0.6831 203 | G01 X-0.8537 Y0.7705 204 | G01 X-0.7741 Y0.8504 205 | G01 X-0.6870 Y0.9222 206 | G01 X-0.5933 Y0.9851 207 | G01 X-0.4939 Y1.0385 208 | G01 X-0.3897 Y1.0819 209 | G01 X-0.2818 Y1.1149 210 | G01 X-0.1712 Y1.1372 211 | G01 X-0.0589 Y1.1485 212 | G01 X0.0000 Y1.1500 213 | G01 X20.3200 Y1.1500 214 | G01 X20.3789 Y1.1485 215 | G01 X20.4863 Y1.1379 216 | G01 X20.5970 Y1.1161 217 | G01 X20.7051 Y1.0836 218 | G01 X20.8095 Y1.0406 219 | G01 X20.9091 Y0.9876 220 | G01 X21.0031 Y0.9251 221 | G01 X21.0905 Y0.8537 222 | G01 X21.1704 Y0.7741 223 | G01 X21.2422 Y0.6870 224 | G01 X21.3051 Y0.5933 225 | G01 X21.3585 Y0.4939 226 | G01 X21.4019 Y0.3897 227 | G01 X21.4349 Y0.2818 228 | G01 X21.4572 Y0.1712 229 | G01 X21.4685 Y0.0589 230 | G01 X21.4700 Y-0.0000 231 | G01 X21.4700 Y-20.3200 232 | G01 X21.4685 Y-20.3789 233 | G01 X21.4579 Y-20.4863 234 | G01 X21.4361 Y-20.5970 235 | G01 X21.4036 Y-20.7051 236 | G01 X21.3606 Y-20.8095 237 | G01 X21.3076 Y-20.9091 238 | G01 X21.2451 Y-21.0031 239 | G01 X21.1737 Y-21.0905 240 | G01 X21.0941 Y-21.1704 241 | G01 X21.0070 Y-21.2422 242 | G01 X20.9133 Y-21.3051 243 | G01 X20.8139 Y-21.3585 244 | G01 X20.7097 Y-21.4019 245 | G01 X20.6018 Y-21.4349 246 | G01 X20.4912 Y-21.4572 247 | G01 X20.3789 Y-21.4685 248 | G01 X20.3200 Y-21.4700 249 | G01 X0.0000 Y-21.4700 250 | G01 X-0.0540 Y-21.4687 251 | G01 X-0.1663 Y-21.4579 252 | G01 X-0.2770 Y-21.4361 253 | G01 X-0.3851 Y-21.4036 254 | G01 X-0.4895 Y-21.3606 255 | G01 X-0.5891 Y-21.3076 256 | G01 X-0.6831 Y-21.2451 257 | G01 X-0.7705 Y-21.1737 258 | G01 X-0.8504 Y-21.0941 259 | G01 X-0.9222 Y-21.0070 260 | G01 X-0.9851 Y-20.9133 261 | G01 X-1.0385 Y-20.8139 262 | G01 X-1.0819 Y-20.7097 263 | G01 X-1.1149 Y-20.6018 264 | G01 X-1.1372 Y-20.4912 265 | G01 X-1.1487 Y-20.3740 266 | G00 Z2.0000 267 | M05 268 | G00 Z2.0000 269 | G00 Z10.00 270 | G00 X0.0 Y0.0 271 | 272 | 273 | -------------------------------------------------------------------------------- /tests/check_pcb_edge/test.nc: -------------------------------------------------------------------------------- 1 | (CNC Regtangle Test File) 2 | 3 | G21 (Units in MM) 4 | G90 (Absolute positioning) 5 | G94 (Units per minute feed rate mode) 6 | 7 | G01 F200 (Move to starting position) 8 | G01 Z10 9 | G00 X-1 Y1 10 | M03 S10000.0 (Start motor) 11 | G01 Z1.0 (Move to dwell height) 12 | 13 | G01 F60 (Approach material) 14 | G01 Z0.0 15 | G01 F120.0 (Draw rectangle) 16 | G01 X21 17 | G01 Y-21 18 | G01 X-1 19 | G01 Y1 20 | 21 | G01 F200 22 | G01 Z10 (Exit material) 23 | M05 (Stop motor) 24 | (Test complete, EOF) 25 | 26 | --------------------------------------------------------------------------------