kicad-mcp/kicad_mcp/utils/pattern_recognition.py
Lama 7343e032c1 Add circuit pattern recognition feature for KiCad schematics
This commit introduces a new circuit pattern recognition system that can
automatically identify common circuit patterns in KiCad schematics, including:

- Power supply circuits (linear regulators, switching converters)
- Amplifier circuits (op-amps, transistor amplifiers)
- Filter circuits (passive and active)
- Oscillator circuits (crystal, RC, IC-based)
- Digital interfaces (I2C, SPI, UART, USB)
- Microcontroller circuits
- Sensor interfaces

The implementation includes:
- Pattern recognition algorithms for common components
- Component value extraction and normalization utilities
- MCP tools for running pattern analysis
- MCP resources for displaying formatted results
- Comprehensive documentation

Users can easily extend the pattern recognition by adding new component
patterns or circuit recognition functions.
2025-03-21 10:43:34 -04:00

860 lines
36 KiB
Python

"""
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