├── .gitignore ├── .gitmodules ├── README.md ├── bmp ├── Illinois.bmp ├── M.bmp ├── USB.bmp ├── block_m_text_200px.bmp ├── solid_m_200px.bmp ├── solid_m_350px.bmp ├── solid_m_500px.bmp └── solid_m_75px.bmp ├── cam ├── 10LPlus-SunstoneWDrill.cam ├── 12LPlus-SunstoneWDrill.cam ├── 14LPlus-SunstoneWDrill.cam ├── 16LPlus-SunstoneWDrill.cam ├── 2LBasic-SunstoneWDrill.cam ├── 2LPlus-SunstoneWDrill.cam ├── 4LPlus-SunstoneWDrill.cam ├── 6LPlus-SunstoneWDrill.cam ├── 8LPlus-SunstoneWDrill.cam └── Sunstone Eagle Export Guide.pdf ├── dbl ├── 112TR_122TR_CR2477.dbl ├── LT3652.dbl ├── SquarePoint.dbl ├── TLV62130.dbl ├── max17222.dbl ├── nrf51822.dbl └── signpost_max6752.dbl ├── doc ├── BOM.md ├── README.md ├── fabrication.md ├── gerbers.md └── tips.md ├── dru ├── CircuitHub-4ly-Recommended.edru ├── SparkFun-2-layer-FAB-LIMIT.dru ├── SparkFun-2-layer-STANDARD.dru ├── SparkFun-2-layer-TIGHT.dru ├── quickturn_co_kr_1oz.dru ├── quickturn_co_kr_1oz_4lyr.dru ├── sunstone-2lyr_v1.0.0.3.dru ├── sunstone-4lyr_v1.0.0.3.dru └── sunstone-6lyr_v1.0.0.3.dru ├── lbr ├── ac.lbr ├── batteries.lbr ├── chips.lbr ├── connector.lbr ├── crystals.lbr ├── element14 │ └── E14_Arduino_revC.lbr ├── epic.lbr ├── fets.lbr ├── gas-sensor.lbr ├── headers.lbr ├── holes.lbr ├── lab11-frames.lbr ├── leds.lbr ├── linxgps.lbr ├── logos.lbr ├── microphones.lbr ├── motor.lbr ├── opamps.lbr ├── passives.lbr ├── pots.lbr ├── regulators.lbr ├── rf.lbr ├── sparkfun │ ├── LilyPad-Wearables.lbr │ ├── SparkFun-Aesthetics.lbr │ ├── SparkFun-AnalogIC.lbr │ ├── SparkFun-Boards.lbr │ ├── SparkFun-Capacitors.lbr │ ├── SparkFun-Connectors.lbr │ ├── SparkFun-DigitalIC.lbr │ ├── SparkFun-DiscreteSemi.lbr │ ├── SparkFun-Displays.lbr │ ├── SparkFun-Electromechanical.lbr │ ├── SparkFun-FreqCtrl.lbr │ ├── SparkFun-LED.lbr │ ├── SparkFun-Passives.lbr │ ├── SparkFun-PowerIC.lbr │ ├── SparkFun-RF.lbr │ ├── SparkFun-Resistors.lbr │ └── SparkFun-Sensors.lbr ├── switches.lbr ├── transformers.lbr └── umich.lbr ├── requirements.txt ├── scr ├── Readme.txt ├── eagle.scr ├── eaglerc ├── pdf-brd.scr ├── pdf-sch.scr ├── png.scr ├── script.scr ├── ulp-brd.scr └── ulp-sch.scr ├── scripts ├── board_bundle.py ├── bom_to_digikey.py ├── bom_to_text.py ├── db │ └── .gitignore ├── eagle.py ├── eagle_attr_from_digikey_add_mpn.py ├── eagle_attributes.py ├── eagle_bom_helper.py ├── eagle_centroid.py ├── eagle_pdf.py ├── eagle_png.py ├── eagle_renumber.py ├── eagle_tofab_zip.py ├── number_pdf.sh ├── pdf_titles.py └── sunstone_to_oshpark.py └── ulp ├── MF_Eagle_XYRS_Generate.ulp ├── attrib-add.ulp ├── attributes.ulp ├── bom.ulp ├── centroid-smd.ulp ├── import_dxf_polygons_v4.ulp └── zoom-unrouted.ulp /.gitignore: -------------------------------------------------------------------------------- 1 | *.l#* 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lbr/Sparkfun_Blocks_Template"] 2 | path = lbr/Sparkfun_Blocks_Template 3 | url = https://github.com/sparkfun/Sparkfun_Blocks_Template.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Lab11 Eagle Support Files 2 | ========================= 3 | 4 | Part libraries, packaging scripts, CAM jobs, and other files we use with [EAGLE](https://www.autodesk.com/products/eagle/overview). 5 | 6 | ### Required package installation 7 | 8 | #### Ubuntu (assuming default python 3.8) 9 | 10 | ``` 11 | sudo apt install pdftk unoconv texlive-core 12 | pip install -r requirements.txt 13 | ``` 14 | 15 | #### Process 16 | We highly recommend reading over some of the [Lab11 Eagle Documentation](doc/). 17 | 18 | Generally: 19 | - Generate bom with ulp. Sort by values and output to csv. 20 | - Generate gerber files with eagle 21 | - Open csv, add notes, delete rows for DNP footprints, and save as xlsx 22 | - Run the main eagle.py script from the board file directory 23 | -------------------------------------------------------------------------------- /bmp/Illinois.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lab11/eagle/dfac6ce3e436a5dd38f2c97d39772f3b485b8599/bmp/Illinois.bmp -------------------------------------------------------------------------------- /bmp/M.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lab11/eagle/dfac6ce3e436a5dd38f2c97d39772f3b485b8599/bmp/M.bmp -------------------------------------------------------------------------------- /bmp/USB.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lab11/eagle/dfac6ce3e436a5dd38f2c97d39772f3b485b8599/bmp/USB.bmp -------------------------------------------------------------------------------- /bmp/block_m_text_200px.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lab11/eagle/dfac6ce3e436a5dd38f2c97d39772f3b485b8599/bmp/block_m_text_200px.bmp -------------------------------------------------------------------------------- /bmp/solid_m_200px.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lab11/eagle/dfac6ce3e436a5dd38f2c97d39772f3b485b8599/bmp/solid_m_200px.bmp -------------------------------------------------------------------------------- /bmp/solid_m_350px.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lab11/eagle/dfac6ce3e436a5dd38f2c97d39772f3b485b8599/bmp/solid_m_350px.bmp -------------------------------------------------------------------------------- /bmp/solid_m_500px.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lab11/eagle/dfac6ce3e436a5dd38f2c97d39772f3b485b8599/bmp/solid_m_500px.bmp -------------------------------------------------------------------------------- /bmp/solid_m_75px.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lab11/eagle/dfac6ce3e436a5dd38f2c97d39772f3b485b8599/bmp/solid_m_75px.bmp -------------------------------------------------------------------------------- /cam/2LBasic-SunstoneWDrill.cam: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "email": "photo@sunstone.com", 4 | "name": "Photo Department" 5 | }, 6 | "description": { 7 | "EN": "Sunstone Default Export Settings - Eagle\nTop Silk 21 & 25\nBottom Silk 22 & 26" 8 | }, 9 | "output_type": "zip", 10 | "outputs": [ 11 | { 12 | "format_specifier": { 13 | "decimal": 4, 14 | "integer": 2 15 | }, 16 | "output_type": "drill", 17 | "outputs": [ 18 | { 19 | "advanced_options": { 20 | "mirror": false, 21 | "offset_x": 0, 22 | "offset_y": 0, 23 | "rotate": false, 24 | "upside_down": false 25 | }, 26 | "drills": { 27 | "NPTH": true, 28 | "PTH": true, 29 | "VIA": true 30 | }, 31 | "filename_format": "%N.dri", 32 | "layers": { 33 | "from": 1, 34 | "to": 16 35 | }, 36 | "name": "Drill", 37 | "type": "excellon" 38 | } 39 | ] 40 | }, 41 | { 42 | "filename_prefix": "outputs", 43 | "format_specifier": { 44 | "decimal": 4, 45 | "integer": 2 46 | }, 47 | "generate_job_file": true, 48 | "output_type": "gerber", 49 | "outputs": [ 50 | { 51 | "advanced_options": { 52 | "mirror": false, 53 | "offset_x": 0, 54 | "offset_y": 0, 55 | "rotate": false, 56 | "upside_down": false 57 | }, 58 | "board_outline": false, 59 | "config": { 60 | "file_function": "Copper", 61 | "layer": 1, 62 | "layer_details": "mixed", 63 | "layer_type": "top" 64 | }, 65 | "filename_format": "%N.L1", 66 | "layers": [ 67 | 1, 68 | 17, 69 | 18 70 | ], 71 | "milling": false, 72 | "name": "Top Copper", 73 | "polarity": "positive", 74 | "type": "gerber_layer" 75 | }, 76 | { 77 | "advanced_options": { 78 | "mirror": false, 79 | "offset_x": 0, 80 | "offset_y": 0, 81 | "rotate": false, 82 | "upside_down": false 83 | }, 84 | "board_outline": false, 85 | "config": { 86 | "file_function": "Copper", 87 | "layer": 2, 88 | "layer_details": "mixed", 89 | "layer_type": "Bottom" 90 | }, 91 | "filename_format": "%N.L2", 92 | "layers": [ 93 | 16, 94 | 17, 95 | 18 96 | ], 97 | "milling": false, 98 | "name": "Bottom Copper", 99 | "polarity": "positive", 100 | "type": "gerber_layer" 101 | }, 102 | { 103 | "advanced_options": { 104 | "mirror": false, 105 | "offset_x": 0, 106 | "offset_y": 0, 107 | "rotate": false, 108 | "upside_down": false 109 | }, 110 | "board_outline": true, 111 | "config": { 112 | "file_function": "Profile", 113 | "plating": "non-plated" 114 | }, 115 | "filename_format": "%N.OLN", 116 | "layers": [ 117 | 20, 118 | 46 119 | ], 120 | "milling": true, 121 | "name": "", 122 | "polarity": "positive", 123 | "type": "gerber_layer" 124 | } 125 | ], 126 | "version": "RS274X" 127 | } 128 | ], 129 | "timestamp": "2018-07-10T18:18:02", 130 | "type": "EAGLE CAM job", 131 | "units": "imperial", 132 | "version": "8.6.1" 133 | } 134 | -------------------------------------------------------------------------------- /cam/2LPlus-SunstoneWDrill.cam: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "email": "photo@sunstone.com", 4 | "name": "Photo Department" 5 | }, 6 | "description": { 7 | "EN": "Sunstone Default Export Settings - Eagle\nTop Silk 21 & 25\nBottom Silk 22 & 26" 8 | }, 9 | "output_type": "zip", 10 | "outputs": [ 11 | { 12 | "format_specifier": { 13 | "decimal": 4, 14 | "integer": 2 15 | }, 16 | "output_type": "drill", 17 | "outputs": [ 18 | { 19 | "advanced_options": { 20 | "mirror": false, 21 | "offset_x": 0, 22 | "offset_y": 0, 23 | "rotate": false, 24 | "upside_down": false 25 | }, 26 | "drills": { 27 | "NPTH": true, 28 | "PTH": true, 29 | "VIA": true 30 | }, 31 | "filename_format": "%N.dri", 32 | "layers": { 33 | "from": 1, 34 | "to": 16 35 | }, 36 | "name": "Drill", 37 | "type": "excellon" 38 | } 39 | ] 40 | }, 41 | { 42 | "filename_prefix": "outputs", 43 | "format_specifier": { 44 | "decimal": 4, 45 | "integer": 2 46 | }, 47 | "generate_job_file": true, 48 | "output_type": "gerber", 49 | "outputs": [ 50 | { 51 | "advanced_options": { 52 | "mirror": false, 53 | "offset_x": 0, 54 | "offset_y": 0, 55 | "rotate": false, 56 | "upside_down": false 57 | }, 58 | "board_outline": false, 59 | "config": { 60 | "file_function": "Copper", 61 | "layer": 1, 62 | "layer_details": "mixed", 63 | "layer_type": "top" 64 | }, 65 | "filename_format": "%N.L1", 66 | "layers": [ 67 | 1, 68 | 17, 69 | 18 70 | ], 71 | "milling": false, 72 | "name": "Top Copper", 73 | "polarity": "positive", 74 | "type": "gerber_layer" 75 | }, 76 | { 77 | "advanced_options": { 78 | "mirror": false, 79 | "offset_x": 0, 80 | "offset_y": 0, 81 | "rotate": false, 82 | "upside_down": false 83 | }, 84 | "board_outline": false, 85 | "config": { 86 | "file_function": "Copper", 87 | "layer": 2, 88 | "layer_details": "mixed", 89 | "layer_type": "Bottom" 90 | }, 91 | "filename_format": "%N.L2", 92 | "layers": [ 93 | 16, 94 | 17, 95 | 18 96 | ], 97 | "milling": false, 98 | "name": "Bottom Copper", 99 | "polarity": "positive", 100 | "type": "gerber_layer" 101 | }, 102 | { 103 | "advanced_options": { 104 | "mirror": false, 105 | "offset_x": 0, 106 | "offset_y": 0, 107 | "rotate": false, 108 | "upside_down": false 109 | }, 110 | "board_outline": false, 111 | "config": { 112 | "file_function": "Soldermask", 113 | "layer_type": "top", 114 | "layers": [ 115 | 29 116 | ], 117 | "polarity": "negative" 118 | }, 119 | "filename_format": "%N.SMT", 120 | "layers": [ 121 | 29 122 | ], 123 | "milling": false, 124 | "name": "Top Soldermask", 125 | "polarity": "positive", 126 | "type": "gerber_layer" 127 | }, 128 | { 129 | "advanced_options": { 130 | "mirror": false, 131 | "offset_x": 0, 132 | "offset_y": 0, 133 | "rotate": false, 134 | "upside_down": false 135 | }, 136 | "board_outline": false, 137 | "config": { 138 | "file_function": "Soldermask", 139 | "layer_type": "Bottom", 140 | "layers": [ 141 | ], 142 | "polarity": "negative" 143 | }, 144 | "filename_format": "%N.SMB", 145 | "layers": [ 146 | 30 147 | ], 148 | "milling": false, 149 | "name": "Bottom Soldermask", 150 | "polarity": "positive", 151 | "type": "gerber_layer" 152 | }, 153 | { 154 | "advanced_options": { 155 | "mirror": false, 156 | "offset_x": 0, 157 | "offset_y": 0, 158 | "rotate": false, 159 | "upside_down": false 160 | }, 161 | "board_outline": false, 162 | "config": { 163 | "file_function": "Legend", 164 | "index": 1, 165 | "layer_type": "top" 166 | }, 167 | "filename_format": "%N.TSK", 168 | "layers": [ 169 | 21, 170 | 25 171 | ], 172 | "milling": false, 173 | "name": "Top Silk", 174 | "polarity": "positive", 175 | "type": "gerber_layer" 176 | }, 177 | { 178 | "advanced_options": { 179 | "mirror": false, 180 | "offset_x": 0, 181 | "offset_y": 0, 182 | "rotate": false, 183 | "upside_down": false 184 | }, 185 | "board_outline": false, 186 | "config": { 187 | "file_function": "Legend", 188 | "index": 1, 189 | "layer_type": "Bottom" 190 | }, 191 | "filename_format": "%N.BSK", 192 | "layers": [ 193 | 22, 194 | 26 195 | ], 196 | "milling": false, 197 | "name": "Bot Silk", 198 | "polarity": "positive", 199 | "type": "gerber_layer" 200 | }, 201 | { 202 | "advanced_options": { 203 | "mirror": false, 204 | "offset_x": 0, 205 | "offset_y": 0, 206 | "rotate": false, 207 | "upside_down": false 208 | }, 209 | "board_outline": false, 210 | "config": { 211 | "file_function": "Paste", 212 | "layer_type": "top" 213 | }, 214 | "filename_format": "%N.TSP", 215 | "layers": [ 216 | 31 217 | ], 218 | "milling": false, 219 | "name": "Top Paste", 220 | "polarity": "positive", 221 | "type": "gerber_layer" 222 | }, 223 | { 224 | "advanced_options": { 225 | "mirror": false, 226 | "offset_x": 0, 227 | "offset_y": 0, 228 | "rotate": false, 229 | "upside_down": false 230 | }, 231 | "board_outline": false, 232 | "config": { 233 | "file_function": "Paste", 234 | "layer_type": "Bottom" 235 | }, 236 | "filename_format": "%N.BSP", 237 | "layers": [ 238 | 32 239 | ], 240 | "milling": false, 241 | "name": "Bottom Paste", 242 | "polarity": "positive", 243 | "type": "gerber_layer" 244 | }, 245 | { 246 | "advanced_options": { 247 | "mirror": false, 248 | "offset_x": 0, 249 | "offset_y": 0, 250 | "rotate": false, 251 | "upside_down": false 252 | }, 253 | "board_outline": true, 254 | "config": { 255 | "file_function": "Profile", 256 | "plating": "non-plated" 257 | }, 258 | "filename_format": "%N.OLN", 259 | "layers": [ 260 | 20, 261 | 46 262 | ], 263 | "milling": true, 264 | "name": "", 265 | "polarity": "positive", 266 | "type": "gerber_layer" 267 | } 268 | ], 269 | "version": "RS274X" 270 | }, 271 | { 272 | "filename_prefix": "CAMOutputs/DrawingFiles", 273 | "output_type": "drawing", 274 | "outputs": [ 275 | ] 276 | } 277 | ], 278 | "timestamp": "2018-10-09T20:34:01", 279 | "type": "EAGLE CAM job", 280 | "units": "imperial", 281 | "version": "8.6.1" 282 | } 283 | -------------------------------------------------------------------------------- /cam/Sunstone Eagle Export Guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lab11/eagle/dfac6ce3e436a5dd38f2c97d39772f3b485b8599/cam/Sunstone Eagle Export Guide.pdf -------------------------------------------------------------------------------- /doc/BOM.md: -------------------------------------------------------------------------------- 1 | BOM Generation 2 | ============== 3 | 4 | Creating a Bill of Material is crucial for being able to assemble a board in 5 | the future. 6 | 7 | ## Renumber Passives 8 | 9 | Before generating your BOM, you should renumber your passives so that 10 | like values are grouped together and are sorted by value (e.g. all 11 | 22 pF caps are C1-C4, 10 µF are C5-12, etc). The 12 | [eagle_renumber.py](../scripts/eagle_renumber.py) script will do this 13 | correctly for you. Parts set with a value of DNP will be numbered last in 14 | order. 15 | 16 | _Tip:_ If you don't want the renumber script to touch a certain part, give 17 | it a part number in the thousands (i.e. D1000 and R1000) and the renumber 18 | script will ignore those parts. 19 | 20 | **Attention:** You'll want to do this step *before* sending the board off to the manufacturer, as it will also change the reference designators on the boards directly and with it the silk screen. Therefore, we suggest you to do this before you start finalizing the silk screen, as you might otherwise have to take a look at it and re-align names again as the names might change their lengths. 21 | 22 | ## Add Part Attributes 23 | 24 | Eagle allows you to add attributes to parts. We primarily use this to 25 | add a `DIGIKEY` attribute to facilitate ordering. All parts in the Lab11 26 | part libraries should already have a `DIGIKEY` attribute. 27 | 28 | > If a _specific part_ (e.g. an IC, specific inductor, etc) doesn't have 29 | > an attribute, please edit it in the library, add it there, and update 30 | > the part on your board 31 | 32 | For passives, we don't want to update the library with a specific 10 kΩ 33 | resistor, because it really doesn't matter that much (and they are currently sold out in a matter of weeks most of the time). Some of us like to add attributes to all the parts in the schematic, some of us hold off 34 | until the BOM is made and add the part purchased for that run there. It's 35 | a bit of a fuzzier personal taste here. 36 | 37 | **Attention:** If you copy parts of your schematic from other boards, it can easily happen that you include parts (such as passives) which already contain a `DIGIKEY` attribute. This becomes dangerous when you start *copying* those parts around and change the value of them, as the attribute does not correspond to the correct value anymore and you end up order the wrong components. Always double-check your passives before ordering! 38 | 39 | 40 | ## Export BOM from Eagle 41 | 42 | In Eagle Schematic mode go File → Export → BOM. Change "List Type" to 43 | "Values" and "Output Type" to "CSV". Then save that to a file named 44 | `board-name_bom.csv`. 45 | 46 | ### Cleaning up the BOM 47 | 48 | Open that CSV file in Libre Office or Excel (see below). Now you want to clean it up. 49 | 50 | 1. I always start by sorting by the "Parts" column. 51 | 2. Delete the things that aren't actually parts (e.g. LOGOs). In case you are using an assembler, make sure that all physical parts listed by EAGLE (including testpoints, battery connectors, TagConnects etc.) remain in the BOM but are specified as DNP (_Do Not Place_) to prevent confusion due to "missing" parts. 52 | 3. Merge rows displaying the same components; this should usually be done directly by EAGLE when exporting parts, but does not happen when one variant of the component already contains attributes and others do not (such as when you copied parts and changed their value). 53 | 4. Make sure all the parts are accurately represented in the BOM. 54 | - There should be a `DIGIKEY` column that contains the digikey part numbers 55 | for all of the parts. 56 | - If a different place carries the part then add that as a column as well. 57 | For instance maybe only mouser sells a part. Then add a column called 58 | `MOUSER` and put the part number in that column. 59 | 60 | Save the bom as `board-name_bom.xlsx`. Yes, it's a little weird to use 61 | Excel format, but it is the industry standard. All assemblers will accept 62 | and understand it and that just makes everything easier. 63 | 64 | **Delete the original csv file** as the packaging scripts will convert the 65 | new xlsx file to an updated csv for you. 66 | 67 | #### Using LibreOffice 68 | 69 | Counter-intuitively, LibreOffice is actually the best at converting the "csv" 70 | into an "xlsx" file. Just open the "csv" file directly, use the "Semicolon" 71 | separator, and hit okay. Then run `Sort` on column "Parts" with the options 72 | "Range contains Column labels", "Include formats", and "Enable natural sort". 73 | This will leave you with a properly sorted BOM which you should "Save As..." an 74 | "xlsx" file. 75 | 76 | 77 | #### Handling `;`-separated "csv" in Excel 78 | 79 | Eagle's "csv" export tool gives semicolon-separated values, not comma-separated. 80 | *Do not* open this "csv" in Excel directly, rather: 81 | 82 | 1. Open a new instance of Excel and choose `File > Import` then `CSV file`. 83 | 2. Make sure `Delimited` is selected 84 | 3. Change the `File origin` to `Unicode (UTF-8)` 85 | 4. Click Next 86 | 5. Choose the right delimiters so the data in the preview looks right, 87 | probably just have the `Semicolon` box checked 88 | 6. Click Finish, you want the data in the `Existing Sheet` at the default place 89 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | Lab11 and Eagle 2 | =============== 3 | 4 | We've adopted some conventions and styles over the years. Some of these 5 | require a little more effort up front, but will pay off in the long run 6 | if you end up needing to scale up board production, or simply to make 7 | stuff that's easily usable by others in lab. 8 | 9 | ## Starting a new board 10 | 11 | ### Directory Structure 12 | 13 | A bunch of scripts rely on this and it works well. Follow it. 14 | 15 | /hardware///board-name.sch 16 | ^^^^^^^^^^ ^^^^^^^^^^ 17 | Note that these MUST be the same 18 | 19 | ### Revisions 20 | 21 | We use _letters_ for revisions. So this should start with rev-a and 22 | increment appropriately. Once you ship a board for manufacture, that 23 | revision is set in stone. Do not modify it. Even if you only made two 24 | boards and they didn't work because of a silly bug, that revision will 25 | still stick around for all of time. Sometimes boards end up on revision 26 | `e`, `g`, `i`, whatever. 27 | 28 | When you need to start a new revision, simply make a new folder 29 | (`rev-b`) and `cp rev-a/board_name.sch rev-a/board-name.brd rev-b/`. 30 | You should then commit this new revision, so that the base starting 31 | point for the new rev is exactly the old rev. 32 | 33 | ### Schematic Setup and Attributes 34 | 35 | Eagle has a metadata system called "Attributes", first, set up some 36 | global attributes for your new board. Go to the Edit menu then Global 37 | Attributes. 38 | 39 | 1. The `REV` attribute should be set to A, B, C... depending on the revision 40 | 2. The `TITLE` attribute should be set to the name of the board, in this case `board-name` 41 | 3. The `AUTHOR` attribute should be set to the name of the creator of the board 42 | 43 | 44 | ## Finishing the Schematic and Board 45 | 46 | This overview is not going to go into how to design or layout PCBs. 47 | 48 | We have some tips and conventions that we've established over time that 49 | are worth reading over: 50 | 51 | - [Lab11 Eagle Tips](tips.md) 52 | 53 | There are some additional resources online that may be useful: 54 | 55 | - [Sparkfun: Installing EAGLE](https://learn.sparkfun.com/tutorials/how-to-install-and-setup-eagle) 56 | - [Sparkfun: EAGLE schematics](https://learn.sparkfun.com/tutorials/using-eagle-schematic) ([old version](https://www.sparkfun.com/tutorials/108)) 57 | - [Sparkfun: EAGLE board layout](https://learn.sparkfun.com/tutorials/using-eagle-board-layout) ([old version](https://www.sparkfun.com/tutorials/109)) 58 | - [Sparkfun: How to create SMD based PCBs](https://learn.sparkfun.com/tutorials/designing-pcbs-advanced-smd) 59 | - [Sparkfun: Designing PCBs with SMB footprints](https://learn.sparkfun.com/tutorials/designing-pcbs-smd-footprints) ([old version](https://www.sparkfun.com/tutorials/110)) 60 | - [UMich Embedded systems information](http://www.eecs.umich.edu/hub/lessons.html) 61 | 62 | For PCB design which contain RF traces ( > 100 MHz), there are some general rules of thumb: 63 | 64 | - [General Layout Guidelines for RF and Mixed-signal PCBs](https://www.maximintegrated.com/en/app-notes/index.mvp/id/5100) 65 | - [Layout for Low Power RF Designs](http://www.ti.com/lit/an/swra367a/swra367a.pdf) 66 | 67 | ## Design Review 68 | 69 | Once you think you are done, it's a really good idea to have others 70 | in labe review your work. Recently, we've been creating an emphemeral 71 | slack channel for each board and tossing reviews there. It's been 72 | working pretty well. 73 | 74 | If this is an earlier board for you (or even if you're experienced 75 | and this is your first Lab11 board), there will probably be a healthy 76 | amount of feedback. That's a good thing! Learn from it :) 77 | 78 | 79 | ## Creating Gerbers 80 | 81 | Once you are happy with the board you need to create gerber files you 82 | can send to a board house. 83 | 84 | - The [Gerbers Documentation](gerbers.md) has more on this. 85 | 86 | 87 | ## Creating a Bill of Materials (BOM) 88 | 89 | Creating a BOM is crucial for future assembly, for you, for others in 90 | lab, and for professional assemblers. We've developed a number of 91 | conventions from experience over the years, follow them - if things 92 | change in the world, update them, but talk about it with older lab 93 | folk first. 94 | 95 | - Follow the [BOM Guide](BOM.md) to make your BOM. 96 | 97 | 98 | ## Package Things Up (AKA, run the magic script) 99 | 100 | Once you have done all the setup work, it's time to let scripts take over. 101 | These scripts will do several things: 102 | 103 | - Put all the gerbers in a zip. 104 | - This you can upload to the board house who will make the PCB. 105 | - Put all the gerbers and a couple addition files in a zip for the assembler. 106 | - This you can send to an assembler when getting a quote/boards assembled. 107 | - Create a documenting PDF. 108 | - This will but the schematic, pictures of all of the copper layers, a 109 | good graphic to use when assembling the board, and a copy of the bom 110 | into a PDF. 111 | - Convert the bom to txt and csv formats. 112 | - These are not as useful but are good for using grep. 113 | - Package everything into several a master zips. 114 | - `board-name_REV_DATE.zip` - an archive of everything, good for posting online or sharing with others 115 | - `board-name_REV_to_fab_DATE.zip` - files needed for PCB fabrication, good for uploading to board houses 116 | - `board-name_REV_to_assembler_DATE.zip` - files needed for assembly, good for upload to an assembly shop 117 | - `board-name_REV_to_stencil_DATE.zip` - files needed for stencil production, good for upload to a stencil producer 118 | 119 | To run, simply invoke the meta-script: 120 | 121 | github.com/lab11/eagle/scripts/eagle.py 122 | 123 | in the same folder as the .brd and .sch files. 124 | 125 | Viola! 126 | 127 | ## Fabrication 128 | 129 | Whoo! You've got a few options to pick from here, including different 130 | board houses and possibly assembly. 131 | 132 | - Head over to the [Fabrication Guide](fabrication.md) 133 | 134 | -------------------------------------------------------------------------------- /doc/fabrication.md: -------------------------------------------------------------------------------- 1 | Fabrication 2 | =========== 3 | 4 | There are Lab accounts for all sites mentioned below; you can find them on the [wiki](https://lab11.eecs.umich.edu/wiki/doku.php). 5 | 6 | # PCB manufacturer 7 | 8 | We currently like [Quick Turn PCB](http://quickturnpcb.co.kr/). They will do the panelization for you, so just tell them the number of boards and they will work it out. 9 | 10 | For custom boards, you can use their [contact email](mailto:gthanuni@quickturnpcb.co.kr) to order special stack-ups. 11 | 12 | You generally want the "Special Offer". Be sure to login to the site before starting or you'll have to start over. 13 | 14 | # Stencil manufacturer 15 | 16 | To make your life easier during assembly, order a stencil. It will help you spread the solder paste manually! 17 | 18 | We mostly order from [OSH Stencils](https://www.oshstencils.com/). We usually take the recommended option (*frameless* and *4mm*). Make sure to add an inscription on the stencil, mentioning for which board, revision and side it is used (`boardname-revX-TOP` and `boardname-revX-BOTTOM` respectively). 19 | 20 | # Parts 21 | 22 | Being the largest vendor of electronic components worldwide, [DigiKey](https://www.digikey.com/) can get you most of the required parts. 23 | 24 | You can use their **BOM manager** to directly upload the parts list. After having done so (you can choose any format such as .csv or .txt), you can then assign the correct column names: 25 | 26 | - *Digi-Key Part Number:* should match the `DIGIKEY` attribute 27 | - *Manufacturer Part Number:* should math the `MPN` (not mandatory) 28 | - *Quantity:* should match the line where you state the amount of an individual part; make sure that e.g. all 1uF capacitors are aggregated on a single line (it might happen that you copied parts around, in which case some might show MPNs from other components and will not be aggregated). 29 | - *Customer Reference:* should match the reference designators of your parts (e.g. R1, C45, U2); this will make assembly easier, as the parts will be in bags which are already labelled correctly. *Note:* Sometimes, DigiKey also directly shows *Reference designator* as an option. 30 | 31 | You can then add all parts. *After* having done so, when clicking to the next page, DigiKey will ask you how many assemblies you desire; here, you can simply multiple the numbers for the number of boards you order. Make sure to add *spare parts* in case some get lost or broken during assembly; we usually order about 10-20% extra for passives and less for more expensive components. 32 | On a last page, it will suggest you to increase to a next price threshold size to save money (larger orders above given thresholds will result in lower prices per piece). 33 | 34 | *Attention:* Make sure to verify the `DIGIKEY` attribute of passives before buying. It easily happens that you copied parts on the schematic and then changed the value afterwards. If that given passive already had attributes, those ones will be preserved and the `DIGIKEY` attribute will *not* correspond to the value you actually intend to order. 35 | -------------------------------------------------------------------------------- /doc/gerbers.md: -------------------------------------------------------------------------------- 1 | Gerbers 2 | ======= 3 | 4 | # CAM files 5 | 6 | To manufacture your board, you first need to convert the EAGLE files into a format which board manufacturers understand, a so-called *Computer-Aided Manufacturing* (CAM) file. Due to historic reasons, for PCBs this format is called *Gerber*. 7 | 8 | For more information, visit: 9 | - [Autodesk: How to generate your Gerber and NC drill files](https://www.autodesk.com/products/eagle/blog/gerber-nc-drill-pcb-manufacturing-basics-1/) 10 | - [SparkFun: Generate Gerbers](https://learn.sparkfun.com/tutorials/using-eagle-board-layout/generating-gerbers) 11 | 12 | ## Creating Gerber files 13 | 14 | Open the `CAM Processor` (File → CAM Processor) and load the appropriate [CAM job](../cam) for your board. For standard 4-layer boards, we use the [4LPlus-Sunstone.cam](../cam/4LPlus-Sunstone.cam). 15 | 16 | **Attention:** If running your job takes an unexpectedly long time, check your polygon widths; they should always be equal to the width of the smallest traces on your board (usually 4 or 6 mil). Especially ground pour polygons and logos can cause problems. 17 | 18 | TODO: CAM job selection (other board houses) 19 | 20 | ## NC files 21 | Gerbers only contain information about the different layers. For vias and other holes requiring drilling, you will also need to run the drill job, which for historic reason is referred to as *Excellon*. You can find the job in the same folder ([excellon](../cam/excellon.cam)). This will generate additional NC (Numeric Controlled) Drill files which tell the manufacturer where he should drill the holes for vias, screws etc. 22 | 23 | # Gerber inspection 24 | 25 | To make sure that the Gerber files actually represent the board as you intended, we use a separate Gerber viewer to inspect it before shipping. For this, we recommend the free & open source tools [gerbv](http://gerbv.geda-project.org/) ([direct download](https://sourceforge.net/projects/gerbv/)). There are also online tools available to quickly have a look at Gerbers such as the [Online Gerber Viewer](http://www.gerber-viewer.com/). 26 | 27 | When inspecting the NC drill file (.drd), make sure that you *first* add another layer (such as L1) and *then* add the drilling information; otherwise, the files might not overlay and seem incorrect due to some dimension information missing in the drill file. 28 | 29 | # Panelizing 30 | 31 | In order to produce more price-efficient boards, you can panelize your boards to fit multiple ones onto the same panel. While we generally do not do this ourselves (it is often done by the board manufacturer directly), feel free to try out EAGLE's new *design block* feature and add multiple versions of your board to the design. 32 | -------------------------------------------------------------------------------- /doc/tips.md: -------------------------------------------------------------------------------- 1 | Eagle Tips 2 | ========== 3 | 4 | ## Using Eagle 5 | 6 | ### Ubuntu Installation 7 | 8 | - To use 32bit libraries, install the following: 9 | - For *12.04* or earlier: `sudo apt-get install ia32-libs` 10 | - For *13.04* or later: `sudo apt-get install lib32z1 lib32ncurses5` 11 | 12 | ### Standards for part names (e.g. Resistor → R, Relay → K) 13 | 14 | - [PCB part naming](http://electronics.stackexchange.com/questions/36008/pcb-part-naming-for-leds) 15 | - [Reference designators - Wikipedia](https://en.wikipedia.org/wiki/Reference_designator) 16 | 17 | ### Hide certain airwires: 18 | 19 | - `ratsnest ! gnd` 20 | 21 | ### Flip text 22 | 23 | - check the spin box when editing the text 24 | 25 | ### Filled in circle 26 | 27 | - set the line width of a circle to 0 28 | 29 | ### Ripup only polygons (useful when running rats and having polygons fill) 30 | 31 | - `rip @;` 32 | 33 | ### Run ratsnest without having polygons fill at all 34 | 35 | - `set polygon_ratsnest off` 36 | 37 | 38 | ### Delete excess layers (hack stolen [from here](http://www.sparkfun.com/tutorials/157)): 39 | 40 | 1. Open the DRC 41 | 2. Go to the 'Layers' Tab 42 | 3. Change the 'Setup' field from `"(1*16)"` to `"1*16"` [remove parentheses] 43 | 4. Click Apply 44 | 5. Change the 'Setup' field back to `"(1*16)"` [seriously] 45 | 6. Click Apply 46 | 47 | 48 | ## Making Parts 49 | 50 | ### Symbols 51 | 52 | - **Don't** change any of the grid / options / etc when making the part symbol. 53 | Doing so will make your life miserable. 54 | - Always add `>NAME` and `>VALUE` tags to the board. Make sure that they are on the correct layer, i.e. *Names* (95) and *Values* (96) respectively. 55 | - If multiple pins have the same name (e.g. `GND`), make sure to add a hint to the corresponding pin (e.g. `GND` on pin 1 and 2 will become `GND@1`and `GND@2`. This will be valuable when connecting symbols and packages when creating the device later-on. 56 | - Define pin types for pins which are not I/O (the default): you will want to have *VCC* and *GND* on the `pw` option, *not connected* will be `nc` and one-directional pins should be defined as `in` and `out` respectively. This will help Eagle in detecting wrong connections in the DRC (*design rule check*) and ERC (*electrical rule check*) while designing the board. 57 | - To hide pin numbers in the schematic (as they tend to clutter the overall representation), use the `Change`→`Visible`→`pin` tool on all pins. 58 | 59 | ### Packages 60 | 61 | - Put things on the right layers and use the 'magic strings', that is: 62 | `>NAME` → *tNames* (25), `>VALUE` → *tValues* (27) 63 | - Make sure you include a part outline 64 | - You can do this on either *tDocu* (51, won't appear on final board) or 65 | *tPlace* (21, will appear). Often *tPlace* is the better choice as having 66 | part outlines is nice when assembling boards. 67 | - Make sure you include an orientation marking: 68 | - Put this on *tPlace* (21) 69 | - Put it somewhere that will not be covered by the part once the part is 70 | on the board. It's convenient to be able to glance over a board and 71 | verify things are assembled correctly. 72 | - Many datasheets include a recommended footprint which will give you a good idea for the initial design. 73 | - To prevent too much solder paste from unevenly spreading across the exposed GND pad below the chip, it is useful to create the inner SMD zone with the option `Cream` unticked (using _Info_ after creating it). You can then manually add rectangles on the *tCream* layer (31) and only cover parts of the pad with paste. 74 | 75 | ### Devices 76 | 77 | - Make sure to add a useful description to your device. You can also include links, e.g. to the manufacturer's website, directly using HTML: `Great source!`. 78 | - Add a "Prefix" to your part (See above for standard part names) 79 | - The _Prefix_ button is in the bottom right of the window 80 | - Add an "Attribute" that has the vendor / part number 81 | - The _Attributes_ "button" is in the _Description_ section to the left of the packaging specifics; it 82 | looks more like a URL than a button. 83 | - When you add a "New" attribute, set its `Name` to a vendor (e.g. 84 | DIGIKEY) and its `Value` to the vendor part number. 85 | - Useful Attributes are: 86 | - _DATASHEET_ (link) 87 | - _DESCRIPTION_ (string from Digikey) 88 | - _DIGIKEY_ 89 | - _MOUSER_ 90 | - _MANUFACTURER_ 91 | - _MPN_ (*Manufacturer Part Number*) 92 | - Change the symbol name (usually `G$1$`) to something nicer-looking, e.g. `U1`, using the *name* tool. 93 | 94 | 95 | ## Board design 96 | 97 | From the beginning, you should consider whether you will be hand-assembling parts or use machines. For hand assembly, only use passives with a package of 0402 or larger (no 0201 if possible). 98 | 99 | ### Schematic 100 | 101 | Some tips to be followed: 102 | 103 | - People tend to read circuits left to right and top to bottom; try to keep anything with labels or other text oriented that way. 104 | - Use flag labels only as terminators; standard ones are more readable when labeling a wire, especially printed. 105 | - If you use flag labels, **don't** use them for signals which are also connected by wires. Either you use flags for all instances of the signals, or you draw wires. Connecting parts with wires implies that it is the only connection; a mixture with flags will cause you to forget about some references and will make the schematic less readable. 106 | - Add Descriptions to your schematic sheets (`Right click`→`Description`) 107 | - Add tolerance values directly in the value of passives (e.g. `1k 1%`) so you'll remember them when creating the BOM. 108 | - Make sure to add enough debugging options (test pads, debug headers, FTDI) so that you can test all buses (SPI, I2C). 109 | - Add enough ground pins (logic analyser, oscilloscope, serial for debugging) for debugging. 110 | 111 | 112 | ### PCB layout 113 | 114 | Some tips to be followed: 115 | 116 | - Leave enough space between parts, especially around ICs. We recommend at least 12mils on space-constraint boards between caps, and 20mils between caps and ICs. 117 | - Try to keep all components on the same side of the board; it will make your life easier during assembly as you can only heat up one side with the hot plate. 118 | - Dont put labels on vias; they tend to become unreadable (overlapping traces is fine). 119 | - Keep traces to crystals short and straight (they *are* RF traces due to their MHz frequency!). Decaps for the crystals must not be in-between crystals and pins. 120 | - Label all testpoints; it is annoying having to open EAGLE every time you want to test something, and other people might not have that possibility. 121 | - Exclusively use vector font. 122 | - To create the silk screen: Only enable *tNames* (25), then group all the names, use the `Reposition attributes` and apply it to the group by right-clicking onto `Reposition: Group`. Do the same thing with the `Change` tool and use `Size: 20`, `Ratio: 18` as well as `Font: vector`. 123 | - Make the ground pour polygons have a lesser rank (usually 6) so that your keepouts and similar polygons are not of the same rank. 124 | - Add version number, date (YYYY-MM-DD) and your name; it will help people to identify who and when somebody designed the board and helps you keep track of different version numbers. 125 | - Make sure that physical components (such as screw heads) have enough space (it might make sense to draw dimensions in silk). 126 | 127 | ### Check-list 128 | 129 | - How do you power it? Did you consider current and voltage levels? 130 | - How do you programm it? 131 | - How do you debug / test it and make sure things are working? Did you add test pads, headers and LEDs? 132 | - What are you most worried about? (*Go read the datasheet again*) 133 | 134 | 135 | ## Legacy Tips 136 | 137 | These help out with older versions of Eagle, but aren't as needed any more 138 | 139 | ### Copy parts from one sheet to another: 140 | 141 | (As of Eagle 8.1.1 you can just ctrl-c / ctrl-v! Crazy!) 142 | 143 | 1. copy the part 144 | 2. select the part with the group tool 145 | 3. use the move tool, right click and move group, and then move it to the other sheet 146 | -------------------------------------------------------------------------------- /dru/CircuitHub-4ly-Recommended.edru: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | <b>EAGLE Design Rules for *4* Layer Designs (DS)</b> 21 | <p> 22 | Quickturn PCB 4 Layer .DRU 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /dru/SparkFun-2-layer-FAB-LIMIT.dru: -------------------------------------------------------------------------------- 1 | description[en] = SparkFun 2 Layer Design Rule Checks - STANDARD/TIGHT/FAB-LIMIT\n

\nThese rules have been curated by SparkFuns DFM commitee. After doing much research, communicating with our multiple fab houses, and getting quotes of various designs, we have compiled three DRU files. \n

\nSTANDARD: This is more of a "best case scenario" set of limitations. If your design has the space, and/or you have the time to work within these parameters, please do. Larger trace width and clearance makes for easier visual inspection of the PCB while troubleshooting (useful in production and to the end user). It also allows for better ability to hack a trace (if you are crazy enough to scrape away the mask and solder to a trace). Another thing to keep in mind is that more metal is just more robust. \n

\nTIGHT: This is where cost comes into play. We have found that most fab houses begin to add extra charges when you go smaller than these specs. In some cases, going to less than 15 mil trace can increase the cost by 10%. (This is why we have set the min drill on this DRU to 15 mil) Same story for traces thinner than 7 mil. To avoid those extra charges, then stay within the rules of this DRU.\n

\nFAB-LIMIT: These set of rules are at the very limit of most fab houses capabilities. You will pay more for these specs, and it should be used on designs that have a darned good reason to need 4 mil vias and 4 mil traces.\n

\n**NOTE Clearance, Distance, Sizes, and Restring are all set to different limits in each of these three DRU files. Please compare the files within the CAM job editor window of eagle to see all the numbers.\n

\n***NOTE, Please set your Net Classes to default (0mil for all settings), so that it won't effect the DRC when you run it with these settings. 2 | layerSetup = (1*16) 3 | mtCopper = 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 4 | mtIsolate = 1.5mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 5 | mdWireWire = 4mil 6 | mdWirePad = 4mil 7 | mdWireVia = 4mil 8 | mdPadPad = 4mil 9 | mdPadVia = 4mil 10 | mdViaVia = 4mil 11 | mdSmdPad = 4mil 12 | mdSmdVia = 4mil 13 | mdSmdSmd = 4mil 14 | mdViaViaSameLayer = 8mil 15 | mnLayersViaInSmd = 2 16 | mdCopperDimension = 4mil 17 | mdDrill = 4mil 18 | mdSmdStop = 0mil 19 | msWidth = 4mil 20 | msDrill = 6mil 21 | msMicroVia = 9.99mm 22 | msBlindViaRatio = 0.500000 23 | rvPadTop = 0.250000 24 | rvPadInner = 0.250000 25 | rvPadBottom = 0.250000 26 | rvViaOuter = 0.250000 27 | rvViaInner = 0.250000 28 | rvMicroViaOuter = 0.250000 29 | rvMicroViaInner = 0.250000 30 | rlMinPadTop = 5mil 31 | rlMaxPadTop = 20mil 32 | rlMinPadInner = 6mil 33 | rlMaxPadInner = 20mil 34 | rlMinPadBottom = 5mil 35 | rlMaxPadBottom = 20mil 36 | rlMinViaOuter = 5mil 37 | rlMaxViaOuter = 20mil 38 | rlMinViaInner = 6mil 39 | rlMaxViaInner = 20mil 40 | rlMinMicroViaOuter = 4mil 41 | rlMaxMicroViaOuter = 20mil 42 | rlMinMicroViaInner = 4mil 43 | rlMaxMicroViaInner = 20mil 44 | psTop = -1 45 | psBottom = -1 46 | psFirst = -1 47 | psElongationLong = 100 48 | psElongationOffset = 100 49 | mvStopFrame = 1.000000 50 | mvCreamFrame = 0.000000 51 | mlMinStopFrame = 4mil 52 | mlMaxStopFrame = 4mil 53 | mlMinCreamFrame = 0mil 54 | mlMaxCreamFrame = 0mil 55 | mlViaStopLimit = 25mil 56 | srRoundness = 0.000000 57 | srMinRoundness = 0mil 58 | srMaxRoundness = 0mil 59 | slThermalIsolate = 10mil 60 | slThermalsForVias = 0 61 | dpMaxLengthDifference = 10mm 62 | dpGapFactor = 2.500000 63 | checkGrid = 0 64 | checkAngle = 1 65 | checkFont = 1 66 | checkRestrict = 1 67 | useDiameter = 13 68 | maxErrors = 50 69 | -------------------------------------------------------------------------------- /dru/SparkFun-2-layer-STANDARD.dru: -------------------------------------------------------------------------------- 1 | description[en] = SparkFun 2 Layer Design Rule Checks - STANDARD/TIGHT/FAB-LIMIT\n

\nThese rules have been curated by SparkFuns DFM commitee. After doing much research, communicating with our multiple fab houses, and getting quotes of various designs, we have compiled three DRU files. \n

\nSTANDARD: This is more of a "best case scenario" set of limitations. If your design has the space, and/or you have the time to work within these parameters, please do. Larger trace width and clearance makes for easier visual inspection of the PCB while troubleshooting (useful in production and to the end user). It also allows for better ability to hack a trace (if you are crazy enough to scrape away the mask and solder to a trace). Another thing to keep in mind is that more metal is just more robust. \n

\nTIGHT: This is where cost comes into play. We have found that most fab houses begin to add extra charges when you go smaller than these specs. In some cases, going to less than 15 mil trace can increase the cost by 10%. (This is why we have set the min drill on this DRU to 15 mil) Same story for traces thinner than 7 mil. To avoid those extra charges, then stay within the rules of this DRU.\n

\nFAB-LIMIT: These set of rules are at the very limit of most fab houses capabilities. You will pay more for these specs, and it should be used on designs that have a darned good reason to need 4 mil vias and 4 mil traces.\n

\n**NOTE Clearance, Distance, Sizes, and Restring are all set to different limits in each of these three DRU files. Please compare the files within the CAM job editor window of eagle to see all the numbers.\n

\n***NOTE, Please set your Net Classes to default (0mil for all settings), so that it won't effect the DRC when you run it with these settings. 2 | layerSetup = (1*16) 3 | mtCopper = 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 4 | mtIsolate = 1.5mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 5 | mdWireWire = 10mil 6 | mdWirePad = 8mil 7 | mdWireVia = 10mil 8 | mdPadPad = 8mil 9 | mdPadVia = 10mil 10 | mdViaVia = 10mil 11 | mdSmdPad = 8mil 12 | mdSmdVia = 8mil 13 | mdSmdSmd = 8mil 14 | mdViaViaSameLayer = 8mil 15 | mnLayersViaInSmd = 2 16 | mdCopperDimension = 8mil 17 | mdDrill = 8mil 18 | mdSmdStop = 0mil 19 | msWidth = 10mil 20 | msDrill = 20mil 21 | msMicroVia = 9.99mm 22 | msBlindViaRatio = 0.500000 23 | rvPadTop = 0.250000 24 | rvPadInner = 0.250000 25 | rvPadBottom = 0.250000 26 | rvViaOuter = 0.250000 27 | rvViaInner = 0.250000 28 | rvMicroViaOuter = 0.250000 29 | rvMicroViaInner = 0.250000 30 | rlMinPadTop = 10mil 31 | rlMaxPadTop = 20mil 32 | rlMinPadInner = 10mil 33 | rlMaxPadInner = 20mil 34 | rlMinPadBottom = 10mil 35 | rlMaxPadBottom = 20mil 36 | rlMinViaOuter = 10mil 37 | rlMaxViaOuter = 20mil 38 | rlMinViaInner = 10mil 39 | rlMaxViaInner = 20mil 40 | rlMinMicroViaOuter = 4mil 41 | rlMaxMicroViaOuter = 20mil 42 | rlMinMicroViaInner = 4mil 43 | rlMaxMicroViaInner = 20mil 44 | psTop = -1 45 | psBottom = -1 46 | psFirst = -1 47 | psElongationLong = 100 48 | psElongationOffset = 100 49 | mvStopFrame = 1.000000 50 | mvCreamFrame = 0.000000 51 | mlMinStopFrame = 4mil 52 | mlMaxStopFrame = 4mil 53 | mlMinCreamFrame = 0mil 54 | mlMaxCreamFrame = 0mil 55 | mlViaStopLimit = 25mil 56 | srRoundness = 0.000000 57 | srMinRoundness = 0mil 58 | srMaxRoundness = 0mil 59 | slThermalIsolate = 10mil 60 | slThermalsForVias = 0 61 | dpMaxLengthDifference = 10mm 62 | dpGapFactor = 2.500000 63 | checkGrid = 0 64 | checkAngle = 1 65 | checkFont = 1 66 | checkRestrict = 1 67 | useDiameter = 13 68 | maxErrors = 50 69 | -------------------------------------------------------------------------------- /dru/SparkFun-2-layer-TIGHT.dru: -------------------------------------------------------------------------------- 1 | description[en] = SparkFun 2 Layer Design Rule Checks - STANDARD/TIGHT/FAB-LIMIT\n

\nThese rules have been curated by SparkFuns DFM commitee. After doing much research, communicating with our multiple fab houses, and getting quotes of various designs, we have compiled three DRU files. \n

\nSTANDARD: This is more of a "best case scenario" set of limitations. If your design has the space, and/or you have the time to work within these parameters, please do. Larger trace width and clearance makes for easier visual inspection of the PCB while troubleshooting (useful in production and to the end user). It also allows for better ability to hack a trace (if you are crazy enough to scrape away the mask and solder to a trace). Another thing to keep in mind is that more metal is just more robust. \n

\nTIGHT: This is where cost comes into play. We have found that most fab houses begin to add extra charges when you go smaller than these specs. In some cases, going to less than 15 mil trace can increase the cost by 10%. (This is why we have set the min drill on this DRU to 15 mil) Same story for traces thinner than 7 mil. To avoid those extra charges, then stay within the rules of this DRU.\n

\nFAB-LIMIT: These set of rules are at the very limit of most fab houses capabilities. You will pay more for these specs, and it should be used on designs that have a darned good reason to need 4 mil vias and 4 mil traces.\n

\n**NOTE Clearance, Distance, Sizes, and Restring are all set to different limits in each of these three DRU files. Please compare the files within the CAM job editor window of eagle to see all the numbers.\n

\n***NOTE, Please set your Net Classes to default (0mil for all settings), so that it won't effect the DRC when you run it with these settings. 2 | layerSetup = (1*16) 3 | mtCopper = 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 0.035mm 4 | mtIsolate = 1.5mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 0.15mm 0.2mm 5 | mdWireWire = 7mil 6 | mdWirePad = 7mil 7 | mdWireVia = 7mil 8 | mdPadPad = 7mil 9 | mdPadVia = 7mil 10 | mdViaVia = 7mil 11 | mdSmdPad = 7mil 12 | mdSmdVia = 7mil 13 | mdSmdSmd = 7mil 14 | mdViaViaSameLayer = 8mil 15 | mnLayersViaInSmd = 2 16 | mdCopperDimension = 7mil 17 | mdDrill = 7mil 18 | mdSmdStop = 0mil 19 | msWidth = 7mil 20 | msDrill = 15mil 21 | msMicroVia = 9.99mm 22 | msBlindViaRatio = 0.500000 23 | rvPadTop = 0.250000 24 | rvPadInner = 0.250000 25 | rvPadBottom = 0.250000 26 | rvViaOuter = 0.250000 27 | rvViaInner = 0.250000 28 | rvMicroViaOuter = 0.250000 29 | rvMicroViaInner = 0.250000 30 | rlMinPadTop = 7mil 31 | rlMaxPadTop = 20mil 32 | rlMinPadInner = 7mil 33 | rlMaxPadInner = 20mil 34 | rlMinPadBottom = 7mil 35 | rlMaxPadBottom = 20mil 36 | rlMinViaOuter = 7mil 37 | rlMaxViaOuter = 20mil 38 | rlMinViaInner = 7mil 39 | rlMaxViaInner = 20mil 40 | rlMinMicroViaOuter = 4mil 41 | rlMaxMicroViaOuter = 20mil 42 | rlMinMicroViaInner = 4mil 43 | rlMaxMicroViaInner = 20mil 44 | psTop = -1 45 | psBottom = -1 46 | psFirst = -1 47 | psElongationLong = 100 48 | psElongationOffset = 100 49 | mvStopFrame = 1.000000 50 | mvCreamFrame = 0.000000 51 | mlMinStopFrame = 4mil 52 | mlMaxStopFrame = 4mil 53 | mlMinCreamFrame = 0mil 54 | mlMaxCreamFrame = 0mil 55 | mlViaStopLimit = 25mil 56 | srRoundness = 0.000000 57 | srMinRoundness = 0mil 58 | srMaxRoundness = 0mil 59 | slThermalIsolate = 10mil 60 | slThermalsForVias = 0 61 | dpMaxLengthDifference = 10mm 62 | dpGapFactor = 2.500000 63 | checkGrid = 0 64 | checkAngle = 1 65 | checkFont = 1 66 | checkRestrict = 1 67 | useDiameter = 13 68 | maxErrors = 50 69 | -------------------------------------------------------------------------------- /dru/quickturn_co_kr_1oz.dru: -------------------------------------------------------------------------------- 1 | description[en] = EAGLE Design Rules for *2* Layer Designs (DS)\n

\nThis Sunstone Circuits 2 Layer .DRU has been set to cover our minimum requirements for our double sided products. For questions, comments, suggestions, please contact Sunstone Customer Support:

Email: mailto:support@sunstone.com
Phone: 800-228-8198.

v1.1Mod10/16/07~Layers-core reversal.Distance C/D.Restring%.Supply G%.

v1.2 Mod12/06/07~Distance C/D to 20mils.

v1.3Mod12/12/07~Supply/Annulus Value to 12.5mils

1/15/08 SSC-EAGLE-2Lyr_v1.0.0.3~For Release

10/3/08 Description Edit.

\n\n

\nAttention: Upon running the design check and making modifications, please remember to save any changes before closing the application and sending in your board file to Sunstone Circuits for conversion. 2 | layerSetup = (1*16) 3 | mtCopper = 0.0178mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0178mm 4 | mtIsolate = 59mil 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 5 | mdWireWire = 6mil 6 | mdWirePad = 6mil 7 | mdWireVia = 6mil 8 | mdPadPad = 6mil 9 | mdPadVia = 6mil 10 | mdViaVia = 6mil 11 | mdSmdPad = 6mil 12 | mdSmdVia = 6mil 13 | mdSmdSmd = 6mil 14 | mdViaViaSameLayer = 8mil 15 | mnLayersViaInSmd = 2 16 | mdCopperDimension = 12mil 17 | mdDrill = 12mil 18 | mdSmdStop = 0mil 19 | msWidth = 4mil 20 | msDrill = 0.200659375mm 21 | msMicroVia = 9.9898mm 22 | msBlindViaRatio = 0.500000 23 | rvPadTop = 0.250000 24 | rvPadInner = 0.250000 25 | rvPadBottom = 0.250000 26 | rvViaOuter = 0.250000 27 | rvViaInner = 0.250000 28 | rvMicroViaOuter = 0.250000 29 | rvMicroViaInner = 0.250000 30 | rlMinPadTop = 8mil 31 | rlMaxPadTop = 999mil 32 | rlMinPadInner = 8mil 33 | rlMaxPadInner = 999mil 34 | rlMinPadBottom = 8mil 35 | rlMaxPadBottom = 999mil 36 | rlMinViaOuter = 4mil 37 | rlMaxViaOuter = 999mil 38 | rlMinViaInner = 4mil 39 | rlMaxViaInner = 999mil 40 | rlMinMicroViaOuter = 4mil 41 | rlMaxMicroViaOuter = 999mil 42 | rlMinMicroViaInner = 4mil 43 | rlMaxMicroViaInner = 999mil 44 | psTop = -1 45 | psBottom = -1 46 | psFirst = -1 47 | psElongationLong = 100 48 | psElongationOffset = 100 49 | mvStopFrame = 1.000000 50 | mvCreamFrame = 0.000000 51 | mlMinStopFrame = 3mil 52 | mlMaxStopFrame = 3mil 53 | mlMinCreamFrame = 0mil 54 | mlMaxCreamFrame = 0mil 55 | mlViaStopLimit = 50mil 56 | srRoundness = 0.000000 57 | srMinRoundness = 0mil 58 | srMaxRoundness = 0mil 59 | slThermalIsolate = 10mil 60 | slThermalsForVias = 0 61 | dpMaxLengthDifference = 10mm 62 | dpGapFactor = 2.500000 63 | checkGrid = 0 64 | checkAngle = 0 65 | checkFont = 1 66 | checkRestrict = 1 67 | useDiameter = 13 68 | maxErrors = 500 69 | -------------------------------------------------------------------------------- /dru/quickturn_co_kr_1oz_4lyr.dru: -------------------------------------------------------------------------------- 1 | description[en] = EAGLE Design Rules for *4* Layer Designs (DS)\n

\nQuickturn PCB 4 Layer .DRU 2 | layerSetup = (1+2*15+16) 3 | mtCopper = 0.0178mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0178mm 4 | mtIsolate = 59mil 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 5 | mdWireWire = 6mil 6 | mdWirePad = 6mil 7 | mdWireVia = 6mil 8 | mdPadPad = 6mil 9 | mdPadVia = 6mil 10 | mdViaVia = 6mil 11 | mdSmdPad = 6mil 12 | mdSmdVia = 6mil 13 | mdSmdSmd = 6mil 14 | mdViaViaSameLayer = 8mil 15 | mnLayersViaInSmd = 2 16 | mdCopperDimension = 12mil 17 | mdDrill = 12mil 18 | mdSmdStop = 0mil 19 | msWidth = 6mil 20 | msDrill = 8mil 21 | msMicroVia = 4mil 22 | msBlindViaRatio = 0.500000 23 | rvPadTop = 0.250000 24 | rvPadInner = 0.250000 25 | rvPadBottom = 0.250000 26 | rvViaOuter = 0.250000 27 | rvViaInner = 0.250000 28 | rvMicroViaOuter = 0.250000 29 | rvMicroViaInner = 0.250000 30 | rlMinPadTop = 8mil 31 | rlMaxPadTop = 999mil 32 | rlMinPadInner = 8mil 33 | rlMaxPadInner = 999mil 34 | rlMinPadBottom = 8mil 35 | rlMaxPadBottom = 999mil 36 | rlMinViaOuter = 4mil 37 | rlMaxViaOuter = 999mil 38 | rlMinViaInner = 4mil 39 | rlMaxViaInner = 999mil 40 | rlMinMicroViaOuter = 4mil 41 | rlMaxMicroViaOuter = 999mil 42 | rlMinMicroViaInner = 4mil 43 | rlMaxMicroViaInner = 999mil 44 | psTop = -1 45 | psBottom = -1 46 | psFirst = -1 47 | psElongationLong = 100 48 | psElongationOffset = 100 49 | mvStopFrame = 1.000000 50 | mvCreamFrame = 0.000000 51 | mlMinStopFrame = 3mil 52 | mlMaxStopFrame = 3mil 53 | mlMinCreamFrame = 0mil 54 | mlMaxCreamFrame = 0mil 55 | mlViaStopLimit = 50mil 56 | srRoundness = 0.000000 57 | srMinRoundness = 0mil 58 | srMaxRoundness = 0mil 59 | slThermalIsolate = 10mil 60 | slThermalsForVias = 0 61 | dpMaxLengthDifference = 10mm 62 | dpGapFactor = 2.500000 63 | checkGrid = 0 64 | checkAngle = 0 65 | checkFont = 1 66 | checkRestrict = 1 67 | useDiameter = 13 68 | maxErrors = 500 69 | -------------------------------------------------------------------------------- /dru/sunstone-2lyr_v1.0.0.3.dru: -------------------------------------------------------------------------------- 1 | description[en] = EAGLE Design Rules for *2* Layer Designs (DS)\n

\nThis Sunstone Circuits 2 Layer .DRU has been set to cover our minimum requirements for our double sided products. For questions, comments, suggestions, please contact Sunstone Customer Support:

Email: mailto:support@sunstone.com
Phone: 800-228-8198.

v1.1Mod10/16/07~Layers-core reversal.Distance C/D.Restring%.Supply G%.

v1.2 Mod12/06/07~Distance C/D to 20mils.

v1.3Mod12/12/07~Supply/Annulus Value to 12.5mils

1/15/08 SSC-EAGLE-2Lyr_v1.0.0.3~For Release

10/3/08 Description Edit.

\n\n

\nAttention: Upon running the design check and making modifications, please remember to save any changes before closing the application and sending in your board file to Sunstone Circuits for conversion. 2 | layerSetup = (1*16) 3 | mtCopper = 0.0178mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0178mm 4 | mtIsolate = 59mil 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 5 | mdWireWire = 6mil 6 | mdWirePad = 6mil 7 | mdWireVia = 6mil 8 | mdPadPad = 6mil 9 | mdPadVia = 6mil 10 | mdViaVia = 6mil 11 | mdSmdPad = 6mil 12 | mdSmdVia = 6mil 13 | mdSmdSmd = 6mil 14 | mdViaViaSameLayer = 8mil 15 | mnLayersViaInSmd = 2 16 | mdCopperDimension = 20mil 17 | mdDrill = 16mil 18 | mdSmdStop = 0mil 19 | msWidth = 6mil 20 | msDrill = 14mil 21 | msMicroVia = 9.9898mm 22 | msBlindViaRatio = 0.500000 23 | rvPadTop = 0.250000 24 | rvPadInner = 0.250000 25 | rvPadBottom = 0.250000 26 | rvViaOuter = 0.250000 27 | rvViaInner = 0.250000 28 | rvMicroViaOuter = 0.250000 29 | rvMicroViaInner = 0.250000 30 | rlMinPadTop = 8mil 31 | rlMaxPadTop = 999mil 32 | rlMinPadInner = 0mil 33 | rlMaxPadInner = 0mil 34 | rlMinPadBottom = 8mil 35 | rlMaxPadBottom = 999mil 36 | rlMinViaOuter = 8mil 37 | rlMaxViaOuter = 999mil 38 | rlMinViaInner = 0mil 39 | rlMaxViaInner = 0mil 40 | rlMinMicroViaOuter = 8mil 41 | rlMaxMicroViaOuter = 999mil 42 | rlMinMicroViaInner = 0mil 43 | rlMaxMicroViaInner = 0mil 44 | psTop = -1 45 | psBottom = -1 46 | psFirst = -1 47 | psElongationLong = 100 48 | psElongationOffset = 100 49 | mvStopFrame = 1.000000 50 | mvCreamFrame = 0.000000 51 | mlMinStopFrame = 3mil 52 | mlMaxStopFrame = 3mil 53 | mlMinCreamFrame = 0mil 54 | mlMaxCreamFrame = 0mil 55 | mlViaStopLimit = 50mil 56 | srRoundness = 0.000000 57 | srMinRoundness = 0mil 58 | srMaxRoundness = 0mil 59 | slThermalGap = 0.500000 60 | slMinThermalGap = 8mil 61 | slMaxThermalGap = 100mil 62 | slAnnulusIsolate = 12.5mil 63 | slThermalIsolate = 10mil 64 | slAnnulusRestring = 0 65 | slThermalRestring = 1 66 | slThermalsForVias = 0 67 | checkGrid = 0 68 | checkAngle = 0 69 | checkFont = 1 70 | checkRestrict = 1 71 | useDiameter = 13 72 | maxErrors = 500 73 | -------------------------------------------------------------------------------- /dru/sunstone-4lyr_v1.0.0.3.dru: -------------------------------------------------------------------------------- 1 | description[en] = EAGLE Design Rules for *4* Layer Designs (MLB)\n

\nThis Sunstone Circuits 4 Layer .DRU has been set to cover our minimum requirements for our 4 layer MLB products. For questions, comments, suggestions, please contact Sunstone Customer Support:

Email: mailto:support@sunstone.com
Phone:
800-228-8198

v1.1Mod10/16/07~Layers-core reversal.Distance C/D.Restring%.Supply G\n

v1.2Mod12/06/07~Distance C/D to 20mils.

v1.3Mod12/12/07~Supply/Annulus Value to 12.5mils

1/15/08 SSC-EAGLE-4Lyr_v1.0.0.3~For Release

10/03/08 Description Edit

\nAttention: Upon running the design check and making modifications, please remember to save any changes before closing the application and sending in your board file to Sunstone Circuits for conversion. 2 | layerSetup = (1+2*15+16) 3 | mtCopper = 0.0178mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0178mm 4 | mtIsolate = 12mil 28mil 12mil 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 12mil 5 | mdWireWire = 6mil 6 | mdWirePad = 6mil 7 | mdWireVia = 6mil 8 | mdPadPad = 6mil 9 | mdPadVia = 6mil 10 | mdViaVia = 6mil 11 | mdSmdPad = 6mil 12 | mdSmdVia = 6mil 13 | mdSmdSmd = 6mil 14 | mdViaViaSameLayer = 8mil 15 | mnLayersViaInSmd = 2 16 | mdCopperDimension = 20mil 17 | mdDrill = 16mil 18 | mdSmdStop = 0mil 19 | msWidth = 6mil 20 | msDrill = 8mil 21 | msMicroVia = 9.9898mm 22 | msBlindViaRatio = 0.500000 23 | rvPadTop = 0.250000 24 | rvPadInner = 0.250000 25 | rvPadBottom = 0.250000 26 | rvViaOuter = 0.250000 27 | rvViaInner = 0.250000 28 | rvMicroViaOuter = 0.250000 29 | rvMicroViaInner = 0.250000 30 | rlMinPadTop = 8mil 31 | rlMaxPadTop = 999mil 32 | rlMinPadInner = 8mil 33 | rlMaxPadInner = 999mil 34 | rlMinPadBottom = 8mil 35 | rlMaxPadBottom = 999mil 36 | rlMinViaOuter = 8mil 37 | rlMaxViaOuter = 999mil 38 | rlMinViaInner = 8mil 39 | rlMaxViaInner = 999mil 40 | rlMinMicroViaOuter = 8mil 41 | rlMaxMicroViaOuter = 999mil 42 | rlMinMicroViaInner = 8mil 43 | rlMaxMicroViaInner = 999mil 44 | psTop = -1 45 | psBottom = -1 46 | psFirst = -1 47 | psElongationLong = 100 48 | psElongationOffset = 100 49 | mvStopFrame = 1.000000 50 | mvCreamFrame = 0.000000 51 | mlMinStopFrame = 3mil 52 | mlMaxStopFrame = 3mil 53 | mlMinCreamFrame = 0mil 54 | mlMaxCreamFrame = 0mil 55 | mlViaStopLimit = 50mil 56 | srRoundness = 0.000000 57 | srMinRoundness = 0mil 58 | srMaxRoundness = 0mil 59 | slThermalGap = 0.500000 60 | slMinThermalGap = 8mil 61 | slMaxThermalGap = 100mil 62 | slAnnulusIsolate = 12.5mil 63 | slThermalIsolate = 10mil 64 | slAnnulusRestring = 0 65 | slThermalRestring = 1 66 | slThermalsForVias = 0 67 | checkGrid = 0 68 | checkAngle = 0 69 | checkFont = 1 70 | checkRestrict = 1 71 | useDiameter = 31 72 | maxErrors = 50 73 | -------------------------------------------------------------------------------- /dru/sunstone-6lyr_v1.0.0.3.dru: -------------------------------------------------------------------------------- 1 | description[en] = \n\n\nEAGLE Design Rules for *6* Layer Designs (MLB)\n

\nThis Sunstone Circuits 6 Layer .DRU has been set to cover our minimum requirements for our 6 layer MLB products. For questions, comments, suggestions, please contact Sunstone Customer Support:

Email: mailto:support@sunstone.com
Phone: 800-228-8198

v1.1Mod10/16/07~Layers-core reversal.Distance C/D.Restring%.Supply G%.\n

v1.2Mod12/06/07~Distance C/D to 20mils.

v1.3Mod12/12/07~Supply/Annulus Value to 12.5mils

1/15/08 SSC-EAGLE-6Lyr_v1.0.0.3~For Release

10/03/08 Description Edit.

\nAttention: Upon running the design check and making modifications, please remember to save any changes before closing the application and sending in your board file to Sunstone Circuits for conversion.\n 2 | layerSetup = (1+2*3+14*15+16) 3 | mtCopper = 0.0178mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0356mm 0.0178mm 4 | mtIsolate = 8mil 14mil 10mil 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 0.1499mm 0.2007mm 14mil 8mil 5 | mdWireWire = 6mil 6 | mdWirePad = 6mil 7 | mdWireVia = 6mil 8 | mdPadPad = 6mil 9 | mdPadVia = 6mil 10 | mdViaVia = 6mil 11 | mdSmdPad = 6mil 12 | mdSmdVia = 6mil 13 | mdSmdSmd = 6mil 14 | mdViaViaSameLayer = 8mil 15 | mnLayersViaInSmd = 2 16 | mdCopperDimension = 20mil 17 | mdDrill = 16mil 18 | mdSmdStop = 0mil 19 | msWidth = 6mil 20 | msDrill = 8mil 21 | msMicroVia = 9.9898mm 22 | msBlindViaRatio = 0.500000 23 | rvPadTop = 0.250000 24 | rvPadInner = 0.250000 25 | rvPadBottom = 0.250000 26 | rvViaOuter = 0.250000 27 | rvViaInner = 0.250000 28 | rvMicroViaOuter = 0.250000 29 | rvMicroViaInner = 0.250000 30 | rlMinPadTop = 8mil 31 | rlMaxPadTop = 999mil 32 | rlMinPadInner = 8mil 33 | rlMaxPadInner = 999mil 34 | rlMinPadBottom = 8mil 35 | rlMaxPadBottom = 999mil 36 | rlMinViaOuter = 8mil 37 | rlMaxViaOuter = 999mil 38 | rlMinViaInner = 8mil 39 | rlMaxViaInner = 999mil 40 | rlMinMicroViaOuter = 8mil 41 | rlMaxMicroViaOuter = 999mil 42 | rlMinMicroViaInner = 8mil 43 | rlMaxMicroViaInner = 999mil 44 | psTop = -1 45 | psBottom = -1 46 | psFirst = -1 47 | psElongationLong = 100 48 | psElongationOffset = 100 49 | mvStopFrame = 1.000000 50 | mvCreamFrame = 0.000000 51 | mlMinStopFrame = 3mil 52 | mlMaxStopFrame = 3mil 53 | mlMinCreamFrame = 0mil 54 | mlMaxCreamFrame = 0mil 55 | mlViaStopLimit = 50mil 56 | srRoundness = 0.000000 57 | srMinRoundness = 0mil 58 | srMaxRoundness = 0mil 59 | slThermalGap = 0.500000 60 | slMinThermalGap = 8mil 61 | slMaxThermalGap = 100mil 62 | slAnnulusIsolate = 12.5mil 63 | slThermalIsolate = 10mil 64 | slAnnulusRestring = 0 65 | slThermalRestring = 1 66 | slThermalsForVias = 0 67 | checkGrid = 0 68 | checkAngle = 0 69 | checkFont = 1 70 | checkRestrict = 1 71 | useDiameter = 31 72 | maxErrors = 300 73 | -------------------------------------------------------------------------------- /lbr/ac.lbr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | >NAME 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | Current Transformer 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | >NAME 117 | >VALUE 118 | 119 | 120 | 121 | 122 | 123 | 124 | >NAME 125 | >VALUE 126 | 127 | 128 | 129 | 130 | Current Transformer General Purpose 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | Solder point for 14AWG wire 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /lbr/linxgps.lbr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | >NAME 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /lbr/motor.lbr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | >NAME 83 | 84 | 85 | 86 | 87 | 88 | M 89 | 90 | 91 | 92 | 93 | >NAME 94 | >VALUE 95 | 96 | 97 | 98 | 99 | Pico Vibe<br/><br /> 100 | 101 | 8mm Vibration Motor - 3mm Type 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /lbr/opamps.lbr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | SOT23-5 landing pad. Adapted from http://www.ti.com/lit/ds/symlink/opa322.pdf 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | >NAME 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | Op-Amp with V+ and V- connections 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | >NAME 157 | V+ 158 | V- 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /lbr/pots.lbr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | >VALUE 92 | >NAME 93 | 94 | 95 | 96 | 97 | 98 | >NAME 99 | >VALUE 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | >NAME 122 | >VALUE 123 | 124 | 125 | 126 | 127 | Potentiometer 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /lbr/rf.lbr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 2.0 x 1.25mm [EIA 0805] 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | >NAME 90 | >VALUE 91 | 92 | 93 | 94 | 95 | <b>Description:</b> Symbol for <a href='https://product.tdk.com/info/en/documents/data_sheet/rf_balun_hhm1595a1_en.pdf'><i>HHM1595A1</i></a>, a multilayer Balun from TDK. 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | >NAME 105 | >VALUE 106 | 107 | 108 | 109 | 110 | <b>Description:</b> Symbol for <a href='https://product.tdk.com/info/en/documents/data_sheet/rf_balun_hhm1595a1_en.pdf'><i>HHM1595A1</i></a>, a multilayer Balun from TDK. 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /lbr/transformers.lbr: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | LAB11 - Transformers. 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | >NAME 141 | >VALUE 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | L1 159 | L2 160 | >NAME 161 | >VALUE 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | LPR6235 Miniature Step-Up Flyback Transformer 100:1 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | DataPrint==1.1 2 | distro==1.5.0 3 | et-xmlfile==1.0.1 4 | openpyxl==3.0.7 5 | PyPDF2==1.27.9 6 | sh==1.14.1 7 | unotools==0.3.3 8 | -------------------------------------------------------------------------------- /scr/Readme.txt: -------------------------------------------------------------------------------- 1 | Replace the eagle.scr file in your eagle/scr folder with this file. 2 | -- or -- 3 | Create a symlink in the eagle/scr folder that points to this. Something like: 4 | $ ln -s ~/shed/eagle/scr/eagle.scr eagle.scr 5 | 6 | Each section controls a particular window: 7 | BRD -> Board 8 | SCH -> Schematic 9 | LBR -> Library 10 | SYM -> Symbol 11 | PAC -> Package 12 | 13 | Anything you can set using the Change command can be set in the same way in eagle.scr. 14 | -------------------------------------------------------------------------------- /scr/eagle.scr: -------------------------------------------------------------------------------- 1 | # Configuration Script 2 | # Lab 11 Eagle Defaults 3 | # This file can be used to configure the editor windows. 4 | 5 | BRD: 6 | 7 | Grid On; 8 | Grid mil 9 | Grid 100; 10 | Grid Alt mil 11 | Grid Alt 10; 12 | Change Width 10; 13 | Change Font vector; 14 | Change Align bottom-center; 15 | 16 | Change Ratio 12%; 17 | Change Shape round; 18 | Change Drill 16; 19 | Change Isolate 12; 20 | 21 | #Menu Add Change Copy Delete Display Grid Group Move Name Quit Rect \ 22 | # Route Script Show Signal Split Text Value Via Window ';' Wire Write Edit; 23 | 24 | 25 | SCH: 26 | 27 | Grid Default On; 28 | Grid .1; 29 | Grid Alt .05; 30 | Change Width 0.010; 31 | Change Font proportional; 32 | Change Align bottom-center; 33 | 34 | Change Size .04; 35 | Change Xref on; 36 | 37 | #Menu Add Bus Change Copy Delete Display Gateswap Grid Group Invoke Junction \ 38 | # Label Move Name Net Pinswap Quit Script Show Split Value Window ';' \ 39 | # Wire Write Edit; 40 | 41 | LBR: 42 | 43 | #Menu Close Export Open Script Write ';' Edit; 44 | 45 | DEV: 46 | 47 | Grid Default On; 48 | 49 | #Menu Add Change Copy Connect Delete Display Export Grid Move Name Package \ 50 | # Prefix Quit Script Show Value Window ';' Write Edit; 51 | 52 | SYM: 53 | 54 | Grid Default On; 55 | Grid .1; 56 | Grid Alt .05; 57 | Change Width 0.010; 58 | Change Size 0.07; 59 | Change Font Proportional; 60 | Change Align bottom-center; 61 | 62 | Change Visible pin; 63 | 64 | #Menu Arc Change Copy Cut Delete Display Export Grid Group Move Name Paste \ 65 | # Pin Quit Script Show Split Text Value Window ';' Wire Write Edit; 66 | 67 | PAC: 68 | 69 | Grid Default On; 70 | Grid .1; 71 | Grid Alt .05; 72 | Change Width 0.005; 73 | Change Size 0.040; 74 | Change Font vector; 75 | Change Ratio 12; 76 | Change Align bottom-center; 77 | 78 | #Menu Add Change Copy Delete Display Grid Group Move Name Pad Quit \ 79 | # Script Show Smd Split Text Window ';' Wire Write Edit; 80 | -------------------------------------------------------------------------------- /scr/eaglerc: -------------------------------------------------------------------------------- 1 | 2 | # Good Eagle Settings 3 | 4 | Directories.IgnoreNonExisting = "1" 5 | 6 | Printer.Paper = "Letter" 7 | Printer.Orientation = "1" 8 | 9 | Sch.MenuText.01 = "" 10 | 11 | 12 | -------------------------------------------------------------------------------- /scr/pdf-brd.scr: -------------------------------------------------------------------------------- 1 | 2 | # Create pdfs of a 2 layer board 3 | 4 | BRD: 5 | 6 | # Display a nice view of the top of the board 7 | Display None; 8 | Display Top Pads Vias Unrouted Dimension tPlace tOrigins tNames; 9 | Rip @; 10 | Print FILE %N_top_drawing.pdf -MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 100 -1; 11 | 12 | # Display the top copper and not much else 13 | Ratsnest; 14 | Display -tPlace -tNames -tOrigins; 15 | Print FILE %N_top_copper.pdf -MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 100 -1; 16 | Print FILE %N_top_copper_1-1.pdf -MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 1; 17 | 18 | # Do a top image thats designed for placing parts 19 | Display None; 20 | Display Dimension tPlace tOrigins tNames ?? tNamesHidden tCream; 21 | Print FILE %N_top_place.pdf -MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 100 -1; 22 | 23 | # Do the same for the bottom 24 | Display None; 25 | Display Bottom Pads Vias Unrouted Dimension bPlace bOrigins bNames; 26 | Rip @; 27 | Print FILE %N_bot_drawing.pdf MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 100 -1; 28 | 29 | Ratsnest; 30 | Display -bPlace -bNames -bOrigins; 31 | Print FILE %N_bot_copper.pdf -MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 100 -1; 32 | Print FILE %N_bot_copper_1-1.pdf -MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 1; 33 | 34 | Display None; 35 | Display Dimension bPlace bOrigins bNames ?? bNamesHidden bCream; 36 | Print FILE %N_bot_place.pdf 100 MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter -1; 37 | 38 | Display None Top Bottom Pads Vias Unrouted Dimension tPlace tOrigins tNames; 39 | 40 | 41 | # Get all traces 42 | Display None; 43 | Display Top Bottom ?? Route2 ?? Route15 Pads Vias Unrouted Dimension tPlace tOrigins tNames bPlace bOrigins bNames; 44 | Rip @; 45 | Print FILE %N_drawing.pdf -MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 100 -1; 46 | Print FILE %N_drawing_1-1.pdf -MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 1; 47 | 48 | 49 | # Get the inner layers, if they exist 50 | Display None; 51 | Display ?? Route2 Pads Vias Unrouted Dimension; 52 | Ratsnest; 53 | Print FILE %N_layer2_copper.pdf -MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 100 -1; 54 | Print FILE %N_layer2_copper_1-1.pdf -MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 1; 55 | 56 | Display None; 57 | Display ?? Route15 Pads Vias Unrouted Dimension; 58 | Ratsnest; 59 | Print FILE %N_layer3_copper.pdf -MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 100 -1; 60 | Print FILE %N_layer3_copper_1-1.pdf -MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 1; 61 | 62 | 63 | # Get a pdf of just the outline and vias to check for multiple layers 64 | Display None; 65 | Display Pads Vias Unrouted Dimension; 66 | Ratsnest; 67 | Print FILE %N_layer_test.pdf -MIRROR -ROTATE -UPSIDEDOWN -BLACK -SOLID -CAPTION PORTRAIT PAPER letter 100 -1; 68 | 69 | -------------------------------------------------------------------------------- /scr/pdf-sch.scr: -------------------------------------------------------------------------------- 1 | 2 | # Create pdfs of a 2 layer board 3 | 4 | SCH: 5 | 6 | Print FILE %N_schematic.pdf -CAPTION LANDSCAPE SHEETS ALL -1; 7 | -------------------------------------------------------------------------------- /scr/png.scr: -------------------------------------------------------------------------------- 1 | 2 | # Create pdfs of a 2 layer board 3 | 4 | BRD: 5 | 6 | # Get all traces 7 | Display None; 8 | Display Top Bottom ?? Route2 ?? Route15 Pads Vias Unrouted Dimension tPlace tOrigins tNames bPlace bOrigins bNames; 9 | Rip @; 10 | Export IMAGE %N_pcb.png 300 11 | 12 | -------------------------------------------------------------------------------- /scr/script.scr: -------------------------------------------------------------------------------- 1 | 2 | # Simple eagle script for running a different script 3 | 4 | SCRIPT %SCRIPT_PATH%; 5 | 6 | BRD: 7 | 8 | quit; 9 | -------------------------------------------------------------------------------- /scr/ulp-brd.scr: -------------------------------------------------------------------------------- 1 | 2 | # Simple eagle script for running a ulp 3 | 4 | BRD: 5 | 6 | run %ULP_PATH% 7 | quit; 8 | -------------------------------------------------------------------------------- /scr/ulp-sch.scr: -------------------------------------------------------------------------------- 1 | 2 | # Simple eagle script for running a ulp 3 | 4 | SCH: 5 | 6 | run %ULP_PATH% 7 | quit; 8 | -------------------------------------------------------------------------------- /scripts/board_bundle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import argparse 4 | import datetime 5 | import glob 6 | import os 7 | import sys 8 | 9 | from sh import mkdir 10 | from sh import rm 11 | from sh import unzip 12 | from sh import ls 13 | from sh import cp 14 | import sh 15 | 16 | about = """ 17 | Bundle all of the created eagle output to a zip that could be posted on the web. 18 | """ 19 | 20 | FAB_FOLDER = 'to_fab' 21 | ASSEM_FOLDER = 'to_assembler' 22 | IMAGE_FOLDER = 'images' 23 | 24 | project_date = datetime.date.today().strftime("%Y-%m-%d") 25 | 26 | # Process all of the command line arguments 27 | argp = argparse.ArgumentParser(description=about) 28 | argp.add_argument('--date', 29 | default=project_date, 30 | help='Specify a date other than today\'s') 31 | args = argp.parse_args() 32 | 33 | # Check for folders we want to create 34 | folders = [FAB_FOLDER, ASSEM_FOLDER, IMAGE_FOLDER] 35 | for folder in folders: 36 | if os.path.isdir(folder): 37 | print('"{}" directory exists. Please remove before continuing.'. 38 | format(folder)) 39 | sys.exit(1) 40 | 41 | # Check for the zip files we need 42 | fab_zip = glob.glob('*to_fab_{}.zip'.format(args.date))[0] 43 | assem_zip = glob.glob('*to_assembler_{}.zip'.format(args.date))[0] 44 | 45 | if not fab_zip: 46 | print('No to_fab .zip file found. Maybe you should run eagle.py.') 47 | sys.exit(1) 48 | 49 | if not assem_zip: 50 | print('No to_assembler .zip file found. Maybe you should run eagle.py.') 51 | sys.exit(1) 52 | 53 | # Check for brd and sch files 54 | schs = glob.glob('*.sch') 55 | brds = glob.glob('*.brd') 56 | 57 | bases = [] 58 | for s in schs: 59 | bases.append(s[0:-4]) 60 | 61 | # Get the pdfs of the board 62 | pdfs = [] 63 | for b in bases: 64 | pdfs += glob.glob('{}.pdf'.format(b)) 65 | 66 | # Get any dxf files 67 | dxfs = [] 68 | dxfs += glob.glob('*.dxf') 69 | 70 | # Get the info files if they exist 71 | infos = [] 72 | for b in bases: 73 | info_file = '{}.info'.format(b) 74 | if os.path.exists(info_file): 75 | infos.append(info_file) 76 | 77 | # Get all images 78 | jpgs = glob.glob('*.jpg') 79 | jpgs.extend(glob.glob('*.png')) 80 | 81 | # Find the bom 82 | boms = glob.glob('*_bom.xls*') 83 | converted_boms = [] 84 | for b in boms: 85 | base = os.path.splitext(b)[0] 86 | csv = '{}.csv'.format(base) 87 | txt = '{}.txt'.format(base) 88 | if os.path.exists(csv): 89 | converted_boms.append(csv) 90 | if os.path.exists(txt): 91 | converted_boms.append(txt) 92 | boms += converted_boms 93 | 94 | # Get the output file name 95 | output_name = fab_zip.split('_to_fab')[0] + '_{}.zip'.format(args.date) 96 | 97 | # Actually make the zip 98 | 99 | # Generate the folders we use to organize things 100 | mkdir(FAB_FOLDER) 101 | mkdir(ASSEM_FOLDER) 102 | mkdir(IMAGE_FOLDER) 103 | 104 | # Put the contents of the zip files in the folders 105 | # This way we don't have to replicate that logic 106 | unzip(fab_zip, '-d', FAB_FOLDER) 107 | unzip(assem_zip, '-d', ASSEM_FOLDER) 108 | 109 | # Put the images in the images folder 110 | for jpg in jpgs: 111 | cp(jpg, IMAGE_FOLDER) 112 | 113 | # Get the filenames for fab 114 | fab_files = glob.glob('{}/*'.format(FAB_FOLDER)) 115 | assem_files = glob.glob('{}/*'.format(ASSEM_FOLDER)) 116 | image_files = glob.glob('{}/*'.format(IMAGE_FOLDER)) 117 | 118 | combined = [output_name] + schs + brds + pdfs + dxfs + infos + boms + \ 119 | fab_files + assem_files + image_files 120 | 121 | sh.zip(*combined) 122 | 123 | rm('-rf', FAB_FOLDER) 124 | rm('-rf', ASSEM_FOLDER) 125 | rm('-rf', IMAGE_FOLDER) 126 | -------------------------------------------------------------------------------- /scripts/bom_to_digikey.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | print("This script takes a BOM (in csv) and turns it into an uploadable Digikey order (also csv)."); 5 | print("To use the script you must have a \"Part\" Column and up to four \"DIGIKEY*\" columns."); 6 | print("You can then enter the rough number of components you want to order.\n\n") 7 | 8 | import sys 9 | 10 | # Get all of the tricky packages out of the way 11 | try: 12 | from sh import rm 13 | except: 14 | print("You need to install the sh module.") 15 | print("https://github.com/amoffat/sh") 16 | sys.exit(1) 17 | 18 | try: 19 | from sh import unoconv 20 | from sh import soffice 21 | import sh 22 | except: 23 | print("You must have unoconv installed to convert the bom.") 24 | print("sudo apt-get install unoconv") 25 | sys.exit(1) 26 | 27 | try: 28 | import dataprint 29 | except: 30 | print("You need to install the dataprint module.") 31 | print("sudo pip install dataprint") 32 | sys.exit(1) 33 | 34 | try: 35 | import natsort 36 | except: 37 | print("You need to pip install natsort.") 38 | sys.exit(1) 39 | 40 | import csv as csvr 41 | from glob import glob 42 | import os 43 | import itertools 44 | import re 45 | import operator 46 | 47 | 48 | def query_yes_no(question, default="yes"): 49 | """Ask a yes/no question via raw_input() and return their answer. 50 | 51 | "question" is a string that is presented to the user. 52 | "default" is the presumed answer if the user just hits . 53 | It must be "yes" (the default), "no" or None (meaning 54 | an answer is required of the user). 55 | 56 | The "answer" return value is True for "yes" or False for "no". 57 | """ 58 | valid = {"yes": True, "y": True, "ye": True, 59 | "no": False, "n": False} 60 | if default is None: 61 | prompt = " [y/n] " 62 | elif default == "yes": 63 | prompt = " [Y/n] " 64 | elif default == "no": 65 | prompt = " [y/N] " 66 | else: 67 | raise ValueError("invalid default answer: '%s'" % default) 68 | 69 | while True: 70 | sys.stdout.write(question + prompt) 71 | choice = raw_input().lower() 72 | if default is not None and choice == '': 73 | return valid[default] 74 | elif choice in valid: 75 | return valid[choice] 76 | else: 77 | sys.stdout.write("Please respond with 'yes' or 'no' " 78 | "(or 'y' or 'n').\n") 79 | 80 | # Display the help if any arguments are provided. 81 | if len(sys.argv) > 1: 82 | print(HELP) 83 | sys.exit(0) 84 | 85 | 86 | boms = glob('*bom.xls*') 87 | 88 | if len(boms) == 0: 89 | print("Could not find a bom to convert.") 90 | sys.exit(1) 91 | 92 | for b in boms: 93 | # Get the root name 94 | base = os.path.splitext(b)[0] 95 | csv_out = '{}_digikey.csv'.format(base) 96 | csv_in = '{}.csv'.format(base) 97 | 98 | # Check if csv and txt versions already exist 99 | if os.path.exists(csv_out): 100 | # Open it and determine if we created it. If we did, then I'm sure it's 101 | # fine to overwrite it. Otherwise, we probably shouldn't just overwrite 102 | # other people's files. 103 | with open(csv_out, 'r') as f: 104 | print('Found existing {}'.format(csv_out)); 105 | if query_yes_no("Would you like to overwrite it?"): 106 | pass 107 | else: 108 | print("Okay, exiting"); 109 | sys.exit(1) 110 | 111 | parts = {}; 112 | qtys = {} 113 | parts_with_refs = {} 114 | with open(csv_in, 'r') as f: 115 | # Figure out where all the columns are there are 116 | csvreader = csvr.reader(f) 117 | columns = next(csvreader); 118 | columns = next(csvreader); 119 | columns = next(csvreader); 120 | columns = next(csvreader); 121 | columns = next(csvreader); 122 | try: 123 | parts_column = columns.index('Part'); 124 | except ValueError: 125 | print("There must be a column called \"Part\" to execute this script. Exiting"); 126 | sys.exit(1); 127 | 128 | digikey_columns = []; 129 | try: 130 | digikey_columns.append(columns.index('DIGIKEY')); 131 | digikey_columns.append(columns.index('DIGIKEY1')); 132 | except ValueError: 133 | pass 134 | 135 | if(len(digikey_columns) < 1): 136 | print("There must a column called \"DIGIKEY*\" to execute this script. Exiting"); 137 | sys.exit(1); 138 | 139 | try: 140 | digikey_columns.append(columns.index('DIGIKEY2')); 141 | digikey_columns.append(columns.index('DIGIKEY3')); 142 | digikey_columns.append(columns.index('DIGIKEY4')); 143 | except ValueError: 144 | pass 145 | 146 | 147 | #process the columns into a dictionary where digikey part is 148 | #the key and a list of part number is the value 149 | #keep another dictionary of qtys for each part 150 | for part in csvreader: 151 | for col in digikey_columns: 152 | if part[col] in parts: 153 | parts[part[col]].append(part[parts_column]); 154 | qtys[part[col]] = qtys[part[col]] + 1; 155 | else: 156 | if part[col] != '': 157 | parts[part[col]] = [part[parts_column]]; 158 | qtys[part[col]] = 1; 159 | 160 | #for each part in the dictionary compress the part numbers 161 | #to put in the digikey reference field 162 | for part, ref_list in parts.items(): 163 | #sort the list alphabetically 164 | #this should group all the prefixes together 165 | ref_list = natsort.natsorted(ref_list); 166 | ref_string = "" 167 | cur_prefix = "" 168 | cur_num = 0; 169 | first_num = 0; 170 | for ref in ref_list: 171 | #we are already processing that prefix, just add the number 172 | if re.match("[a-zA-Z]+",ref).group(0) == cur_prefix: 173 | num = int(re.match(".*?([0-9]+)$",ref).group(1)); 174 | if cur_num + 1 is num: 175 | cur_num = num; 176 | else: 177 | if first_num == cur_num: 178 | ref_string = ref_string + (str(first_num)+" ") 179 | else: 180 | ref_string = ref_string + (str(first_num)+"-"+str(cur_num)+" ") 181 | cur_num = num; 182 | first_num = num; 183 | 184 | else: 185 | #finish up the older prefix 186 | if cur_prefix is "": 187 | pass 188 | else: 189 | if cur_num is first_num: 190 | ref_string = ref_string + str(cur_num)+" "; 191 | else: 192 | ref_string = ref_string+(str(first_num)+"-"+str(cur_num)+" "); 193 | #we need to start a new prefix 194 | cur_prefix = re.match("[a-zA-Z]+",ref).group(0); 195 | ref_string = ref_string + re.match("[a-zA-Z]+",ref).group(0); 196 | cur_num = int(re.match(".*?([0-9]+)$",ref).group(1)); 197 | first_num = int(re.match(".*?([0-9]+)$",ref).group(1)); 198 | 199 | 200 | if cur_num is first_num: 201 | ref_string = ref_string + (str(cur_num)); 202 | else: 203 | ref_string = ref_string + (str(first_num)+"-"+str(cur_num)); 204 | 205 | parts_with_refs[part] = ref_string; 206 | 207 | print("How many of each resistor would you like to buy?:"); 208 | try: 209 | res_qty = int(raw_input()); 210 | except ValueError: 211 | print("Must input an integer, exiting."); 212 | sys.exit(1); 213 | 214 | print("How many of each capacitor would you like to buy?:"); 215 | try: 216 | cap_qty = int(raw_input()); 217 | except ValueError: 218 | print("Must input an integer, exiting."); 219 | sys.exit(1); 220 | 221 | print("How many of every other part would you like to buy?:"); 222 | try: 223 | board_qty = int(raw_input()); 224 | except ValueError: 225 | print("Must input an integer, exiting."); 226 | sys.exit(1); 227 | 228 | #sort the dictionary so similar parts are grouped 229 | #transform it into a tuple for this 230 | sorted_parts_with_refs = sorted(parts_with_refs.items(), key = operator.itemgetter(1)) 231 | 232 | # Write back CSV 233 | with open(csv_out, 'w') as f: 234 | # Add header to csv 235 | f.write("Part List, Digikey Part Number, QTY\n"); 236 | for part, ref_list in sorted_parts_with_refs: 237 | if(ref_list[0] == "R"): 238 | f.write(ref_list + "," + part + "," + str(qtys[part]*res_qty) + "\n"); 239 | elif ref_list[0] == "C": 240 | f.write(ref_list + "," + part + "," + str(qtys[part]*cap_qty)+ "\n"); 241 | else: 242 | f.write(ref_list + "," + part + "," + str(qtys[part]*board_qty)+ "\n"); 243 | f.close(); 244 | -------------------------------------------------------------------------------- /scripts/bom_to_text.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | HELP = """ 5 | Convert an xls[x] bom to csv and text formats. 6 | 7 | Accepts no arguments. 8 | """ 9 | 10 | import csv 11 | from glob import glob 12 | import itertools 13 | import os 14 | import sys 15 | 16 | try: 17 | from sh import rm 18 | except: 19 | print("You need to install the sh module.") 20 | print("https://github.com/amoffat/sh") 21 | sys.exit(1) 22 | 23 | try: 24 | import openpyxl 25 | except: 26 | print("You need the openpyxl module.") 27 | print("(sudo) pip3 install openpyxl") 28 | sys.exit(1) 29 | 30 | try: 31 | import dataprint 32 | except: 33 | print("You need to install the dataprint module.") 34 | print("(sudo) pip3 install dataprint") 35 | sys.exit(1) 36 | 37 | 38 | # Display the help if any arguments are provided. 39 | if len(sys.argv) > 1: 40 | print(HELP) 41 | sys.exit(0) 42 | 43 | header = """\ 44 | # Bill of Materials 45 | # Converted from {} 46 | # Generated by bom_to_text.py 47 | # 48 | """ 49 | 50 | boms = glob('*bom*xls*') 51 | if len(boms) == 0: 52 | path = os.path.dirname(os.path.realpath(__file__)) 53 | for l in open(os.path.join(path, '..', 'doc', 'BOM.md')): 54 | sys.stdout.write(l) 55 | print('') 56 | print('-' * 60) 57 | print('ERR: Could not find a bom to convert.') 58 | print(' You need to generate a bill of materials.') 59 | print(' The above directions may be helpful.') 60 | sys.exit(1) 61 | 62 | for bom in boms: 63 | if '~$' in bom: 64 | continue 65 | 66 | # Get the root name 67 | base = os.path.splitext(bom)[0] 68 | csvfile = '{}.csv'.format(base) 69 | txtfile = '{}.txt'.format(base) 70 | 71 | # Check if csv and txt versions already exist 72 | if os.path.exists(csvfile): 73 | # Open it and determine if we created it. If we did, then I'm sure it's 74 | # fine to overwrite it. Otherwise, we probably shouldn't just overwrite 75 | # other people's files. 76 | with open(csvfile, 'r') as f: 77 | for l in f: 78 | if 'Generated by bom_to_text.py' in l: 79 | break 80 | else: 81 | print("Found existing {} that this script didn't create.".format(csvfile)) 82 | resp = input("Overwrite {}? [y/N] ".format(csvfile)) 83 | if not (len(resp) and resp[0].lower() == 'y'): 84 | print("Quitting BOM conversion") 85 | sys.exit(1) 86 | 87 | if os.path.exists(txtfile): 88 | with open(txtfile, 'r') as f: 89 | for l in f: 90 | if 'Generated by bom_to_text.py' in l: 91 | break 92 | else: 93 | print("Found existing {} that this script didn't create.".format(txtfile)) 94 | resp = input("Overwrite {}? [y/N] ".format(txtfile)) 95 | if not (len(resp) and resp[0].lower() == 'y'): 96 | print("Quitting BOM conversion") 97 | sys.exit(1) 98 | 99 | # Convert xlsx -> csv 100 | # http://stackoverflow.com/questions/10802417/how-to-save-an-excel-worksheet-as-csv-from-python-unix 101 | workbook = openpyxl.load_workbook(bom) 102 | sheet = workbook.active 103 | # Github needs the first row to have as many ',''s as the widest row 104 | # Find the widest row 105 | cols = 0 106 | for row in sheet.rows: 107 | cols = max(cols, len(row)) 108 | with open(csvfile, 'w') as outfile: 109 | csv_writer = csv.writer(outfile) 110 | for line in header.split('\n'): 111 | row = ['']*cols 112 | row[0] = line 113 | csv_writer.writerow(row) 114 | for row in sheet.rows: 115 | csv_writer.writerow([cell.value for cell in row]) 116 | 117 | # Convert CSV to txt 118 | rows = [] 119 | with open(csvfile,'r') as f: 120 | csvreader = csv.reader(f) 121 | for row in csvreader: 122 | rows.append(row) 123 | 124 | txt_contents = dataprint.to_string(data=rows, separator=' ') 125 | 126 | with open(txtfile, 'w') as f: 127 | f.write(header.replace('"', '').format(bom)) 128 | f.write(txt_contents) 129 | 130 | -------------------------------------------------------------------------------- /scripts/db/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /scripts/eagle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | HELP = """ 4 | Run all of the scripts relevant to eagle in the correct order. 5 | """ 6 | 7 | import glob 8 | import os 9 | import platform 10 | import distro 11 | import sys 12 | 13 | # Get all of the tricky packages out of the way 14 | try: 15 | import sh 16 | except: 17 | print("You need to install the sh module.") 18 | print("https://github.com/amoffat/sh") 19 | print("Try `pip install sh` or `pip3 install sh`") 20 | sys.exit(1) 21 | 22 | try: 23 | from sh import eagle 24 | except ImportError: 25 | # Try to find Eagle if we can 26 | print("You need to have eagle in your PATH, trying to find it...") 27 | if platform.system() == 'Darwin': 28 | versions = glob.glob('/Applications/EAGLE-*') 29 | if len(versions): 30 | versions.sort() 31 | latest = versions[-1] 32 | print('Using {}'.format(latest)) 33 | path = os.path.join(latest, 'EAGLE.app', 'Contents', 'MacOS') 34 | os.environ['PATH'] += ':' + path 35 | 36 | try: 37 | from sh import eagle 38 | except ImportError: 39 | print("Could not find eagle automatically, need to add to PATH manually") 40 | print("Something like `export PATH=$PATH:~/eagle-6.5.0/bin`") 41 | sys.exit(1) 42 | 43 | # filename arguments 44 | scripts = [('eagle_centroid.py', False), 45 | ('bom_to_text.py', False), 46 | ('eagle_pdf.py', False), 47 | ('eagle_png.py', False), 48 | ('eagle_attributes.py', False), 49 | ('eagle_tofab_zip.py', True), 50 | ('board_bundle.py', True) 51 | ] 52 | 53 | here = os.path.dirname(os.path.realpath(__file__)) 54 | 55 | 56 | if platform.system() == 'Linux': 57 | distro = distro.linux_distribution() 58 | if distro[0] == 'Ubuntu' and float(distro[1]) > 18: 59 | print("") 60 | print("Heads up. If you're on Ubuntu 18+ and are missing pdftk,") 61 | print("you may need to install a special version of pdftk, here:") 62 | print("https://askubuntu.com/questions/1028522/how-can-i-install-pdftk-in-ubuntu-18-04-bionic") 63 | print("") 64 | 65 | 66 | if platform.system() == 'Darwin': 67 | print("") 68 | print("Heads up. If you're on a mac and this script seems to hang during PDF") 69 | print("generation, you may need to install an updated version of pdftk, here:") 70 | print(" http://stackoverflow.com/questions/39750883/pdftk-hanging-on-macos-sierra") 71 | print("") 72 | 73 | 74 | for s, args in scripts: 75 | print("Running {}...".format(s)) 76 | 77 | path = os.path.join(here, s) 78 | if args: 79 | cmd = '{} {}'.format(path, ' '.join(sys.argv[1:])) 80 | else: 81 | cmd = path 82 | 83 | ret = os.system(cmd) 84 | if ret != 0: 85 | print("Error running scripts") 86 | sys.exit(1) 87 | 88 | -------------------------------------------------------------------------------- /scripts/eagle_attr_from_digikey_add_mpn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | print("NOPE. DIDN'T FINISH THIS ONE. WANT TO GET BACK TO IT SOMEDAY.") 4 | print("Really needs actual XML parsing, not regular expression hacks.") 5 | print("Should check each node for s and operate") 6 | print("on that structure. I hope to come back to this someday. Sorry.") 7 | sys.exit(1) 8 | 9 | from bs4 import BeautifulSoup 10 | import collections 11 | import glob 12 | import lxml.objectify 13 | from pprint import pprint 14 | import requests 15 | import shutil 16 | import sys 17 | import tempfile 18 | import termcolor 19 | import time 20 | 21 | print_attn = lambda x: termcolor.cprint(x, attrs=['bold']) 22 | print_err = lambda x: termcolor.cprint(x, 'red', attrs=['bold']) 23 | 24 | print("") 25 | print("*** MAKE SURE YOU HAVE CLOSED THE EAGLE FILE BEFORE RUNNING THIS ***") 26 | print("") 27 | 28 | print("I also recommend doing this with clean git, because, I wrote this in") 29 | print("an afternoon and make no promises. But it worked for me!") 30 | print("") 31 | 32 | schs = glob.glob('*.sch') 33 | 34 | if len(schs) == 0: 35 | print("Could not find any Eagle schematic files.") 36 | sys.exit(1) 37 | elif len(schs) > 1: 38 | print("Multiple sch's found.") 39 | print("Please type the full file name of the sch you want.") 40 | for f in schs: 41 | print(" - " + f) 42 | sch = input("sch file: ") 43 | else: 44 | sch = schs[0] 45 | 46 | print("> Using {} as schematic".format(sch)) 47 | with open(sch) as s: 48 | schematic = lxml.objectify.parse(s) 49 | 50 | print(schematic) 51 | sys.exit() 52 | 53 | brd = sch[:-4] + '.brd' 54 | 55 | try: 56 | with open(brd) as b: 57 | board = b.readlines() 58 | print("> Using {} as board".format(brd)) 59 | except IOError: 60 | print("") 61 | print("WARN: No matching .brd file") 62 | r = input(" Continue without updating a board? [y/N] ").upper() 63 | if not( len(r) and r[0] == 'Y' ): 64 | sys.exit() 65 | board = None 66 | print("") 67 | 68 | 69 | # Grab all of the attributes 70 | attr_digikey_re = re.compile('') 71 | digikey_part_nos = dict() 72 | for line in schematic: 73 | result = attr_digikey_re.match(line) 74 | if result is not None: 75 | digikey_part_no = result.groups()[0] 76 | digikey_part_nos[digikey_part_no] = None 77 | 78 | # Check if the board has any other ones 79 | if board: 80 | for line in board: 81 | result = attr_digikey_re.match(line) 82 | if result is not None: 83 | digikey_part_no = result.groups()[0] 84 | if digikey_part_no not in digikey_part_nos: 85 | print("WARN: Attribute {} in .brd file but not in .sch?".format(digikey_part_no)) 86 | 87 | 88 | # DigiKey Search API: https://api-portal.digikey.com/node/1364 89 | # Lies. That requires OAuth. Too hard. Soup it. 90 | def digikey_part_to_mfn(part_number): 91 | DIGIKEY_SEARCH = 'http://search.digikey.com/scripts/DkSearch/dksus.dll?Detail&name={}' 92 | 93 | part_number = part_number.replace('+', '%2B') 94 | url = DIGIKEY_SEARCH.format(part_number) 95 | digikey_html = requests.get(url).text 96 | try: 97 | soup = BeautifulSoup(digikey_html, "html.parser") 98 | mpn_th = soup(text="Manufacturer Part Number")[0] 99 | mpn_th_tag = mpn_th.parent 100 | mpn_td_tag = mpn_th_tag.findNext('td') 101 | mpn_h1_tag = mpn_td_tag.findNext('h1') 102 | except: 103 | #print(digikey_html) 104 | #print("-" * 80) 105 | #print(url) 106 | #print("-" * 80) 107 | #print("Sigh.. perhaps DigiKey has changed. Check out the HTML see what's wrong") 108 | #raise 109 | print("WARN: The attribute {} is not a valid DigiKey part number".format(part_number)) 110 | return None 111 | return mpn_h1_tag.get_text().strip() 112 | 113 | 114 | # Look up Manufacturer Part Numbers from DigiKey 115 | for part in digikey_part_nos: 116 | sys.stdout.write('Lookup {}\r'.format(part)) 117 | digikey_part_nos[part] = digikey_part_to_mfn(part) 118 | 119 | 120 | print("") 121 | if len(digikey_part_nos) == 0: 122 | print('No parts with DIGIKEY attributes found. Exiting.') 123 | sys.exit(1); 124 | 125 | print("This script will add the following attributes:") 126 | for digikey,mpn in digikey_part_nos.items(): 127 | print_attn("{:20} => {:20}".format("DIGIKEY", "MPN")) 128 | print ("{:20} => {:20}".format(digikey, mpn)) 129 | 130 | r = input("Write new attributes to eagle files? [Y/n] ").upper() 131 | if len(r) and r[0] == 'N': 132 | sys.exit() 133 | 134 | # Update schematic and board. Do everything in memory before touching files on disk 135 | new_sch = tempfile.TemporaryFile() 136 | for line in schematic: 137 | try: 138 | result = sch_part_tag_re.match(line) 139 | if result is not None: 140 | number, value, unit_prefix, suffix = result.groups() 141 | if int(number) >= 1000: 142 | if ignore_high_parts: 143 | continue 144 | old_name = prefix + number 145 | old_value = value + unit_prefix + suffix 146 | line = line.replace("name=\"" + old_name, "name=\"" + rename[old_name]['new_name'], 1) 147 | line = line.replace("value=\"" + old_value,"value=\"" + rename[old_name]['new_value'], 1) 148 | continue 149 | result = sch_instance_tag_re.match(line) 150 | if result is not None: 151 | number, = result.groups() 152 | if int(number) >= 1000: 153 | if ignore_high_parts: 154 | continue 155 | old_name = prefix + number 156 | line = line.replace(old_name, rename[old_name]['new_name'], 1) 157 | continue 158 | result = sch_pinref_tag_re.match(line) 159 | if result is not None: 160 | number, = result.groups() 161 | if int(number) >= 1000: 162 | if ignore_high_parts: 163 | continue 164 | old_name = prefix + number 165 | line = line.replace(old_name, rename[old_name]['new_name'], 1) 166 | continue 167 | result = sch_part_tag_novalue_re.match(line) 168 | if result is not None: 169 | number, deviceset = result.groups() 170 | if int(number) >= 1000 and ignore_high_parts: 171 | continue 172 | old_name = prefix + number 173 | line = line.replace(old_name, rename[old_name]['new_name'], 1) 174 | continue 175 | finally: 176 | new_sch.write(line.encode('utf-8')) 177 | 178 | if board: 179 | new_brd = tempfile.TemporaryFile() 180 | for line in board: 181 | try: 182 | result = brd_element_tag_re.match(line) 183 | if result is not None: 184 | number, value, unit_prefix, suffix = result.groups() 185 | if int(number) >= 1000: 186 | if ignore_high_parts: 187 | continue 188 | old_name = prefix + number 189 | old_value = value + unit_prefix + suffix 190 | line = line.replace("name=\"" + old_name, "name=\"" + rename[old_name]['new_name'], 1) 191 | line = line.replace("value=\"" + old_value, "value=\"" + rename[old_name]['new_value'], 1) 192 | continue 193 | result = brd_contactref_tag_re.match(line) 194 | if result is not None: 195 | number, = result.groups() 196 | if int(number) >= 1000: 197 | if ignore_high_parts: 198 | continue 199 | old_name = prefix + number 200 | line = line.replace(old_name, rename[old_name]['new_name'], 1) 201 | continue 202 | result = brd_element_tag_othervalue_re.match(line) 203 | if result is not None: 204 | number, value = result.groups() 205 | if int(number) >= 1000: 206 | if ignore_high_parts: 207 | continue 208 | old_name = prefix + number 209 | line = line.replace(old_name, rename[old_name]['new_name'], 1) 210 | continue 211 | finally: 212 | new_brd.write(line.encode('utf-8')) 213 | 214 | new_sch.seek(0) 215 | shutil.copyfileobj(new_sch, open(sch, 'wb')) 216 | if board: 217 | new_brd.seek(0) 218 | shutil.copyfileobj(new_brd, open(brd, 'wb')) 219 | 220 | -------------------------------------------------------------------------------- /scripts/eagle_attributes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | HELP = """ 5 | This script runs an eagle ULP to get the global attributes for a board. 6 | """ 7 | 8 | import sys 9 | # Get all of the tricky packages out of the way 10 | try: 11 | import sh 12 | from sh import mktemp 13 | from sh import rm 14 | except: 15 | print("You need to install the sh module.") 16 | print("https://github.com/amoffat/sh") 17 | sys.exit(1) 18 | 19 | try: 20 | from sh import eagle 21 | except: 22 | print("You need to have eagle in your path to generate the pdfs.") 23 | print("export PATH=$PATH:~/eagle-6.5.0/bin") 24 | sys.exit(1) 25 | 26 | import glob 27 | import os 28 | 29 | def read_in_info (filename, d): 30 | with open(filename) as f: 31 | for line in f: 32 | if len(line.strip()) == 0: 33 | continue 34 | opts = line.split(':', 1) 35 | d[opts[0].strip().lower()] = opts[1].strip() 36 | 37 | # Display the help if any arguments are provided. 38 | if len(sys.argv) > 1: 39 | print(HELP) 40 | sys.exit(0) 41 | 42 | # Determine the location of this script and the .ulp file 43 | here = os.path.dirname(os.path.realpath(__file__)) 44 | ulp = os.path.join(here, '..', 'ulp', 'attributes.ulp') 45 | scr = os.path.join(here, '..', 'scr', 'ulp-sch.scr') 46 | tmpscr = str(mktemp(os.path.join('/', 'tmp', 'ulp.scrXXXXXXXXXX'))).strip() 47 | 48 | contents = '' 49 | with open(scr, 'r') as f: 50 | contents = f.read() 51 | 52 | contents = contents.replace('%ULP_PATH%', ulp) 53 | 54 | with open(tmpscr, 'w') as f: 55 | f.write(contents) 56 | 57 | # Figure out the name of the schematic to run this on. 58 | for sch in glob.glob('*.sch'): 59 | attrs = {} 60 | 61 | sch_name = sch[:-4] 62 | 63 | # Read in the old .info file to save the original values 64 | if os.path.exists(sch_name + '.info'): 65 | read_in_info(sch_name + '.info', attrs) 66 | 67 | # Generate a new .info file in case anything changed 68 | eagle('-S', tmpscr, sch) 69 | 70 | # Read the new .info file in to update any changed values 71 | read_in_info(sch_name + '.info', attrs) 72 | 73 | # Write out the key values 74 | with open(sch_name + '.info', 'w') as f: 75 | for k,v in attrs.items(): 76 | f.write('{}: {}\n'.format(k, v)) 77 | 78 | 79 | rm(tmpscr) 80 | -------------------------------------------------------------------------------- /scripts/eagle_centroid.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | 6 | HELP = """ 7 | This script runs an eagle ULP to generate the pick and place file for smd parts. 8 | 9 | Accepts no arguments. 10 | """ 11 | 12 | # Get all of the tricky packages out of the way 13 | try: 14 | import sh 15 | except: 16 | print("You need to install the sh module.") 17 | print("sudo pip install sh") 18 | sys.exit(1) 19 | 20 | try: 21 | from sh import eagle 22 | except: 23 | print("You need to have eagle in your path to generate the pdfs.") 24 | print("export PATH=$PATH:~/eagle-6.5.0/bin") 25 | sys.exit(1) 26 | 27 | from glob import glob 28 | import os 29 | import sys 30 | import sh 31 | 32 | # Display the help if any arguments are provided. 33 | if len(sys.argv) > 1: 34 | print(HELP) 35 | sys.exit(0) 36 | 37 | # Determine the location of this script and the .ulp file 38 | here = os.path.dirname(os.path.realpath(__file__)) 39 | ulp = os.path.join(here, '..', 'ulp', 'centroid-smd.ulp') 40 | scr = os.path.join(here, '..', 'scr', 'ulp-brd.scr') 41 | tmpscr = os.path.join('/', 'tmp', 'ulp.scr') 42 | 43 | contents = '' 44 | with open(scr, 'r') as f: 45 | contents = f.read() 46 | 47 | contents = contents.replace('%ULP_PATH%', ulp) 48 | 49 | with open(tmpscr, 'w') as f: 50 | f.write(contents) 51 | 52 | 53 | # Figure out the name of the schematic to run this on. 54 | for brd in glob('*.brd'): 55 | # Generate the centroid file 56 | eagle('-S', tmpscr, brd) 57 | -------------------------------------------------------------------------------- /scripts/eagle_pdf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | HELP = """ 5 | This script runs an eagle script to generate pdf documentation of a 6 | PCB. It creates a pdf of the schematic and various layers to make a simple 7 | reference for others to use. 8 | 9 | Simply run this script in a folder containing a .sch and .brd file to 10 | generate the pdfs. 11 | """ 12 | 13 | import os 14 | import platform 15 | import sys 16 | import tempfile 17 | 18 | # Get all of the tricky packages out of the way 19 | try: 20 | from sh import rm 21 | from sh import mv 22 | except: 23 | print("You need to install the sh module.") 24 | print("https://github.com/amoffat/sh") 25 | sys.exit(1) 26 | 27 | try: 28 | import PyPDF2 29 | except: 30 | print("You need to install pypdf2") 31 | print("(sudo) pip3 install pypdf2") 32 | sys.exit(1) 33 | 34 | try: 35 | from sh import eagle 36 | except: 37 | print("You need to have eagle in your path to generate the pdfs.") 38 | print("export PATH=$PATH:~/eagle-6.5.0/bin") 39 | sys.exit(1) 40 | 41 | if platform.system().lower() == 'darwin': 42 | if os.path.exists('/usr/local/bin/wkhtmltopdf'): 43 | include_bom = True 44 | else: 45 | print("If you want to include the bom you must have wkhtmltopdf installed.") 46 | print("Get it from here: https://downloads.wkhtmltopdf.org/0.12/0.12.5/wkhtmltox-0.12.5-1.macos-cocoa.pkg") 47 | include_bom = False 48 | else: 49 | try: 50 | from sh import unoconv 51 | include_bom = True 52 | except: 53 | print("If you want to include the bom you must have unoconv installed.") 54 | print("sudo apt-get install unoconv") 55 | include_bom = False 56 | 57 | 58 | from glob import glob 59 | import sh 60 | from subprocess import call 61 | 62 | # List of all of the pdfs that get generated 63 | pdf_files = [('schematic', '~'), 64 | ('drawing', 'Top and Bottom Layers'), 65 | ('top_drawing', 'Top Layer'), 66 | ('bot_drawing', 'Bottom Layer'), 67 | ('top_copper', 'Top Copper Layer'), 68 | ('layer2_copper', 'Layer 2 Copper'), 69 | ('layer3_copper', 'Layer 3 Copper'), 70 | ('bot_copper', 'Bottom Copper Layer'), 71 | ('top_place', 'Top Paste Layer with Silkscreen'), 72 | ('bot_place', 'Bottom Paste Layer with Silkscreen'), 73 | ('drawing_1-1', 'Top and Bottom Layers 1:1 Scale'), 74 | ('top_copper_1-1', 'Top Layer 1:1 Scale'), 75 | ('layer2_copper_1-1', 'Layer 2 Copper 1:1 Scale'), 76 | ('layer3_copper_1-1', 'Layer 3 Copper 1:1 Scale'), 77 | ('bot_copper_1-1', 'Bottom Copper Layer 1:1 Scale') 78 | ] 79 | 80 | # Default layer count 81 | layer_count = 2 82 | 83 | # Display the help if any arguments are provided. 84 | if len(sys.argv) > 1: 85 | print(HELP) 86 | sys.exit(0) 87 | 88 | # Determine the location of this script and the .scr file 89 | here = os.path.dirname(os.path.realpath(__file__)) 90 | pdfscrSch = os.path.join(here, '..', 'scr', 'pdf-sch.scr') 91 | pdfscrBrd = os.path.join(here, '..', 'scr', 'pdf-brd.scr') 92 | numberpdf = os.path.join(here, 'number_pdf.sh') 93 | titlepdf = os.path.join(here, 'pdf_titles.py') 94 | 95 | 96 | # Figure out the name of the schematic to run this on. 97 | for sch in glob('*.sch'): 98 | sch_name, sch_ext = os.path.splitext(sch) 99 | brd = sch_name + '.brd' 100 | if not os.path.exists(brd): 101 | print("ERR: No .brd file for {}?".format(sch)) 102 | 103 | print("Running for {}".format(sch_name)) 104 | 105 | # Delete the old pdfs if they exist 106 | for pdf, title in pdf_files + [('layer_test', '~')]: 107 | rm('-f', '{}_{}.pdf'.format(sch_name, pdf)) 108 | 109 | # Delete the merged version 110 | rm ('-f', '{}.pdf'.format(sch)) 111 | 112 | # Something broke in Eagle with the section specifiers and command blocking, 113 | # so work around by just making new scr files that includes a traling quit 114 | print(" Generating schematic pdf") 115 | with tempfile.NamedTemporaryFile() as temp_scr: 116 | with open(pdfscrSch) as source: 117 | for line in source: 118 | temp_scr.write(line.encode('utf-8')) 119 | temp_scr.write('\nquit;'.encode('utf-8')) 120 | temp_scr.flush() 121 | 122 | # Generate the schematic pdfs 123 | eagle('-S', temp_scr.name, sch) 124 | 125 | print(" Generating board pdf") 126 | with tempfile.NamedTemporaryFile() as temp_scr: 127 | with open(pdfscrBrd) as source: 128 | for line in source: 129 | temp_scr.write(line.encode('utf-8')) 130 | temp_scr.write('\nquit;'.encode('utf-8')) 131 | temp_scr.flush() 132 | 133 | # Generate the board pdfs 134 | eagle('-S', temp_scr.name, brd) 135 | 136 | # If a bom is present also convert it to pdf 137 | if include_bom: 138 | print(" Generating BOM pdf") 139 | boms = glob('*bom*xls*') 140 | if len(boms) > 0: 141 | bom_tries = 3 142 | while bom_tries > 0: 143 | if platform.system().lower() == 'darwin': 144 | call(['/bin/rm', '-rf', 'osx_bom_to_pdf']) 145 | os.mkdir('osx_bom_to_pdf') 146 | call(['qlmanage','-o','osx_bom_to_pdf','-p',boms[0]]) 147 | qldir = boms[0] + '.qlpreview' 148 | os.chdir("%s/%s" % ('osx_bom_to_pdf',qldir)) 149 | call(['wkhtmltopdf', 150 | 'Preview.html', '{}_bom.pdf'.format(sch_name)]) 151 | mv('{}_bom.pdf'.format(sch_name), '../../') 152 | os.chdir('../..') 153 | call(['/bin/rm', '-rf', 'osx_bom_to_pdf']) 154 | break 155 | else: 156 | try: 157 | unoconv('-f', 'pdf', '-o', '{}_bom.pdf'.format(sch_name), boms[0]) 158 | break 159 | except sh.ErrorReturnCode: 160 | print('Unable to convert bom on this go. Will try again \ because that seems to fix it.') 161 | bom_tries -= 1 162 | if bom_tries == 0: 163 | # Failed to convert bom. Exclude it 164 | print('Just could not convert bom. You\'ll have to go \ without it.') 165 | include_bom = False 166 | else: 167 | include_bom = False 168 | 169 | # Check if it is a four layer board by determining if the layer2 or layer3 170 | # pdf is bigger than the test pdf 171 | test_size = os.stat('{}_layer_test.pdf'.format(sch_name)).st_size 172 | layer2_size = os.stat('{}_layer2_copper.pdf'.format(sch_name)).st_size 173 | layer3_size = os.stat('{}_layer3_copper.pdf'.format(sch_name)).st_size 174 | if layer2_size > test_size or layer3_size > test_size: 175 | layer_count = 4 176 | 177 | # Combine them 178 | pdfs = [] 179 | titles = [] 180 | for pdf, title in pdf_files: 181 | if ('layer2' in pdf or 'layer3' in pdf) and layer_count == 2: 182 | continue 183 | pdfname = '{}_{}.pdf'.format(sch_name, pdf) 184 | if os.path.exists(pdfname): 185 | reader = PyPDF2.PdfFileReader(pdfname) 186 | numpages = reader.getNumPages() 187 | titles += [title]*numpages 188 | pdfs.append(pdfname) 189 | if include_bom: 190 | pdfs.append('{}_bom.pdf'.format(sch_name)) 191 | 192 | print(" Merging PDFs...") 193 | merger = PyPDF2.PdfFileMerger() 194 | for pdf in pdfs: 195 | merger.append(pdf) 196 | merger.write('{}.pdf'.format(sch_name)) 197 | 198 | # Delete the generated pdfs if they exist 199 | print(" Cleaning up temporary PDF files") 200 | for pdf, title in pdf_files + [('layer_test', '~')]: 201 | try: 202 | rm('{}_{}.pdf'.format(sch_name, pdf)) 203 | except sh.ErrorReturnCode_1: 204 | pass 205 | 206 | try: 207 | rm('-f', '{}_bom.pdf'.format(sch_name)) 208 | except sh.ErrorReturnCode_1: 209 | pass 210 | 211 | # Add page numbers to the generated pdf 212 | print(" Add page numbers to pdf") 213 | os.system(numberpdf + ' {}.pdf'.format(sch_name)) 214 | 215 | # Add titles to generated pdf 216 | print(" Add titles to pdf") 217 | os.system(titlepdf + ' {}_numbered.pdf "'.format(sch_name) + \ 218 | '" "'.join(titles) + '"') 219 | 220 | rm('{}.pdf'.format(sch_name)) 221 | rm('{}_numbered.pdf'.format(sch_name)) 222 | mv('{}_numbered_titles.pdf'.format(sch_name), '{}.pdf'.format(sch_name)) 223 | 224 | print(" Done!") 225 | 226 | # Delete doc_data.txt if it was created 227 | rm('-f', 'doc_data.txt') 228 | 229 | -------------------------------------------------------------------------------- /scripts/eagle_png.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | HELP = """ 5 | This script runs an eagle script to generate a png of the PCB. 6 | 7 | Simply run this script in a folder containing a .sch and .brd file to 8 | generate the png. 9 | """ 10 | import sys 11 | 12 | # Get all of the tricky packages out of the way 13 | try: 14 | from sh import rm 15 | from sh import mv 16 | from sh import mktemp 17 | except: 18 | print("You need to install the sh module.") 19 | print("https://github.com/amoffat/sh") 20 | sys.exit(1) 21 | 22 | try: 23 | from sh import eagle 24 | except: 25 | print("You need to have eagle in your path to generate the pdfs.") 26 | print("export PATH=$PATH:~/eagle-6.5.0/bin") 27 | sys.exit(1) 28 | 29 | 30 | from glob import glob 31 | import os 32 | import sys 33 | import sh 34 | 35 | 36 | # Display the help if any arguments are provided. 37 | if len(sys.argv) > 1: 38 | print(HELP) 39 | sys.exit(0) 40 | 41 | # Determine the location of this script and the .scr file 42 | here = os.path.dirname(os.path.realpath(__file__)) 43 | pngscr = os.path.join(here, '..', 'scr', 'png.scr') 44 | scr = os.path.join(here, '..', 'scr', 'script.scr') 45 | tmppng = os.path.join(str(mktemp('/tmp/pngXXXXXXXXXX')).strip() + '.scr') 46 | tmpscr = os.path.join(str(mktemp('/tmp/scrXXXXXXXXXX')).strip() + '.scr') 47 | 48 | # Get the text from the png script file so that we can replace the 49 | # schematic name later. 50 | pngscr_contents = '' 51 | with open(pngscr, 'r') as f: 52 | pngscr_contents = f.read() 53 | 54 | # Create script to run the pdf script 55 | contents = '' 56 | with open(scr, 'r') as f: 57 | contents = f.read() 58 | contents = contents.replace('%SCRIPT_PATH%', tmppng) 59 | with open(tmpscr, 'w') as f: 60 | f.write(contents) 61 | 62 | # Figure out the name of the schematic to run this on. 63 | for sch in glob('*.sch'): 64 | sch_name, sch_ext = os.path.splitext(sch) 65 | 66 | png_name = '{}_pcb.png'.format(sch_name) 67 | rm('-f', png_name) 68 | 69 | pngscr_contents = pngscr_contents.replace('%N', sch_name) 70 | with open(tmppng, 'w') as f: 71 | f.write(pngscr_contents) 72 | 73 | # Generate the png 74 | eagle('-S', tmpscr, sch) 75 | 76 | # Trip whitespace 77 | sh.convert(png_name, '-trim', png_name) 78 | 79 | rm('-f', tmpscr) 80 | rm('-f', tmppng) 81 | 82 | -------------------------------------------------------------------------------- /scripts/eagle_tofab_zip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | about = """ 4 | Script to automatically zip up the proper gerber files for shipping 5 | to a board company. This script recommends this folder hierarchy: 6 | 7 | - eagle/hardware/cad folder 8 | - 9 | - rev_ 10 | - 11 | 12 | but will try its best to figure out your project name and revision. 13 | 14 | This script works with the sunstone CAM process and the other one in Eagle. 15 | """ 16 | 17 | import argparse 18 | import datetime 19 | import glob 20 | import os 21 | import sys 22 | try: 23 | from sh import rm 24 | import sh 25 | except: 26 | print('You need to install the sh module.') 27 | print('sudo pip install sh') 28 | sys.exit(1) 29 | 30 | revision = '' 31 | project_name = '' 32 | project_date = '' 33 | 34 | fab_extensions = [x.upper() for x in ['bot', 'bsk', 'tsk', \ 35 | 'dri','0102','0116','1516','0216','0215','0115', 'oln', 'slk', 'smb', \ 36 | 'smt', 'top', 'L1', 'L2', 'L3', 'L4', 'L5', 'L6', 'L7', 'L8', 'L9', 'L10', \ 37 | 'GBL', 'GBO', 'GBS', 'GTL', 'GTO', 'GTS', 'TXT', 'DIM', 'mil', 'gbs', \ 38 | 'gts']] 39 | assembler_extensions = [x.upper() for x in ['bps', 'tps', 'tsp', 'bsp', 'centroid', 'pdf']] 40 | stencil_extensions = [x.upper() for x in ['tsp', 'bsp', 'oln', 'DIM', 'mil']] 41 | # Files to add if they exist 42 | fab_add_files = ['fabnotes.txt'] 43 | 44 | fab_files = [] 45 | assem_files = [] 46 | stencil_files = [] 47 | 48 | schematic = '' 49 | 50 | # Process all of the command line arguments 51 | project_date = datetime.date.today().strftime('%Y-%m-%d') 52 | argp = argparse.ArgumentParser(description=about) 53 | argp.add_argument('--date', 54 | default=project_date, 55 | help='Specify a date other than today\'s') 56 | args = argp.parse_args() 57 | project_date = args.date 58 | 59 | # find the schematic filename 60 | files = os.listdir('.') 61 | for f in files: 62 | fname, fext = os.path.splitext(f) 63 | if fext[1:] == 'sch': 64 | schematic = fname 65 | break 66 | 67 | # figure out the proper filename 68 | folder_str = os.getcwd() 69 | folders = folder_str.split(os.sep) 70 | 71 | # get revision 72 | if folders[-1][0:4] == 'rev_': 73 | revs = folders[-1].split('_') 74 | revision = revs[-1] 75 | else: 76 | try: 77 | # not in a rev_X folder, see if it is in a filename 78 | if 'rev' in schematic: 79 | # get only letters or numbers after the word rev 80 | remainder = schematic.rsplit('rev', 1)[1] 81 | revision = filter(str.isalnum, remainder) 82 | else: 83 | # get only the letters after the last _ in the filename 84 | remainder = schematic.rsplit('_', 1)[1] 85 | revision = filter(str.isalnum, remainder) 86 | except: 87 | revision = '' 88 | 89 | if len(revision) > 1 or revision == '': 90 | revision = raw_input('Could not find revision. \ 91 | Please enter revision value: ') 92 | 93 | # get project name 94 | if 'rev' == folders[-1][0:3]: 95 | # offset for a revision folder 96 | folder_index = -2 97 | else: 98 | # use the immediate predecessor 99 | folder_index = -1 100 | 101 | if folders[folder_index] in ['eagle', 'hardware', 'cad']: 102 | # use the project name in the projects directory 103 | for i,f in zip(folders, range(0,len(folders))): 104 | if f == 'projects': 105 | project_name = folders[i+1] 106 | break 107 | # check if no projects dir 108 | if project_name == '': 109 | # use the folder name of whatever the eagle folder is in 110 | project_name = folders[folder_index-1] 111 | 112 | else: 113 | # files are not in an eagle directory, use whatever folder they are in 114 | project_name = folders[folder_index] 115 | 116 | 117 | # get the gerbers that need to be added 118 | files = os.listdir('.') 119 | for f in files: 120 | fname, fext = os.path.splitext(f) 121 | if fext[1:].upper() in fab_extensions: 122 | fab_files.append(f) 123 | assem_files.append(f) 124 | if fext[1:].upper() in assembler_extensions: 125 | assem_files.append(f) 126 | if fext[1:].upper() in stencil_extensions: 127 | stencil_files.append(f) 128 | if os.path.basename(f) in fab_add_files: 129 | fab_files.append(f) 130 | assem_files.append(f) 131 | 132 | # Add any bom files to the assembler zip 133 | boms = glob.glob('*bom*') 134 | assem_files += boms 135 | 136 | fab_filename = '{0}_{1}_to_fab_{2}'.format(project_name, revision, project_date) 137 | assem_filename = '{0}_{1}_to_assembler_{2}'.format(project_name, revision, 138 | project_date) 139 | stencil_filename = '{0}_{1}_to_stencil_{2}'.format(project_name, revision, 140 | project_date) 141 | 142 | print('Project name: {}'.format(project_name)) 143 | print('Revision: {}'.format(revision)) 144 | print('Date: {}'.format(project_date)) 145 | print('Fab files: {}'.format(' '.join(fab_files))) 146 | print('Assembler files: {}'.format(' '.join(assem_files))) 147 | print('Stencil files: {}'.format(' '.join(stencil_files))) 148 | 149 | if len(fab_files) == 0: 150 | print('Nothing to do!') 151 | print('Maybe you should run the cam job') 152 | sys.exit(1) 153 | 154 | rm('-f', fab_filename) 155 | rm('-f', assem_filename) 156 | rm('-f', stencil_filename) 157 | sh.zip(fab_filename, fab_files) 158 | sh.zip(assem_filename, assem_files) 159 | sh.zip(stencil_filename, stencil_files) 160 | -------------------------------------------------------------------------------- /scripts/number_pdf.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function usage { 4 | echo "USAGE: $0 PDF_TO_NUMBER.pdf [OUTFILE]" 5 | } 6 | 7 | if [ ! -r $1 ]; then 8 | usage 9 | exit 1 10 | fi 11 | 12 | command -v pdftk >/dev/null 2>&1 || { 13 | echo >&2 "You need pdftk.\nsudo apt-get install pdftk"; exit 1; 14 | } 15 | 16 | 17 | set -e 18 | 19 | tmp=`mktemp -d pdf_numberXXXXXXXXXX` 20 | if [ -z "$2" ]; then 21 | out=`basename "$1" .pdf`"_numbered.pdf" 22 | else 23 | out=$2 24 | fi 25 | 26 | cp $1 $tmp 27 | pushd $tmp &> /dev/null 28 | 29 | echo "Breaking original pdf into individual pages" 30 | pdftk $1 burst output ZXZX_%04d.pdf 31 | 32 | echo "Generating latex template..." 33 | cat > page_numbers.tex << EOF 34 | \documentclass{article} 35 | \usepackage[bottom=3cm,pdftex]{geometry} 36 | \pagestyle{plain} 37 | \renewcommand{\familydefault}{\sfdefault} 38 | \begin{document} 39 | EOF 40 | 41 | total=0 42 | for f in `ls ZXZX_*pdf`; do 43 | echo "~ \newpage" >> page_numbers.tex 44 | let total=total+1 45 | done 46 | 47 | echo "\end{document}" >> page_numbers.tex 48 | 49 | echo "Creating latex pages" 50 | pdflatex page_numbers.tex &> /dev/null 51 | 52 | if [[ ! -e page_numbers.pdf ]]; then 53 | echo "Failed to create page numbers?" 54 | exit 1 55 | fi 56 | 57 | echo "Breaking latex pages into individual documents" 58 | pdftk page_numbers.pdf burst output b_ZXZX_%04d.pdf 59 | 60 | echo "Overlaying pages" 61 | cnt=1 62 | for i in ZXZX_*.pdf; do 63 | echo " Processing page $cnt/$total" 64 | pdftk $i background b_$i output x_$i.pdf 65 | let cnt=cnt+1 66 | done 67 | 68 | echo "Creating unified pdf" 69 | pdftk x_ZXZX_*.pdf cat output $out 70 | 71 | popd &> /dev/null 72 | 73 | cp $tmp/$out . 74 | rm -rf $tmp 75 | 76 | echo "Finished. Numbered pdf saved to '$out'" 77 | 78 | -------------------------------------------------------------------------------- /scripts/pdf_titles.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Adds titles to each page of a pdf. 5 | 6 | Use ~ for a blank title. 7 | """ 8 | 9 | import sys 10 | 11 | try: 12 | import sh 13 | from sh import mv 14 | from sh import rm 15 | except ImportError: 16 | print('You need the sh module.') 17 | print('sudo pip install sh') 18 | sys.exit(1) 19 | try: 20 | from sh import pdflatex 21 | except ImportError: 22 | print('Requires pdflatex') 23 | sys.exit(1) 24 | try: 25 | from sh import pdftk 26 | except ImportError: 27 | print('Requires pdftk') 28 | sys.exit(1) 29 | 30 | import glob 31 | import os 32 | import tempfile 33 | 34 | usage = '{} ' 35 | 36 | tdir = tempfile.mkdtemp() 37 | 38 | TITLES_TEX = os.path.join(tdir, 'pdftitles.tex') 39 | TITLES_PDF = os.path.join(tdir, 'pdftitles.pdf') 40 | TITLES_BURST = os.path.join(tdir, 'pdftitles_b_%04d.pdf') 41 | TITLES_GLOB = os.path.join(tdir, 'pdftitles_b_{}.pdf') 42 | 43 | INPUT_BURST = os.path.join(tdir, 'input_b_%04d.pdf') 44 | INPUT_GLOB = os.path.join(tdir, 'input_b_*.pdf') 45 | 46 | MERGED_GLOB = os.path.join(tdir, 'merged_{}.pdf') 47 | MERGED_PDF = os.path.join(tdir, 'merged.pdf') 48 | 49 | latex_header = """ 50 | \\documentclass{article} 51 | \\usepackage[margin=1cm,pdftex]{geometry} 52 | \\pagestyle{plain} 53 | \\renewcommand{\\familydefault}{\\sfdefault} 54 | \\pagenumbering{gobble} 55 | \\begin{document} 56 | \\center """ 57 | latex_footer = """ 58 | \\end{document} 59 | """ 60 | 61 | if len(sys.argv) < 3: 62 | print(usage) 63 | sys.exit(1) 64 | 65 | input_file = sys.argv[1] 66 | output_file = input_file[:-4] + '_titles' + '.pdf' 67 | 68 | # Get a list of all the titles 69 | # Make sure that there are at least as many titles as input pages 70 | titles = sys.argv[2:] 71 | 72 | pdfinfo = pdftk(input_file, 'dump_data') 73 | pdfinfol = pdfinfo.strip().split('\n') 74 | for line in pdfinfol: 75 | items = line.split(':') 76 | if len(items) == 2 and items[0] == 'NumberOfPages': 77 | numpages = int(items[1].strip()) 78 | break 79 | 80 | while numpages > len(titles): 81 | titles.append('~') 82 | 83 | # Combine the headers and create a .tex file with the headers 84 | latex_titles = ' \\newpage\n'.join(titles) 85 | 86 | latex_out = latex_header + latex_titles + latex_footer 87 | 88 | with open(TITLES_TEX, 'w') as f: 89 | f.write(latex_out) 90 | 91 | # Create pdf 92 | pdflatex('-output-directory={}'.format(tdir), TITLES_TEX) 93 | 94 | # Split title pdf into pages 95 | pdftk(TITLES_PDF, 'burst', 'output', TITLES_BURST) 96 | 97 | # Split the input file into pages 98 | pdftk(input_file, 'burst', 'output', INPUT_BURST) 99 | 100 | # Merge 101 | inputs = glob.glob(INPUT_GLOB) 102 | for inputf in inputs: 103 | num = inputf[-8:-4] 104 | 105 | #pdftk(TITLES_GLOB.format(num), 'background', inputf, 'output', 106 | # MERGED_GLOB.format(num)) 107 | pdftk(inputf, 'background', TITLES_GLOB.format(num), 'output', 108 | MERGED_GLOB.format(num)) 109 | 110 | # Combine 111 | merged_files = sorted(glob.glob(MERGED_GLOB.format('*'))) 112 | args = merged_files + ['cat', 'output', MERGED_PDF] 113 | pdftk(args) 114 | 115 | # Move to output file 116 | mv(MERGED_PDF, output_file) 117 | 118 | # Delete temp folder 119 | rm('-rf', tdir) 120 | 121 | -------------------------------------------------------------------------------- /scripts/sunstone_to_oshpark.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | ''' 4 | Copy Sunstone gerbers output for OSH Park 5 | ''' 6 | 7 | import os 8 | import sys 9 | 10 | try: 11 | from sh import cp 12 | import sh 13 | except: 14 | print('Need sh module') 15 | print('sudo pip3 install sh') 16 | sys.exit(1) 17 | 18 | def query_yes_no(question, default="yes"): 19 | """Ask a yes/no question via input() and return their answer. 20 | 21 | "question" is a string that is presented to the user. 22 | "default" is the presumed answer if the user just hits . 23 | It must be "yes" (the default), "no" or None (meaning 24 | an answer is required of the user). 25 | 26 | The "answer" return value is True for "yes" or False for "no". 27 | """ 28 | valid = {"yes": True, "y": True, "ye": True, 29 | "no": False, "n": False} 30 | if default is None: 31 | prompt = " [y/n] " 32 | elif default == "yes": 33 | prompt = " [Y/n] " 34 | elif default == "no": 35 | prompt = " [y/N] " 36 | else: 37 | raise ValueError("invalid default answer: '%s'" % default) 38 | 39 | while True: 40 | sys.stdout.write(question + prompt) 41 | choice = input().lower() 42 | if default is not None and choice == '': 43 | return valid[default] 44 | elif choice in valid: 45 | return valid[choice] 46 | else: 47 | sys.stdout.write("Please respond with 'yes' or 'no' " 48 | "(or 'y' or 'n').\n") 49 | 50 | extensions = { 51 | 'top': 'GTL', 52 | 'bot': 'GBL', 53 | 'smt': 'GTS', 54 | 'smb': 'GBS', 55 | 'slk': 'GTO', 56 | 'bsk': 'GBO', 57 | 'oln': 'GKO', 58 | 'L1': 'GTL', 59 | 'L2': 'G2L', 60 | 'L3': 'G3L', 61 | 'L4': 'GBL', 62 | 'drd': 'XLN' 63 | } 64 | 65 | print('Looking for gerber files created by Sunstone CAM') 66 | 67 | new_files = [] 68 | for f in os.listdir('.'): 69 | fname, ext = os.path.splitext(f) 70 | 71 | if ext[1:] in extensions: 72 | new_name = '{}_oshpark.{}'.format(fname, extensions[ext[1:]]) 73 | new_files.append(new_name) 74 | cp(f, new_name) 75 | 76 | make_zip = query_yes_no('Would you like to create a ZIP of the OSH Park Gerbers') 77 | 78 | if make_zip and len(new_files) > 0: 79 | sh.zip('osh_park_gerbers.zip', *new_files) 80 | 81 | -------------------------------------------------------------------------------- /ulp/attrib-add.ulp: -------------------------------------------------------------------------------- 1 | #usage "Add attribute to group selected part(s) in a schematic

" 2 | "A number of predefined attributes are defined for use with bom-ex.ulp

" 3 | 4 | string Version = "1.09"; 5 | 6 | string fileName; 7 | 8 | int fDisplay = 1; 9 | 10 | string strCmd; // script command text 11 | 12 | string strAttrDigikey; 13 | string strAttrManufacturer; 14 | string strAttrMPN; 15 | string strAttrOtherName; 16 | string strAttrOtherValue; 17 | string strAttrDescription; 18 | string strList; 19 | 20 | 21 | ////////////////////////////////////////////////////////////////////////////// 22 | // Script Entry Point 23 | 24 | if (schematic) 25 | { 26 | schematic(SCH) 27 | { 28 | strCmd = ""; 29 | strAttrDigikey = ""; 30 | strAttrManufacturer = ""; 31 | strAttrMPN = ""; 32 | strAttrOtherName = ""; 33 | strAttrOtherValue = ""; 34 | strAttrDescription = ""; 35 | strList = ""; 36 | 37 | fileName = filesetext(SCH.name, "_AddAttrib.scr"); 38 | 39 | string title = "Add Attribute - v" + Version; 40 | 41 | int nResult = dlgDialog(title) 42 | { 43 | dlgHBoxLayout 44 | { 45 | dlgGroup("Attribute") 46 | { 47 | dlgGridLayout 48 | { 49 | dlgCell(1,0) dlgLabel("Part List (comma separated!!)"); 50 | dlgCell(1,1) dlgStringEdit(strList); 51 | 52 | dlgCell(3,0) dlgLabel("Attribute Name"); 53 | dlgCell(3,1) dlgLabel("Attribute Value"); 54 | 55 | dlgCell(5,0) dlgLabel("Digikey"); 56 | dlgCell(5,1) dlgStringEdit(strAttrDigikey); 57 | 58 | dlgCell(7,0) dlgLabel("Manufacturer"); 59 | dlgCell(7,1) dlgStringEdit(strAttrManufacturer); 60 | 61 | dlgCell(9,0) dlgLabel("MPN"); 62 | dlgCell(9,1) dlgStringEdit(strAttrMPN); 63 | 64 | dlgCell(11,0) dlgLabel("Description"); 65 | dlgCell(11,1) dlgStringEdit(strAttrDescription); 66 | 67 | dlgCell(13,0) dlgStringEdit(strAttrOtherName); 68 | dlgCell(13,1) dlgStringEdit(strAttrOtherValue); 69 | } 70 | } 71 | } 72 | dlgHBoxLayout 73 | { 74 | dlgPushButton("Add") dlgAccept(1); 75 | } 76 | }; 77 | 78 | if (!nResult) 79 | exit(0); 80 | 81 | int cnt = 0; 82 | int sheetswitched; 83 | 84 | // Get the list of parts split 85 | string parts[]; 86 | cnt += strsplit(parts, strList, ','); 87 | 88 | SCH.sheets(S) 89 | { 90 | sheetswitched = 0; 91 | S.parts(P) 92 | { 93 | //Set the display to off 94 | strCmd += "CHANGE DISPLAY OFF;\n"; 95 | 96 | for(int i = 0; i < cnt; i++) { 97 | if(parts[i] == P.name && P.device.package) { 98 | // Add attribute to group 99 | if (nResult == 1) 100 | { 101 | string tmp; 102 | if (sheetswitched == 0) 103 | { 104 | sprintf(tmp, "EDIT .s%d;\n", S.number); 105 | strCmd += tmp; 106 | sheetswitched = 1; 107 | } 108 | 109 | if(strAttrDigikey != "") 110 | { 111 | sprintf(tmp, "ATTRIBUTE %s DIGIKEY '%s';\n", P.name, strAttrDigikey); 112 | strCmd += tmp; 113 | } 114 | 115 | if(strAttrManufacturer != "") 116 | { 117 | sprintf(tmp, "ATTRIBUTE %s MANUFACTURER '%s';\n", P.name, strAttrManufacturer); 118 | strCmd += tmp; 119 | } 120 | 121 | if(strAttrMPN != "") 122 | { 123 | sprintf(tmp, "ATTRIBUTE %s MPN '%s';\n", P.name, strAttrMPN); 124 | strCmd += tmp; 125 | } 126 | 127 | if(strAttrOtherName != "" && strAttrOtherValue != "") 128 | { 129 | sprintf(tmp, "ATTRIBUTE %s %s '%s';\n", P.name, strAttrOtherName, strAttrOtherValue); 130 | strCmd += tmp; 131 | } 132 | 133 | if(strAttrDescription == "") 134 | { 135 | sprintf(tmp, "ATTRIBUTE %s DESCRIPTION DELETE;\n", P.name); 136 | strCmd += tmp; 137 | } else { 138 | sprintf(tmp, "ATTRIBUTE %s DESCRIPTION '%s';\n", P.name, strAttrDescription); 139 | strCmd += tmp; 140 | } 141 | } 142 | } 143 | } 144 | } 145 | } 146 | 147 | exit(strCmd); 148 | } 149 | } 150 | else 151 | { 152 | dlgMessageBox("Start this ULP in Schematic!", "OK"); 153 | } 154 | 155 | // End-Of-File 156 | -------------------------------------------------------------------------------- /ulp/attributes.ulp: -------------------------------------------------------------------------------- 1 | #usage "List the global attributes<\b>\n" 2 | 3 | string FileName; 4 | 5 | if (schematic) { 6 | schematic(SCH) { 7 | FileName = filesetext(SCH.name, ".info"); 8 | output(FileName) { 9 | SCH.attributes(A) { // global attributes 10 | printf("%s: %s\n", A.name, A.value); 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ulp/centroid-smd.ulp: -------------------------------------------------------------------------------- 1 | /* 2 | * This EAGLE User Language Program creates a CSV file for placing surface 3 | * mount components. 4 | */ 5 | 6 | #usage "Create Centroid file." 7 | 8 | string Version = "1.3.0"; // Version 1.3, August 12, 2013 9 | string fileMessage; 10 | 11 | string rotation(real Angle) { 12 | string s; 13 | sprintf(s, "%.1f", Angle); 14 | int pos = strchr(s, '.'); 15 | if (pos >= 0) if (s[pos + 1] == '0') s[pos] = 0; 16 | return s; 17 | } 18 | 19 | string side(int Mirror) { 20 | string s; 21 | 22 | if (Mirror){ 23 | s = "Bottom"; 24 | } 25 | else { 26 | s = "Top"; 27 | } 28 | return s; 29 | } 30 | 31 | if (!board) { 32 | dlgMessageBox("


ERROR: This ULP will only operate in the board layout view.

Switch to the board layout editor and re-run."); 33 | exit(1); 34 | } 35 | 36 | if (board) board(B) { 37 | output(filesetext(B.name, ".centroid")) { 38 | printf("# SMD component position file.\n"); 39 | printf("# Created by centroid_smd.ulp %s.\n\n", Version); 40 | printf("# Centroid Data for pc board: \"%s\" as of: %s\n", filename(B.name), t2string(time())); 41 | printf("# Measurements are in inches.\n"); 42 | printf("# Comma delimited.\n"); 43 | printf("# Only surface mount components included.\n\n"); 44 | printf("%s,%s,%s,%s,%s\n", "RefDes", "Layer", "LocationX", "LocationY", "Rotation"); 45 | B.elements(E) { 46 | int isSmd; 47 | isSmd = 0; 48 | E.package.contacts(C) { if (C.smd) isSmd = 1; } 49 | if (isSmd) printf("%s,%s,%5.5f,%5.5f,%s\n", E.name, side(E.mirror), u2inch(E.x), u2inch(E.y), rotation(E.angle)); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ulp/import_dxf_polygons_v4.ulp: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | ** ** 3 | ** Filename: import_dxf_polygons.ulp ** 4 | ** ** 5 | ** Author: Tim Ruetz ** 6 | ** tim at caiaq.de ** 7 | ** ** 8 | ** This ULP imports polylines and splines from DXF files ** 9 | ** Use it for importing vectorized logos, fonts etc. ** 10 | ** Arcs, circles, splines, curves are not supported yet. ** 11 | ** Since this is a very simple, rudimentary script it just uses ** 12 | ** straight lines as approximation. ** 13 | ** Please use a vector graphics editor to edit/refine you DXF file: ** 14 | ** - add as many as needed vertexes to the shape ** 15 | ** - convert all curves to straight lines ** 16 | ** - no negative shapes are supported by eagle, so create openings ** 17 | ** to connect negative shapes to outside (see example DXF) ** 18 | ** ** 19 | *********************************************************************** 20 | ** ** 21 | ** Legal issues: This program is provided as it is. Without any ** 22 | ** warranties of any kind of data lose or damages. ** 23 | ** ** 24 | ** Feel free to modify and improve this program and let me know. ** 25 | ** ** 26 | ** Version: 0.4 ** 27 | ** Date: 04.07.2011 ** 28 | ** ** 29 | *********************************************************************** 30 | ** ** 31 | ** Version history ** 32 | ** ** 33 | ** 0.1 initial version ** 34 | ** ** 35 | ** 0.2 added default pen with = 0.0 ** 36 | ** added insert offset settings to dialog ** 37 | ** ** 38 | ** 0.3 increased coordinate precision ** 39 | ** to import very tiny shapes ** 40 | ** ** 41 | ** 0.4 added WIRE vs POLYGON option ** 42 | ** by Tod E. Kurt, http://todbot.com/blog/ ** 43 | ** ** 44 | *********************************************************************** 45 | */ 46 | 47 | #usage "Simple DXF Polyline Import V0.3\n" 48 | "

" 49 | "Imports and scales (only!) POLYLINE and SPLINE entries in DXF files." 50 | "Splines are not drawn as splines but as stright lines!" 51 | "Author: Tim Ruetz (tim@caiaq.de)" 52 | 53 | string dxf_filename; 54 | int dxf_len; 55 | int i, j; 56 | string dxf_filedata[]; 57 | 58 | int dxf_code; 59 | string dxf_value; 60 | 61 | int state = 0; 62 | 63 | real vertex_x; 64 | real vertex_y; 65 | real vertex_x_buf; 66 | real vertex_y_buf; 67 | real scale = 1.0; 68 | 69 | real pen_width = 0.2; // mm 70 | real angle = 0.0; // degrees 71 | real rot_s; // rotation sinus 72 | real rot_c; // rotation cosinus 73 | int mirror_flag = 0; // 1=mirror 74 | 75 | real xmin, xmax; 76 | real ymin, ymax; 77 | 78 | real width_orig; 79 | real height_orig; 80 | real w, h; 81 | 82 | string parse_msg = ""; 83 | 84 | string l; 85 | string cmd; 86 | string script_out; 87 | 88 | int layer_cnt; 89 | int layer_sel=0; 90 | string layer_list[]; 91 | 92 | int pen_sel = 0; 93 | string pen_list[] = { "0.0", "0.1", "0.2", "0.3", "0.4", "0.5", "1.0", "1.5", "2.0" }; 94 | 95 | int line_sel = 0; 96 | string line_list[] = { "WIRE", "POLYGON" }; 97 | 98 | real offset_x = 0.0; 99 | real offset_y = 0.0; 100 | 101 | 102 | void find_layers() 103 | { 104 | if (library) { 105 | layer_cnt=0; 106 | library(L) { 107 | L.layers(LA) { 108 | if (LA.visible) 109 | { 110 | if ((LA.number < 17 || LA.number > 19) && (LA.number < 23 || LA.number > 25) && LA.number != 28) 111 | { 112 | if (LA.number == 94) layer_sel = layer_cnt; 113 | sprintf(layer_list[layer_cnt++], "%3d - %s", LA.number, LA.name); 114 | } 115 | } 116 | } 117 | } 118 | } 119 | if (board) { 120 | layer_cnt=0; 121 | board(B) { 122 | B.layers(LA) { 123 | if (LA.visible) 124 | { 125 | if ((LA.number < 17 || LA.number > 19) && (LA.number < 23 || LA.number > 25) && LA.number != 28) 126 | sprintf(layer_list[layer_cnt++], "%3d - %s", LA.number, LA.name); 127 | } 128 | } 129 | } 130 | } 131 | if (schematic) { 132 | layer_cnt=0; 133 | schematic(S) { 134 | S.layers(LA) { 135 | if (LA.visible) 136 | { 137 | if (LA.number < 95 || LA.number > 96) 138 | { 139 | if (LA.number == 94) layer_sel = layer_cnt; 140 | sprintf(layer_list[layer_cnt++], "%3d - %s", LA.number, LA.name); 141 | } 142 | } 143 | } 144 | } 145 | } 146 | } 147 | 148 | 149 | 150 | void update_script() 151 | { 152 | script_out = ""; 153 | // script_out += "SET UNDO_LOG OFF;\n"; 154 | script_out += "GRID MM;\n"; 155 | script_out += "SET WIDTH "+pen_list[pen_sel]+";\n"; 156 | script_out += "CHANGE POUR SOLID;\n"; 157 | script_out += "LAYER "+strsub(layer_list[layer_sel],0, 3)+";\n"; 158 | script_out += "SET WIRE_BEND 2;\n"; 159 | script_out += cmd; 160 | // script_out += "SET UNDO_LOG ON;\n"; 161 | } 162 | 163 | 164 | void parse_dxf() 165 | { 166 | string cmd_temp = ""; 167 | int vertexes = 0; 168 | cmd = ""; 169 | xmin = ymin = 100000.0; 170 | xmax = ymax = -100000.0; 171 | 172 | rot_s = sin(angle / 180.0 * PI); 173 | rot_c = cos(angle / 180.0 * PI); 174 | 175 | for (i=0; i 2 && dxf_value != "VERTEX") 184 | { 185 | if (vertexes > 2) // only draw polygons with at least 3 vertexes 186 | { 187 | cmd += cmd_temp+";\n"; 188 | } 189 | state = 0; 190 | cmd_temp=""; 191 | vertexes = 0; 192 | 193 | } 194 | 195 | 196 | if (dxf_value == "POLYLINE") 197 | state = 1; 198 | 199 | if (dxf_value == "VERTEX" && state==1) 200 | { 201 | cmd_temp += line_list[line_sel]; 202 | // cmd_temp += "POLYGON"; 203 | state=2; 204 | } 205 | 206 | if (dxf_value == "SPLINE") 207 | { 208 | cmd_temp += line_list[line_sel]; 209 | // cmd_temp += "POLYGON"; 210 | state = 2; 211 | } 212 | 213 | } 214 | 215 | 216 | 217 | if (state >= 2) 218 | { 219 | if (dxf_code == 10) 220 | vertex_x = strtod(dxf_value) * scale; 221 | 222 | if (dxf_code == 20) 223 | { 224 | state = 3; 225 | vertex_y = strtod(dxf_value) * scale; 226 | 227 | if (vertex_x > xmax) xmax = vertex_x; 228 | if (vertex_x < xmin) xmin = vertex_x; 229 | if (vertex_y > ymax) ymax = vertex_y; 230 | if (vertex_y < ymin) ymin = vertex_y; 231 | 232 | if (vertex_x != vertex_x_buf || vertex_y != vertex_y_buf) 233 | { 234 | vertex_x_buf = vertex_x; 235 | vertex_y_buf = vertex_y; 236 | if (mirror_flag) 237 | vertex_x = -vertex_x; 238 | 239 | sprintf(l, " (%5.6f %5.6f)", (vertex_x * rot_c - vertex_y * rot_s) + offset_x, (vertex_y * rot_c + vertex_x * rot_s) + offset_y); 240 | 241 | cmd_temp += l; 242 | vertexes++; 243 | } 244 | } 245 | } 246 | } 247 | sprintf(parse_msg, "%s\n\nWidth: %5.3f mm\nHeight: %5.3f mm\n\nLeft: %5.3f mm\nRight: %5.3f mm\n\nTop: %5.3f mm\nBottom: %5.3f mm\n\n", 248 | dxf_filename, xmax-xmin, ymax-ymin, xmin + offset_x, xmax + offset_x, ymin + offset_y, ymax + offset_y); 249 | 250 | if (scale==1.0) // at least the first time 251 | { 252 | w = width_orig = xmax-xmin; 253 | h = height_orig = ymax-ymin; 254 | 255 | } 256 | 257 | update_script(); 258 | } 259 | 260 | 261 | 262 | 263 | // 264 | // main() 265 | // 266 | 267 | find_layers(); 268 | 269 | dxf_filename = dlgFileOpen("DXF file to import", ".", "DXF files (*.dxf);;All files (*)"); 270 | if (dxf_filename == "") exit (0); 271 | 272 | dxf_len = fileread(dxf_filedata, dxf_filename); 273 | if (dxf_len<1) exit(0); 274 | 275 | parse_dxf(); 276 | 277 | // dialog 278 | int result = dlgDialog("Simple DXF Polyline Import") { 279 | dlgTabWidget { 280 | dlgTabPage("Settings") 281 | { 282 | dlgGridLayout { 283 | dlgCell(0, 0) dlgLabel("Scale"); 284 | dlgCell(0, 1) dlgRealEdit(scale, 0.0, 999.0); 285 | dlgCell(0, 2) dlgPushButton("+Rescale") parse_dxf(); 286 | 287 | dlgCell(1, 0) dlgLabel("Scale to width [mm]"); 288 | dlgCell(1, 1) dlgRealEdit(w, 0.1, 9999.0); 289 | dlgCell(1, 2) dlgPushButton("+Scale to width") { scale = w/width_orig; h=scale*height_orig; parse_dxf(); } 290 | 291 | dlgCell(2, 0) dlgLabel("Scale to height [mm]"); 292 | dlgCell(2, 1) dlgRealEdit(h, 0.1, 9999.0); 293 | dlgCell(2, 2) dlgPushButton("+Scale to height") { scale = h/height_orig; w=scale*width_orig; parse_dxf(); } 294 | 295 | dlgCell(3, 0) dlgLabel("Info"); 296 | dlgCell(3, 1, 3, 2) dlgTextView(parse_msg);; 297 | 298 | dlgCell(4, 0) dlgLabel("Import to layer"); 299 | dlgCell(4, 1, 4, 2) dlgComboBox(layer_list, layer_sel) update_script(); 300 | 301 | dlgCell(5, 0) dlgLabel("Wire or Polygon"); 302 | dlgCell(5, 1, 5,2) dlgComboBox( line_list, line_sel) parse_dxf(); 303 | 304 | dlgCell(6, 0) dlgLabel("Pen width [mm]"); 305 | dlgCell(6, 1, 6, 2) dlgComboBox(pen_list, pen_sel) update_script(); 306 | 307 | dlgCell(7, 0) dlgLabel("Angle (ccw)"); 308 | dlgCell(7, 1) dlgRealEdit(angle, 0.0, 360.0) parse_dxf(); 309 | dlgCell(7, 2) dlgCheckBox("Mirror", mirror_flag) parse_dxf(); 310 | 311 | dlgCell(8, 0) dlgLabel("Insert offset [mm] x="); 312 | dlgCell(8, 1, 8,2) { 313 | dlgGridLayout { 314 | dlgCell (1, 1) dlgRealEdit(offset_x, -999.0, 999.0) parse_dxf(); 315 | dlgCell (1, 2) dlgLabel("y="); 316 | dlgCell (1, 3) dlgRealEdit(offset_y, -999.0, 999.0) parse_dxf(); 317 | } 318 | } 319 | } 320 | } 321 | dlgTabPage("Generated Script") 322 | { 323 | dlgTextView(script_out); 324 | } 325 | 326 | } 327 | 328 | dlgGridLayout { 329 | dlgCell(0, 0) dlgPushButton("-Cancel") dlgReject(); 330 | dlgCell(0, 3) { 331 | dlgPushButton("+Execute") 332 | { 333 | parse_dxf(); 334 | dlgAccept(); 335 | } 336 | } 337 | } 338 | }; 339 | 340 | if (result == 0) 341 | exit(0); 342 | else 343 | { 344 | exit(script_out); 345 | } -------------------------------------------------------------------------------- /ulp/zoom-unrouted.ulp: -------------------------------------------------------------------------------- 1 | /* 2 | * UNROUTED.ULP 3 | * 4 | * simple script to zoom to the first unrouted wire in board editor. 5 | * this might be helpful when searching for tiny left-overs. 6 | * 7 | * done by Daniel Mack , http://caiaq.de 8 | */ 9 | 10 | int gridunit; 11 | 12 | real u2u(int val) { 13 | switch (gridunit) { 14 | case GRID_UNIT_MIC : return u2mic(val); 15 | case GRID_UNIT_MM : return u2mm(val); 16 | case GRID_UNIT_MIL : return u2mil(val); 17 | case GRID_UNIT_INCH : return u2inch(val); 18 | } 19 | } 20 | 21 | string RunOnExit = ""; 22 | 23 | if (board) { 24 | board(B) { 25 | gridunit = (B.grid.unit); 26 | B.signals(S) { 27 | S.wires(W) { 28 | if (W.layer == 19) { 29 | real x1 = u2u(W.x1); 30 | real x2 = u2u(W.x2); 31 | real y1 = u2u(W.y1); 32 | real y2 = u2u(W.y2); 33 | 34 | x1 -= (x2 - x1) / 2; 35 | x2 += (x2 - x1) / 2; 36 | y1 -= (y2 - y1) / 2; 37 | y2 += (y2 - y1) / 2; 38 | 39 | sprintf(RunOnExit, "\nWINDOW (%.6f %.6f) (%.6f %.6f);\n", x1, y1, x2, y2); 40 | exit (RunOnExit); 41 | } 42 | } 43 | } 44 | } 45 | } 46 | 47 | --------------------------------------------------------------------------------