IPC-A-610 Inspection Tracking with TofuPilot
IPC-A-610 is the acceptability standard for electronic assemblies. It defines what "good" looks like for solder joints, component placement, cleanliness, and mechanical assembly. TofuPilot tracks inspection results systematically instead of relying on paper checklists.
What IPC-A-610 Covers
IPC-A-610 classifies defects into three product classes:
| Class | Application | Acceptance criteria |
|---|---|---|
| Class 1 | General electronics (consumer) | Least strict |
| Class 2 | Dedicated service electronics (industrial) | Moderate |
| Class 3 | High-performance electronics (medical, aerospace, military) | Most strict |
Inspection criteria include:
| Category | Examples |
|---|---|
| Solder joints | Wetting, fillets, bridges, cold joints |
| Component placement | Alignment, orientation, polarity |
| Cleanliness | Flux residue, contamination, corrosion |
| Mechanical | Wire routing, strain relief, conformal coating |
| Marking | Labels, part numbers, date codes |
Logging Inspection Results to TofuPilot
Per-Board Inspection
from tofupilot import TofuPilotClient
client = TofuPilotClient()
def log_inspection(serial, inspector, defects):
"""Log IPC-A-610 inspection results."""
measurements = [
{"name": "inspector_id", "value": inspector, "unit": ""},
{"name": "ipc_class", "value": 2, "unit": "class"},
{"name": "total_defects", "value": len(defects), "unit": "count", "limit_high": 0},
]
# Log each defect category
defect_categories = {
"solder": 0, "placement": 0, "cleanliness": 0,
"mechanical": 0, "marking": 0,
}
for d in defects:
if d["category"] in defect_categories:
defect_categories[d["category"]] += 1
for cat, count in defect_categories.items():
measurements.append({
"name": f"defects_{cat}",
"value": count,
"unit": "count",
"limit_high": 0,
})
passed = len(defects) == 0
client.create_run(
procedure_id="IPC610-VISUAL-INSPECTION",
unit_under_test={
"serial_number": serial,
"part_number": "MAIN-BOARD-V4",
},
run_passed=passed,
steps=[{
"name": "IPC-A-610 Class 2 Inspection",
"step_type": "measurement",
"status": passed,
"measurements": measurements,
}],
)
# Example: board with two defects
log_inspection(
serial="PCB-2025-04521",
inspector="OP-012",
defects=[
{"category": "solder", "location": "U12-pin3", "type": "insufficient_wetting"},
{"category": "cleanliness", "location": "J5-area", "type": "flux_residue"},
],
)Defect Classification
Log specific defect types to build a defect pareto.
# Detailed defect logging
defect_types = {
"solder_bridge": "Solder bridge between adjacent pins",
"cold_joint": "Cold or disturbed solder joint",
"insufficient_wetting": "Insufficient solder wetting on pad or lead",
"solder_ball": "Loose solder ball on board surface",
"tombstone": "Component standing on end (tombstoning)",
"misalignment": "Component offset from pad center",
"wrong_polarity": "Polarized component installed backwards",
"missing_component": "Component not placed",
"flux_residue": "Excessive flux residue after cleaning",
"damaged_component": "Component body cracked or chipped",
}
# Log each defect with its type
for defect in board_defects:
measurements.append({
"name": f"defect_{defect['type']}",
"value": 1,
"unit": "count",
"limit_high": 0,
})Defect Pareto Analysis
TofuPilot's analytics show you the most common defect types across production.
| Rank | Defect type | Count | Percentage |
|---|---|---|---|
| 1 | Insufficient wetting | 45 | 32% |
| 2 | Solder bridge | 28 | 20% |
| 3 | Flux residue | 22 | 16% |
| 4 | Misalignment | 15 | 11% |
| 5 | Cold joint | 12 | 9% |
| - | All others | 18 | 13% |
Fix the top defect type first. Insufficient wetting (32%) is likely a solder paste issue (stencil wear, paste viscosity, or reflow profile).
Inspector Consistency
Track defect detection rates by inspector to identify training needs.
from tofupilot import TofuPilotClient
client = TofuPilotClient()
runs = client.get_runs(
procedure_id="IPC610-VISUAL-INSPECTION",
limit=1000,
)
# Group by inspector
inspector_stats = {}
for run in runs:
for step in run.get("steps", []):
for m in step.get("measurements", []):
if m["name"] == "inspector_id":
inspector = m["value"]
if inspector not in inspector_stats:
inspector_stats[inspector] = {"total": 0, "rejected": 0}
inspector_stats[inspector]["total"] += 1
if not run["run_passed"]:
inspector_stats[inspector]["rejected"] += 1
for inspector, stats in inspector_stats.items():
reject_rate = stats["rejected"] / stats["total"] * 100
print(f"Inspector {inspector}: {stats['total']} boards, {reject_rate:.1f}% rejection rate")If Inspector A rejects 8% and Inspector B rejects 2% on the same product, either A is too strict or B is missing defects. Calibrate inspectors using reference boards with known defects.
Connecting Inspection to Electrical Test
The real power is correlating visual inspection results with electrical test results.
If boards that pass visual inspection but fail functional test at a specific measurement, there may be a defect type that visual inspection isn't catching. Conversely, if visually rejected boards are reworked and always pass functional test, the visual criteria may be too strict for your product class.
TofuPilot links both inspection and electrical test results to the same serial number, making this correlation straightforward.
AOI Integration
Automated Optical Inspection (AOI) systems can push results to TofuPilot the same way manual inspections do.
# Parse AOI machine output and upload to TofuPilot
import json
with open("aoi_results.json") as f:
aoi_data = json.load(f)
for board in aoi_data["boards"]:
defects = board.get("defects", [])
client.create_run(
procedure_id="AOI-INSPECTION",
unit_under_test={"serial_number": board["serial"]},
run_passed=len(defects) == 0,
steps=[{
"name": "AOI Scan",
"step_type": "measurement",
"status": len(defects) == 0,
"measurements": [
{"name": "defect_count", "value": len(defects), "unit": "count", "limit_high": 0},
{"name": "scan_coverage_pct", "value": board.get("coverage", 100), "unit": "%"},
],
}],
)Combine AOI data with manual inspection data and electrical test data in TofuPilot for a complete quality picture of every board.