示例#1
0
def add_zone(net_name, layer_name, rect, zones):
    layer = pcb.GetLayerID(layer_name)
    zone, poly = kad.add_zone(rect, layer, len(zones), net_name)
    zone.SetZoneClearance(pcbnew.FromMils(20))
    zone.SetMinThickness(pcbnew.FromMils(16))
    #zone.SetThermalReliefGap( pcbnew.FromMils( 12 ) )
    #zone.SetThermalReliefCopperBridge( pcbnew.FromMils( 24 ) )
    zone.Hatch()
    #
    zones.append(zone)
示例#2
0
    def Run(self):
        SCALE = 1000000.0

        msg = "Loading Board\n"
        board = pcbnew.GetBoard()

        gndnet = find_net("GND", board)
        set_all_pads_to_net(gndnet, board)

        pcboutline = find_pcb_outline_bbox(board)

        pcboutline.Inflate(-1 * pcbnew.FromMils(10))
        leftside = pcboutline.GetLeft()
        rightside = pcboutline.GetRight()
        bottomside = pcboutline.GetBottom()
        topside = pcboutline.GetTop()

        print "Creating copper pour on GND: top={} bottom={} left={} right={}".format(
            topside / SCALE, bottomside / SCALE, leftside / SCALE,
            rightside / SCALE)

        zone_container = board.InsertArea(gndnet.GetNet(), 0, pcbnew.B_Cu,
                                          leftside, topside,
                                          pcbnew.ZONE_CONTAINER.DIAGONAL_EDGE)
        shape_poly_set = zone_container.Outline()
        shape_poly_set.Append(leftside, bottomside)
        shape_poly_set.Append(rightside, bottomside)
        shape_poly_set.Append(rightside, topside)
        zone_container.SetPadConnection(pcbnew.PAD_ZONE_CONN_FULL)
        zone_container.Hatch()

        msg += "\n"
        msg += "You may need to refresh the display now. Select Legacy mode, then Modern mode" + "\n"
示例#3
0
    def AddParam(self, section, param, unit, default, hint=''):
        """
        Add a parameter with some properties.

        TODO: Hints are not supported, as there is as yet nowhere to
        put them in the KiCAD interface
        """
        error = ""
        val = None
        if unit == self.uMM:
            val = pcbnew.FromMM(default)
        elif unit == self.uMils:
            val = pcbnew.FromMils(default)
        elif unit == self.uNatural:
            val = default
        elif unit == self.uString:
            val = str(default)
        elif unit == self.uBool:
            val = "True" if default else "False"  # ugly stringing
        else:
            error = "Warning: Unknown unit type: %s" % unit
            return error

        if unit in [self.uNatural, self.uBool, self.uString]:
            param = "*%s" % param  # star prefix for natural

        if section not in self.parameters:
            self.parameters[section] = {}

        self.parameters[section][param] = val

        return error
    def Run( self ):
        SCALE = 1000000.0

        msg="Loading Board\n"
        board = pcbnew.GetBoard()

        gndnet = find_net("GND", board)
        if gndnet == None:
            return
            #TODO: create a new net

        set_all_pads_to_net(gndnet, board)

        pcboutline = find_pcb_outline_bbox(board)

        pcboutline.Inflate(-1 * pcbnew.FromMils(10))
        leftside = pcboutline.GetLeft()
        rightside = pcboutline.GetRight()
        bottomside = pcboutline.GetBottom()
        topside = pcboutline.GetTop()

        msg+="Creating copper pour on GND: top={} bottom={} left={} right={}".format(topside/SCALE, bottomside/SCALE, leftside/SCALE, rightside/SCALE)
#brd.AddArea(aNewZonesList: 'PICKED_ITEMS_LIST *', aNetcode: 'int', aLayer: 'PCB_LAYER_ID', aStartPointPosition: 'wxPoint', aHatch: 'ZONE_BORDER_DISPLAY_STYLE') -> 'ZONE *)

        zone_container = board.AddArea(None, gndnet.GetNetCode(), pcbnew.B_Cu, pcbnew.wxPoint(leftside, topside), pcbnew.ZONE_FILL_MODE_POLYGONS)
        shape_poly_set = zone_container.Outline()
        shape_poly_set.Append(leftside, bottomside)
        shape_poly_set.Append(rightside, bottomside)
        shape_poly_set.Append(rightside, topside)
        zone_container.SetPadConnection(pcbnew.ZONE_CONNECTION_FULL)
        #zone_container.Hatch()

        msg+="\n"
        msg+="You may need to refresh the display now. Select Legacy mode, then Modern mode" + "\n"
示例#5
0
 def PutOnGridMils(self, value, gridSizeMil=2):
     """
     Round the value (in KiCAD internal units 1nm) according to the 
     provided gridSize in mil.
     """
     thresh = pcbnew.FromMils(gridSizeMil)
     res = round(value / thresh) * thresh
     return res
示例#6
0
def set_trace_widths(board, target_widths):

    board.BuildListOfNets()  # required so 'board' contains valid netclass data
    # build list with copper layer names
    copper_layer_count = board.GetCopperLayerCount()
    layer_names = [
        board.GetLayerName(layer_id)
        for layer_id in range(board.GetCopperLayerCount() - 1)
    ] + [board.GetLayerName(31)]

    # check the target widths structure
    for nc, width_map in target_widths.items():
        # TODO check for valid net class (API only available in kicad 5)
        # nc = board.GetAllNetClasses()
        for layer_name in width_map.keys():
            if layer_name != 'Default' and layer_name not in layer_names:
                raise Exception('Invalid layer name: %s' % layer_name)

    # initialize counters for changes
    count = collections.OrderedDict()
    for layer_name in layer_names:
        count.setdefault(layer_name, 0)

    for track in board.GetTracks():
        for nc, width_map in target_widths.items():
            default_width = width_map['Default']
            if type(track) == pcbnew.TRACK and track.GetNet().GetClassName(
            ) == nc:
                layer_name = track.GetLayerName()
                if layer_name in width_map:
                    track.SetWidth(pcbnew.FromMils(width_map[layer_name]))
                else:
                    if default_width <= 0:
                        pos = track.GetPosition()
                        x = pcbnew.ToMM(pos.x)
                        y = pcbnew.ToMM(pos.y)
                        raise Exception(
                            'Found track on net %s on unexpected layer: %s at position %.2fx%.2f mm'
                            % (track.GetNetname(), layer_name, x, y))
                    else:
                        track.SetWidth(pcbnew.FromMils(default_width))
                count[layer_name] += 1

    return count
示例#7
0
文件: kad.py 项目: orihikarna/Hermit
def add_line_rawunit(a, b, layer='Edge.Cuts', width=2):
    if a[0] == b[0] and a[1] == b[1]:
        print("add_line_rawunit: identical", a)
        return None
    line = pcbnew.DRAWSEGMENT()
    line.SetStart(a)
    line.SetEnd(b)
    line.SetLayer(pcb.GetLayerID(layer))
    line.SetWidth(pcbnew.FromMils(width))
    pcb.Add(line)
    return line
示例#8
0
文件: kad.py 项目: orihikarna/Hermit
def scalar_to_unit(v, mm_or_mils):
    if mm_or_mils:
        return pcbnew.FromMM(v)
    else:
        return pcbnew.FromMils(v)
示例#9
0
import os.path
import sys
import time


CPU_REF = 'U7'      # CPU reference designator

# four SDRAM memory chips:
DDR_LF  = 'U15'     # left front DRAM
DDR_RF  = 'U17'     # right front DRAM
DDR_LB  = 'U16'     # left back DRAM
DDR_RB  = 'U18'     # right back DRAM


# Length of SDRAM clock, it sets the maximum or equal needed for other traces
CLOCK_LEN = pcbnew.FromMils( 2.25 * 1000 )

def addr_line_netname(line_no):
    """From an address line number, return the netname"""
    netname = '/DDR3/DRAM_A' + str(line_no)
    return netname

# Establish GOALS which are LENs, TOLERANCEs and NETs for each group of nets.

# Net Group: ADDR_AND_CMD
ADDR_AND_CMD_LEN = pcbnew.FromMils( 2.22 * 1000 )
ADDR_AND_CMD_TOLERANCE = pcbnew.FromMils( 25 ) / 2
ADDR_AND_CMD_NETS = [addr_line_netname(a) for a in range(0,16)]
ADDR_AND_CMD_NETS += [
    '/DDR3/DRAM_SDBA0',
    '/DDR3/DRAM_SDBA1',
示例#10
0
def FromInch(a):
    if isinstance(a, pcbnew.wxPoint):
        return pcbnew.wxPoint(pcbnew.FromMils(a.x * 1000.0),
                              pcbnew.FromMils(a.y * 1000.0))
    else:
        return pcbnew.FromMils(a * 1000.0)
示例#11
0
def generate_gerbers(args):
    """ Generates Gerber output files from a Kicad PCB design. Uses the
    arguments constructed elsewhere in this script. """

    pcb_file = sanitize(args.pcb_file)
    output_dir = sanitize(args.output_dir)

    board = pcbnew.LoadBoard(pcb_file)
    plotter = pcbnew.PLOT_CONTROLLER(board)
    options = plotter.GetPlotOptions()

    options.SetPlotFrameRef(False)
    options.SetPlotPadsOnSilkLayer(False)
    options.SetPlotValue(True)
    options.SetPlotReference(True)

    options.SetPlotInvisibleText(False)
    options.SetPlotViaOnMaskLayer(False)
    options.SetPlotPadsOnSilkLayer(False)
    options.SetExcludeEdgeLayer(True)

    options.SetUseAuxOrigin(True)

    options.SetUseGerberProtelExtensions(False)
    options.SetUseGerberAttributes(False)
    options.SetSubtractMaskFromSilk(True)

    options.SetLineWidth(pcbnew.FromMils(4))
    options.SetGerberPrecision(6)

    # At the time of this writing, KiCAD appears to have some kind of bug
    # that prevents SetOutputDirectory() from accepting absolute paths.
    # As a result, this script creates a relative path to args.tempdir,
    # and uses that instead.

    path_components = []
    result = os.path.split(args.pcb_file)

    while result[1] != "":
        path_components.append(result[1])
        result = os.path.split(result[0])

    rel_root = (os.path.pardir + os.path.sep) * (len(path_components) - 1)
    rel_root = rel_root[:-1 * len(os.path.sep)]
    tempdir = rel_root + args.tempdir
    options.SetOutputDirectory(tempdir)

    plot_plan = [
        ("CuTop", pcbnew.F_Cu, "Top layer"),
        ("CuBottom", pcbnew.B_Cu, "Bottom layer"),
        ("PasteBottom", pcbnew.B_Paste, "Paste Bottom"),
        ("PasteTop", pcbnew.F_Paste, "Paste top"),
        ("SilkTop", pcbnew.F_SilkS, "Silk top"),
        ("SilkBottom", pcbnew.B_SilkS, "Silk top"),
        ("MaskBottom", pcbnew.B_Mask, "Mask bottom"),
        ("MaskTop", pcbnew.F_Mask, "Mask top"),
        ("EdgeCuts", pcbnew.Edge_Cuts, "Edges"),
        ("FabTop", pcbnew.F_Fab, "Fab drawing top"),
        ("FabBottom", pcbnew.B_Fab, "Fab drawing bottom"),
    ]

    for layer_info in plot_plan:
        plotter.SetLayer(layer_info[1])
        plotter.OpenPlotfile(layer_info[0], pcbnew.PLOT_FORMAT_GERBER,
                             layer_info[2])
        plotter.PlotLayer()

    if not os.path.isdir(output_dir):
        try:
            os.mkdir(output_dir)
        except OSError:
            err_msg = "Error: Couldn't make output directory [%s]" % output_dir
            sys.stderr.write(err_msg + "\n")
            return 1

    for filename in os.listdir(args.tempdir):
        filename = os.path.join(args.tempdir, filename)
        if os.path.getsize(filename) != 0:
            shutil.copy(filename, output_dir)

    return 0
示例#12
0
        if (d.GetLayerName() != "Edge.Cuts"):
            continue
        if (boundingbox == None):
            boundingbox = d.GetBoundingBox()
        else:
            boundingbox.Merge(d.GetBoundingBox())
    boundingbox.Inflate(-150000)  #assume a 0.15mm line width
    return boundingbox


gndnet = find_net("GND")
set_all_pads_to_net(gndnet)

pcboutline = find_pcb_outline_bbox()

pcboutline.Inflate(-1 * pcbnew.FromMils(10))
leftside = pcboutline.GetLeft()
rightside = pcboutline.GetRight()
bottomside = pcboutline.GetBottom()
topside = pcboutline.GetTop()

print "Creating copper pour on GND: top={} bottom={} left={} right={}".format(
    topside / SCALE, bottomside / SCALE, leftside / SCALE, rightside / SCALE)

zone_container = board.InsertArea(gndnet.GetNet(), 0, pcbnew.B_Cu, leftside,
                                  topside, pcbnew.CPolyLine.DIAGONAL_EDGE)
shape_poly_set = zone_container.Outline()
shape_poly_set.Append(leftside, bottomside)
shape_poly_set.Append(rightside, bottomside)
shape_poly_set.Append(rightside, topside)
zone_container.SetPadConnection(pcbnew.PAD_ZONE_CONN_FULL)
示例#13
0
            raise IOError('Too many matches')
        return matches[0]
    return filename


parser = argparse.ArgumentParser(description="""\
Modify kicad pcb file to place footprints like in the schematic.
Your project needs to have a .kicad-pcb file with loaded netlist and footprint
associations.""")
parser.add_argument('project', nargs='?', help='Kicad project')
parser.add_argument('--schematic', help='Input .sch file')
parser.add_argument('--pcb', help='Input .kicad_pcb file')
parser.add_argument(
    '--scale',
    type=float,
    default=pcbnew.FromMils(1),
    help='Distance scale factor from schematic to PCB (default 1)')
args = parser.parse_args()

if args.schematic and args.pcb:
    schfile = args.schematic
    pcbfile = args.pcb
else:
    if os.path.isfile(args.project):
        # If the user passed the .pro file, extract the directory
        directory = os.path.dirname(args.project)
        project_name = os.path.basename(args.project)
        project_name = project_name[:project_name.rindex('.')]
    else:
        directory = args.project
        try:
示例#14
0
def main():
    kad.removeDrawings()
    kad.removeTracksAndVias()

    #PCB_Rect = map( lambda pt: (PCB_Width * pt[0], PCB_Height * pt[1]), FourCorners )
    #kad.drawRect( PCB_Rect, 'Edge.Cuts', R = 40 )
    drawEdgeCuts()

    zones = []
    add_zone( 'GND', 'F.Cu', make_rect( (PCB_Width - 100, PCB_Height), (100, 0) ), zones )
    add_zone( 'GND', 'B.Cu', make_rect( (PCB_Width - 500, PCB_Height), (500, 0)), zones )
    add_zone( 'VCC', 'B.Cu', make_rect( (300, 100), (110, 110)), zones )

    kad.add_text( (440, 405), 0, '   STM32\n   F042K6\n     tiny\n   orihikarna\n200904', 'F.SilkS', (0.9, 0.9), 6,
        pcbnew.GR_TEXT_HJUSTIFY_LEFT, pcbnew.GR_TEXT_VJUSTIFY_CENTER )

    ###
    ### Set mod positios
    ###
    kad.move_mods( (300, 350), 0, [
        # Pin headers
        ('J1', (-250, +300), 90),
        ('J2', (-250, -300), 90),
        ('J3', (-250, -200),  0),
        # USB connector
        (None, (550, -5), 0, [
            ('J4', (0, 0), -90),
            (None, (-40, -25), 0, [
                ('R3', (0, -75), 0),# BOOT0 pull-down
                ('R8', (0,   0), 0),
                ('R9', (0, +75), 0),
            ] ),
        ] ),
        # BOOT0 (SW1)
        ('SW1', (525, -275), -90),
        # D1/D2
        (None, (575, 230), 0, [
            (None, (0,  0), 180, [ ('D1', (0, 0), 0), ('R1', (0, 0), 0) ] ),
            (None, (0, 75), 180, [ ('D2', (0, 0), 0), ('R2', (0, 0), 0) ] ),
        ] ),
        # mcu
        (None, (15, 0), 0, [
            ('U1', (0, 0), -90),
            # pass caps
            (None, ( 235, -155), -90, [ ('C3', (0, 0), 0), ('C4', (0, 0), 0) ] ),
            (None, (-235,  155), +90, [ ('C5', (0, 0), 0), ('C6', (0, 0), 0) ] ),
            # Vcca pass caps
            (None, (  90, -115), 0, [ ('C7', (0, 0), 0), ('C8', (0, -75), 0) ] ),
        ] ),
        # D3
        ('D3', (-280, 150), 0),
        # NRST
        ('C9', (-100, -125), 180),
        # USB DM/DP
        (None, (164, 112.5), 0, [ ('R6', (0, 0), 180), ('R7', (-10, 75), 180) ] ),
        # I2C pull-up's
        (None, (-80, 155), 0, [ ('R4', (0, 0), 90), ('R5', (75, 0), 90) ] ),
        # regulators
        (None, (-40, 0), 0, [
            ('U2', (   0, 6), 90),
            ('C1', ( 104, 0), 90),
            ('C2', (-104, 0), 90),
            ('L1', ( 215, +37.5),   0),
            ('F1', ( 225, -37.5), 180),
        ] ),
    ] )

    ###
    ### Wire mods
    ###
    # regulator
    via_reggnd1 = kad.add_via_relative( 'U2', '3', (-15, -48), VIA_Size[1] )
    via_reggnd2 = kad.add_via_relative( 'U2', '3', (-15, +48), VIA_Size[1] )
    kad.wire_mods( [
        ('F1', '2', 'L1', '2', 24, (ZgZg, 0, 70, -20)),
        ('C1', '1', 'L1', '1', 24, (Strt)),
        ('U2', '1', 'C1', '1', 24, (Dird, 90, 0)),# 5V
        ('U2', '3', 'C1', '2', 24, (Dird, 90, 0)),# Gnd
        ('U2', '3', 'C2', '2', 24, (Dird, 90, 0)),# Gnd
        ('U2', '2', 'C2', '1', 24, (Dird, 90, 0)),# Vcc
        # Gnd
        (None, via_reggnd1, 'U2', '3', 24, (Dird, 0, 90)),
        (None, via_reggnd2, 'U2', '3', 24, (Dird, 0, 90)),
        (None, via_reggnd1, 'U2', '3', 24, (Dird, ([(35, 180)], 90), ([(50, 180)], 0), -32)),
        (None, via_reggnd2, 'U2', '3', 24, (Dird, ([(35, 180)], 90), ([(50, 180)], 0), -32)),
    ] )
    # mcu
    via_vcca = kad.add_via_relative( 'U1', '5', (-65, 10), VIA_Size[0] )
    kad.wire_mods( [
        # mcu <--> Vcc x 2
        ('U1', '1', 'C3', '1', 24, (ZgZg, 90, 45, -20)),# Vcc
        ('U1','32', 'C3', '2', 24, (Dird, 90, 0)),# Gnd
        ('U1','17', 'C5', '1', 24, (ZgZg, 90, 45, -20)),# Vcc
        ('U1','16', 'C5', '2', 24, (Dird, 90, 0)),# Gnd
        # Vcc <--> Vcc
        ('U1', '1', 'U1', '17', 20, (Dird,
            ([(33,   0), (80, -45), (80, -90)], 135),
            ([(33, 180), (80, 135)], 180), -16)),# Vcc
        # Vcca
        ('U1', '5', None, via_vcca, 24, (Dird, 0, 90)),
        ('C8', '1', None, via_vcca, 24, (Dird, 0, 90)),
        ('C7', '1', 'C8', '1', 24, (Strt)),
        ('C7', '2', 'C8', '2', 24, (Strt)),
        # Gnd
        ('C4', '2', 'C7', '2', 24, (Dird, 0, 0)),
        ('U1', via_reggnd1, 'U1', '32', 20, (Dird, 90, ([(33, -90)], 135), -16)),
        ('U1', via_reggnd2, 'U1', '16', 20, (Dird,  0, ([(33, +90)], -45), -16)),
    ] )
    via_pads = [
        ('C3', '1'), ('C3', '2'),
        ('C5', '1'), ('C5', '2'),
    ]
    for mod_name, pad_name in via_pads:
        kad.add_via_on_pad( mod_name, pad_name, VIA_Size[1] )

    ## pin headers [(80, 45), (56, 90)] is a basic shape
    # J1
    kad.wire_mods( [
        # left B.Cu
        ('R4', '1', 'J1', '2', 20, (Dird, 0, -45, -16)),# PA9
        ('R5', '1', 'J1', '3', 20, (Dird, 0, -45, -16)),# PA10
        # left F.Cu
        ('U1','19', 'J1', '2', 20, (Dird, 0, ([(80, -45), (40, -90)], -45), -16)),# PA9
        ('U1','20', 'J1', '3', 20, (Dird, 0, -45, -16)),# PA10
        ('U1','23', 'J1', '4', 20, (Dird, 0, -45, -16)),# PA13
        ('U1','24', 'J1', '5', 20, (Dird, 0, +45, -16)),# PA14
        # right (separation = 30 mils, 30 * sin(22.5) = 11.48, (30 - 11.5) x 1.414 ~ 26.2)
        ('U1','26', 'J1', '6', 20, (Dird, ([(40,      90), (12,      45)], 0), 45, -16)),# PB3
        ('U1','27', 'J1', '7', 20, (Dird, ([(40+11.5, 90), (12+26.2, 45)], 0), 45, -16)),# PB4
        ('U1','28', 'J1', '8', 20, (Dird, ([(40+23.0, 90), (12+52.4, 45)], 0), ([(80, 45), (56, 90)], 45), -16)),# PB5
    ] )
    # J2
    kad.wire_mods( [
        ('U1', '9', 'J2', '1', 20, (Dird, 0, +45, -16)),# PA3
        ('U1', '8', 'J2', '2', 20, (Dird, 0, +45, -16)),# PA2
        (None, via_vcca, 'J2', '3', 24, (Dird, 45, 0), 'F.Cu'),
        (None, via_vcca, 'J2', '3', 24, (Dird, 90, 0), 'B.Cu'),
        ('U1', '4', 'J2', '4', 20, (ZgZg, 0, 25, -16)),# NRST
        ('U1', '3', 'J2', '5', 20, (Dird, 0, -45, -16)),# PF1
        ('U1', '2', 'J2', '6', 20, (Dird, 0, ([(80, 135), (56, 90)], -45), -16)),# PF0
    ] )
    # J3
    kad.wire_mods( [
        ('U1', '13', 'J3', '3', 20, (Dird, -90, -45, -16)),# PA7
        ('U1', '12', 'J3', '2', 20, (Dird, -90, ([(27, -90)], -45), -16)),# PA6
        ('U1', '11', 'J3', '1', 20, (Dird, ([(33, -90)], 45), ([(25, -90), (75, -45)], -90), -16)),# PA5
    ] )
    # J4 (USB)
    via_cc1 = kad.add_via_relative( 'J4', 'A5', (+22, +70), VIA_Size[2] )
    via_cc2 = kad.add_via_relative( 'J4', 'B5', (-22, +70), VIA_Size[2] )
    kad.wire_mods( [
        # VBUS A4 <--> B4
        # ('J4', 'A4', 'J4', 'B4', 12.4, (Strt2,
        #     [(-3.1, 0), (15, 90), (24,  60), (40, 90), (40,  45)],
        #     [(+3.1, 0), (15, 90), (24, 120), (40, 90), (40, 135)], 5)),
        ('J4', 'A4', 'J4', 'B4', 20, (Strt2,
            [(-1.75, 0), (30, -90), (20,  -62), (65, -90)],
            [(+1.75, 0), (30, -90), (20, -118), (65, -90)], -32)),
        # Vcc <--> D1
        ('D1', '2', 'J4', 'B4', 20, (Dird,
            ([(100, 45)], 90),
            ([(+1.75, 0), (30, -90), (20, -118)], -90), -32)),
        # Gnd Shield
        ('J4', 'A1', 'J4', 'S1', 23.5, (Dird, 0, +45)),
        ('J4', 'B1', 'J4', 'S2', 23.5, (Dird, 0, -45)),
        ('J4', 'A1', 'J4', 'S1', 23.5, (Dird, ([(55, 90)], 0), 90, -20)),
        ('J4', 'B1', 'J4', 'S2', 23.5, (Dird, ([(55, 90)], 0), 90, -20)),
        ('J4', 'S1', 'J4', 'S4', 24, (Strt), 'B.Cu'),
        ('J4', 'S2', 'J4', 'S3', 24, (Strt), 'F.Cu'),
        ('C3', '2',  'J4', 'S1', 24, (Dird, 90, 45, -20), 'F.Cu'),
        ('J2', '7',  'J4', 'S1', 24, (Dird, 0, -45, -20), 'B.Cu'),
        ('R3', '2',  'J4', 'S4', 24, (Dird, 0, 0), 'F.Cu'),
        ('R9', '2',  'J4', 'S3', 24, (Dird, 0, 0), 'F.Cu'),
        ('R1', '2',  'J4', 'S3', 24, (Dird, 90, 90), 'F.Cu'),
        # DM/DP
        ('J4', 'A7', 'J4', 'B7', 11.72, (Strt2, [(50, +90)], [(50, +90)], -16)),# DM
        ('J4', 'A7', 'J4', 'B7', 11.72, (Strt2, [(50+30, +90), (-30, +90)], [(50, +90)], -16)),# DM
        ('J4', 'A6', 'J4', 'B6', 11.72, (Strt2, [(50, -90)], [(50, -90)], -16)),# DP
        # DM/DP <--> R6, R7
        ('J4', 'A7', 'R6', '1', 11.72, (Dird, -90, ([(26, -90), (40, 180), (45, 90), (28, 135)], 90), -16)),# DM
        ('J4', 'B6', 'R7', '1', 11.72, (Dird, -90, ([(26, +90), (72, 180), (55, 90), (28, 135)], 90), -16)),# DP
        # Gnd: CC1/CC2/BOOT0
        ('R8', '2', 'R9', '2', 20, (Strt)),
        ('R8', '2', 'R3', '2', 20, (Strt)),
        # CC1/CC2
        (None, via_cc1, 'J4', 'A5', 11.72, (Dird, -90, ([(50, -90)], +135), -20)),
        (None, via_cc1, 'R8',  '1', 16, (Dird, 0, 90, 25)),
        (None, via_cc2, 'J4', 'B5', 11.72, (Dird, -90, ([(50, -90)], -135), -20)),
        (None, via_cc2, 'R9',  '1', 16, (Dird, 0, 90, 25)),
    ] )
    # Vcc rounting
    kad.wire_mods( [
        # C4 <--> C8
        ('C4', '1', 'C8', '1', 24, (Dird, ([(46, 180)], 90), 90, -20)),
        # C2 <--> C6
        ('C2', '1', 'C6', '1', 24, (Dird, ([(25, -90)], 0), -90, -20)),
        # C2 <--> C4
        ('C4', '1', 'C2', '1', 24, (Dird, ([(46, 180)], 90), ([(50, -90)], 0), -20)),# Vcc
    ] )
    # 5V routing
    kad.wire_mods( [
        # regulator <--> I2C
        ('R5', '2', 'U2', '1', 24, (Dird, 0, 90)),
        # I2C pull-up's
        ('R4', '2', 'R5', '2', 24, (Strt)),
        # I2C <--> J1
        ('R4', '2', 'J1', '1', 24, (Dird, ([(50, -90)], 180), ([(80, -45), (40, -90)], -45), -20), 'B.Cu'),# 5V
    ] )
    # USB DM/DP <--> mcu
    via_dm = kad.add_via_relative( 'U1', '21', (-55, -26), VIA_Size[2] )
    via_dp = kad.add_via_relative( 'U1', '22', ( 55,   4), VIA_Size[2] )
    kad.wire_mods( [
        (None, via_dm, 'U1', '21', 20, (Dird, 90, 0, -16)),
        (None, via_dp, 'U1', '22', 20, (Dird, 90, 0, -16)),
        (None, via_dm, 'R6',  '2', 20, (Dird, 0, 90, -16)),
        (None, via_dp, 'R7',  '2', 20, (Dird, 90, 0, -16)),
    ] )
    # connections
    kad.wire_mods( [
        # VBUS: J4 <--> Regulator/F1
        ('J4', 'A4', 'F1', '1', 23.5, (Dird, ([(55, 90)], 0), ([(45, 180)], -45), -20)),
        # Gnd: J4/A1 <--> mcu/C4
        ('J4', 'A1', 'C4', '2', 23.5, (Dird, 90, -45, -20)),
        # Gnd: Regulator/C1 <--> Vdda/C7
        ('C1', '2', 'C7', '2', 24, (ZgZg, 90, 55, -20)),
        # BOOT0: mcu/PB8 <--> R3
        ('U1', '31', 'R3', '1', 16, (Dird, ([(36, 90), (14, 45), (50, 90), (24, 135)], 90), 90, -12)),
    ] )
    # NRST
    via_nrst = kad.add_via_relative( 'U1', '4', (55, 30), VIA_Size[2] )
    kad.wire_mods( [
        ('U1', '4', None, via_nrst, 16, (Dird, 0, 90, -12)),
        ('C9', '1', None, via_nrst, 16, (Dird, 90, 0)),
        # Gnd: C9 <--> Regulator/C2
        ('C9', '2', 'C2', '2', 16, (Dird, 0, 0)),
    ] )
    # BOOT0
    via_sw1   = kad.add_via_relative( 'SW1', '1', (50, -40), VIA_Size[2] )
    via_boot0 = kad.add_via_relative( 'SW1', '2', (0, -125), VIA_Size[2] )
    kad.wire_mods( [
        (None, via_boot0, 'SW1', '2', 16, (Dird, 90, 0, -12)),
        (None, via_boot0, 'R3',  '1', 16, (ZgZg, 90, 45, -12)),
        (None, via_sw1, 'SW1', '1', 16, (Dird, 90, 0, -12)),# Vcc
        (None, via_sw1, 'C3',  '1', 16, (ZgZg, 90, 60, -12)),# Vcc
    ] )
    # D1/D2
    via_pads = [ ('R1', '1'), ('R2', '1') ]
    for mod_name, pad_name in via_pads:
        kad.add_via_on_pad( mod_name, pad_name, VIA_Size[1] )
    # D2
    via_d2 = kad.add_via_relative( 'U1', '25', (135, 0), VIA_Size[1] )
    kad.wire_mods( [
        ('R1', '2', 'R2', '2', 20, (Strt)),
        (None, via_d2, 'U1', '25', 20, (Dird, 90, 0)),# PA15
        (None, via_d2, 'D2', '2', 20, (Dird, 0, 45, -16)),
    ] )
    # D3
    via_d3 = kad.add_via_relative( 'D3', '3', (40, 62), VIA_Size[2] )
    kad.wire_mods( [
        ('D3', '4', 'J1', '1', 20, (Dird, ([(10, 0)], 90), 45, -16), 'B.Cu'),# 5V
        ('D3', '2', 'C5', '2', 20, (Dird, ([(10, 0)], 90), 90), 'F.Cu'),# Gnd
        (None, via_d3, 'D3',  '3', 16, (Dird, 0, ([(10, 0)], 90), -16)),
        (None, via_d3, 'U1', '15', 16, (ZgZg, 90, 45, -16)),
    ] )


    ###
    ### Ref
    ###
    refs = [
        ('C1', 90, +40, 90),
        ('C2', 90, -40, 90),
        ('C3', -60, 70, 180),
        ('C4',  60,120, 180),
        ('C5', -60, 70, 180),
        ('C6',  60,-60, 180),
        ('C7',  90,  0,  90),
        ('C8',  90,  0,  90),
        ('C9', -90,  0, -90),
        ('R1', -15, 0, 0),
        ('R2', -15, 0, 0),
        ('R4',  60,120, 180),
        ('R5', -60, 60, 180),
        ('R6', -100, 0, -90),
        ('R7', -100, 0, -90),
        ('L1',  100, 0,  90),
        ('F1', -100, 0, -90),
        ('R3', 100, 0, 90),
        ('R8', 100, 0, 90),
        ('R9', 100, 0, 90),
        ('U1', 120, 135, 0),
        ('U2', 0, 0, -90),
        ('D1', 15, 0, 0),
        ('D2', 15, 0, 0),
        ('D3', 50, 90, -90),
        ('SW1', 0, 0, -90),
        ('J1', 0, 0, None),
        ('J2', 0, 0, None),
        ('J3', 0, 0, None),
    ]
    for mod_name, offset_length, offset_angle, text_angle in refs:
        mod = kad.get_mod( mod_name )
        pos, angle = kad.get_mod_pos_angle( mod_name )
        ref = mod.Reference()
        if text_angle == None:
            ref.SetVisible( False )
        else:
            ref.SetTextSize( pcbnew.wxSizeMM( 0.9, 0.9 ) )
            ref.SetThickness( pcbnew.FromMils( 7 ) )
            pos_ref = vec2.scale( offset_length, vec2.rotate( - (offset_angle + angle) ), pos )
            ref.SetPosition( pnt.mils2unit( vec2.round( pos_ref ) ) )
            ref.SetTextAngle( text_angle * 10 )
            ref.SetKeepUpright( False )

    refs = [ ('J3', (0, 90), (90, 0), [
            ('1', 'A5', 'SCK'),
            ('2', 'A6', 'MISO'),
            ('3', 'A7', 'MOSI'),
        ] ), ('J2', (90, 0), (0, 90), [
            ('1', 'A3', 'RX'),
            ('2', 'A2', 'TX'),
            ('3', 'nRST', 'nRST'),
            ('4', 'F1', 'F1'),
            ('5', 'F0', 'F0'),
            ('6', 'GND', 'GND'),
            ('7', '3V3', '3V3'),
        ] ), ('J1', (-90, 180), (0, -90), [
            ('1', '5V', '5V'),
            ('2', 'A9',  'SCL'),
            ('3', 'A10', 'SDA'),
            ('4', 'A13', 'DIO'),
            ('5', 'A14', 'CLK'),
            ('6', 'B3', 'SCK'),
            ('7', 'B4', 'MISO'),
            ('8', 'B5', 'MOSI'),
        ] ),
    ]
    for mod, angles1, angles2, pads in refs:
        for pad, text1, text2 in pads:
            pos = kad.get_pad_pos( mod, pad )
            angle_pos1, angle_text1 = angles1
            angle_pos2, angle_text2 = angles2
            for idx, layer in enumerate( ['F.SilkS', 'B.SilkS'] ):
                pos1 = vec2.scale( [65, 65][idx], vec2.rotate( angle_pos1 ), pos )
                pos2 = vec2.scale( 50, vec2.rotate( angle_pos2 ), pos )
                kad.add_text( pos1, angle_text1, text1, layer, (0.7, 0.7), 6,
                    pcbnew.GR_TEXT_HJUSTIFY_CENTER, pcbnew.GR_TEXT_VJUSTIFY_CENTER  )
                kad.add_text( pos2, angle_text2, text2, layer, (0.7, 0.7), 6,
                    pcbnew.GR_TEXT_HJUSTIFY_CENTER, pcbnew.GR_TEXT_VJUSTIFY_CENTER  )

    pos, angle = kad.get_mod_pos_angle( 'SW1' )
    kad.add_text( pos, angle + 90, 'BOOT0', 'F.SilkS', (0.85, 0.65), 6,
        pcbnew.GR_TEXT_HJUSTIFY_CENTER, pcbnew.GR_TEXT_VJUSTIFY_CENTER )

    pcbnew.Refresh()