Fab automation (#2)

- Use KiKit for fab automation
- Add splitflap scripts for exporting pcb pdfs, and switch to splitflap script for 3d rendering (for consistency with fab automation)
- Added support in 3d rendering scripts for soldermask & silkscreen colors, option to skip virtual components
- Added silkscreen to base and screen pcbs for commit and date info
This commit is contained in:
Scott Bezek 2022-02-15 01:27:38 -08:00 committed by GitHub
parent 8957d8b28e
commit 56feeb484c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 5655 additions and 4138 deletions

View File

@ -14,13 +14,62 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up outputs directory - name: Set up outputs directory
run: mkdir -p electronics/build run: mkdir -p electronics/build/outputs
- name: Install dependencies
run: ./electronics/scripts/dependencies.sh
- name: Export JLCPCB fabrication files [view_base]
run: |
./electronics/scripts/export_jlcpcb.py electronics/view_base/view_base.kicad_pcb
cp -r electronics/build/view_base-jlc electronics/build/outputs
- name: Export PCB overview PDF [view_base]
run: |
./electronics/scripts/generate_pdf.py electronics/view_base/view_base.kicad_pcb
cp electronics/build/view_base-pcb-packet.pdf electronics/build/outputs
env:
PYTHONUNBUFFERED: 1
- name: Render PCB 3D [view_base]
run: |
./electronics/scripts/export_3d.py electronics/view_base/view_base.kicad_pcb --suffix front --width 1280 --height 1280 --skip-virtual --color-soldermask 0.95 0.95 0.95 --color-silk 0.1 0.1 0.1 transform z+ z+
cp electronics/build/view_base-front-3d.png electronics/build/outputs
./scripts/annotate_image.sh electronics/build/outputs/view_base-front-3d.png
./electronics/scripts/export_3d.py electronics/view_base/view_base.kicad_pcb --suffix back --width 1280 --height 1280 --skip-virtual --color-soldermask 0.95 0.95 0.95 --color-silk 0.1 0.1 0.1 transform z+ z+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+
cp electronics/build/view_base-back-3d.png electronics/build/outputs
./scripts/annotate_image.sh electronics/build/outputs/view_base-back-3d.png
env:
PYTHONUNBUFFERED: 1
- name: Export JLCPCB fabrication files [view_screen]
run: |
./electronics/scripts/export_jlcpcb.py electronics/view_screen/view_screen.kicad_pcb
cp -r electronics/build/view_screen-jlc electronics/build/outputs
- name: Export PCB overview PDF [view_screen]
run: |
./electronics/scripts/generate_pdf.py electronics/view_screen/view_screen.kicad_pcb
cp electronics/build/view_screen-pcb-packet.pdf electronics/build/outputs
env:
PYTHONUNBUFFERED: 1
- name: Render PCB 3D [view_screen]
run: |
./electronics/scripts/export_3d.py electronics/view_screen/view_screen.kicad_pcb --suffix front --width 1280 --height 1280 --skip-virtual --color-soldermask 0.95 0.95 0.95 --color-silk 0.1 0.1 0.1 transform z+ z+
cp electronics/build/view_screen-front-3d.png electronics/build/outputs
./scripts/annotate_image.sh electronics/build/outputs/view_screen-front-3d.png
./electronics/scripts/export_3d.py electronics/view_screen/view_screen.kicad_pcb --suffix back --width 1280 --height 1280 --skip-virtual --color-soldermask 0.95 0.95 0.95 --color-silk 0.1 0.1 0.1 transform z+ z+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+ ry+
cp electronics/build/view_screen-back-3d.png electronics/build/outputs
./scripts/annotate_image.sh electronics/build/outputs/view_screen-back-3d.png
env:
PYTHONUNBUFFERED: 1
- name: Export artifacts (KiBot) [view_base] - name: Export artifacts (KiBot) [view_base]
uses: INTI-CMNB/KiBot@fb6d637c17fbba2daf005a9a7972e4c02c1c997c uses: INTI-CMNB/KiBot@fb6d637c17fbba2daf005a9a7972e4c02c1c997c
with: with:
config: electronics/view_base/view_base.kibot.yml config: electronics/view_base/view_base.kibot.yml
dir: electronics/build dir: electronics/build/outputs
schema: 'electronics/view_base/view_base.sch' schema: 'electronics/view_base/view_base.sch'
board: 'electronics/view_base/view_base.kicad_pcb' board: 'electronics/view_base/view_base.kicad_pcb'
@ -28,7 +77,7 @@ jobs:
uses: INTI-CMNB/KiBot@fb6d637c17fbba2daf005a9a7972e4c02c1c997c uses: INTI-CMNB/KiBot@fb6d637c17fbba2daf005a9a7972e4c02c1c997c
with: with:
config: electronics/view_screen/view_screen.kibot.yml config: electronics/view_screen/view_screen.kibot.yml
dir: electronics/build dir: electronics/build/outputs
schema: 'electronics/view_screen/view_screen.sch' schema: 'electronics/view_screen/view_screen.sch'
board: 'electronics/view_screen/view_screen.kicad_pcb' board: 'electronics/view_screen/view_screen.kicad_pcb'
@ -51,5 +100,5 @@ jobs:
- name: Sync artifacts to S3 - name: Sync artifacts to S3
if: github.event_name == 'push' && github.repository_owner == 'scottbez1' if: github.event_name == 'push' && github.repository_owner == 'scottbez1'
run: | run: |
aws s3 sync electronics/build s3://smartknob-artifacts/${GITHUB_REF#refs/heads/}/electronics --delete --cache-control max-age=0,no-cache aws s3 sync electronics/build/outputs s3://smartknob-artifacts/${GITHUB_REF#refs/heads/}/electronics --delete --cache-control max-age=0,no-cache

2
.gitignore vendored
View File

@ -27,3 +27,5 @@ build/
!.vscode/extensions.json !.vscode/extensions.json
!.vscode/*.code-snippets !.vscode/*.code-snippets
__pycache__

View File

@ -13,11 +13,11 @@ Premium SmartKnob experience. Under active development. Untested & unproven.
#### Base PCB #### Base PCB
<a href="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_base-3D_top.png"> <a href="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_base-front-3d.png">
<img src="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_base-3D_top.png" width="300" /> <img src="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_base-front-3d.png" width="300" />
</a> </a>
<a href="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_base-3D_bottom.png"> <a href="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_base-back-3d.png">
<img src="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_base-3D_bottom.png" width="300" /> <img src="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_base-back-3d.png" width="300" />
</a> </a>
@ -25,13 +25,17 @@ Premium SmartKnob experience. Under active development. Untested & unproven.
[Interactive BOM](https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_base-ibom.html) [Interactive BOM](https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_base-ibom.html)
[PCB Packet](https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_base-pcb-packet.pdf)
[Gerbers](https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_base-jlc/gerbers.zip)
#### Screen PCB #### Screen PCB
<a href="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_screen-3D_top.png"> <a href="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_screen-front-3d.png">
<img src="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_screen-3D_top.png" width="300" /> <img src="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_screen-front-3d.png" width="300" />
</a> </a>
<a href="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_screen-3D_bottom.png"> <a href="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_screen-back-3d.png">
<img src="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_screen-3D_bottom.png" width="300" /> <img src="https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_screen-back-3d.png" width="300" />
</a> </a>
@ -39,6 +43,9 @@ Premium SmartKnob experience. Under active development. Untested & unproven.
[Interactive BOM](https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_screen-ibom.html) [Interactive BOM](https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_screen-ibom.html)
[PCB Packet](https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_screen-pcb-packet.pdf)
[Gerbers](https://smartknob-artifacts.s3.us-west-1.amazonaws.com/master/electronics/view_screen-jlc/gerbers.zip)
### SmartKnob Handheld ### SmartKnob Handheld
An exploration of a small handheld form-factor. Under active development. An exploration of a small handheld form-factor. Under active development.

0
__init__.py Normal file
View File

View File

View File

@ -0,0 +1,87 @@
SchematicFramePos_x=89
SchematicFramePos_y=38
SchematicFrameSize_x=1125
SchematicFrameSize_y=739
SchematicFrameMaximized=0
SchematicFrameAutoSaveInterval=600
SchematicFramePerspective=layout2|name=MainToolbar;caption=;state=139644;dir=1;layer=6;row=0;pos=0;prop=100000;bestw=1016;besth=36;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=OptToolbar;caption=;state=139596;dir=4;layer=3;row=0;pos=0;prop=100000;bestw=36;besth=212;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=ToolsToolbar;caption=;state=139596;dir=2;layer=1;row=0;pos=0;prop=100000;bestw=36;besth=688;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=DrawFrame;caption=;state=1020;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=387;besth=181;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=MsgPanel;caption=;state=131452;dir=3;layer=6;row=0;pos=0;prop=100000;bestw=400;besth=34;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|dock_size(1,6,0)=36|dock_size(4,3,0)=36|dock_size(2,1,0)=36|dock_size(5,0,0)=337|dock_size(3,6,0)=34|
SchematicFrameMostRecentlyUsedPath=
SchematicFrameUnits=1
SchematicFrameShowGrid=1
SchematicFrame_LastGridSize=5
SchematicFrameFirstRunShown=0
SchematicFrameDevelMaxUndoItems=0
SchematicFrameGalDisplayOptionsGridStyle=0
SchematicFrameGalDisplayOptionsGridLineWidth=1
SchematicFrameGalDisplayOptionsGridMaxDensity=10
SchematicFrameGalDisplayOptionsGridAxesEnabled=0
SchematicFrameGalDisplayOptionsCursorFullscreen=0
SchematicFrameGalDisplayOptionsForceDisplayCursor=1
ShowPageLimits=1
Units=1
PrintMonochrome=1
PrintSheetReferenceAndTitleBlock=1
RepeatStepX=0
RepeatStepY=100
RepeatLabelIncrement=1
ShowIllegalSymbolLibDialog=1
ShowSheetFileNameCaseSensitivityDlg=1
DefaultBusWidth=12
DefaultWireWidth=6
DefaultJunctionSize=40
ShowHiddenPins=0
HorizVertLinesOnly=1
AutoplaceFields=1
AutoplaceJustify=1
AutoplaceAlign=0
FootprintPreview=0
SimCmdLine=
LastFindReplaceFlags=1
LastFindString=
LastReplaceString=
FieldNames=(templatefields)
canvas_type=2
Color4DWireEx=rgb(0, 132, 0)
Color4DBusEx=rgb(0, 0, 132)
Color4DConnEx=rgb(0, 132, 0)
Color4DLLabelEx=rgb(0, 0, 0)
Color4DHLabelEx=rgb(132, 132, 0)
Color4DGLabelEx=rgb(132, 0, 0)
Color4DPinNumEx=rgb(132, 0, 0)
Color4DPinNameEx=rgb(0, 132, 132)
Color4DFieldEx=rgb(132, 0, 132)
Color4DReferenceEx=rgb(0, 132, 132)
Color4DValueEx=rgb(0, 132, 132)
Color4DNoteEx=rgb(0, 0, 194)
Color4DBodyEx=rgb(132, 0, 0)
Color4DBodyBgEx=rgb(255, 255, 194)
Color4DNetNameEx=rgb(132, 132, 132)
Color4DPinEx=rgb(132, 0, 0)
Color4DSheetEx=rgb(132, 0, 132)
Color4DSheetFileNameEx=rgb(132, 132, 0)
Color4DSheetNameEx=rgb(0, 132, 132)
Color4DSheetLabelEx=rgb(132, 132, 0)
Color4DNoConnectEx=rgb(0, 0, 132)
Color4DErcWEx=rgba(0, 132, 0, 0.800)
Color4DErcEEx=rgba(132, 0, 0, 0.800)
Color4DGridEx=rgb(132, 132, 132)
Color4DBgCanvasEx=rgb(255, 255, 255)
Color4DCursorEx=rgb(0, 0, 0)
Color4DBrightenedEx=rgb(255, 0, 255)
Color4DHiddenEx=rgb(194, 194, 194)
Color4DWorksheetEx=rgb(132, 0, 0)
file1=
file2=
file3=
file4=
file5=
file6=
file7=
file8=
file9=
PlotModeColor=1
PlotFrameRef=1
PlotFormat=3
PlotHPGLOrg=0
PlotHPGLPaperSize=0
PlotHPGLPenSize=0.4826

View File

@ -0,0 +1,556 @@
Color4DPCBLayer_F.Cu=rgb(132, 0, 0)
Color4DPCBLayer_In1.Cu=rgb(194, 194, 0)
Color4DPCBLayer_In2.Cu=rgb(194, 0, 194)
Color4DPCBLayer_In3.Cu=rgb(194, 0, 0)
Color4DPCBLayer_In4.Cu=rgb(0, 132, 132)
Color4DPCBLayer_In5.Cu=rgb(0, 132, 0)
Color4DPCBLayer_In6.Cu=rgb(0, 0, 132)
Color4DPCBLayer_In7.Cu=rgb(132, 132, 132)
Color4DPCBLayer_In8.Cu=rgb(132, 0, 132)
Color4DPCBLayer_In9.Cu=rgb(194, 194, 194)
Color4DPCBLayer_In10.Cu=rgb(132, 0, 132)
Color4DPCBLayer_In11.Cu=rgb(132, 0, 0)
Color4DPCBLayer_In12.Cu=rgb(132, 132, 0)
Color4DPCBLayer_In13.Cu=rgb(194, 194, 194)
Color4DPCBLayer_In14.Cu=rgb(0, 0, 132)
Color4DPCBLayer_In15.Cu=rgb(0, 132, 0)
Color4DPCBLayer_In16.Cu=rgb(132, 0, 0)
Color4DPCBLayer_In17.Cu=rgb(194, 194, 0)
Color4DPCBLayer_In18.Cu=rgb(194, 0, 194)
Color4DPCBLayer_In19.Cu=rgb(194, 0, 0)
Color4DPCBLayer_In20.Cu=rgb(0, 132, 132)
Color4DPCBLayer_In21.Cu=rgb(0, 132, 0)
Color4DPCBLayer_In22.Cu=rgb(0, 0, 132)
Color4DPCBLayer_In23.Cu=rgb(132, 132, 132)
Color4DPCBLayer_In24.Cu=rgb(132, 0, 132)
Color4DPCBLayer_In25.Cu=rgb(194, 194, 194)
Color4DPCBLayer_In26.Cu=rgb(132, 0, 132)
Color4DPCBLayer_In27.Cu=rgb(132, 0, 0)
Color4DPCBLayer_In28.Cu=rgb(132, 132, 0)
Color4DPCBLayer_In29.Cu=rgb(194, 194, 194)
Color4DPCBLayer_In30.Cu=rgb(0, 0, 132)
Color4DPCBLayer_B.Cu=rgba(0, 132, 0, 0.439)
Color4DPCBLayer_B.Adhes=rgb(0, 0, 132)
Color4DPCBLayer_F.Adhes=rgb(132, 0, 132)
Color4DPCBLayer_B.Paste=rgb(0, 194, 194)
Color4DPCBLayer_F.Paste=rgb(132, 0, 0)
Color4DPCBLayer_B.SilkS=rgb(132, 0, 132)
Color4DPCBLayer_F.SilkS=rgb(0, 132, 132)
Color4DPCBLayer_B.Mask=rgb(132, 132, 0)
Color4DPCBLayer_F.Mask=rgb(132, 0, 132)
Color4DPCBLayer_Dwgs.User=rgb(194, 194, 194)
Color4DPCBLayer_Cmts.User=rgb(0, 0, 132)
Color4DPCBLayer_Eco1.User=rgb(0, 132, 0)
Color4DPCBLayer_Eco2.User=rgb(194, 194, 0)
Color4DPCBLayer_Edge.Cuts=rgb(194, 194, 0)
Color4DPCBLayer_Margin=rgb(194, 0, 194)
Color4DPCBLayer_B.CrtYd=rgb(132, 132, 132)
Color4DPCBLayer_F.CrtYd=rgb(194, 194, 194)
Color4DPCBLayer_B.Fab=rgb(0, 0, 132)
Color4DPCBLayer_F.Fab=rgb(132, 132, 132)
Color4DTxtFrontEx=rgb(194, 194, 194)
Color4DTxtBackEx=rgb(0, 0, 132)
Color4DTxtInvisEx=rgb(132, 132, 132)
Color4DPadBackEx=rgb(0, 132, 0)
Color4DAnchorEx=rgb(0, 0, 132)
Color4DPadFrontEx=rgb(132, 0, 0)
Color4DPadThruHoleEx=rgb(194, 194, 0)
Color4DNonPlatedEx=rgb(194, 194, 0)
Color4DPCBBackground=rgb(0, 0, 0)
Color4DPCBCursor=rgb(255, 255, 255)
Color4DAuxItems=rgb(255, 255, 255)
Color4DWorksheet=rgb(72, 0, 0)
Color4DGrid=rgb(132, 132, 132)
Color4DViaThruEx=rgb(194, 194, 194)
Color4DViaBBlindEx=rgb(132, 132, 0)
Color4DViaMicroEx=rgb(0, 132, 132)
Color4DRatsEx=rgb(255, 255, 255)
Color4DNoNetPadMarker=rgb(0, 0, 132)
LegacyAutoDeleteOldTrack=1
LegacyUse45DegreeTracks=1
LegacyUseTwoSegmentTracks=1
Use45DegreeGraphicSegments=0
MagneticPads=2
MagneticTracks=1
EditActionChangesTrackWidth=0
DragSelects=1
PcbFramePos_x=72
PcbFramePos_y=27
PcbFrameSize_x=2488
PcbFrameSize_y=1413
PcbFrameMaximized=1
PcbFrameAutoSaveInterval=600
PcbFramePerspective=layout2|name=MainToolbar;caption=;state=139644;dir=1;layer=6;row=0;pos=0;prop=100000;bestw=956;besth=38;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=AuxToolbar;caption=;state=139644;dir=1;layer=4;row=0;pos=0;prop=100000;bestw=995;besth=38;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=MsgPanel;caption=;state=131452;dir=3;layer=6;row=0;pos=0;prop=100000;bestw=400;besth=34;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=OptToolbar;caption=;state=139596;dir=4;layer=3;row=0;pos=0;prop=100000;bestw=36;besth=597;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=MicrowaveToolbar;caption=;state=139598;dir=2;layer=1;row=0;pos=0;prop=100000;bestw=36;besth=187;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=ToolsToolbar;caption=;state=139596;dir=2;layer=2;row=0;pos=0;prop=100000;bestw=36;besth=708;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=LayersManager;caption=Layers Manager;state=1532;dir=2;layer=3;row=0;pos=0;prop=100000;bestw=198;besth=32;minw=198;minh=32;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=198;floath=32|name=DrawFrame;caption=;state=1022;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=387;besth=181;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=DrawFrameGal;caption=;state=1020;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=1112;besth=731;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|dock_size(1,6,0)=38|dock_size(1,4,0)=38|dock_size(3,6,0)=34|dock_size(4,3,0)=36|dock_size(2,2,0)=36|dock_size(2,3,0)=198|dock_size(5,0,0)=389|
PcbFrameMostRecentlyUsedPath=
PcbFrameShowGrid=1
PcbFrameGridColor=rgb(132, 132, 132)
PcbFrame_LastGridSize=2
PcbFrameFirstRunShown=1
PcbFrameDevelMaxUndoItems=0
PcbFrameGalDisplayOptionsOpenGLAntialiasingMode=0
PcbFrameGalDisplayOptionsGridStyle=0
PcbFrameGalDisplayOptionsGridLineWidth=1
PcbFrameGalDisplayOptionsGridMaxDensity=10
PcbFrameGalDisplayOptionsGridAxesEnabled=0
PcbFrameGalDisplayOptionsCursorFullscreen=0
PcbFrameGalDisplayOptionsForceDisplayCursor=0
PcbFramePcbUserGrid_X=0.254
PcbFramePcbUserGrid_Y=0.254
PcbFramePcbUserGrid_Unit=1
PcbFrameDiPadFi=1
PcbFrameDiViaFi=1
PcbFrameDiPadNu=1
PcbFrameDiModEd=1
PcbFrameDiModTx=1
PcbFrameFastGrid1=0
PcbFrameFastGrid2=0
Units=1
DisplayPolarCoords=0
ShowNetNamesMode=3
DisplayTrackFilled=1
TrackDisplayClearance=2
PadFill=1
ViaFill=1
PadAffG=1
PadSNum=1
ModAffC=1
ModAffT=1
PcbAffT=1
PcbShowZonesMode=0
RotationAngle=900
MaxLnkS=3
PlotLineWidth_mm=0.1
ShowMicrowaveTools=0
ShowLayerManagerTools=1
ShowPageLimits=0
canvas_type=1
pcbnew.InteractiveRouter.Mode=1
pcbnew.InteractiveRouter.OptimizerEffort=1
pcbnew.InteractiveRouter.RemoveLoops=1
pcbnew.InteractiveRouter.SmartPads=1
pcbnew.InteractiveRouter.ShoveVias=1
pcbnew.InteractiveRouter.StartDiagonal=0
pcbnew.InteractiveRouter.ShoveTimeLimit=1000
pcbnew.InteractiveRouter.ShoveIterationLimit=250
pcbnew.InteractiveRouter.WalkaroundIterationLimit=50
pcbnew.InteractiveRouter.JumpOverObstacles=0
pcbnew.InteractiveRouter.SmoothDraggedSegments=1
pcbnew.InteractiveRouter.CanViolateDRC=0
pcbnew.InteractiveRouter.SuggestFinish=0
pcbnew.InteractiveRouter.FreeAngleMode=0
pcbnew.InteractiveRouter.InlineDragEnabled=0
MousewheelPAN=0
ZoomNoCenter=0
AutoPAN=1
file1=
file2=
file3=
file4=
file5=
file6=
file7=
file8=
file9=
PcbFrameCursorShape=0
PcbFrameMaxUndoItems=0
ViaHoleDisplayMode=1
ColorPCBLayer_F.Cu=Red 2
ColorPCBLayer_In1.Cu=Yellow 3
ColorPCBLayer_In2.Cu=Magenta 3
ColorPCBLayer_In3.Cu=Red 3
ColorPCBLayer_In4.Cu=Cyan 2
ColorPCBLayer_In5.Cu=Green 2
ColorPCBLayer_In6.Cu=Blue 2
ColorPCBLayer_In7.Cu=Gray 2
ColorPCBLayer_In8.Cu=Magenta 2
ColorPCBLayer_In9.Cu=Gray 3
ColorPCBLayer_In10.Cu=Magenta 2
ColorPCBLayer_In11.Cu=Red 2
ColorPCBLayer_In12.Cu=Brown 2
ColorPCBLayer_In13.Cu=Gray 3
ColorPCBLayer_In14.Cu=Blue 2
ColorPCBLayer_In15.Cu=Green 2
ColorPCBLayer_In16.Cu=Red 2
ColorPCBLayer_In17.Cu=Yellow 3
ColorPCBLayer_In18.Cu=Magenta 3
ColorPCBLayer_In19.Cu=Red 3
ColorPCBLayer_In20.Cu=Cyan 2
ColorPCBLayer_In21.Cu=Green 2
ColorPCBLayer_In22.Cu=Blue 2
ColorPCBLayer_In23.Cu=Gray 2
ColorPCBLayer_In24.Cu=Magenta 2
ColorPCBLayer_In25.Cu=Gray 3
ColorPCBLayer_In26.Cu=Magenta 2
ColorPCBLayer_In27.Cu=Red 2
ColorPCBLayer_In28.Cu=Brown 2
ColorPCBLayer_In29.Cu=Gray 3
ColorPCBLayer_In30.Cu=Blue 2
ColorPCBLayer_B.Cu=Green 2
ColorPCBLayer_B.Adhes=Blue 2
ColorPCBLayer_F.Adhes=Magenta 2
ColorPCBLayer_B.Paste=Cyan 3
ColorPCBLayer_F.Paste=Red 2
ColorPCBLayer_B.SilkS=Magenta 2
ColorPCBLayer_F.SilkS=Cyan 2
ColorPCBLayer_B.Mask=Brown 2
ColorPCBLayer_F.Mask=Magenta 2
ColorPCBLayer_Dwgs.User=Gray 3
ColorPCBLayer_Cmts.User=Blue 2
ColorPCBLayer_Eco1.User=Green 2
ColorPCBLayer_Eco2.User=Yellow 3
ColorPCBLayer_Edge.Cuts=Yellow 3
ColorPCBLayer_Margin=Magenta 3
ColorPCBLayer_B.CrtYd=Yellow 3
ColorPCBLayer_F.CrtYd=Gray 2
ColorPCBLayer_B.Fab=Red 2
ColorPCBLayer_F.Fab=Yellow 3
ColorTxtFrontEx=Gray 3
ColorTxtBackEx=Blue 2
ColorTxtInvisEx=Gray 2
ColorAnchorEx=Blue 2
ColorPadBackEx=Green 2
ColorPadFrontEx=Red 2
ColorViaThruEx=Gray 3
ColorViaBBlindEx=Brown 2
ColorViaMicroEx=Cyan 2
ColorNonPlatedEx=Yellow 3
ColorRatsEx=White
ShowMRa=1
TwoSegT=1
SegmPcb45Only=1
PcbMagPadOpt=1
PcbMagTrackOpt=1
MiddleButtonPAN=1
MiddleBtnPANLimited=0
FootprintWizardPos_x=384
FootprintWizardPos_y=514
FootprintWizardSize_x=2272
FootprintWizardSize_y=1000
FootprintWizardMaximized=0
FootprintWizardPerspective=layout2|name=m_mainToolBar;caption=;state=139632;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=393;besth=36;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=m_parametersPanel;caption=;state=508;dir=4;layer=0;row=0;pos=0;prop=100000;bestw=20;besth=20;minw=360;minh=180;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=m_buildMessageBox;caption=;state=508;dir=4;layer=0;row=0;pos=1;prop=100000;bestw=104;besth=73;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=DrawFrame;caption=;state=256;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=382;besth=63;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=MsgPanel;caption=;state=131388;dir=3;layer=1;row=0;pos=0;prop=100000;bestw=400;besth=68;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|dock_size(1,10,0)=36|dock_size(4,0,0)=513|dock_size(5,0,0)=300|dock_size(3,1,0)=68|
FootprintWizardMostRecentlyUsedPath=
FootprintWizardShowGrid=1
FootprintWizardGridColor=rgb(132, 132, 132)
FootprintWizard_LastGridSize=0
FootprintWizardFirstRunShown=0
FootprintWizardDevelMaxUndoItems=0
FootprintWizardGalDisplayOptionsOpenGLAntialiasingMode=0
FootprintWizardGalDisplayOptionsGridStyle=0
FootprintWizardGalDisplayOptionsGridLineWidth=0.5
FootprintWizardGalDisplayOptionsGridMaxDensity=10
FootprintWizardGalDisplayOptionsGridAxesEnabled=0
FootprintWizardGalDisplayOptionsCursorFullscreen=0
FootprintWizardGalDisplayOptionsForceDisplayCursor=0
Fpwizard_auiPerspective=layout2|name=m_mainToolBar;caption=;state=139632;dir=1;layer=10;row=0;pos=0;prop=100000;bestw=393;besth=36;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=m_parametersPanel;caption=;state=508;dir=4;layer=0;row=0;pos=0;prop=100000;bestw=20;besth=20;minw=360;minh=180;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=m_buildMessageBox;caption=;state=508;dir=4;layer=0;row=0;pos=1;prop=100000;bestw=104;besth=73;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=DrawFrame;caption=;state=256;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=382;besth=63;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=MsgPanel;caption=;state=131388;dir=3;layer=1;row=0;pos=0;prop=100000;bestw=400;besth=68;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|dock_size(1,10,0)=36|dock_size(4,0,0)=513|dock_size(5,0,0)=300|dock_size(3,1,0)=68|
ModEditColor4DPCBLayer_F.Cu=rgb(132, 0, 0)
ModEditColor4DPCBLayer_In1.Cu=rgb(194, 194, 0)
ModEditColor4DPCBLayer_In2.Cu=rgb(194, 0, 194)
ModEditColor4DPCBLayer_In3.Cu=rgb(194, 0, 0)
ModEditColor4DPCBLayer_In4.Cu=rgb(0, 132, 132)
ModEditColor4DPCBLayer_In5.Cu=rgb(0, 132, 0)
ModEditColor4DPCBLayer_In6.Cu=rgb(0, 0, 132)
ModEditColor4DPCBLayer_In7.Cu=rgb(132, 132, 132)
ModEditColor4DPCBLayer_In8.Cu=rgb(132, 0, 132)
ModEditColor4DPCBLayer_In9.Cu=rgb(194, 194, 194)
ModEditColor4DPCBLayer_In10.Cu=rgb(132, 0, 132)
ModEditColor4DPCBLayer_In11.Cu=rgb(132, 0, 0)
ModEditColor4DPCBLayer_In12.Cu=rgb(132, 132, 0)
ModEditColor4DPCBLayer_In13.Cu=rgb(194, 194, 194)
ModEditColor4DPCBLayer_In14.Cu=rgb(0, 0, 132)
ModEditColor4DPCBLayer_In15.Cu=rgb(0, 132, 0)
ModEditColor4DPCBLayer_In16.Cu=rgb(132, 0, 0)
ModEditColor4DPCBLayer_In17.Cu=rgb(194, 194, 0)
ModEditColor4DPCBLayer_In18.Cu=rgb(194, 0, 194)
ModEditColor4DPCBLayer_In19.Cu=rgb(194, 0, 0)
ModEditColor4DPCBLayer_In20.Cu=rgb(0, 132, 132)
ModEditColor4DPCBLayer_In21.Cu=rgb(0, 132, 0)
ModEditColor4DPCBLayer_In22.Cu=rgb(0, 0, 132)
ModEditColor4DPCBLayer_In23.Cu=rgb(132, 132, 132)
ModEditColor4DPCBLayer_In24.Cu=rgb(132, 0, 132)
ModEditColor4DPCBLayer_In25.Cu=rgb(194, 194, 194)
ModEditColor4DPCBLayer_In26.Cu=rgb(132, 0, 132)
ModEditColor4DPCBLayer_In27.Cu=rgb(132, 0, 0)
ModEditColor4DPCBLayer_In28.Cu=rgb(132, 132, 0)
ModEditColor4DPCBLayer_In29.Cu=rgb(194, 194, 194)
ModEditColor4DPCBLayer_In30.Cu=rgb(0, 0, 132)
ModEditColor4DPCBLayer_B.Cu=rgb(0, 132, 0)
ModEditColor4DPCBLayer_B.Adhes=rgb(0, 0, 132)
ModEditColor4DPCBLayer_F.Adhes=rgb(132, 0, 132)
ModEditColor4DPCBLayer_B.Paste=rgb(0, 194, 194)
ModEditColor4DPCBLayer_F.Paste=rgb(132, 0, 0)
ModEditColor4DPCBLayer_B.SilkS=rgb(132, 0, 132)
ModEditColor4DPCBLayer_F.SilkS=rgb(0, 132, 132)
ModEditColor4DPCBLayer_B.Mask=rgb(132, 132, 0)
ModEditColor4DPCBLayer_F.Mask=rgb(132, 0, 132)
ModEditColor4DPCBLayer_Dwgs.User=rgb(194, 194, 194)
ModEditColor4DPCBLayer_Cmts.User=rgb(0, 0, 132)
ModEditColor4DPCBLayer_Eco1.User=rgb(0, 132, 0)
ModEditColor4DPCBLayer_Eco2.User=rgb(194, 194, 0)
ModEditColor4DPCBLayer_Edge.Cuts=rgb(194, 194, 0)
ModEditColor4DPCBLayer_Margin=rgb(194, 0, 194)
ModEditColor4DPCBLayer_B.CrtYd=rgb(132, 132, 132)
ModEditColor4DPCBLayer_F.CrtYd=rgb(194, 194, 194)
ModEditColor4DPCBLayer_B.Fab=rgb(0, 0, 132)
ModEditColor4DPCBLayer_F.Fab=rgb(132, 132, 132)
ModEditColor4DTxtFrontEx=rgb(194, 194, 194)
ModEditColor4DTxtBackEx=rgb(0, 0, 132)
ModEditColor4DTxtInvisEx=rgb(132, 132, 132)
ModEditColor4DPadBackEx=rgb(0, 132, 0)
ModEditColor4DAnchorEx=rgb(0, 0, 132)
ModEditColor4DPadFrontEx=rgb(132, 0, 0)
ModEditColor4DPadThruHoleEx=rgb(194, 194, 0)
ModEditColor4DNonPlatedEx=rgb(194, 194, 0)
ModEditColor4DPCBBackground=rgb(0, 0, 0)
ModEditColor4DPCBCursor=rgb(255, 255, 255)
ModEditColor4DAuxItems=rgb(255, 255, 255)
ModEditColor4DWorksheet=rgb(72, 0, 0)
ModEditColor4DGrid=rgb(132, 132, 132)
ModEditFramePos_x=72
ModEditFramePos_y=27
ModEditFrameSize_x=1848
ModEditFrameSize_y=1053
ModEditFrameMaximized=1
ModEditFramePerspective=layout2|name=MainToolbar;caption=;state=139644;dir=1;layer=6;row=0;pos=0;prop=100000;bestw=982;besth=38;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=MsgPanel;caption=;state=131452;dir=3;layer=6;row=0;pos=0;prop=100000;bestw=400;besth=34;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=OptToolbar;caption=;state=139596;dir=4;layer=3;row=0;pos=0;prop=100000;bestw=36;besth=366;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=Footprints;caption=Libraries;state=2044;dir=4;layer=1;row=0;pos=0;prop=100000;bestw=552;besth=-1;minw=250;minh=400;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=ToolsToolbar;caption=;state=139596;dir=2;layer=1;row=0;pos=0;prop=100000;bestw=36;besth=418;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=LayersManager;caption=Layers Manager;state=1532;dir=2;layer=3;row=0;pos=0;prop=100000;bestw=188;besth=32;minw=188;minh=32;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=188;floath=32|name=DrawFrame;caption=;state=1022;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=387;besth=144;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=DrawFrameGal;caption=;state=1020;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=1112;besth=731;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|dock_size(1,6,0)=38|dock_size(3,6,0)=34|dock_size(4,3,0)=36|dock_size(4,1,0)=554|dock_size(2,1,0)=36|dock_size(2,3,0)=188|dock_size(5,0,0)=554|
ModEditFrameMostRecentlyUsedPath=
ModEditFrameShowGrid=1
ModEditFrameGridColor=rgb(132, 132, 132)
ModEditFrame_LastGridSize=16
ModEditFrameFirstRunShown=0
ModEditFrameDevelMaxUndoItems=0
ModEditFrameGalDisplayOptionsOpenGLAntialiasingMode=0
ModEditFrameGalDisplayOptionsGridStyle=0
ModEditFrameGalDisplayOptionsGridLineWidth=0.5
ModEditFrameGalDisplayOptionsGridMaxDensity=10
ModEditFrameGalDisplayOptionsGridAxesEnabled=1
ModEditFrameGalDisplayOptionsCursorFullscreen=0
ModEditFrameGalDisplayOptionsForceDisplayCursor=0
ModEditFramePcbUserGrid_X=0.254
ModEditFramePcbUserGrid_Y=0.254
ModEditFramePcbUserGrid_Unit=1
ModEditFrameDiPadFi=1
ModEditFrameDiViaFi=1
ModEditFrameDiPadNu=1
ModEditFrameDiModEd=1
ModEditFrameDiModTx=1
ModEditFrameFastGrid1=0
ModEditFrameFastGrid2=0
FpEditorUnits=1
FpEditorDisplayPolarCoords=0
FpEditorPadDisplayMode=1
FpEditorGraphicLinesDisplayMode=1
FpEditorTextsDisplayMode=1
FpEditorTextsRefDefaultText=REF**
FpEditorGrlineWidth=0.15
FpEditorTextsDefaultSizeH=1
FpEditorTextsDefaultSizeV=1
FpEditorTextsDefaultThickness=0.15
FpEditorRefDefaultText=REF**
FpEditorRefDefaultVisibility=1
FpEditorRefDefaultLayer=37
FpEditorValueDefaultText=
FpEditorValueDefaultVisibility=1
FpEditorValueDefaultLayer=49
ModViewFramePos_x=72
ModViewFramePos_y=27
ModViewFrameSize_x=2488
ModViewFrameSize_y=1413
ModViewFrameMaximized=1
ModViewFramePerspective=layout2|name=MainToolbar;caption=;state=139644;dir=1;layer=6;row=0;pos=0;prop=100000;bestw=779;besth=38;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=MsgPanel;caption=;state=131452;dir=3;layer=6;row=0;pos=0;prop=100000;bestw=400;besth=34;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=Libraries;caption=;state=1020;dir=4;layer=2;row=0;pos=0;prop=100000;bestw=200;besth=-1;minw=100;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=Footprints;caption=;state=1020;dir=4;layer=1;row=0;pos=0;prop=100000;bestw=300;besth=-1;minw=100;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=DrawFrame;caption=;state=1022;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=387;besth=144;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=DrawFrameGal;caption=;state=1020;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=1835;besth=1040;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|dock_size(1,6,0)=38|dock_size(3,6,0)=34|dock_size(4,2,0)=202|dock_size(4,1,0)=302|dock_size(5,0,0)=389|
ModViewFrameMostRecentlyUsedPath=
ModViewFrameShowGrid=1
ModViewFrameGridColor=rgb(132, 132, 132)
ModViewFrame_LastGridSize=1
ModViewFrameFirstRunShown=0
ModViewFrameDevelMaxUndoItems=0
ModViewFrameGalDisplayOptionsOpenGLAntialiasingMode=0
ModViewFrameGalDisplayOptionsGridStyle=0
ModViewFrameGalDisplayOptionsGridLineWidth=0.5
ModViewFrameGalDisplayOptionsGridMaxDensity=10
ModViewFrameGalDisplayOptionsGridAxesEnabled=1
ModViewFrameGalDisplayOptionsCursorFullscreen=0
ModViewFrameGalDisplayOptionsForceDisplayCursor=0
import_last_path=
FpWizardListWidth=588
FpWizardListHeight=482
Viewer3DFrameNamePos_x=72
Viewer3DFrameNamePos_y=27
Viewer3DFrameNameSize_x=2488
Viewer3DFrameNameSize_y=1413
Viewer3DFrameNameMaximized=1
Viewer3DFrameNamePerspective=layout2|name=MainToolbar;caption=;state=139644;dir=1;layer=6;row=0;pos=0;prop=100000;bestw=735;besth=36;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|name=DrawFrame;caption=;state=1020;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=20;besth=20;minw=-1;minh=-1;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|dock_size(1,6,0)=36|dock_size(5,0,0)=22|
Viewer3DFrameNameMostRecentlyUsedPath=
BgColor_Red=0.4
BgColor_Green=0.4
BgColor_Blue=0.5
BgColor_Red_Top=0.8
BgColor_Green_Top=0.8
BgColor_Blue_Top=0.9
SMaskColor_Red=0.0784314
SMaskColor_Green=0.2
SMaskColor_Blue=0.141176
SPasteColor_Red=0.501961
SPasteColor_Green=0.501961
SPasteColor_Blue=0.501961
SilkColor_Red=0.9
SilkColor_Green=0.9
SilkColor_Blue=0.9
CopperColor_Red=0.7
CopperColor_Green=0.612157
CopperColor_Blue=0
BoardBodyColor_Red=0.2
BoardBodyColor_Green=0.168627
BoardBodyColor_Blue=0.0862745
ShowRealisticMode=1
MousewheelPAN3D=0
RenderEngine=0
Render_Material=0
Render_OGL_ShowCopperThickness=1
Render_OGL_ShowModelBoudingBoxes=0
Render_RAY_Shadows=1
Render_RAY_Backfloor=1
Render_RAY_Refractions=1
Render_RAY_Reflections=1
Render_RAY_PostProcess=1
Render_RAY_AntiAliasing=1
Render_RAY_ProceduralTextures=1
ShowAxis=1
ShowGrid3D=0
ShowFootprints_Normal=1
ShowFootprints_Insert=1
ShowFootprints_Virtual=1
ShowZones=1
ShowAdhesiveLayers=1
ShowSilkScreenLayers=1
ShowSolderMasLayers=1
ShowSolderPasteLayers=1
ShowCommentsLayers=1
ShowEcoLayers=1
ShowBoardBody=1
SilentMode=0
NetlistDeleteSinglePadNets=1
NetlistReportFilterMsg=15
TestMissingCourtyard=0
TestFootprintCourtyard=0
RefillZonesBeforeDrc=0
Zone_Ouline_Hatch_Opt=2
Zone_Filter_Opt=Net-*
Zone_Clearance=20
Zone_Thickness=10
Zone_TH_Gap=20
Zone_TH_Copper_Width=39.37007874015748
PcbIconScale=-1
PrintModuleScale=3
PrintMonochrome=1
ModEditColor4DPCBLayer_Rescue=rgb(132, 0, 0)
ActionPluginButtons=
ModEditFrameUnits=1
FpEditorSilkLineWidth=0.15
FpEditorSilkTextSizeH=1
FpEditorSilkTextSizeV=1
FpEditorSilkTextThickness=0.15
FpEditorSilkTextItalic=0
FpEditorCopperLineWidth=0.12
FpEditorCopperTextSizeV=1.5
FpEditorCopperTextSizeH=1.5
FpEditorCopperTextThickness=0.3
FpEditorCopperTextItalic=0
FpEditorEdgeCutLineWidth=0.12
FpEditorCourtyardLineWidth=0.12
FpEditorOthersLineWidth=0.12
FpEditorOthersTextSizeV=1
FpEditorOthersTextSizeH=1
FpEditorOthersTextSizeThickness=0.15
FpEditorOthersTextItalic=0
ModeditLibWidth=552
NetlistUpdateFootprints=1
NetlistDeleteShortingTracks=0
NetlistDeleteExtraFootprints=1
FootprintTextShownColumns=0 1 2 3 4 5 6
DrcTrackToZoneTest=0
ModViewFrameUnits=1
ModViewFrameModalPcbUserGrid_X=0.254
ModViewFrameModalPcbUserGrid_Y=0.254
ModViewFrameModalPcbUserGrid_Unit=1
ModViewFrameModalDiPadFi=1
ModViewFrameModalDiViaFi=1
ModViewFrameModalDiPadNu=1
ModViewFrameModalDiModEd=1
ModViewFrameModalDiModTx=1
ModViewFrameModalFastGrid1=0
ModViewFrameModalFastGrid2=0
ModViewFrameAutoZoom=1
ModViewFrameZoom=17.1134
Color4DPCBLayer_Rescue=rgb(132, 0, 0)
MagneticGraphics=1
PcbFrameUnits=1
PrintScale=1
PrintPageFrame=1
PlotLayer_0=1
PlotLayer_1=0
PlotLayer_2=0
PlotLayer_3=0
PlotLayer_4=0
PlotLayer_5=0
PlotLayer_6=0
PlotLayer_7=0
PlotLayer_8=0
PlotLayer_9=0
PlotLayer_10=0
PlotLayer_11=0
PlotLayer_12=0
PlotLayer_13=0
PlotLayer_14=0
PlotLayer_15=0
PlotLayer_16=0
PlotLayer_17=0
PlotLayer_18=0
PlotLayer_19=0
PlotLayer_20=0
PlotLayer_21=0
PlotLayer_22=0
PlotLayer_23=0
PlotLayer_24=0
PlotLayer_25=0
PlotLayer_26=0
PlotLayer_27=0
PlotLayer_28=0
PlotLayer_29=0
PlotLayer_30=0
PlotLayer_31=1
PlotLayer_32=0
PlotLayer_33=0
PlotLayer_34=1
PlotLayer_35=1
PlotLayer_36=1
PlotLayer_37=1
PlotLayer_38=1
PlotLayer_39=1
PlotLayer_40=0
PlotLayer_41=0
PlotLayer_42=0
PlotLayer_43=0
PlotLayer_44=1
PlotLayer_45=0
PlotLayer_46=0
PlotLayer_47=0
PlotLayer_48=0
PlotLayer_49=0
PlotLayer_50=0
PrintPadsDrillOpt=2
PrintSinglePage=0
PlotXFineScaleAdj=1
PlotYFineScaleAdj=1
CheckZonesBeforePlotting=1
PSPlotFineWidthAdj=0
DrillZerosFormat=0
DrillMirrorYOpt=0
DrillMergePTHNPTH=0
DrillMinHeader=0
DrillUnit=1
DrillMapFileType=1
DrillFileType=0
OvalHolesRouteMode=1
PlaceFileUnits=1
PlaceFileOpts=0
PlaceFileFormat=1
LibFootprintTextShownColumns=0 1 2 3 4 5 6

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policymap [
<!ELEMENT policymap (policy)+>
<!ATTLIST policymap xmlns CDATA #FIXED ''>
<!ELEMENT policy EMPTY>
<!ATTLIST policy xmlns CDATA #FIXED '' domain NMTOKEN #REQUIRED
name NMTOKEN #IMPLIED pattern CDATA #IMPLIED rights NMTOKEN #IMPLIED
stealth NMTOKEN #IMPLIED value CDATA #IMPLIED>
]>
<!--
Configure ImageMagick policies.
Domains include system, delegate, coder, filter, path, or resource.
Rights include none, read, write, execute and all. Use | to combine them,
for example: "read | write" to permit read from, or write to, a path.
Use a glob expression as a pattern.
Suppose we do not want users to process MPEG video images:
<policy domain="delegate" rights="none" pattern="mpeg:decode" />
Here we do not want users reading images from HTTP:
<policy domain="coder" rights="none" pattern="HTTP" />
The /repository file system is restricted to read only. We use a glob
expression to match all paths that start with /repository:
<policy domain="path" rights="read" pattern="/repository/*" />
Lets prevent users from executing any image filters:
<policy domain="filter" rights="none" pattern="*" />
Any large image is cached to disk rather than memory:
<policy domain="resource" name="area" value="1GP"/>
Define arguments for the memory, map, area, width, height and disk resources
with SI prefixes (.e.g 100MB). In addition, resource policies are maximums
for each instance of ImageMagick (e.g. policy memory limit 1GB, -limit 2GB
exceeds policy maximum so memory limit is 1GB).
Rules are processed in order. Here we want to restrict ImageMagick to only
read or write a small subset of proven web-safe image types:
<policy domain="delegate" rights="none" pattern="*" />
<policy domain="filter" rights="none" pattern="*" />
<policy domain="coder" rights="none" pattern="*" />
<policy domain="coder" rights="read|write" pattern="{GIF,JPEG,PNG,WEBP}" />
-->
<policymap>
<!-- <policy domain="system" name="shred" value="2"/> -->
<!-- <policy domain="system" name="precision" value="6"/> -->
<!-- <policy domain="system" name="memory-map" value="anonymous"/> -->
<!-- <policy domain="system" name="max-memory-request" value="256MiB"/> -->
<!-- <policy domain="resource" name="temporary-path" value="/tmp"/> -->
<policy domain="resource" name="memory" value="256MiB"/>
<policy domain="resource" name="map" value="512MiB"/>
<policy domain="resource" name="width" value="16KP"/>
<policy domain="resource" name="height" value="16KP"/>
<!-- <policy domain="resource" name="list-length" value="128"/> -->
<policy domain="resource" name="area" value="128MB"/>
<policy domain="resource" name="disk" value="1GiB"/>
<!-- <policy domain="resource" name="file" value="768"/> -->
<!-- <policy domain="resource" name="thread" value="4"/> -->
<!-- <policy domain="resource" name="throttle" value="0"/> -->
<!-- <policy domain="resource" name="time" value="3600"/> -->
<!-- <policy domain="coder" rights="none" pattern="MVG" /> -->
<!-- <policy domain="module" rights="none" pattern="{PS,PDF,XPS}" /> -->
<!-- <policy domain="delegate" rights="none" pattern="HTTPS" /> -->
<!-- <policy domain="path" rights="none" pattern="@*" /> -->
<!-- <policy domain="cache" name="memory-map" value="anonymous"/> -->
<!-- <policy domain="cache" name="synchronize" value="True"/> -->
<!-- <policy domain="cache" name="shared-secret" value="passphrase" stealth="true"/> -->
<!-- <policy domain="system" name="pixel-cache-memory" value="anonymous"/> -->
<!-- <policy domain="system" name="shred" value="2"/> -->
<!-- <policy domain="system" name="precision" value="6"/> -->
<!-- not needed due to the need to use explicitly by mvg: -->
<!-- <policy domain="delegate" rights="none" pattern="MVG" /> -->
<!-- use curl -->
<policy domain="delegate" rights="none" pattern="URL" />
<policy domain="delegate" rights="none" pattern="HTTPS" />
<policy domain="delegate" rights="none" pattern="HTTP" />
<!-- in order to avoid to get image with password text -->
<policy domain="path" rights="none" pattern="@*"/>
<!-- disable ghostscript format types -->
<policy domain="coder" rights="none" pattern="PS" />
<policy domain="coder" rights="none" pattern="PS2" />
<policy domain="coder" rights="none" pattern="PS3" />
<policy domain="coder" rights="none" pattern="EPS" />
<policy domain="coder" rights="read|write" pattern="PDF" />
<policy domain="coder" rights="none" pattern="XPS" />
</policymap>

View File

@ -0,0 +1,28 @@
#!/bin/bash
set -e
if [[ -z "${GITHUB_WORKFLOW}" ]]; then
>&2 echo "Aborting! This script is meant to be run in CI (Github Actions) only. It may modify/damage your system configuration if run outside of CI."
exit 1
fi
set -v
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
sudo add-apt-repository --yes ppa:kicad/kicad-5.1-releases
sudo apt-get update -qq
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y kicad inkscape poppler-utils xdotool recordmydesktop python3-dev python3-pip
# KiKit is installed via pinned git revision to get "new" CLI with support for V-Cuts layer specification. TODO: switch to release version once landed.
sudo python3 -m pip install psutil kikit==1.0.2 xvfbwrapper
mkdir -p ~/.config/kicad
cp /usr/share/kicad/template/fp-lib-table ~/.config/kicad/
cp /usr/share/kicad/template/sym-lib-table ~/.config/kicad/
cp "$DIR/config/eeschema" ~/.config/kicad/
cp "$DIR/config/pcbnew" ~/.config/kicad/
# Install ImageMagick policy that allows PDF conversion (safe in CI because we control all inputs/outputs)
sudo cp "$DIR/config/policy.xml" /etc/ImageMagick-6/policy.xml

192
electronics/scripts/export_3d.py Executable file
View File

@ -0,0 +1,192 @@
#!/usr/bin/env python3
# Copyright 2015-2021 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import logging
import os
import psutil
import sys
import time
electronics_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
repo_root = os.path.dirname(electronics_root)
sys.path.append(repo_root)
from util import file_util
from export_util import (
patch_config,
PopenContext,
versioned_file,
xdotool,
wait_for_window,
recorded_xvfb,
)
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
RENDER_TIMEOUT = 10 * 60
def _wait_for_pcbnew_idle():
start = time.time()
while time.time() < start + RENDER_TIMEOUT:
for proc in psutil.process_iter():
if proc.name() == 'pcbnew':
cpu = proc.cpu_percent(interval=1)
print(f'CPU={cpu}', flush=True)
if cpu < 5:
print('Render took %d seconds' % (time.time() - start))
return
time.sleep(1)
raise RuntimeError('Timeout waiting for pcbnew to go idle')
def _zoom_in():
xdotool([
'click',
'4',
])
time.sleep(0.2)
def _invoke_view_option(index):
command = ['key', 'alt+v'] + ['Down']*index + ['Return']
xdotool(command)
time.sleep(2)
_transforms = {
'z+': ('Zoom in', _zoom_in),
'rx+': ('Rotate X Clockwise', lambda: _invoke_view_option(4)),
'rx-': ('Rotate X Counterclockwise', lambda: _invoke_view_option(5)),
'ry+': ('Rotate Y Clockwise', lambda: _invoke_view_option(6)),
'ry-': ('Rotate Y Counterclockwise', lambda: _invoke_view_option(7)),
'rz+': ('Rotate Z Clockwise', lambda: _invoke_view_option(8)),
'rz-': ('Rotate Z Counterclockwise', lambda: _invoke_view_option(9)),
'ml': ('Move left', lambda: _invoke_view_option(10)),
'mr': ('Move right', lambda: _invoke_view_option(11)),
'mu': ('Move up', lambda: _invoke_view_option(12)),
'md': ('Move down', lambda: _invoke_view_option(13)),
}
def _pcbnew_export_3d(output_file, width, height, transforms):
if os.path.exists(output_file):
os.remove(output_file)
wait_for_window('pcbnew', 'Pcbnew ', additional_commands=['windowfocus'])
time.sleep(1)
logger.info('Open 3d viewer')
xdotool(['key', 'alt+3'])
wait_for_window('3D Viewer', '3D Viewer', additional_commands=['windowfocus'])
time.sleep(3)
# Maximize window
xdotool(['search', '--name', '3D Viewer', 'windowmove', '0', '0'])
xdotool(['search', '--name', '3D Viewer', 'windowsize', str(width), str(height)])
time.sleep(3)
for transform in transforms:
description, func = _transforms[transform]
logger.info(description)
func()
logger.info('Wait for rendering...')
_wait_for_pcbnew_idle()
time.sleep(5)
logger.info('Export current view')
xdotool([
'key',
'alt+f',
'Return',
])
logger.info('Enter build output filename')
xdotool([
'key',
'ctrl+a',
])
xdotool(['type', output_file])
logger.info('Save')
xdotool(['key', 'Return'])
logger.info('Wait before shutdown')
time.sleep(2)
def export_3d(filename, suffix, width, height, transforms, raytrace, virtual, color_soldermask, color_silk):
pcb_file = os.path.abspath(filename)
output_dir = os.path.join(electronics_root, 'build')
file_util.mkdir_p(output_dir)
screencast_output_file = os.path.join(output_dir, 'export_3d_screencast.ogv')
name, _ = os.path.splitext(os.path.basename(pcb_file))
if suffix:
name = name + '-' + suffix
output_file = os.path.join(output_dir, f'{name}-3d.png')
settings = {
'canvas_type': '1',
'SMaskColor_Red': str(color_soldermask[0]),
'SMaskColor_Green': str(color_soldermask[1]),
'SMaskColor_Blue': str(color_soldermask[2]),
'SilkColor_Red': str(color_silk[0]),
'SilkColor_Green': str(color_silk[1]),
'SilkColor_Blue': str(color_silk[2]),
'RenderEngine': '1' if raytrace else '0',
'ShowFootprints_Virtual': '1' if virtual else '0',
'Render_RAY_ProceduralTextures': '0',
}
with patch_config(os.path.expanduser('~/.config/kicad/pcbnew'), settings):
with versioned_file(pcb_file):
with recorded_xvfb(screencast_output_file, width=width, height=height, colordepth=24):
with PopenContext(['pcbnew', pcb_file], close_fds=True) as pcbnew_proc:
_pcbnew_export_3d(output_file, width, height, transforms)
pcbnew_proc.terminate()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('pcb')
parser.add_argument('--suffix', default='')
parser.add_argument('--width', type=int, default=2560)
parser.add_argument('--height', type=int, default=1440)
parser.add_argument('--skip-raytrace', action='store_true')
parser.add_argument('--skip-virtual', action='store_true', help='Don\'t render virtual footprints')
parser.add_argument('--color-soldermask', type=float, nargs=3, help='Soldermask color as 3 floats from 0-1', default=[0.1, 0.1, 0.1])
parser.add_argument('--color-silk', type=float, nargs=3, help='Silkscreen color as 3 floats from 0-1', default=[0.9, 0.9, 0.9])
# Use subparsers to for an optional nargs="*" choices argument (workaround for https://bugs.python.org/issue9625)
subparsers = parser.add_subparsers(dest='which')
transform_parser = subparsers.add_parser('transform', help='Apply one or more transforms before capturing image')
transform_parser.add_argument('transform', nargs='+', choices=list(_transforms.keys()))
args = parser.parse_args()
transforms = args.transform if args.which == 'transform' else []
export_3d(args.pcb, args.suffix, args.width, args.height, transforms, not args.skip_raytrace, not args.skip_virtual, args.color_soldermask, args.color_silk)

View File

@ -0,0 +1,70 @@
#!/usr/bin/env python3
# Copyright 2021 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import logging
import os
import subprocess
import sys
electronics_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
repo_root = os.path.dirname(electronics_root)
sys.path.append(repo_root)
from util import file_util
from export_util import (
versioned_file,
)
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
def export_jlcpcb(pcb, schematic, alt_fields):
pcb_file = os.path.abspath(pcb)
output_dir = os.path.join(electronics_root, 'build', os.path.splitext(os.path.basename(pcb_file))[0] + '-jlc')
file_util.mkdir_p(output_dir)
with versioned_file(pcb_file):
command = [
'kikit',
'fab',
'jlcpcb',
]
if schematic is not None:
schematic_file = os.path.abspath(schematic)
command += [
'--assembly',
'--schematic',
schematic_file,
'--field',
]
command.append(','.join(alt_fields + ['LCSC']))
command += [
pcb_file,
output_dir,
]
subprocess.check_call(command)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('pcb')
parser.add_argument('--assembly-schematic')
parser.add_argument('--alt-fields', nargs='+')
args = parser.parse_args()
export_jlcpcb(args.pcb, args.assembly_schematic, args.alt_fields)

View File

@ -0,0 +1,138 @@
#!/usr/bin/env python
# Copyright 2015-2016 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import os
import re
import subprocess
import sys
import tempfile
import time
from contextlib import contextmanager
electronics_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
repo_root = os.path.dirname(electronics_root)
sys.path.append(repo_root)
from xvfbwrapper import Xvfb
from util import file_util, rev_info
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
class PopenContext(subprocess.Popen):
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
if self.stdout:
self.stdout.close()
if self.stderr:
self.stderr.close()
if self.stdin:
self.stdin.close()
if type:
self.terminate()
# Wait for the process to terminate, to avoid zombies.
self.wait()
def xdotool(command):
return subprocess.check_output(['xdotool'] + command)
def wait_for_window(name, window_regex, additional_commands=None, timeout=10):
if additional_commands is not None:
commands = additional_commands
else:
commands = []
DELAY = 0.5
logger.info('Waiting for %s window...', name)
for i in range(int(timeout/DELAY)):
try:
xdotool(['search', '--name', window_regex] + commands)
logger.info('Found %s window', name)
return
except subprocess.CalledProcessError:
pass
time.sleep(DELAY)
raise RuntimeError('Timed out waiting for %s window' % name)
@contextmanager
def recorded_xvfb(video_filename, **xvfb_args):
with Xvfb(**xvfb_args):
with PopenContext([
'recordmydesktop',
'--no-sound',
'--no-frame',
'--on-the-fly-encoding',
'-o', video_filename], close_fds=True) as screencast_proc:
yield
screencast_proc.terminate()
def get_versioned_contents(filename):
with open(filename, 'r') as f:
original_contents = f.read()
date = rev_info.git_date()
date_long = rev_info.git_date(short=False)
rev = rev_info.git_short_rev()
logger.info('Replacing placeholders with %s and %s' % (date, rev))
return original_contents, original_contents \
.replace('Date ""', 'Date "%s"' % date_long) \
.replace('DATE: YYYY-MM-DD TIME TZ', 'DATE: %s' % date_long) \
.replace('DATE: YYYY-MM-DD', 'DATE: %s' % date) \
.replace('Rev ""', 'Rev "%s"' % rev) \
.replace('COMMIT: deadbeef', 'COMMIT: %s' % rev)
@contextmanager
def versioned_file(filename):
original_contents, versioned_contents = get_versioned_contents(filename)
with open(filename, 'w') as temp_schematic:
logger.debug('Writing to %s', filename)
temp_schematic.write(versioned_contents)
try:
yield
finally:
with open(filename, 'w') as temp_schematic:
logger.debug('Restoring %s', filename)
temp_schematic.write(original_contents)
@contextmanager
def patch_config(filename, replacements):
if not os.path.exists(filename):
yield
return
with open(filename, 'r') as f:
original_contents = f.read()
new_contents = original_contents
for (key, value) in replacements.items():
pattern = '^' + re.escape(key) + '=(.*)$'
new_contents = re.sub(pattern, key + '=' + value, new_contents, flags=re.MULTILINE)
with open(filename, 'w') as f:
logger.debug('Writing to %s', filename)
f.write(new_contents)
try:
yield
finally:
with open(filename, 'w') as f:
logger.debug('Restoring %s', filename)
f.write(original_contents)

View File

@ -0,0 +1,80 @@
#!/usr/bin/env python3
# Copyright 2015-2021 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import logging
import os
import pcbnew
import shutil
import subprocess
from collections import namedtuple
import pcb_util
electronics_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
def run(pcb_file):
output_directory = os.path.join(electronics_root, 'build')
temp_dir = os.path.join(output_directory, 'temp_pdfs')
shutil.rmtree(temp_dir, ignore_errors=True)
try:
os.makedirs(temp_dir)
plot_to_directory(pcb_file, output_directory, temp_dir)
finally:
shutil.rmtree(temp_dir, ignore_errors=True)
def plot_to_directory(pcb_file, output_directory, temp_dir):
board_name = os.path.splitext(os.path.basename(pcb_file))[0]
with pcb_util.get_plotter(pcb_file, temp_dir) as plotter:
plotter.plot_options.SetDrillMarksType(pcbnew.PCB_PLOT_PARAMS.NO_DRILL_SHAPE)
plotter.plot_options.SetExcludeEdgeLayer(False)
LayerDef = namedtuple('LayerDef', ['layer', 'mirror'])
layers = [
LayerDef(pcbnew.F_Cu, False),
LayerDef(pcbnew.B_Cu, True),
LayerDef(pcbnew.F_SilkS, False),
LayerDef(pcbnew.B_SilkS, True),
LayerDef(pcbnew.F_Mask, False),
LayerDef(pcbnew.B_Mask, True),
LayerDef(pcbnew.F_Paste, False),
]
pdfs = []
for layer in layers:
plotter.plot_options.SetMirror(layer.mirror)
output_filename = plotter.plot(layer.layer, pcbnew.PLOT_FORMAT_PDF)
pdfs.append(output_filename)
_, map_file = plotter.plot_drill()
pdfs.append(map_file)
output_pdf_filename = os.path.join(output_directory, '%s-pcb-packet.pdf' % (board_name,))
command = ['pdfunite'] + pdfs + [output_pdf_filename]
subprocess.check_call(command)
if __name__ == '__main__':
parser = argparse.ArgumentParser('Generate a pdf of the PCB')
parser.add_argument('pcb_file')
args = parser.parse_args()
run(args.pcb_file)

View File

@ -0,0 +1,112 @@
#!/usr/bin/env python3
# Copyright 2015-2021 Scott Bezek and the splitflap contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import logging
import os
import pcbnew
import tempfile
from contextlib import contextmanager
from export_util import (
get_versioned_contents
)
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
@contextmanager
def versioned_board(filename):
_, versioned_contents = get_versioned_contents(filename)
with tempfile.NamedTemporaryFile(suffix='.kicad_pcb', mode='w') as temp_pcb:
logger.debug('Writing to %s', temp_pcb.name)
temp_pcb.write(versioned_contents)
temp_pcb.flush()
logger.debug('Load board')
board = pcbnew.LoadBoard(temp_pcb.name)
yield board
@contextmanager
def get_plotter(pcb_filename, build_directory):
with versioned_board(pcb_filename) as board:
yield Plotter(board, build_directory)
class Plotter(object):
def __init__(self, board, build_directory):
self.board = board
self.build_directory = build_directory
self.plot_controller = pcbnew.PLOT_CONTROLLER(board)
self.plot_options = self.plot_controller.GetPlotOptions()
self.plot_options.SetOutputDirectory(build_directory)
self.plot_options.SetPlotFrameRef(False)
self.plot_options.SetLineWidth(pcbnew.FromMM(0.35))
self.plot_options.SetScale(1)
self.plot_options.SetUseAuxOrigin(True)
self.plot_options.SetMirror(False)
self.plot_options.SetExcludeEdgeLayer(True)
def plot(self, layer, plot_format):
layer_name = self.board.GetLayerName(layer)
logger.info('Plotting layer %s (kicad layer=%r)', layer_name, layer)
self.plot_controller.SetLayer(layer)
self.plot_controller.OpenPlotfile(layer_name, plot_format , 'Plot')
output_filename = self.plot_controller.GetPlotFileName()
self.plot_controller.PlotLayer()
self.plot_controller.ClosePlot()
return output_filename
def plot_drill(self):
board_name = os.path.splitext(os.path.basename(self.board.GetFileName()))[0]
logger.info('Plotting drill file')
drill_writer = pcbnew.EXCELLON_WRITER(self.board)
drill_writer.SetMapFileFormat(pcbnew.PLOT_FORMAT_PDF)
mirror = False
minimalHeader = False
offset = pcbnew.wxPoint(0, 0)
merge_npth = True
drill_writer.SetOptions(mirror, minimalHeader, offset, merge_npth)
metric_format = True
drill_writer.SetFormat(metric_format)
generate_drill = True
generate_map = True
drill_writer.CreateDrillandMapFilesSet(self.build_directory, generate_drill, generate_map)
drill_file_name = os.path.join(
self.build_directory,
'%s.drl' % (board_name,)
)
map_file_name = os.path.join(
self.build_directory,
'%s-drl_map.pdf' % (board_name,)
)
return drill_file_name, map_file_name
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Test pcb util')
parser.add_argument('input_file', help='Input .kicad_pcb file')
args = parser.parse_args()
with versioned_board(args.input_file) as board:
logger.info('Loaded %s', board.GetFileName())
for module in board.GetModules():
logger.info('Module %s: %s', module.GetReference(), module.GetValue())

View File

@ -8,6 +8,32 @@ preflight:
update_xml: true update_xml: true
outputs: outputs:
# - name: pdf
# type: pdf_pcb_print
# dir: pdf
# options:
# separated: true
# layers:
# - 'F.Cu'
# - 'B.Cu'
# - 'F.Paste'
# - 'B.Paste'
# - 'F.SilkS'
# - 'B.SilkS'
# - 'Edge.Cuts'
# - name: gerber
# type: gerber
# dir: gerber
# layers:
# - 'F.Cu'
# - 'B.Cu'
# - 'F.Paste'
# - 'B.Paste'
# - 'F.SilkS'
# - 'B.SilkS'
# - 'Edge.Cuts'
- name: ibom - name: ibom
comment: 'Interactive BOM' comment: 'Interactive BOM'
type: ibom type: ibom
@ -19,24 +45,24 @@ outputs:
- name: schematic - name: schematic
type: pdf_sch_print type: pdf_sch_print
- name: 3d # - name: 3d
type: render_3d # type: render_3d
options: # options:
no_virtual: true # no_virtual: true
view: 'top' # view: 'top'
silk: '#111111' # silk: '#111111'
solder_mask: '#ffffff' # solder_mask: '#ffffff'
board: '#C3BA9B' # board: '#C3BA9B'
copper: '#99967F' # copper: '#99967F'
zoom: 4 # zoom: 4
orthographic: true # orthographic: true
ray_tracing: true # ray_tracing: true
height: 1920 # height: 1920
width: 1920 # width: 1920
- name: 3d_back # - name: 3d_back
type: render_3d # type: render_3d
extends: 3d # extends: 3d
options: # options:
view: 'bottom' # view: 'bottom'
zoom: 3 # zoom: 3

File diff suppressed because it is too large Load Diff

View File

@ -19,24 +19,24 @@ outputs:
- name: schematic - name: schematic
type: pdf_sch_print type: pdf_sch_print
- name: 3d # - name: 3d
type: render_3d # type: render_3d
options: # options:
no_virtual: true # no_virtual: true
view: 'top' # view: 'top'
silk: '#111111' # silk: '#111111'
solder_mask: '#ffffff' # solder_mask: '#ffffff'
board: '#C3BA9B' # board: '#C3BA9B'
copper: '#99967F' # copper: '#99967F'
orthographic: true # orthographic: true
ray_tracing: true # ray_tracing: true
height: 1920 # height: 1920
width: 1920 # width: 1920
zoom: 3 # zoom: 4
- name: 3d_back # - name: 3d_back
type: render_3d # type: render_3d
extends: 3d # extends: 3d
options: # options:
view: 'bottom' # view: 'bottom'
zoom: 3 # zoom: 3

File diff suppressed because it is too large Load Diff

BIN
scripts/RobotoMono-Bold.ttf Normal file

Binary file not shown.

16
scripts/annotate_image.sh Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
set -e
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
FONT="$DIR/RobotoMono-Bold.ttf"
# Make sure font exists
ls "$FONT"
set -v
LABEL="`date --rfc-3339=seconds`\n`git rev-parse --short HEAD`"
convert -background black -fill white -pointsize 12 -font "$FONT" -size 300x36 label:"$LABEL" -bordercolor black -border 3 $1 +swap -append $1

0
util/__init__.py Normal file
View File

14
util/file_util.py Normal file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env python
import errno
import os
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
raise

32
util/rev_info.py Normal file
View File

@ -0,0 +1,32 @@
import datetime
import subprocess
def git_short_rev():
try:
return subprocess.check_output([
'git',
'rev-parse',
'--short',
'HEAD',
]).decode('utf-8').strip()
except Exception:
raise RuntimeError("Could not read git revision. Make sure you have git installed and you're working with a git clone of the repository.")
def current_date():
return datetime.date.today().strftime('%Y-%m-%d')
def git_date(short=True):
try:
iso = subprocess.check_output([
'git',
'log',
'-1',
'--format=%ci',
'HEAD',
]).decode('utf-8').strip()
if short:
return iso.split(' ')[0]
else:
return iso
except Exception:
raise RuntimeError("Could not read git commit date. Make sure you have git installed and you're working with a git clone of the repository.")