148 lines
4.5 KiB
Bash
Executable File
148 lines
4.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# export_for_msla.sh — KiCad Gerber export + pm4n generation for Anycubic Photon Mono 4
|
|
#
|
|
# Usage:
|
|
# ./export_for_msla.sh [OPTIONS] <path/to/board.kicad_pcb>
|
|
#
|
|
# Options:
|
|
# --layers LAYER,LAYER,... KiCad layer names to export (default: F.Cu)
|
|
# --invert LAYER,LAYER,... Layers to invert (comma-separated)
|
|
# --mirror LAYER,LAYER,... Layers to mirror (comma-separated)
|
|
# --exposure SECONDS Exposure time in seconds (default: 60)
|
|
# --dummy FILE Dummy .pm4n template (default: Dummy.pm4n beside this script)
|
|
# --out DIR Output directory (default: ./output)
|
|
# --dpmm N Render resolution in dots/mm (default: native 58.824)
|
|
# --pos X,Y Board position on LCD in mm (default: centred)
|
|
# --verbose Print detailed progress
|
|
# -h, --help Show this help
|
|
#
|
|
# Normal output: one output filepath per layer.
|
|
# Verbose output: full progress from KiCad and the converter.
|
|
#
|
|
# Example:
|
|
# ./export_for_msla.sh --invert F.Cu,B.Mask --mirror F.Cu,F.Mask panel/board.kicad_pcb
|
|
#
|
|
# KiCad layer name → Gerber filename stem:
|
|
# F.Cu → F_Cu B.Cu → B_Cu
|
|
# F.Mask → F_Mask B.Mask → B_Mask
|
|
# F.SilkS → F_Silkscreen B.SilkS → B_Silkscreen
|
|
# F.Paste → F_Paste B.Paste → B_Paste
|
|
# Edge.Cuts → Edge_Cuts
|
|
# Front → Front Back → Back
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PYTHON="${PYTHON:-python3}"
|
|
CONVERTER="$SCRIPT_DIR/gerber_to_pm4n.py"
|
|
|
|
# ---- defaults ----
|
|
LAYERS="F.Cu"
|
|
INVERT_LAYERS=""
|
|
MIRROR_LAYERS=""
|
|
EXPOSURE="60"
|
|
DUMMY="$SCRIPT_DIR/Dummy.pm4n"
|
|
OUT_DIR="./output"
|
|
DPMM=""
|
|
POS=""
|
|
VERBOSE=0
|
|
|
|
# ---- helpers ----
|
|
usage() {
|
|
grep '^#' "$0" | sed 's/^# \{0,1\}//'
|
|
exit 0
|
|
}
|
|
|
|
log() { [[ $VERBOSE -eq 1 ]] && echo "$@" || true; }
|
|
|
|
contains() {
|
|
local list="$1" item="$2"
|
|
echo "$list" | tr ',' '\n' | grep -qx "$item"
|
|
}
|
|
|
|
layer_to_filename() {
|
|
case "$1" in
|
|
F.SilkS) echo "F_Silkscreen" ;;
|
|
B.SilkS) echo "B_Silkscreen" ;;
|
|
Edge.Cuts) echo "Edge_Cuts" ;;
|
|
*) echo "${1//./_}" ;;
|
|
esac
|
|
}
|
|
|
|
# ---- parse arguments ----
|
|
PCB_FILE=""
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--layers) LAYERS="$2"; shift 2 ;;
|
|
--invert) INVERT_LAYERS="$2"; shift 2 ;;
|
|
--mirror) MIRROR_LAYERS="$2"; shift 2 ;;
|
|
--exposure) EXPOSURE="$2"; shift 2 ;;
|
|
--dummy) DUMMY="$2"; shift 2 ;;
|
|
--out) OUT_DIR="$2"; shift 2 ;;
|
|
--dpmm) DPMM="$2"; shift 2 ;;
|
|
--pos) POS="$2"; shift 2 ;;
|
|
--verbose) VERBOSE=1; shift ;;
|
|
-h|--help) usage ;;
|
|
-*) echo "ERROR: unknown option: $1" >&2; exit 1 ;;
|
|
*) PCB_FILE="$1"; shift ;;
|
|
esac
|
|
done
|
|
|
|
[[ -z "$PCB_FILE" ]] && { echo "ERROR: no .kicad_pcb file specified" >&2; exit 1; }
|
|
[[ ! -f "$PCB_FILE" ]] && { echo "ERROR: file not found: $PCB_FILE" >&2; exit 1; }
|
|
[[ ! -f "$DUMMY" ]] && { echo "ERROR: Dummy.pm4n not found: $DUMMY" >&2; exit 1; }
|
|
|
|
BOARD_NAME="$(basename "$PCB_FILE" .kicad_pcb)"
|
|
GERBERS_DIR="$OUT_DIR/gerbers"
|
|
PM4N_DIR="$OUT_DIR/pm4n"
|
|
mkdir -p "$GERBERS_DIR" "$PM4N_DIR"
|
|
|
|
# ---- Step 1: export Gerbers ----
|
|
log "=== Exporting Gerbers ==="
|
|
log " Board: $PCB_FILE"
|
|
log " Layers: $LAYERS"
|
|
|
|
kicad-cli pcb export gerbers \
|
|
--output "$GERBERS_DIR" \
|
|
--layers "$LAYERS" \
|
|
--no-protel-ext \
|
|
--subtract-soldermask \
|
|
--no-netlist \
|
|
"$PCB_FILE" \
|
|
2>/dev/null
|
|
|
|
log ""
|
|
|
|
# ---- Step 2: convert to pm4n ----
|
|
log "=== Converting to .pm4n ==="
|
|
|
|
IFS=',' read -ra LAYER_LIST <<< "$LAYERS"
|
|
for LAYER in "${LAYER_LIST[@]}"; do
|
|
LAYER_STEM="$(layer_to_filename "$LAYER")"
|
|
GBR_FILE="$GERBERS_DIR/${BOARD_NAME}-${LAYER_STEM}.gbr"
|
|
|
|
if [[ ! -f "$GBR_FILE" ]]; then
|
|
echo "WARNING: Gerber not found for layer $LAYER (expected: $GBR_FILE)" >&2
|
|
continue
|
|
fi
|
|
|
|
OUT_PM4N="$PM4N_DIR/${BOARD_NAME}-${LAYER_STEM}.pm4n"
|
|
|
|
FLAGS=()
|
|
contains "$INVERT_LAYERS" "$LAYER" && FLAGS+=(--invert)
|
|
contains "$MIRROR_LAYERS" "$LAYER" && FLAGS+=(--mirror)
|
|
[[ -n "$DPMM" ]] && FLAGS+=(--dpmm "$DPMM")
|
|
[[ -n "$POS" ]] && FLAGS+=(--pos "$POS")
|
|
[[ $VERBOSE -eq 1 ]] && FLAGS+=(--verbose)
|
|
|
|
log " $LAYER → $OUT_PM4N [${FLAGS[*]:-} exposure=${EXPOSURE}s]"
|
|
|
|
"$PYTHON" "$CONVERTER" \
|
|
"$DUMMY" \
|
|
"$GBR_FILE" \
|
|
--output "$OUT_PM4N" \
|
|
--exposure "$EXPOSURE" \
|
|
"${FLAGS[@]}"
|
|
done
|
|
|
|
log "=== Done ===" |