554 lines
29 KiB
Python
554 lines
29 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generate KiCad 7 schematic for Baby Mobile Audio Board v2.
|
|
|
|
Design: MDBT50Q (nRF52840) + IS25LP128F SPI Flash + PAM8302A Class-D Amp
|
|
Power: 2xAA → TPS61220 boost → 3.3V, USB-C via MCP1700 LDO, diode OR-ing
|
|
Audio: 30+ min at 8kHz/8-bit mono via 16MB SPI flash
|
|
USB: Mass storage for drag-and-drop audio upload
|
|
BLE: Wireless audio transfer from phone
|
|
|
|
FIX: Sub-symbol names (the _0_1, _1_1 variants) must NOT include the
|
|
library prefix. Only the outer symbol declaration gets "baby_mobile:".
|
|
"""
|
|
|
|
import uuid
|
|
import json
|
|
import os
|
|
|
|
LIB = "baby_mobile"
|
|
|
|
def uid():
|
|
return str(uuid.uuid4())
|
|
|
|
|
|
# ============================================================
|
|
# SYMBOL HELPERS
|
|
# ============================================================
|
|
|
|
def _sym_start(name, ref_prefix, default_value, footprint, extra_props=""):
|
|
"""Outer symbol with library prefix."""
|
|
return f"""
|
|
(symbol "{LIB}:{name}" (in_bom yes) (on_board yes)
|
|
(property "Reference" "{ref_prefix}" (at 0 1 0) (effects (font (size 1.27 1.27))))
|
|
(property "Value" "{default_value}" (at 0 -1 0) (effects (font (size 1.27 1.27))))
|
|
(property "Footprint" "{footprint}" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
|
|
(property "Datasheet" "" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
|
|
{extra_props}"""
|
|
|
|
|
|
def _unit(name, unit_id):
|
|
"""Inner sub-symbol WITHOUT library prefix — this was the bug."""
|
|
return f""" (symbol "{name}_{unit_id}" """
|
|
|
|
|
|
# ============================================================
|
|
# SYMBOL DEFINITIONS
|
|
# ============================================================
|
|
|
|
def sym_mdbt50q():
|
|
"""MDBT50Q-P nRF52840 BLE module, 32 pins exposed."""
|
|
s = _sym_start("MDBT50Q", "U", "MDBT50Q-P1MV2",
|
|
"RF_Module:Raytac_MDBT50Q",
|
|
"")
|
|
s += f"""
|
|
(symbol "MDBT50Q_0_1"
|
|
(rectangle (start -20 30) (end 20 -30) (stroke (width 0.254) (type default)) (fill (type background)))
|
|
)
|
|
(symbol "MDBT50Q_1_1"
|
|
(pin power_in line (at -5 -33 90) (length 3) (name "GND" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
|
|
(pin power_in line (at -5 33 270) (length 3) (name "VCC" (effects (font (size 1 1)))) (number "32" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 20 0) (length 3) (name "P0.00/XL1" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 17.5 0) (length 3) (name "P0.01/XL2" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 15 0) (length 3) (name "P0.02/AIN0" (effects (font (size 1 1)))) (number "4" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 12.5 0) (length 3) (name "P0.03/AIN1" (effects (font (size 1 1)))) (number "5" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 10 0) (length 3) (name "P0.04/AIN2" (effects (font (size 1 1)))) (number "6" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 7.5 0) (length 3) (name "P0.05/AIN3" (effects (font (size 1 1)))) (number "7" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 5 0) (length 3) (name "P0.06" (effects (font (size 1 1)))) (number "8" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 2.5 0) (length 3) (name "P0.07" (effects (font (size 1 1)))) (number "9" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 0 0) (length 3) (name "P0.08" (effects (font (size 1 1)))) (number "10" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 -2.5 0) (length 3) (name "P0.09/NFC1" (effects (font (size 1 1)))) (number "11" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 -5 0) (length 3) (name "P0.10/NFC2" (effects (font (size 1 1)))) (number "12" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 -7.5 0) (length 3) (name "P0.11" (effects (font (size 1 1)))) (number "13" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 -10 0) (length 3) (name "P0.12" (effects (font (size 1 1)))) (number "14" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 -12.5 0) (length 3) (name "P0.13" (effects (font (size 1 1)))) (number "15" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 -15 0) (length 3) (name "P0.14" (effects (font (size 1 1)))) (number "16" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -23 -17.5 0) (length 3) (name "P0.15" (effects (font (size 1 1)))) (number "17" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 20 180) (length 3) (name "P0.16" (effects (font (size 1 1)))) (number "18" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 17.5 180) (length 3) (name "P0.17" (effects (font (size 1 1)))) (number "19" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 15 180) (length 3) (name "P0.18/~{{RESET}}" (effects (font (size 1 1)))) (number "20" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 12.5 180) (length 3) (name "P0.19" (effects (font (size 1 1)))) (number "21" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 10 180) (length 3) (name "P0.20" (effects (font (size 1 1)))) (number "22" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 7.5 180) (length 3) (name "P0.21" (effects (font (size 1 1)))) (number "23" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 5 180) (length 3) (name "P0.22" (effects (font (size 1 1)))) (number "24" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 2.5 180) (length 3) (name "P0.23" (effects (font (size 1 1)))) (number "25" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 0 180) (length 3) (name "P0.24" (effects (font (size 1 1)))) (number "26" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 -2.5 180) (length 3) (name "P1.00" (effects (font (size 1 1)))) (number "27" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 -5 180) (length 3) (name "D-" (effects (font (size 1 1)))) (number "28" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 -7.5 180) (length 3) (name "D+" (effects (font (size 1 1)))) (number "29" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 -10 180) (length 3) (name "P0.25" (effects (font (size 1 1)))) (number "30" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at 23 -12.5 180) (length 3) (name "P0.26" (effects (font (size 1 1)))) (number "31" (effects (font (size 1 1)))))
|
|
(pin input line (at 23 -17.5 180) (length 3) (name "SWDIO" (effects (font (size 1 1)))) (number "33" (effects (font (size 1 1)))))
|
|
(pin input line (at 23 -20 180) (length 3) (name "SWDCLK" (effects (font (size 1 1)))) (number "34" (effects (font (size 1 1)))))
|
|
)
|
|
)"""
|
|
return s
|
|
|
|
|
|
def sym_is25lp128f():
|
|
s = _sym_start("IS25LP128F", "U", "IS25LP128F", "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm")
|
|
s += f"""
|
|
(symbol "IS25LP128F_0_1"
|
|
(rectangle (start -7 6) (end 7 -6) (stroke (width 0.254) (type default)) (fill (type background)))
|
|
)
|
|
(symbol "IS25LP128F_1_1"
|
|
(pin input line (at -10 4 0) (length 3) (name "~{{CS}}" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
|
|
(pin output line (at -10 1.5 0) (length 3) (name "SO/IO1" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
|
|
(pin input line (at -10 -1.5 0) (length 3) (name "~{{WP}}/IO2" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
|
|
(pin power_in line (at -10 -4 0) (length 3) (name "GND" (effects (font (size 1 1)))) (number "4" (effects (font (size 1 1)))))
|
|
(pin input line (at 10 -4 180) (length 3) (name "SI/IO0" (effects (font (size 1 1)))) (number "5" (effects (font (size 1 1)))))
|
|
(pin input line (at 10 -1.5 180) (length 3) (name "SCLK" (effects (font (size 1 1)))) (number "6" (effects (font (size 1 1)))))
|
|
(pin input line (at 10 1.5 180) (length 3) (name "~{{HOLD}}/IO3" (effects (font (size 1 1)))) (number "7" (effects (font (size 1 1)))))
|
|
(pin power_in line (at 10 4 180) (length 3) (name "VCC" (effects (font (size 1 1)))) (number "8" (effects (font (size 1 1)))))
|
|
)
|
|
)"""
|
|
return s
|
|
|
|
|
|
def sym_pam8302a():
|
|
s = _sym_start("PAM8302A", "U", "PAM8302A", "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm")
|
|
s += f"""
|
|
(symbol "PAM8302A_0_1"
|
|
(rectangle (start -8 8) (end 8 -8) (stroke (width 0.254) (type default)) (fill (type background)))
|
|
)
|
|
(symbol "PAM8302A_1_1"
|
|
(pin input line (at -11 5 0) (length 3) (name "~{{SD}}" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
|
|
(pin power_in line (at -11 2 0) (length 3) (name "VDD" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
|
|
(pin power_in line (at -11 -1 0) (length 3) (name "GND" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
|
|
(pin input line (at -11 -4 0) (length 3) (name "A+" (effects (font (size 1 1)))) (number "4" (effects (font (size 1 1)))))
|
|
(pin input line (at -11 -7 0) (length 3) (name "A-" (effects (font (size 1 1)))) (number "5" (effects (font (size 1 1)))))
|
|
(pin output line (at 11 2 180) (length 3) (name "VO+" (effects (font (size 1 1)))) (number "8" (effects (font (size 1 1)))))
|
|
(pin output line (at 11 -1 180) (length 3) (name "VO-" (effects (font (size 1 1)))) (number "7" (effects (font (size 1 1)))))
|
|
(pin power_in line (at 11 -4 180) (length 3) (name "PAD" (effects (font (size 1 1)))) (number "6" (effects (font (size 1 1)))))
|
|
)
|
|
)"""
|
|
return s
|
|
|
|
|
|
def sym_tps61220():
|
|
s = _sym_start("TPS61220", "U", "TPS61220DCK", "Package_TO_SOT_SMD:SOT-353_SC-70-5")
|
|
s += f"""
|
|
(symbol "TPS61220_0_1"
|
|
(rectangle (start -7 5) (end 7 -5) (stroke (width 0.254) (type default)) (fill (type background)))
|
|
)
|
|
(symbol "TPS61220_1_1"
|
|
(pin power_in line (at -10 3 0) (length 3) (name "VIN" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
|
|
(pin power_in line (at -10 -3 0) (length 3) (name "GND" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
|
|
(pin input line (at 10 -3 180) (length 3) (name "EN" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
|
|
(pin power_out line (at 10 3 180) (length 3) (name "VOUT" (effects (font (size 1 1)))) (number "4" (effects (font (size 1 1)))))
|
|
(pin passive line (at 0 8 270) (length 3) (name "L" (effects (font (size 1 1)))) (number "5" (effects (font (size 1 1)))))
|
|
)
|
|
)"""
|
|
return s
|
|
|
|
|
|
def sym_mcp1700():
|
|
s = _sym_start("MCP1700", "U", "MCP1700-3302E", "Package_TO_SOT_SMD:SOT-23")
|
|
s += f"""
|
|
(symbol "MCP1700_0_1"
|
|
(rectangle (start -6 4) (end 6 -4) (stroke (width 0.254) (type default)) (fill (type background)))
|
|
)
|
|
(symbol "MCP1700_1_1"
|
|
(pin power_in line (at -9 2 0) (length 3) (name "VIN" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
|
|
(pin power_in line (at 0 -7 90) (length 3) (name "GND" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
|
|
(pin power_out line (at 9 2 180) (length 3) (name "VOUT" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
|
|
)
|
|
)"""
|
|
return s
|
|
|
|
|
|
def sym_generic_2pin(name, ref, value, fp):
|
|
"""Generic 2-pin symbol for caps, resistors, diodes, connectors."""
|
|
s = _sym_start(name, ref, value, fp)
|
|
s += f"""
|
|
(symbol "{name}_0_1"
|
|
(polyline (pts (xy -1.5 1) (xy 1.5 1)) (stroke (width 0.4) (type default)) (fill (type none)))
|
|
(polyline (pts (xy -1.5 -1) (xy 1.5 -1)) (stroke (width 0.4) (type default)) (fill (type none)))
|
|
)
|
|
(symbol "{name}_1_1"
|
|
(pin passive line (at 0 3.5 270) (length 2.5) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
|
|
(pin passive line (at 0 -3.5 90) (length 2.5) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
|
|
)
|
|
)"""
|
|
return s
|
|
|
|
|
|
def sym_usb_c():
|
|
s = _sym_start("USB_C", "J", "USB_C_Receptacle", "Connector_USB:USB_C_Receptacle_GCT_USB4085")
|
|
s += f"""
|
|
(symbol "USB_C_0_1"
|
|
(rectangle (start -6 8) (end 6 -8) (stroke (width 0.254) (type default)) (fill (type background)))
|
|
)
|
|
(symbol "USB_C_1_1"
|
|
(pin power_in line (at -9 6 0) (length 3) (name "VBUS" (effects (font (size 1 1)))) (number "A4" (effects (font (size 1 1)))))
|
|
(pin passive line (at -9 3 0) (length 3) (name "CC1" (effects (font (size 1 1)))) (number "A5" (effects (font (size 1 1)))))
|
|
(pin passive line (at -9 0 0) (length 3) (name "CC2" (effects (font (size 1 1)))) (number "B5" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -9 -3 0) (length 3) (name "D+" (effects (font (size 1 1)))) (number "A6" (effects (font (size 1 1)))))
|
|
(pin bidirectional line (at -9 -6 0) (length 3) (name "D-" (effects (font (size 1 1)))) (number "A7" (effects (font (size 1 1)))))
|
|
(pin power_in line (at 9 -6 180) (length 3) (name "GND" (effects (font (size 1 1)))) (number "A1" (effects (font (size 1 1)))))
|
|
(pin passive line (at 9 0 180) (length 3) (name "SHIELD" (effects (font (size 1 1)))) (number "S1" (effects (font (size 1 1)))))
|
|
)
|
|
)"""
|
|
return s
|
|
|
|
|
|
def sym_switch():
|
|
s = _sym_start("Switch", "SW", "SW", "Button_Switch_SMD:SW_SPST_TL3342")
|
|
s += f"""
|
|
(symbol "Switch_0_1"
|
|
(circle (center -2 0) (radius 0.4) (stroke (width 0) (type default)) (fill (type none)))
|
|
(circle (center 2 0) (radius 0.4) (stroke (width 0) (type default)) (fill (type none)))
|
|
(polyline (pts (xy -1.6 0) (xy 1.6 1)) (stroke (width 0.254) (type default)) (fill (type none)))
|
|
)
|
|
(symbol "Switch_1_1"
|
|
(pin passive line (at -5 0 0) (length 3) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
|
|
(pin passive line (at 5 0 180) (length 3) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
|
|
)
|
|
)"""
|
|
return s
|
|
|
|
|
|
def sym_nmos():
|
|
s = _sym_start("NMOS", "Q", "AO3400A", "Package_TO_SOT_SMD:SOT-23")
|
|
s += f"""
|
|
(symbol "NMOS_0_1"
|
|
(polyline (pts (xy -0.5 2) (xy -0.5 -2)) (stroke (width 0.254) (type default)) (fill (type none)))
|
|
)
|
|
(symbol "NMOS_1_1"
|
|
(pin passive line (at 0 5 270) (length 2.5) (name "D" (effects (font (size 1 1)))) (number "3" (effects (font (size 1 1)))))
|
|
(pin input line (at -4 0 0) (length 2.5) (name "G" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
|
|
(pin passive line (at 0 -5 90) (length 2.5) (name "S" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
|
|
)
|
|
)"""
|
|
return s
|
|
|
|
|
|
def sym_inductor():
|
|
s = _sym_start("Inductor", "L", "4.7uH", "Inductor_SMD:L_0805_2012Metric")
|
|
s += f"""
|
|
(symbol "Inductor_0_1"
|
|
(arc (start 0 2.5) (mid 0.75 1.875) (end 0 1.25) (stroke (width 0.254) (type default)) (fill (type none)))
|
|
(arc (start 0 1.25) (mid 0.75 0.625) (end 0 0) (stroke (width 0.254) (type default)) (fill (type none)))
|
|
(arc (start 0 0) (mid 0.75 -0.625) (end 0 -1.25) (stroke (width 0.254) (type default)) (fill (type none)))
|
|
(arc (start 0 -1.25) (mid 0.75 -1.875) (end 0 -2.5) (stroke (width 0.254) (type default)) (fill (type none)))
|
|
)
|
|
(symbol "Inductor_1_1"
|
|
(pin passive line (at 0 5 270) (length 2.5) (name "1" (effects (font (size 1 1)))) (number "1" (effects (font (size 1 1)))))
|
|
(pin passive line (at 0 -5 90) (length 2.5) (name "2" (effects (font (size 1 1)))) (number "2" (effects (font (size 1 1)))))
|
|
)
|
|
)"""
|
|
return s
|
|
|
|
|
|
def sym_conn(name, ref, npins, fp):
|
|
s = _sym_start(name, ref, name, fp)
|
|
h = npins * 2.5 + 1
|
|
s += f"""
|
|
(symbol "{name}_0_1"
|
|
(rectangle (start -3 {h}) (end 3 {-h}) (stroke (width 0.254) (type default)) (fill (type background)))
|
|
)
|
|
(symbol "{name}_1_1"
|
|
"""
|
|
for i in range(npins):
|
|
y = h - 2 - i * 2.5
|
|
s += f""" (pin passive line (at -6 {y:.1f} 0) (length 3) (name "P{i+1}" (effects (font (size 1 1)))) (number "{i+1}" (effects (font (size 1 1)))))
|
|
"""
|
|
s += """ )
|
|
)"""
|
|
return s
|
|
|
|
|
|
# ============================================================
|
|
# INSTANCE HELPER
|
|
# ============================================================
|
|
|
|
def _inst(lib_name, ref, value, footprint, x, y, angle=0):
|
|
return f""" (symbol (lib_id "{LIB}:{lib_name}") (at {x} {y} {angle}) (unit 1)
|
|
(in_bom yes) (on_board yes) (dnp no)
|
|
(uuid "{uid()}")
|
|
(property "Reference" "{ref}" (at {x+3} {y-3} 0) (effects (font (size 1.27 1.27))))
|
|
(property "Value" "{value}" (at {x+3} {y+3} 0) (effects (font (size 1.27 1.27))))
|
|
(property "Footprint" "{footprint}" (at {x} {y} 0) (effects (font (size 1.27 1.27)) hide))
|
|
(property "Datasheet" "" (at {x} {y} 0) (effects (font (size 1.27 1.27)) hide))
|
|
)"""
|
|
|
|
|
|
# ============================================================
|
|
# MAIN SCHEMATIC
|
|
# ============================================================
|
|
|
|
def generate_schematic():
|
|
sch = []
|
|
sch.append(f"""(kicad_sch (version 20230121) (generator "baby_mobile_gen_v2")
|
|
|
|
(uuid "{uid()}")
|
|
(paper "A3")
|
|
|
|
(title_block
|
|
(title "Baby Mobile Audio Board v2")
|
|
(date "2026-03-09")
|
|
(rev "2.0")
|
|
(comment 1 "MDBT50Q (nRF52840) + IS25LP128F + PAM8302A")
|
|
(comment 2 "2xAA Battery + USB-C, Diode OR, TPS61220 Boost")
|
|
(comment 3 "BLE + USB mass storage for audio upload")
|
|
(comment 4 "8-ohm 0.25W Speaker, DC Motor, 8 Buttons")
|
|
)
|
|
""")
|
|
|
|
# ---- LIB_SYMBOLS ----
|
|
sch.append(" (lib_symbols")
|
|
sch.append(sym_mdbt50q())
|
|
sch.append(sym_is25lp128f())
|
|
sch.append(sym_pam8302a())
|
|
sch.append(sym_tps61220())
|
|
sch.append(sym_mcp1700())
|
|
sch.append(sym_generic_2pin("Capacitor", "C", "C", "Capacitor_SMD:C_0805_2012Metric"))
|
|
sch.append(sym_generic_2pin("Resistor", "R", "R", "Resistor_SMD:R_0805_2012Metric"))
|
|
sch.append(sym_generic_2pin("Diode_Schottky", "D", "BAT54S", "Diode_SMD:D_SOT-23_ANK"))
|
|
sch.append(sym_generic_2pin("LED", "D", "LED", "LED_SMD:LED_0805_2012Metric"))
|
|
sch.append(sym_generic_2pin("Conn_2Pin", "J", "Conn_2", "Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal"))
|
|
sch.append(sym_switch())
|
|
sch.append(sym_nmos())
|
|
sch.append(sym_inductor())
|
|
sch.append(sym_usb_c())
|
|
sch.append(sym_conn("SWD_Header", "J", 4, "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical"))
|
|
sch.append(sym_conn("Serial_Header", "J", 3, "Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical"))
|
|
sch.append(" )") # end lib_symbols
|
|
|
|
# ---- COMPONENT INSTANCES ----
|
|
|
|
# == POWER: Battery path ==
|
|
sch.append(_inst("Conn_2Pin", "J1", "BATTERY", "Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal", 20, 40))
|
|
sch.append(_inst("Switch", "SW9", "PWR", "Button_Switch_THT:SW_SPDT_CK-JS102011SAQN", 38, 35))
|
|
sch.append(_inst("Diode_Schottky", "D1", "BAT54S", "Diode_SMD:D_SOT-23_ANK", 55, 35)) # battery diode
|
|
sch.append(_inst("Capacitor", "C1", "100uF", "Capacitor_THT:CP_Radial_D5.0mm_P2.00mm", 48, 45))
|
|
|
|
# == POWER: Boost converter TPS61220 ==
|
|
sch.append(_inst("TPS61220", "U4", "TPS61220DCK", "Package_TO_SOT_SMD:SOT-353_SC-70-5", 80, 40))
|
|
sch.append(_inst("Inductor", "L1", "4.7uH", "Inductor_SMD:L_0805_2012Metric", 80, 28))
|
|
sch.append(_inst("Capacitor", "C9", "10uF", "Capacitor_SMD:C_0805_2012Metric", 68, 45)) # boost input
|
|
sch.append(_inst("Capacitor", "C10", "10uF", "Capacitor_SMD:C_0805_2012Metric", 95, 45)) # boost output
|
|
|
|
# == POWER: USB path ==
|
|
sch.append(_inst("USB_C", "J6", "USB_C", "Connector_USB:USB_C_Receptacle_GCT_USB4085", 20, 80))
|
|
sch.append(_inst("Resistor", "R7", "5.1K", "Resistor_SMD:R_0805_2012Metric", 38, 83)) # CC1
|
|
sch.append(_inst("Resistor", "R8", "5.1K", "Resistor_SMD:R_0805_2012Metric", 38, 88)) # CC2
|
|
sch.append(_inst("Diode_Schottky", "D3", "BAT54S", "Diode_SMD:D_SOT-23_ANK", 55, 74)) # USB diode
|
|
sch.append(_inst("MCP1700", "U5", "MCP1700-3302E", "Package_TO_SOT_SMD:SOT-23", 72, 74))
|
|
sch.append(_inst("Capacitor", "C11", "1uF", "Capacitor_SMD:C_0805_2012Metric", 62, 82)) # LDO in
|
|
sch.append(_inst("Capacitor", "C12", "1uF", "Capacitor_SMD:C_0805_2012Metric", 82, 82)) # LDO out
|
|
|
|
# == MCU: MDBT50Q (nRF52840 module) ==
|
|
sch.append(_inst("MDBT50Q", "U1", "MDBT50Q-P1MV2", "RF_Module:Raytac_MDBT50Q", 160, 100))
|
|
|
|
# MCU decoupling
|
|
sch.append(_inst("Capacitor", "C2", "100nF", "Capacitor_SMD:C_0805_2012Metric", 135, 60))
|
|
sch.append(_inst("Capacitor", "C3", "10uF", "Capacitor_SMD:C_0805_2012Metric", 128, 60))
|
|
|
|
# == SPI FLASH: IS25LP128F ==
|
|
sch.append(_inst("IS25LP128F", "U2", "IS25LP128F", "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm", 260, 60))
|
|
sch.append(_inst("Capacitor", "C5", "100nF", "Capacitor_SMD:C_0805_2012Metric", 285, 50))
|
|
|
|
# == AUDIO AMP: PAM8302A ==
|
|
sch.append(_inst("PAM8302A", "U3", "PAM8302A", "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm", 260, 140))
|
|
sch.append(_inst("Resistor", "R2", "4.7K", "Resistor_SMD:R_0805_2012Metric", 220, 135)) # LPF R
|
|
sch.append(_inst("Capacitor", "C7", "10nF", "Capacitor_SMD:C_0805_2012Metric", 230, 145)) # LPF C
|
|
sch.append(_inst("Capacitor", "C6", "1uF", "Capacitor_SMD:C_0805_2012Metric", 240, 135)) # coupling
|
|
sch.append(_inst("Capacitor", "C8", "10uF", "Capacitor_SMD:C_0805_2012Metric", 285, 130)) # amp bypass
|
|
sch.append(_inst("Resistor", "R3", "100K", "Resistor_SMD:R_0805_2012Metric", 240, 155)) # SD pulldown
|
|
sch.append(_inst("Conn_2Pin", "J2", "SPEAKER", "Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal", 290, 145))
|
|
|
|
# == MOTOR DRIVER ==
|
|
sch.append(_inst("NMOS", "Q1", "AO3400A", "Package_TO_SOT_SMD:SOT-23", 160, 200))
|
|
sch.append(_inst("Resistor", "R4", "100R", "Resistor_SMD:R_0805_2012Metric", 140, 200))
|
|
sch.append(_inst("Resistor", "R5", "100K", "Resistor_SMD:R_0805_2012Metric", 150, 212))
|
|
sch.append(_inst("Diode_Schottky", "D4", "SS14", "Diode_SMD:D_SMA", 175, 190, 90))
|
|
sch.append(_inst("Conn_2Pin", "J3", "MOTOR", "Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal", 180, 180))
|
|
|
|
# == 8 BUTTONS ==
|
|
for i in range(8):
|
|
y = 80 + i * 14
|
|
sch.append(_inst("Switch", f"SW{i+1}", f"BTN{i+1}", "Button_Switch_SMD:SW_SPST_TL3342", 300, y))
|
|
|
|
# == STATUS LED ==
|
|
sch.append(_inst("LED", "D2", "LED_GRN", "LED_SMD:LED_0805_2012Metric", 115, 200))
|
|
sch.append(_inst("Resistor", "R6", "1K", "Resistor_SMD:R_0805_2012Metric", 105, 200))
|
|
|
|
# == HEADERS ==
|
|
sch.append(_inst("SWD_Header", "J4", "SWD", "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical", 200, 60))
|
|
sch.append(_inst("Serial_Header", "J5", "SERIAL", "Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical", 200, 30))
|
|
|
|
# ---- NET LABELS ----
|
|
labels = [
|
|
# 3.3V rail (output of boost OR LDO)
|
|
("3V3", 95, 37), ("3V3", 135, 55), ("3V3", 128, 55),
|
|
("3V3", 285, 45), ("3V3", 285, 125), ("3V3", 155, 67),
|
|
("3V3", 82, 72), ("3V3", 180, 175),
|
|
|
|
# Battery rail (pre-boost)
|
|
("VBAT", 48, 35), ("VBAT", 55, 30),
|
|
("VBAT", 68, 37),
|
|
|
|
# Boost input/output
|
|
("VBOOST_IN", 65, 37), ("VBOOST_IN", 70, 37),
|
|
("3V3", 90, 37),
|
|
|
|
# USB VBUS
|
|
("USB_VBUS", 29, 74), ("USB_VBUS", 55, 69),
|
|
("USB_LDO_OUT", 81, 72),
|
|
|
|
# GND everywhere
|
|
("GND", 48, 55), ("GND", 68, 55), ("GND", 95, 55),
|
|
("GND", 80, 50), ("GND", 155, 133),
|
|
("GND", 260, 70), ("GND", 285, 55), ("GND", 285, 155),
|
|
("GND", 160, 215), ("GND", 150, 218),
|
|
("GND", 230, 155), ("GND", 240, 165),
|
|
("GND", 62, 92), ("GND", 82, 92), ("GND", 72, 81),
|
|
("GND", 38, 93), ("GND", 38, 98),
|
|
("GND", 135, 65), ("GND", 128, 65),
|
|
|
|
# USB data
|
|
("USB_D+", 29, 77), ("USB_D+", 183, 92.5),
|
|
("USB_D-", 29, 80), ("USB_D-", 183, 95),
|
|
|
|
# USB CC (to pulldowns)
|
|
("USB_CC1", 29, 83), ("USB_CC1", 38, 78),
|
|
("USB_CC2", 29, 86), ("USB_CC2", 38, 83),
|
|
|
|
# SPI bus
|
|
("SPI_MOSI", 183, 87.5), ("SPI_MOSI", 250, 56),
|
|
("SPI_MISO", 183, 85), ("SPI_MISO", 250, 61.5),
|
|
("SPI_SCK", 183, 82.5), ("SPI_SCK", 270, 58.5),
|
|
("FLASH_CS", 183, 80), ("FLASH_CS", 250, 64),
|
|
|
|
# Audio
|
|
("AUDIO_PWM", 183, 100), ("AUDIO_PWM", 210, 135),
|
|
("AMP_SD", 183, 102.5), ("AMP_SD", 240, 150),
|
|
|
|
# Motor
|
|
("MOTOR_PWM", 137, 112.5), ("MOTOR_PWM", 130, 200),
|
|
|
|
# Buttons (directly to GPIOs)
|
|
("BTN1", 295, 80), ("BTN1", 137, 120),
|
|
("BTN2", 295, 94), ("BTN2", 137, 117.5),
|
|
("BTN3", 295, 108), ("BTN3", 137, 115),
|
|
("BTN4", 295, 122), ("BTN4", 137, 112.5),
|
|
("BTN5", 295, 136), ("BTN5", 137, 110),
|
|
("BTN6", 295, 150), ("BTN6", 137, 107.5),
|
|
("BTN7", 295, 164), ("BTN7", 137, 105),
|
|
("BTN8", 295, 178), ("BTN8", 137, 102.5),
|
|
|
|
# Button GND side
|
|
("GND", 305, 80), ("GND", 305, 94), ("GND", 305, 108), ("GND", 305, 122),
|
|
("GND", 305, 136), ("GND", 305, 150), ("GND", 305, 164), ("GND", 305, 178),
|
|
|
|
# LED
|
|
("LED_OUT", 95, 200), ("LED_OUT", 137, 100),
|
|
|
|
# SWD header
|
|
("SWDIO", 194, 55.5), ("SWDIO", 183, 82.5),
|
|
("SWDCLK", 194, 58), ("SWDCLK", 183, 80),
|
|
("3V3", 194, 53), ("GND", 194, 60.5),
|
|
|
|
# Serial header
|
|
("TXD", 194, 25.5), ("TXD", 137, 80),
|
|
("RXD", 194, 28), ("RXD", 137, 82.5),
|
|
("GND", 194, 30.5),
|
|
|
|
# Flash WP and HOLD tied to VCC
|
|
("3V3", 250, 58.5), ("3V3", 270, 61.5),
|
|
|
|
# Boost EN tied to VIN (always on when battery present)
|
|
("VBAT", 90, 43),
|
|
]
|
|
|
|
for name, x, y in labels:
|
|
sch.append(f""" (label "{name}" (at {x} {y} 0) (effects (font (size 1.27 1.27))) (uuid "{uid()}"))""")
|
|
|
|
# ---- TEXT NOTES ----
|
|
notes = [
|
|
(10, 12, "POWER: 2xAA → BAT54S → TPS61220 boost → 3.3V (1.8-3.2V input)"),
|
|
(10, 15, "USB: VBUS → BAT54S → MCP1700 LDO → 3.3V (safe with any battery chemistry)"),
|
|
(10, 18, "MCU: MDBT50Q (nRF52840) — BLE 5.0, USB 2.0, 1.7-5.5V, UF2 bootloader"),
|
|
(10, 21, "FLASH: IS25LP128F 16MB (2.3-3.6V) = ~33 min @ 8kHz 8-bit PCM"),
|
|
(10, 24, "AMP: PAM8302A Class-D, 90% eff, Vmin=2.0V, shutdown pin for power save"),
|
|
(10, 27, "MOTOR: AO3400A logic-level N-MOSFET, PWM speed control"),
|
|
(10, 230, "USB-C: Drag-and-drop audio as mass storage device"),
|
|
(10, 233, "BLE: Wireless audio upload from phone (Web Bluetooth compatible)"),
|
|
(10, 236, "FIRMWARE: UF2 bootloader — update by drag-and-drop, no programmer needed"),
|
|
(10, 239, "RUNTIME: Audio+Motor ~25h, Audio only ~167h, Sleep ~years on 2xAA"),
|
|
]
|
|
for x, y, text in notes:
|
|
sch.append(f""" (text "{text}" (at {x} {y} 0)
|
|
(effects (font (size 1.5 1.5)) (justify left))
|
|
(uuid "{uid()}")
|
|
)""")
|
|
|
|
sch.append(")") # end kicad_sch
|
|
return "\n".join(sch)
|
|
|
|
|
|
# ============================================================
|
|
# PROJECT FILE
|
|
# ============================================================
|
|
|
|
def generate_project():
|
|
return json.dumps({
|
|
"meta": {"filename": "baby-mobile-v2.kicad_pro", "version": 1},
|
|
"board": {
|
|
"design_settings": {
|
|
"defaults": {"board_outline_line_width": 0.1, "copper_line_width": 0.2},
|
|
"rules": {"min_copper_edge_clearance": 0.3, "min_track_width": 0.2,
|
|
"min_via_diameter": 0.6, "min_via_annular_width": 0.13}
|
|
}
|
|
},
|
|
"net_settings": {
|
|
"classes": [{"name": "Default", "clearance": 0.2, "track_width": 0.3,
|
|
"via_diameter": 0.6, "via_drill": 0.3}]
|
|
},
|
|
"schematic": {"legacy_lib_dir": "", "legacy_lib_list": []},
|
|
"sheets": [["", ""]]
|
|
}, indent=2)
|
|
|
|
|
|
# ============================================================
|
|
# MAIN
|
|
# ============================================================
|
|
|
|
if __name__ == "__main__":
|
|
outdir = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
sch = generate_schematic()
|
|
sch_path = os.path.join(outdir, "baby-mobile-v2.kicad_sch")
|
|
with open(sch_path, "w") as f:
|
|
f.write(sch)
|
|
print(f"Written: {sch_path} ({len(sch)} bytes)")
|
|
|
|
proj = generate_project()
|
|
proj_path = os.path.join(outdir, "baby-mobile-v2.kicad_pro")
|
|
with open(proj_path, "w") as f:
|
|
f.write(proj)
|
|
print(f"Written: {proj_path} ({len(proj)} bytes)")
|
|
|
|
# Validate: check no sub-symbol has library prefix
|
|
import re
|
|
outer = re.findall(r'\(symbol "baby_mobile:[^"]+?"', sch)
|
|
inner_bad = re.findall(r'\(symbol "baby_mobile:[^"]+?_[01]_1"', sch)
|
|
print(f"\nValidation: {len(outer)} outer symbols, {len(inner_bad)} bad inner symbols")
|
|
if inner_bad:
|
|
print("ERROR: Found inner symbols with library prefix:")
|
|
for m in inner_bad:
|
|
print(f" {m}")
|
|
else:
|
|
print("OK: All inner sub-symbols correctly omit library prefix")
|