""" Circuit pattern recognition functions for KiCad schematics. """ import re from typing import Any from kicad_mcp.utils.component_utils import ( extract_frequency_from_value, extract_voltage_from_regulator, ) def identify_power_supplies( components: dict[str, Any], nets: dict[str, Any] ) -> list[dict[str, Any]]: """Identify power supply circuits in the schematic. Args: components: Dictionary of components from netlist nets: Dictionary of nets from netlist Returns: List of identified power supply circuits """ power_supplies = [] # Look for voltage regulators (Linear) regulator_patterns = { "78xx": r"78\d\d|LM78\d\d|MC78\d\d", # 7805, 7812, etc. "79xx": r"79\d\d|LM79\d\d|MC79\d\d", # 7905, 7912, etc. "LDO": r"LM\d{3}|LD\d{3}|AMS\d{4}|LT\d{4}|TLV\d{3}|AP\d{4}|MIC\d{4}|NCP\d{3}|LP\d{4}|L\d{2}|TPS\d{5}", } for ref, component in components.items(): # Check for voltage regulators by part value or lib_id component_value = component.get("value", "").upper() component_lib = component.get("lib_id", "").upper() for reg_type, pattern in regulator_patterns.items(): if re.search(pattern, component_value, re.IGNORECASE) or re.search( pattern, component_lib, re.IGNORECASE ): # Found a regulator, look for associated components power_supplies.append( { "type": "linear_regulator", "subtype": reg_type, "main_component": ref, "value": component_value, "input_voltage": "unknown", # Would need more analysis to determine "output_voltage": extract_voltage_from_regulator(component_value), "associated_components": [], # Would need connection analysis to find these } ) # Look for switching regulators switching_patterns = { "buck": r"LM\d{4}|TPS\d{4}|MP\d{4}|RT\d{4}|LT\d{4}|MC\d{4}|NCP\d{4}|TL\d{4}|LTC\d{4}", "boost": r"MC\d{4}|LT\d{4}|TPS\d{4}|MAX\d{4}|NCP\d{4}|LTC\d{4}", "buck_boost": r"LTC\d{4}|LM\d{4}|TPS\d{4}|MAX\d{4}", } for ref, component in components.items(): component_value = component.get("value", "").upper() component_lib = component.get("lib_id", "").upper() # Check for inductor (key component in switching supplies) if ref.startswith("L") or "Inductor" in component_lib: # Look for nearby ICs that might be switching controllers for ic_ref, ic_component in components.items(): if ic_ref.startswith("U") or ic_ref.startswith("IC"): ic_value = ic_component.get("value", "").upper() ic_lib = ic_component.get("lib_id", "").upper() for converter_type, pattern in switching_patterns.items(): if re.search(pattern, ic_value, re.IGNORECASE) or re.search( pattern, ic_lib, re.IGNORECASE ): power_supplies.append( { "type": "switching_regulator", "subtype": converter_type, "main_component": ic_ref, "inductor": ref, "value": ic_value, } ) return power_supplies def identify_amplifiers(components: dict[str, Any], nets: dict[str, Any]) -> list[dict[str, Any]]: """Identify amplifier circuits in the schematic. Args: components: Dictionary of components from netlist nets: Dictionary of nets from netlist Returns: List of identified amplifier circuits """ amplifiers = [] # Look for op-amps opamp_patterns = [ r"LM\d{3}|TL\d{3}|NE\d{3}|LF\d{3}|OP\d{2}|MCP\d{3}|AD\d{3}|LT\d{4}|OPA\d{3}", r"Opamp|Op-Amp|OpAmp|Operational Amplifier", ] for ref, component in components.items(): component_value = component.get("value", "").upper() component_lib = component.get("lib_id", "").upper() # Check for op-amps for pattern in opamp_patterns: if re.search(pattern, component_value, re.IGNORECASE) or re.search( pattern, component_lib, re.IGNORECASE ): # Common op-amps if re.search( r"LM358|LM324|TL072|TL082|NE5532|LF353|MCP6002|AD8620|OPA2134", component_value, re.IGNORECASE, ): amplifiers.append( { "type": "operational_amplifier", "subtype": "general_purpose", "component": ref, "value": component_value, } ) # Audio op-amps elif re.search( r"NE5534|OPA134|OPA1612|OPA1652|LM4562|LME49720|LME49860|TL071|TL072", component_value, re.IGNORECASE, ): amplifiers.append( { "type": "operational_amplifier", "subtype": "audio", "component": ref, "value": component_value, } ) # Instrumentation amplifiers elif re.search( r"INA\d{3}|AD620|AD8221|AD8429|LT1167", component_value, re.IGNORECASE ): amplifiers.append( { "type": "operational_amplifier", "subtype": "instrumentation", "component": ref, "value": component_value, } ) else: amplifiers.append( { "type": "operational_amplifier", "subtype": "unknown", "component": ref, "value": component_value, } ) # Look for transistor amplifiers transistor_refs = [ref for ref in components if ref.startswith("Q")] for ref in transistor_refs: component = components[ref] component_lib = component.get("lib_id", "").upper() # Check if it's a BJT or FET if "BJT" in component_lib or "NPN" in component_lib or "PNP" in component_lib: # Look for resistors connected to transistor (biasing network) has_biasing = False for net_name, pins in nets.items(): # Check if this net connects to our transistor if any(pin.get("component") == ref for pin in pins): # Check if the net also connects to resistors if any(pin.get("component", "").startswith("R") for pin in pins): has_biasing = True break if has_biasing: amplifiers.append( { "type": "transistor_amplifier", "subtype": "BJT", "component": ref, "value": component.get("value", ""), } ) elif "FET" in component_lib or "MOSFET" in component_lib or "JFET" in component_lib: # Similar check for FET amplifiers has_biasing = False for net_name, pins in nets.items(): if any(pin.get("component") == ref for pin in pins): if any(pin.get("component", "").startswith("R") for pin in pins): has_biasing = True break if has_biasing: amplifiers.append( { "type": "transistor_amplifier", "subtype": "FET", "component": ref, "value": component.get("value", ""), } ) # Look for audio amplifier ICs audio_amp_patterns = [ r"LM386|LM383|LM380|LM1875|LM3886|TDA\d{4}|TPA\d{4}|SSM\d{4}|PAM\d{4}|TAS\d{4}" ] for ref, component in components.items(): component_value = component.get("value", "").upper() component_lib = component.get("lib_id", "").upper() for pattern in audio_amp_patterns: if re.search(pattern, component_value, re.IGNORECASE) or re.search( pattern, component_lib, re.IGNORECASE ): amplifiers.append( {"type": "audio_amplifier_ic", "component": ref, "value": component_value} ) return amplifiers def identify_filters(components: dict[str, Any], nets: dict[str, Any]) -> list[dict[str, Any]]: """Identify filter circuits in the schematic. Args: components: Dictionary of components from netlist nets: Dictionary of nets from netlist Returns: List of identified filter circuits """ filters = [] # Look for RC low-pass filters # These typically have a resistor followed by a capacitor to ground resistor_refs = [ref for ref in components if ref.startswith("R")] capacitor_refs = [ref for ref in components if ref.startswith("C")] for r_ref in resistor_refs: r_nets = [] # Find which nets this resistor is connected to for net_name, pins in nets.items(): if any(pin.get("component") == r_ref for pin in pins): r_nets.append(net_name) # For each net, check if there's a capacitor connected to it for net_name in r_nets: # Find capacitors connected to this net connected_caps = [] for pin in nets.get(net_name, []): comp = pin.get("component") if comp and comp.startswith("C"): connected_caps.append(comp) if connected_caps: # Check if the other side of the capacitor goes to ground for c_ref in connected_caps: c_is_to_ground = False for gnd_name in ["GND", "AGND", "DGND", "VSS"]: for pin in nets.get(gnd_name, []): if pin.get("component") == c_ref: c_is_to_ground = True break if c_is_to_ground: break if c_is_to_ground: filters.append( { "type": "passive_filter", "subtype": "rc_low_pass", "components": [r_ref, c_ref], } ) # Look for active filters (op-amp with feedback RC components) opamp_refs = [] for ref, component in components.items(): component_value = component.get("value", "").upper() component_lib = component.get("lib_id", "").upper() if ( re.search( r"LM\d{3}|TL\d{3}|NE\d{3}|LF\d{3}|OP\d{2}|MCP\d{3}|AD\d{3}|LT\d{4}|OPA\d{3}", component_value, re.IGNORECASE, ) or "OP_AMP" in component_lib ): opamp_refs.append(ref) for op_ref in opamp_refs: # Find op-amp output # In a full implementation, we'd know which pin is the output # For simplicity, we'll look for feedback components has_feedback_r = False has_feedback_c = False for net_name, pins in nets.items(): # If this net connects to our op-amp if any(pin.get("component") == op_ref for pin in pins): # Check if it also connects to resistors and capacitors connects_to_r = any(pin.get("component", "").startswith("R") for pin in pins) connects_to_c = any(pin.get("component", "").startswith("C") for pin in pins) if connects_to_r: has_feedback_r = True if connects_to_c: has_feedback_c = True if has_feedback_r and has_feedback_c: filters.append( { "type": "active_filter", "main_component": op_ref, "value": components[op_ref].get("value", ""), } ) # Look for crystal filters or ceramic filters for ref, component in components.items(): component_value = component.get("value", "").upper() component_lib = component.get("lib_id", "").upper() if ( ref.startswith("Y") or ref.startswith("X") or "CRYSTAL" in component_lib or "XTAL" in component_lib ): filters.append({"type": "crystal_filter", "component": ref, "value": component_value}) if ( "FILTER" in component_lib or "MURATA" in component_lib or "CERAMIC_FILTER" in component_lib ): filters.append({"type": "ceramic_filter", "component": ref, "value": component_value}) return filters def identify_oscillators(components: dict[str, Any], nets: dict[str, Any]) -> list[dict[str, Any]]: """Identify oscillator circuits in the schematic. Args: components: Dictionary of components from netlist nets: Dictionary of nets from netlist Returns: List of identified oscillator circuits """ oscillators = [] # Look for crystal oscillators for ref, component in components.items(): component_value = component.get("value", "").upper() component_lib = component.get("lib_id", "").upper() # Crystals if ( ref.startswith("Y") or ref.startswith("X") or "CRYSTAL" in component_lib or "XTAL" in component_lib ): # Check if the crystal has load capacitors has_load_caps = False crystal_nets = [] for net_name, pins in nets.items(): if any(pin.get("component") == ref for pin in pins): crystal_nets.append(net_name) # Look for capacitors connected to the crystal nets for net_name in crystal_nets: for pin in nets.get(net_name, []): comp = pin.get("component") if comp and comp.startswith("C"): has_load_caps = True break if has_load_caps: break oscillators.append( { "type": "crystal_oscillator", "component": ref, "value": component_value, "frequency": extract_frequency_from_value(component_value), "has_load_capacitors": has_load_caps, } ) # Oscillator ICs if ( "OSC" in component_lib or "OSCILLATOR" in component_lib or re.search(r"OSC|OSCILLATOR", component_value, re.IGNORECASE) ): oscillators.append( { "type": "oscillator_ic", "component": ref, "value": component_value, "frequency": extract_frequency_from_value(component_value), } ) # RC oscillators (555 timer, etc) if ( re.search(r"NE555|LM555|ICM7555|TLC555", component_value, re.IGNORECASE) or "555" in component_lib ): oscillators.append( { "type": "rc_oscillator", "subtype": "555_timer", "component": ref, "value": component_value, } ) return oscillators def identify_digital_interfaces( components: dict[str, Any], nets: dict[str, Any] ) -> list[dict[str, Any]]: """Identify digital interface circuits in the schematic. Args: components: Dictionary of components from netlist nets: Dictionary of nets from netlist Returns: List of identified digital interface circuits """ interfaces = [] # I2C interface detection i2c_signals = {"SCL", "SDA", "I2C_SCL", "I2C_SDA"} has_i2c = False for net_name in nets: if any(signal in net_name.upper() for signal in i2c_signals): has_i2c = True break if has_i2c: interfaces.append( { "type": "i2c_interface", "signals_found": [ net for net in nets if any(signal in net.upper() for signal in i2c_signals) ], } ) # SPI interface detection spi_signals = {"MOSI", "MISO", "SCK", "SS", "SPI_MOSI", "SPI_MISO", "SPI_SCK", "SPI_CS"} has_spi = False for net_name in nets: if any(signal in net_name.upper() for signal in spi_signals): has_spi = True break if has_spi: interfaces.append( { "type": "spi_interface", "signals_found": [ net for net in nets if any(signal in net.upper() for signal in spi_signals) ], } ) # UART interface detection uart_signals = {"TX", "RX", "TXD", "RXD", "UART_TX", "UART_RX"} has_uart = False for net_name in nets: if any(signal in net_name.upper() for signal in uart_signals): has_uart = True break if has_uart: interfaces.append( { "type": "uart_interface", "signals_found": [ net for net in nets if any(signal in net.upper() for signal in uart_signals) ], } ) # USB interface detection usb_signals = {"USB_D+", "USB_D-", "USB_DP", "USB_DM", "D+", "D-", "DP", "DM", "VBUS"} has_usb = False for net_name in nets: if any(signal in net_name.upper() for signal in usb_signals): has_usb = True break # Also check for USB interface ICs for ref, component in components.items(): component_value = component.get("value", "").upper() if re.search(r"FT232|CH340|CP210|MCP2200|TUSB|FT231|FT201", component_value, re.IGNORECASE): has_usb = True break if has_usb: interfaces.append( { "type": "usb_interface", "signals_found": [ net for net in nets if any(signal in net.upper() for signal in usb_signals) ], } ) # Ethernet interface detection ethernet_signals = {"TX+", "TX-", "RX+", "RX-", "MDI", "MDIO", "ETH"} has_ethernet = False for net_name in nets: if any(signal in net_name.upper() for signal in ethernet_signals): has_ethernet = True break # Also check for Ethernet PHY ICs for ref, component in components.items(): component_value = component.get("value", "").upper() if re.search(r"W5500|ENC28J60|LAN87|KSZ80|DP83|RTL8|AX88", component_value, re.IGNORECASE): has_ethernet = True break if has_ethernet: interfaces.append( { "type": "ethernet_interface", "signals_found": [ net for net in nets if any(signal in net.upper() for signal in ethernet_signals) ], } ) return interfaces def identify_sensor_interfaces( components: dict[str, Any], nets: dict[str, Any] ) -> list[dict[str, Any]]: """Identify sensor interface circuits in the schematic. Args: components: Dictionary of components from netlist nets: Dictionary of nets from netlist Returns: List of identified sensor interface circuits """ sensor_interfaces = [] # Common sensor IC patterns sensor_patterns = { "temperature": r"LM35|DS18B20|DHT11|DHT22|BME280|BMP280|TMP\d+|MCP9808|MAX31855|MAX6675|SI7021|HTU21|SHT[0123]\d|PCT2075", "humidity": r"DHT11|DHT22|BME280|SI7021|HTU21|SHT[0123]\d|HDC1080", "pressure": r"BMP\d+|BME280|LPS\d+|MS5611|DPS310|MPL3115|SPL06", "accelerometer": r"ADXL\d+|LIS3DH|MMA\d+|MPU\d+|LSM\d+|BMI\d+|BMA\d+|KX\d+", "gyroscope": r"L3G\d+|MPU\d+|BMI\d+|LSM\d+|ICM\d+", "magnetometer": r"HMC\d+|QMC\d+|LSM\d+|MMC\d+|RM\d+", "proximity": r"APDS9960|VL53L0X|VL6180|GP2Y|VCNL4040|VCNL4010", "light": r"BH1750|TSL\d+|MAX4\d+|VEML\d+|APDS9960|LTR329|OPT\d+", "air_quality": r"CCS811|BME680|SGP\d+|SEN\d+|MQ\d+|MiCS", "current": r"ACS\d+|INA\d+|MAX\d+|ZXCT\d+", "voltage": r"INA\d+|MCP\d+|ADS\d+", "ADC": r"ADS\d+|MCP33\d+|MCP32\d+|LTC\d+|NAU7802|HX711", "GPS": r"NEO-[67]M|L80|MTK\d+|SIM\d+|SAM-M8Q|MAX-M8", } for ref, component in components.items(): component_value = component.get("value", "").upper() component_lib = component.get("lib_id", "").upper() for sensor_type, pattern in sensor_patterns.items(): if re.search(pattern, component_value, re.IGNORECASE) or re.search( pattern, component_lib, re.IGNORECASE ): # Identify specific sensors # Temperature sensors if sensor_type == "temperature": if re.search(r"DS18B20", component_value, re.IGNORECASE): sensor_interfaces.append( { "type": "temperature_sensor", "model": "DS18B20", "component": ref, "interface": "1-Wire", "range": "-55°C to +125°C", } ) elif re.search(r"BME280|BMP280", component_value, re.IGNORECASE): sensor_interfaces.append( { "type": "multi_sensor", "model": component_value, "component": ref, "measures": [ "temperature", "pressure", "humidity" if "BME" in component_value else "pressure", ], "interface": "I2C/SPI", } ) elif re.search(r"LM35", component_value, re.IGNORECASE): sensor_interfaces.append( { "type": "temperature_sensor", "model": "LM35", "component": ref, "interface": "Analog", "range": "0°C to +100°C", } ) else: sensor_interfaces.append( { "type": "temperature_sensor", "model": component_value, "component": ref, } ) # Motion sensors (accelerometer, gyroscope, etc.) elif sensor_type in ["accelerometer", "gyroscope"]: if re.search(r"MPU6050", component_value, re.IGNORECASE): sensor_interfaces.append( { "type": "motion_sensor", "model": "MPU6050", "component": ref, "measures": ["accelerometer", "gyroscope"], "interface": "I2C", } ) elif re.search(r"MPU9250", component_value, re.IGNORECASE): sensor_interfaces.append( { "type": "motion_sensor", "model": "MPU9250", "component": ref, "measures": ["accelerometer", "gyroscope", "magnetometer"], "interface": "I2C/SPI", } ) elif re.search(r"LSM6DS3", component_value, re.IGNORECASE): sensor_interfaces.append( { "type": "motion_sensor", "model": "LSM6DS3", "component": ref, "measures": ["accelerometer", "gyroscope"], "interface": "I2C/SPI", } ) else: sensor_interfaces.append( { "type": "motion_sensor", "model": component_value, "component": ref, "measures": [sensor_type], } ) # Light and proximity sensors elif sensor_type in ["light", "proximity"]: if re.search(r"APDS9960", component_value, re.IGNORECASE): sensor_interfaces.append( { "type": "optical_sensor", "model": "APDS9960", "component": ref, "measures": ["proximity", "light", "gesture", "color"], "interface": "I2C", } ) elif re.search(r"VL53L0X", component_value, re.IGNORECASE): sensor_interfaces.append( { "type": "optical_sensor", "model": "VL53L0X", "component": ref, "measures": ["time-of-flight distance"], "interface": "I2C", "range": "Up to 2m", } ) elif re.search(r"BH1750", component_value, re.IGNORECASE): sensor_interfaces.append( { "type": "optical_sensor", "model": "BH1750", "component": ref, "measures": ["ambient light"], "interface": "I2C", } ) else: sensor_interfaces.append( { "type": "optical_sensor", "model": component_value, "component": ref, "measures": [sensor_type], } ) # ADCs (often used for sensor interfaces) elif sensor_type == "ADC": if re.search(r"ADS1115", component_value, re.IGNORECASE): sensor_interfaces.append( { "type": "analog_interface", "model": "ADS1115", "component": ref, "resolution": "16-bit", "channels": 4, "interface": "I2C", } ) elif re.search(r"HX711", component_value, re.IGNORECASE): sensor_interfaces.append( { "type": "analog_interface", "model": "HX711", "component": ref, "resolution": "24-bit", "common_usage": "Load cell/strain gauge", "interface": "Digital", } ) else: sensor_interfaces.append( {"type": "analog_interface", "model": component_value, "component": ref} ) # Other types of sensors else: sensor_interfaces.append( { "type": f"{sensor_type}_sensor", "model": component_value, "component": ref, } ) # Once identified a component as a specific sensor, no need to check other types break # Look for common analog sensors # These often don't have specific ICs but have designators like "RT" for thermistors thermistor_refs = [ ref for ref in components if ref.startswith("RT") or ref.startswith("TH") ] for ref in thermistor_refs: component = components[ref] sensor_interfaces.append( { "type": "temperature_sensor", "subtype": "thermistor", "component": ref, "value": component.get("value", ""), "interface": "Analog", } ) # Look for photodiodes, photoresistors (LDRs) photosensor_refs = [ ref for ref in components if ref.startswith("PD") or ref.startswith("LDR") ] for ref in photosensor_refs: component = components[ref] sensor_interfaces.append( { "type": "optical_sensor", "subtype": "photosensor", "component": ref, "value": component.get("value", ""), "interface": "Analog", } ) # Look for potentiometers (often used for manual sensing/control) pot_refs = [ref for ref in components if ref.startswith("RV") or ref.startswith("POT")] for ref in pot_refs: component = components[ref] sensor_interfaces.append( { "type": "position_sensor", "subtype": "potentiometer", "component": ref, "value": component.get("value", ""), "interface": "Analog", } ) return sensor_interfaces def identify_microcontrollers(components: dict[str, Any]) -> list[dict[str, Any]]: """Identify microcontroller circuits in the schematic. Args: components: Dictionary of components from netlist Returns: List of identified microcontroller circuits """ microcontrollers = [] # Common microcontroller families mcu_patterns = { "AVR": r"ATMEGA\d+|ATTINY\d+|AT90\w+", "STM32": r"STM32\w+", "PIC": r"PIC\d+\w+", "ESP": r"ESP32|ESP8266", "Arduino": r"ARDUINO", "MSP430": r"MSP430\w+", "RP2040": r"RP2040|PICO", "NXP": r"LPC\d+|IMXRT\d+|MK\d+", "SAM": r"SAMD\d+|SAM\w+", "ARM Cortex": r"CORTEX|ARM", "8051": r"8051|AT89", } for ref, component in components.items(): component_value = component.get("value", "").upper() component_lib = component.get("lib_id", "").upper() for family, pattern in mcu_patterns.items(): if re.search(pattern, component_value, re.IGNORECASE) or re.search( pattern, component_lib, re.IGNORECASE ): # Identify specific models identified = False # ATmega328P (Arduino Uno/Nano) if re.search(r"ATMEGA328P|ATMEGA328", component_value, re.IGNORECASE): microcontrollers.append( { "type": "microcontroller", "family": "AVR", "model": "ATmega328P", "component": ref, "common_usage": "Arduino Uno/Nano compatible", } ) identified = True # ATmega32U4 (Arduino Leonardo/Micro) elif re.search(r"ATMEGA32U4", component_value, re.IGNORECASE): microcontrollers.append( { "type": "microcontroller", "family": "AVR", "model": "ATmega32U4", "component": ref, "common_usage": "Arduino Leonardo/Micro compatible", } ) identified = True # ESP32 elif re.search(r"ESP32", component_value, re.IGNORECASE): microcontrollers.append( { "type": "microcontroller", "family": "ESP", "model": "ESP32", "component": ref, "features": "Wi-Fi & Bluetooth", } ) identified = True # ESP8266 elif re.search(r"ESP8266", component_value, re.IGNORECASE): microcontrollers.append( { "type": "microcontroller", "family": "ESP", "model": "ESP8266", "component": ref, "features": "Wi-Fi", } ) identified = True # STM32 series elif re.search(r"STM32F\d+", component_value, re.IGNORECASE): model = re.search(r"(STM32F\d+)", component_value, re.IGNORECASE).group(1) microcontrollers.append( { "type": "microcontroller", "family": "STM32", "model": model.upper(), "component": ref, "features": "ARM Cortex-M", } ) identified = True # Raspberry Pi Pico (RP2040) elif re.search(r"RP2040|PICO", component_value, re.IGNORECASE): microcontrollers.append( { "type": "microcontroller", "family": "RP2040", "model": "RP2040", "component": ref, "common_usage": "Raspberry Pi Pico", } ) identified = True # PIC microcontrollers elif re.search(r"PIC\d+", component_value, re.IGNORECASE): model = re.search(r"(PIC\d+\w+)", component_value, re.IGNORECASE) if model: microcontrollers.append( { "type": "microcontroller", "family": "PIC", "model": model.group(1).upper(), "component": ref, } ) identified = True # MSP430 series elif re.search(r"MSP430\w+", component_value, re.IGNORECASE): model = re.search(r"(MSP430\w+)", component_value, re.IGNORECASE) if model: microcontrollers.append( { "type": "microcontroller", "family": "MSP430", "model": model.group(1).upper(), "component": ref, "features": "Ultra-low power", } ) identified = True # If not identified specifically but matches a family if not identified: microcontrollers.append( { "type": "microcontroller", "family": family, "component": ref, "value": component_value, } ) # Once identified a component as a microcontroller, no need to check other families break # Look for microcontroller development boards dev_board_patterns = { "Arduino": r"ARDUINO|UNO|NANO|MEGA|LEONARDO|DUE", "ESP32 Dev Board": r"ESP32-DEVKIT|NODEMCU-32S|ESP-WROOM-32", "ESP8266 Dev Board": r"NODEMCU|WEMOS|D1_MINI|ESP-01", "STM32 Dev Board": r"NUCLEO|DISCOVERY|BLUEPILL", "Raspberry Pi": r"RASPBERRY|RPI|RPICO|PICO", } for ref, component in components.items(): component_value = component.get("value", "").upper() component_lib = component.get("lib_id", "").upper() for board_type, pattern in dev_board_patterns.items(): if re.search(pattern, component_value, re.IGNORECASE) or re.search( pattern, component_lib, re.IGNORECASE ): microcontrollers.append( { "type": "development_board", "board_type": board_type, "component": ref, "value": component_value, } ) break return microcontrollers def analyze_circuit_patterns(schematic_file: str) -> dict[str, Any]: """Analyze circuit patterns in a schematic file. Args: schematic_file: Path to KiCad schematic file Returns: Dictionary of identified patterns """ try: from kicad_mcp.utils.netlist_parser import parse_netlist_file # Parse netlist to get components and nets netlist_data = parse_netlist_file(schematic_file) components = netlist_data.get("components", {}) nets = netlist_data.get("nets", {}) patterns = {} # Identify various circuit patterns power_supplies = identify_power_supplies(components, nets) if power_supplies: patterns["power_supply"] = power_supplies amplifiers = identify_amplifiers(components, nets) if amplifiers: patterns["amplifier"] = amplifiers oscillators = identify_oscillators(components, nets) if oscillators: patterns["crystal_oscillator"] = oscillators interfaces = identify_digital_interfaces(components, nets) if interfaces: patterns["digital_interface"] = interfaces sensors = identify_sensor_interfaces(components, nets) if sensors: patterns["sensor_interface"] = sensors mcus = identify_microcontrollers(components) if mcus: patterns["microcontroller"] = mcus # Look for decoupling capacitors decoupling_caps = [] for ref, component in components.items(): if ref.startswith("C") and component.get("value", "").lower() in ["100nf", "0.1uf"]: decoupling_caps.append(ref) if decoupling_caps: patterns["decoupling"] = decoupling_caps return patterns except Exception as e: return {"error": f"Failed to analyze patterns: {str(e)}"} def get_component_recommendations(patterns: dict[str, Any]) -> list[dict[str, Any]]: """Get component recommendations based on identified patterns. Args: patterns: Dictionary of identified circuit patterns Returns: List of component recommendations """ recommendations = [] # Power supply recommendations if "power_supply" in patterns: power_circuits = patterns["power_supply"] for circuit in power_circuits: if circuit.get("type") == "linear_regulator": recommendations.append({ "category": "power_management", "component": "Filter Capacitor", "value": "1000µF", "reason": "Output filtering for linear regulator", "priority": "high" }) # Microcontroller recommendations if "microcontroller" in patterns: recommendations.extend([ { "category": "power_management", "component": "Decoupling Capacitor", "value": "100nF", "reason": "Power supply decoupling for microcontroller", "priority": "high" }, { "category": "reset_circuit", "component": "Pull-up Resistor", "value": "10kΩ", "reason": "Reset pin pull-up for microcontroller", "priority": "medium" } ]) # Crystal oscillator recommendations if "crystal_oscillator" in patterns: recommendations.extend([ { "category": "timing", "component": "Load Capacitor", "value": "22pF", "reason": "Crystal load capacitance", "priority": "high" } ]) return recommendations