""" Circuit pattern recognition functions for KiCad schematics. """ import re from typing import Dict, List, Any from kicad_mcp.utils.component_utils import extract_voltage_from_regulator, extract_frequency_from_value 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.keys() 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.keys() if ref.startswith('R')] capacitor_refs = [ref for ref in components.keys() 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.keys(): 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.keys() 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.keys(): 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.keys() 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.keys(): 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.keys() 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.keys(): 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.keys() 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.keys(): 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.keys() 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.keys() 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.keys() 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.keys() 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