#!/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")