first commit
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
gcode/
|
||||||
|
gerbers/
|
||||||
|
.flatcam-env/
|
||||||
|
flatcam-beta/
|
||||||
146
Readme.md
Normal file
146
Readme.md
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
# KiCad → Stencil G-code pipeline
|
||||||
|
|
||||||
|
Automates the conversion of a KiCad v9 `F.Paste` and `B.Paste` Gerber to G-code for CNC milling a solder paste stencil, using FlatCAM beta 8.995 headless. Tested on Ubuntu 24.04 with Python 3.12 and a Frezu 3040 / FluidNC controller.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Ubuntu 24.04
|
||||||
|
- Python 3.10–3.12
|
||||||
|
- KiCad v9
|
||||||
|
- A flat end mill (corn bit), 0.5 mm recommended
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### 1. System dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install python3-venv python3-pip libgeos-dev libspatialindex-dev libxcb-cursor0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Clone FlatCAM beta 8.995
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone --branch mstanciu_Beta_8.995 --depth 1 https://github.com/dwrobel/flatcam.git flatcam-beta
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Create a Python venv and install dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd flatcam-beta
|
||||||
|
|
||||||
|
python3 -m venv .flatcam-env
|
||||||
|
source .flatcam-env/bin/activate
|
||||||
|
|
||||||
|
pip install PyQt6 PyQt6-Qt6 PyQt6-sip
|
||||||
|
|
||||||
|
# Skip ortools (path optimizer), rasterio, and gdal (image import) — not needed for stencils
|
||||||
|
grep -vE "ortools|rasterio|gdal" requirements.txt | pip install -r /dev/stdin
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Verify
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python flatcam.py --version
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see the FlatCAM version string printed without errors.
|
||||||
|
|
||||||
|
Go back to the project directory:
|
||||||
|
```bash
|
||||||
|
cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Export the stencil gerbers
|
||||||
|
|
||||||
|
Export the gerber layers `F.Paste` and `B.Paste`
|
||||||
|
```bash
|
||||||
|
kicad-cli pcb export gerbers -o gerbers -l F.Paste,B.Paste ../kicad2panel/panel/Flow_Controller_Panel.kicad_pcb
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Generate the stencil G-code for both layers:
|
||||||
|
```bash
|
||||||
|
./run_stencil.sh gerbers/Flow_Controller_Panel-F_Paste.gtp \
|
||||||
|
gcode/Flow_Controller_Panel-F_Paste-stencil.nc
|
||||||
|
|
||||||
|
./run_stencil.sh gerbers/Flow_Controller_Panel-B_Paste.gbp \
|
||||||
|
gcode/Flow_Controller_Panel-B_Paste-stencil.nc
|
||||||
|
```
|
||||||
|
|
||||||
|
G-code for both layers should be in the:
|
||||||
|
|
||||||
|
```
|
||||||
|
gcode/Flow_Controller_Panel-F_Paste-stencil.nc
|
||||||
|
gcode/Flow_Controller_Panel-B_Paste-stencil.nc
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
All cutting parameters are at the top of `paste_stencil.tcl`:
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `TOOL_DIA` | `0.5` | End mill diameter in mm |
|
||||||
|
| `CUT_Z` | `-0.2` | Cutting depth in mm (negative). Set slightly past material thickness |
|
||||||
|
| `TRAVEL_Z` | `2.0` | Safe travel height in mm |
|
||||||
|
| `FEEDRATE` | `120` | XY feed rate in mm/min |
|
||||||
|
| `SPINDLE` | `15000` | Spindle speed in RPM |
|
||||||
|
| `OVERLAP` | `60` | Tool overlap in % — higher = cleaner clearing, more paths |
|
||||||
|
| `MARGIN` | `0.0` | Inset from aperture edge in mm. 0 = cut to exact pad size |
|
||||||
|
|
||||||
|
### Depth by material
|
||||||
|
|
||||||
|
| Material | Thickness | Recommended `CUT_Z` |
|
||||||
|
|---|---|---|
|
||||||
|
| Brass shim (CW614N) | 0.15 mm | `-0.18` |
|
||||||
|
| Polyimide (Kapton) | 0.10 mm | `-0.13` |
|
||||||
|
| Stainless steel shim | 0.15 mm | `-0.18` |
|
||||||
|
|
||||||
|
For stainless, also reduce `FEEDRATE` to `80` mm/min.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Preprocessor
|
||||||
|
|
||||||
|
The script uses the `GRBL_11` preprocessor (`-pp GRBL_11` in `paste_stencil.tcl`), which produces grbl 1.1 compatible G-code. FluidNC implements the grbl protocol so this works directly with no modifications.
|
||||||
|
|
||||||
|
Alternative: `GRBL_11_no_M6` suppresses tool change commands — not needed for a single-tool stencil job but harmless either way.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workholding notes
|
||||||
|
|
||||||
|
- Stencil material is thin and will flex — use double-sided tape on a flat spoilboard
|
||||||
|
- Zero Z directly on the stencil surface, not the spoilboard
|
||||||
|
- For brass shim, clamp the edges with small clips if the tape isn't holding — chatter from an unanchored sheet ruins aperture quality
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**FlatCAM hangs after writing G-code**
|
||||||
|
The `run_stencil.sh` wrapper detects the `TclCommandQuit` log line and kills the process after 3 seconds. This is expected behavior — FlatCAM's Qt GUI cleanup hangs in headless mode.
|
||||||
|
|
||||||
|
**`ImportError: cannot import name 'Inf' from numpy`**
|
||||||
|
You have the old PyPI FlatCAM 8.5 installed. Follow the installation steps above to switch to the beta 8.995 clone.
|
||||||
|
|
||||||
|
**`ImportError: cannot import name 'cascaded_union' from shapely.ops`**
|
||||||
|
Same cause — old FlatCAM 8.5 is incompatible with Shapely 2.0. Switch to beta 8.995.
|
||||||
|
|
||||||
|
**G-code cuts everything except the pads**
|
||||||
|
You used `ncc` (non-copper clear) instead of `paint`. The correct command is `paint` — it pockets the pad polygons. `ncc` clears everything around them, which is the inverse.
|
||||||
|
|
||||||
|
**`gdal` fails to install**
|
||||||
|
Skip it — `gdal` is only needed for the image import plugin. Use:
|
||||||
|
```bash
|
||||||
|
grep -vE "ortools|rasterio|gdal" requirements.txt | pip install -r /dev/stdin
|
||||||
|
```
|
||||||
79
paste_stencil.tcl
Normal file
79
paste_stencil.tcl
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# paste_stencil.tcl — FlatCAM beta 8.995
|
||||||
|
# Automates B.Paste Gerber -> G-code for stencil milling
|
||||||
|
# Tool: 0.5mm flat end mill (corn bit)
|
||||||
|
#
|
||||||
|
# Usage (headless):
|
||||||
|
# python flatcam.py --shellfile=paste_stencil.tcl --headless=1
|
||||||
|
|
||||||
|
# ─── USER CONFIGURATION ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
set GERBER "gerbers/myboard-B_Paste.gbp" ;# path to your B.Paste Gerber
|
||||||
|
set OUTPUT "gcode/stencil.nc" ;# output G-code file
|
||||||
|
|
||||||
|
# Tool
|
||||||
|
set TOOL_DIA 0.5 ;# mm — 0.5mm corn bit
|
||||||
|
|
||||||
|
# Stencil material depth
|
||||||
|
# Brass shim 0.15mm → use -0.18, polyimide 0.1mm → use -0.13
|
||||||
|
set CUT_Z -0.3 ;# mm — cutting depth (negative)
|
||||||
|
set TRAVEL_Z 2.0 ;# mm — safe travel height
|
||||||
|
|
||||||
|
# Feeds & speeds
|
||||||
|
set FEEDRATE 120 ;# mm/min
|
||||||
|
set SPINDLE 15000 ;# RPM
|
||||||
|
|
||||||
|
# NCC (copper_clear) settings
|
||||||
|
# overlap is in PERCENT (0–100), not fraction — 8.995 change from classic
|
||||||
|
set OVERLAP 60 ;# % — 60% overlap for clean clearing with 0.5mm tool
|
||||||
|
set MARGIN 0.0 ;# mm — 0 = clear exactly to aperture edge
|
||||||
|
|
||||||
|
# Multidepth — set dpp to 0 to disable (single pass)
|
||||||
|
set DPP 0.0 ;# mm per pass; 0 = single full-depth pass
|
||||||
|
|
||||||
|
# ─── DERIVED NAMES ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
set GBR_NAME "paste_gerber"
|
||||||
|
set NCC_GEO "paste_paint_geo"
|
||||||
|
set CNC_JOB "paste_cnc"
|
||||||
|
|
||||||
|
# ─── SCRIPT ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# 1. Fresh project
|
||||||
|
new
|
||||||
|
|
||||||
|
# 2. Load B.Paste Gerber
|
||||||
|
open_gerber $GERBER -outname $GBR_NAME
|
||||||
|
|
||||||
|
# 3. Paint (pocket) each aperture opening
|
||||||
|
# -all processes every polygon in the object
|
||||||
|
# method lines is most reliable for small SMD apertures
|
||||||
|
paint $GBR_NAME \
|
||||||
|
-tooldia $TOOL_DIA \
|
||||||
|
-overlap $OVERLAP \
|
||||||
|
-offset $MARGIN \
|
||||||
|
-method lines \
|
||||||
|
-connect 1 \
|
||||||
|
-contour 1 \
|
||||||
|
-all \
|
||||||
|
-outname $NCC_GEO
|
||||||
|
|
||||||
|
# 4. Generate CNC job
|
||||||
|
# -dpp 0 means single pass (multidepth disabled)
|
||||||
|
# -pp default uses the standard grbl-compatible preprocessor
|
||||||
|
cncjob $NCC_GEO \
|
||||||
|
-dia $TOOL_DIA \
|
||||||
|
-z_cut $CUT_Z \
|
||||||
|
-z_move $TRAVEL_Z \
|
||||||
|
-feedrate $FEEDRATE \
|
||||||
|
-spindlespeed $SPINDLE \
|
||||||
|
-pp GRBL_11 \
|
||||||
|
-outname $CNC_JOB
|
||||||
|
|
||||||
|
# 5. Write G-code to file
|
||||||
|
write_gcode $CNC_JOB $OUTPUT
|
||||||
|
|
||||||
|
puts "Done: $OUTPUT"
|
||||||
|
|
||||||
|
# Clear project so FlatCAM doesn't prompt to save on exit
|
||||||
|
new
|
||||||
|
quit_app
|
||||||
64
run_stencil.sh
Executable file
64
run_stencil.sh
Executable file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# run_stencil.sh — headless FlatCAM 8.995 stencil G-code generator
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./run_stencil.sh <B_Paste.gbp> <output.nc>
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
GERBER="${1:?Usage: $0 <B_Paste.gbp> <output.nc>}"
|
||||||
|
OUTPUT="${2:?Usage: $0 <B_Paste.gbp> <output.nc>}"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
TCL_SCRIPT="$SCRIPT_DIR/paste_stencil.tcl"
|
||||||
|
|
||||||
|
FLATCAM_PY="$HOME/proj/kicad2stencil/flatcam-beta/flatcam.py"
|
||||||
|
FLATCAM_ENV="$HOME/proj/kicad2stencil/.flatcam-env"
|
||||||
|
|
||||||
|
if [ ! -f "$FLATCAM_PY" ]; then
|
||||||
|
echo "ERROR: flatcam.py not found at $FLATCAM_PY"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
GERBER_ABS="$(realpath "$GERBER")"
|
||||||
|
OUTPUT_ABS="$(realpath -m "$OUTPUT")"
|
||||||
|
mkdir -p "$(dirname "$GERBER_ABS")"
|
||||||
|
mkdir -p "$(dirname "$OUTPUT_ABS")"
|
||||||
|
|
||||||
|
TMP_TCL=$(mktemp /tmp/paste_stencil_XXXXXX.tcl)
|
||||||
|
sed \
|
||||||
|
-e "s|gerbers/myboard-B_Paste.gbp|${GERBER_ABS}|g" \
|
||||||
|
-e "s|gcode/stencil.nc|${OUTPUT_ABS}|g" \
|
||||||
|
"$TCL_SCRIPT" > "$TMP_TCL"
|
||||||
|
|
||||||
|
echo "→ Running FlatCAM headless..."
|
||||||
|
echo " Gerber : $GERBER_ABS"
|
||||||
|
echo " Output : $OUTPUT_ABS"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
source "$FLATCAM_ENV/bin/activate"
|
||||||
|
|
||||||
|
# Run FlatCAM, watch for the WriteGCode confirmation line, then kill it
|
||||||
|
# FlatCAM hangs on quit due to Qt GUI cleanup — we kill it cleanly once done
|
||||||
|
python "$FLATCAM_PY" --shellfile="$TMP_TCL" --headless=1 2>&1 | \
|
||||||
|
while IFS= read -r line; do
|
||||||
|
echo "$line"
|
||||||
|
if [[ "$line" == *"TclCommandQuit"* ]]; then
|
||||||
|
# Give it a moment to finish flushing the file
|
||||||
|
sleep 3
|
||||||
|
# Kill the FlatCAM process
|
||||||
|
pkill -f "flatcam.py.*headless" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -f "$TMP_TCL"
|
||||||
|
|
||||||
|
# Verify output was created
|
||||||
|
if [ -f "$OUTPUT_ABS" ]; then
|
||||||
|
SIZE=$(wc -l < "$OUTPUT_ABS")
|
||||||
|
echo ""
|
||||||
|
echo "✓ Done: $OUTPUT_ABS ($SIZE lines)"
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "✗ ERROR: output file not created — check log above"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user