Command Line Appendices — Complete Reference
Supplemental automation references for integrating OpenSCAD and 3dMake workflows with the three command-line environments covered in this curriculum. Each appendix is self-contained and maps directly to the corresponding CLI foundation curriculum.
Contents
| Appendix | Shell | Title |
|---|---|---|
| Appendix A | CMD / Batch | Command Line (CMD/Batch) Integration for SCAD Workflows |
| Appendix B | PowerShell | PowerShell Integration for SCAD Workflows |
| Appendix C | Git Bash / POSIX | Git Bash / POSIX Shell Integration for SCAD Workflows |
All three appendices cover the same workflow stages — single-file build, batch directory build, parametric sweeps, logging, USB transfer, and network printer monitoring — using the idioms and tools native to each shell.
Appendix A: Command Line (CMD/Batch) Integration for SCAD Workflows
This appendix shows how traditional command-line (Windows CMD / batch) scripting automates SCAD workflows without requiring PowerShell. It mirrors the patterns in Appendix D but provides examples and idioms for cmd / batch files and systems that prefer native Windows command prompt scripts.
Overview: Why Automate with CMD/Batch?
- Minimal dependencies: works on basic Windows installations.
- Easy to call from other tools and CI systems that expect
.bathelpers. - Useful for environments where PowerShell policy restrictions exist.
Prerequisites & Setup
Required Software
where openscad :: OpenSCAD (path in PATH or full path required)
where prusa-slicer :: PrusaSlicer (or your slicer)
where python :: Python (optional)
Directory Structure (Windows style)
C:\Projects\3dMake\
+------ src\
+------ stl\
+------ gcode\
+------ logs\
+------ scripts\
+------ build.bat
+------ batch_build.bat
Notes on CMD vs PowerShell
- CMD/batch has more limited error handling and no structured objects; rely on return codes and file checks.
- Use full executable paths to avoid PATH surprises.
Basic Workflow: Single-file build (batch)
Create build.bat (simple example):
@echo off
rem build.bat - Convert SCAD -> STL -> G-code (minimal)
setlocal enabledelayedexpansion
set PROJECTROOT=%~dp0\..\
set SCADFILE=%1
if "%SCADFILE%"=="" (
echo Usage: build.bat file.scad
exit /b 1
)
set SCADPATH=%PROJECTROOT%src\%SCADFILE%
set STLPATH=%PROJECTROOT%stl\%~n1.stl
set GCODEPATH=%PROJECTROOT%gcode\%~n1.gcode
rem Export STL using OpenSCAD
"C:\Program Files\OpenSCAD\openscad.exe" -o "%STLPATH%" "%SCADPATH%"
if not exist "%STLPATH%" (
echo ERROR: STL not created
exit /b 1
)
rem Slice (example) - adjust path to slicer
"C:\Program Files\Prusa3D\PrusaSlicer\prusa-slicer.exe" --load-config-file "%~dp0\default.ini" -o "%GCODEPATH%" "%STLPATH%"
if not exist "%GCODEPATH%" (
echo ERROR: G-code not created
exit /b 1
)
echo BUILD COMPLETE: %GCODEPATH%
endlocal
Batch (directory) build
batch_build.bat can iterate files and call build.bat:
@echo off
set PROJECTROOT=%~dp0\..\
for /r "%PROJECTROOT%src" %%f in (*.scad) do (
echo Processing %%~nxf
call "%~dp0build.bat" "%%~nxf"
)
Parametric Sweeps (simple templating)
Because batch is limited for text templating, a common pattern is to use small helper scripts with sed/python or write a minimal templating helper in a language such as Python. Example invocation in batch:
rem Example: call a Python script to generate variants, then build
python "%~dp0/generate_variants.py" "%PROJECTROOT%src\bracelet_holder.scad"
call "%~dp0/batch_build.bat"
Logging and CSV output
Batch scripts can append simple CSV lines using echo redirection:
echo %DATE% %TIME%,%~n1,Success >> "%PROJECTROOT%logs\build_history.csv"
Send to Printer (USB copy)
rem Copy gcode to removable drive (E: example)
copy "%GCODEPATH%" E:\
Monitoring (networked printer) - using curl or PowerShell helper
curl on Windows or a small PowerShell one-liner may be used to query APIs. For pure CMD, ship a small helper .exe (curl) or call powershell -Command "Invoke-RestMethod ...".
Best Practices
- Use full paths for all tools.
- Check return codes (
if errorlevel 1) after each step. - Keep batch scripts small; delegate complex text processing to Python/PowerShell.
- Log to simple CSVs for human and machine parsing.
Appendix B: PowerShell Integration for SCAD Workflows
This appendix shows how PowerShell (command-line scripting) streamlines 3D design workflows, automating the repetitive tasks from design through printing. It bridges PowerShell_Foundation concepts with 3dMake_Foundation practical applications.
Referenced in: Lessons 9 (Automation), 10 (Mastery), and any lesson requiring batch operations
Prerequisites: Completion of PowerShell_Foundation (Lessons 1-6)
Overview: Why Automate SCAD Workflows?
Manual Workflow (Time-Consuming)
- Open SCAD manually -> Edit parameters -> Save -> Export STL
- Open Slicer manually -> Load STL -> Adjust settings -> Slice -> Export G-code
- Transfer G-code to printer via USB
- Record results in notebook
- Repeat for next variation (variant 2, variant 3, etc.)
Time: ~20-30 minutes per design iteration
Automated Workflow (Fast & Reproducible)
- Create PowerShell script with design parameters
- Script automatically:
- Generates SCAD code
- Exports to STL
- Slices to G-code
- Transfers to printer
- Logs results
- Run script once, walk away
- Check results later
Time: 2-3 minutes per iteration (plus print time)
Benefits of Automation
- [YES] Speed: 10x faster for batch operations
- [YES] Consistency: No manual errors
- [YES] Reproducibility: Same settings every time
- [YES] Scalability: Test 5, 10, or 100 variations easily
- [YES] Logging: Automatic documentation
- [YES] Accessibility: Screen reader captures script output (not UI clicks)
Prerequisites & Setup
Required Software
# Check what you have installed
where openscad # OpenSCAD
where prusa-slicer # PrusaSlicer (or your slicer)
where python3 # Python (optional, for advanced scripts)
Directory Structure
Create a project directory:
C:\Projects\3dMake\
+------ src/
| +------ bracelet_holder.scad
| +------ phone_stand.scad
| +------ stackable_bins.scad
+------ stl/
| +------ bracelet_holder.stl
| +------ phone_stand.stl
| +------ stackable_bins.stl
+------ gcode/
| +------ bracelet_holder.gcode
| +------ phone_stand.gcode
| +------ stackable_bins.gcode
+------ logs/
| +------ batch_2024-01-15.log
| +------ print_history.csv
+------ scripts/
+------ build.ps1 (main build script)
+------ batch_test.ps1 (test variations)
+------ monitor.ps1 (monitor printer)
PowerShell Execution Policy
# Check current policy
Get-ExecutionPolicy
# If it's "Restricted", change it (requires admin)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# Verify change
Get-ExecutionPolicy
Basic Workflow Automation
Script 1: Single-File Build
This script takes a SCAD file and converts it through the entire workflow:
# build.ps1 - Convert SCAD -> STL -> G-code
param(
[string]$ScadFile, # Input: bracelet_holder.scad
[string]$OutputDir = ".\", # Output directory
[string]$SlicerConfig = "default"
)
# Set up paths
$ProjectRoot = "C:\Projects\3dMake"
$ScadFile = Join-Path $ProjectRoot "src" $ScadFile
$StlFile = Join-Path $ProjectRoot "stl" ([System.IO.Path]::GetFileNameWithoutExtension($ScadFile) + ".stl")
$GcodeFile = Join-Path $ProjectRoot "gcode" ([System.IO.Path]::GetFileNameWithoutExtension($ScadFile) + ".gcode")
$LogFile = Join-Path $ProjectRoot "logs" "build_$(Get-Date -Format 'yyyy-MM-dd').log"
# Log function
function Write-Log {
param([string]$Message)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "[$timestamp] $Message"
Write-Host $logEntry
Add-Content -Path $LogFile -Value $logEntry
}
Write-Log "Starting build for $($ScadFile | Split-Path -Leaf)"
# Step 1: Export STL
Write-Log "Step 1: Exporting SCAD to STL..."
$startTime = Get-Date
& "C:\Program Files\OpenSCAD\openscad.exe" `
-o "$StlFile" `
"$ScadFile"
$duration = (Get-Date) - $startTime
Write-Log " STL exported in $($duration.TotalSeconds) seconds: $StlFile"
# Verify STL exists
if (-not (Test-Path $StlFile)) {
Write-Log "[NO] ERROR: STL file not created"
exit 1
}
# Step 2: Slice to G-code
Write-Log "Step 2: Slicing STL to G-code..."
$startTime = Get-Date
& "C:\Program Files\Prusa3D\PrusaSlicer\prusa-slicer.exe" `
--load-config-file "$SlicerConfig" `
--export-gcode "$GcodeFile" `
"$StlFile"
$duration = (Get-Date) - $startTime
Write-Log " G-code generated in $($duration.TotalSeconds) seconds: $GcodeFile"
# Verify G-code exists
if (-not (Test-Path $GcodeFile)) {
Write-Log "[NO] ERROR: G-code file not created"
exit 1
}
# Step 3: Get file info
$stlSize = (Get-Item $StlFile).Length / 1MB
$gcodeSize = (Get-Item $GcodeFile).Length / 1MB
Write-Log "File sizes: STL=$([math]::Round($stlSize, 2))MB, G-code=$([math]::Round($gcodeSize, 2))MB"
Write-Log " BUILD COMPLETE"
Write-Log "Ready to print: $GcodeFile"
Usage:
# Run the script
.\build.ps1 -ScadFile "bracelet_holder.scad"
# Output example:
# [2024-01-15 14:30:22] Starting build for bracelet_holder.scad
# [2024-01-15 14:30:23] Step 1: Exporting SCAD to STL...
# [2024-01-15 14:30:25] STL exported in 2.41 seconds: ...
# [2024-01-15 14:30:26] Step 2: Slicing STL to G-code...
# [2024-01-15 14:30:28] G-code generated in 2.31 seconds: ...
# [2024-01-15 14:30:28] BUILD COMPLETE
Script 2: Batch Build (Multiple Files)
# batch_build.ps1 - Build all SCAD files in a directory
param(
[string]$SourceDir = "C:\Projects\3dMake\src",
[string]$SlicerConfig = "default"
)
$ProjectRoot = Split-Path $SourceDir
$buildScript = Join-Path $ProjectRoot "scripts" "build.ps1"
# Find all SCAD files
$scadFiles = Get-ChildItem -Path $SourceDir -Filter "*.scad" -Recurse
Write-Host "Found $($scadFiles.Count) SCAD files"
if ($scadFiles.Count -eq 0) {
Write-Host "No SCAD files found in $SourceDir"
exit 1
}
# Process each file
$results = @()
foreach ($file in $scadFiles) {
Write-Host "`n--- Processing: $($file.Name) ---"
$startTime = Get-Date
# Run build script for this file
& $buildScript -ScadFile $file.Name -SlicerConfig $SlicerConfig
$duration = (Get-Date) - $startTime
$results += [PSCustomObject]@{
FileName = $file.Name
DurationSeconds = $duration.TotalSeconds
Status = "Success"
Timestamp = Get-Date
}
}
# Summary report
Write-Host "`n=== BATCH BUILD SUMMARY ==="
Write-Host "Files processed: $($results.Count)"
Write-Host "Total time: $([math]::Round(($results | Measure-Object -Property DurationSeconds -Sum).Sum, 1)) seconds"
# Save results to CSV
$csvPath = Join-Path $ProjectRoot "logs" "batch_$(Get-Date -Format 'yyyy-MM-dd-HHmmss').csv"
$results | Export-Csv -Path $csvPath -NoTypeInformation
Write-Host "Results saved: $csvPath"
Usage:
.\batch_build.ps1 -SourceDir "C:\Projects\3dMake\src"
# Processes all .scad files automatically
Parametric Design Variation Testing
The Problem: Testing Multiple Parameter Values
You want to test the same design with different parameters (e.g., peg diameter: 5mm, 6mm, 7mm):
Manually:
- Open bracelet_holder.scad
- Change peg_diameter = 5
- Save, export, slice, print
- Change peg_diameter = 6
- Save, export, slice, print
- Change peg_diameter = 7
- Save, export, slice, print Time: 30 minutes
Automated Solution: Parametric Sweep
# parametric_sweep.ps1 - Test multiple parameter values
param(
[string]$TemplateScad = "C:\Projects\3dMake\src\bracelet_holder.scad",
[hashtable]$ParameterRanges = @{
"peg_diameter" = @(5, 5.5, 6, 6.5, 7)
"holder_width" = @(120, 127, 135)
}
)
$ProjectRoot = "C:\Projects\3dMake"
$variantsDir = Join-Path $ProjectRoot "variants"
$buildScript = Join-Path $ProjectRoot "scripts" "build.ps1"
$logFile = Join-Path $ProjectRoot "logs" "parametric_sweep_$(Get-Date -Format 'yyyy-MM-dd-HHmmss').csv"
# Create variants directory
New-Item -ItemType Directory -Path $variantsDir -Force | Out-Null
# Read template
$template = Get-Content $TemplateScad -Raw
# Generate variants
$results = @()
$variantNum = 0
# For each diameter value
foreach ($dia in $ParameterRanges["peg_diameter"]) {
# For each width value
foreach ($width in $ParameterRanges["holder_width"]) {
$variantNum++
$variantName = "variant_dia${dia}_width${width}"
$variantScad = Join-Path $variantsDir "$variantName.scad"
# Create variant SCAD file with parameters
$content = $template `
-replace 'peg_diameter = [\d.]+', "peg_diameter = $dia" `
-replace 'holder_width = [\d.]+', "holder_width = $width"
$content | Set-Content $variantScad
Write-Host "Generated: $variantName"
# Build variant (STL + G-code)
$startTime = Get-Date
# Export to STL
& "C:\Program Files\OpenSCAD\openscad.exe" `
-o (Join-Path $ProjectRoot "stl" "$variantName.stl") `
$variantScad
$duration = (Get-Date) - $startTime
# Record result
$results += [PSCustomObject]@{
Variant = $variantName
PegDiameter = $dia
HolderWidth = $width
BuildTimeSeconds = $duration.TotalSeconds
Status = "Success"
Timestamp = Get-Date
}
Write-Host " Built in $($duration.TotalSeconds) seconds"
}
}
# Save results
$results | Export-Csv -Path $logFile -NoTypeInformation
Write-Host "`nParametric sweep complete: $($results.Count) variants generated"
Write-Host "Results: $logFile"
# Summary statistics
Write-Host "`n=== PARAMETER RANGES TESTED ==="
Write-Host "Peg diameters: $($ParameterRanges['peg_diameter'] -join ', ')"
Write-Host "Holder widths: $($ParameterRanges['holder_width'] -join ', ')"
Write-Host "Total combinations: $($results.Count)"
Usage:
$ranges = @{
"peg_diameter" = @(5, 5.5, 6, 6.5, 7)
"holder_width" = @(120, 127, 135)
}
.\parametric_sweep.ps1 -TemplateScad "bracelet_holder.scad" -ParameterRanges $ranges
# Generates 15 variants automatically (5 x 3)
Output CSV:
Variant,PegDiameter,HolderWidth,BuildTimeSeconds,Status,Timestamp
variant_dia5_width120,5,120,2.4,Success,2024-01-15 14:35:22
variant_dia5_width127,5,127,2.5,Success,2024-01-15 14:35:25
variant_dia5_width135,5,135,2.3,Success,2024-01-15 14:35:28
...
Automated Print Documentation
Script: Print Logging & Quality Tracking
# log_print.ps1 - Document each print with metadata
param(
[string]$GcodeFile,
[string]$ProjectName,
[string]$Material = "PLA",
[string]$Notes = ""
)
$ProjectRoot = "C:\Projects\3dMake"
$printLogCsv = Join-Path $ProjectRoot "logs" "print_history.csv"
# Get G-code file info
$gcodeInfo = Get-Item $GcodeFile
$gcodeSize = $gcodeInfo.Length / 1MB
$estimatedTime = EstimateTime $GcodeFile # See function below
# Calculate estimated cost
$weightG = EstimateWeight $GcodeFile
$materialCostPerKg = 20 # PLA at $20/kg
$estimatedCost = ($weightG / 1000) * $materialCostPerKg
# Create log entry
$entry = [PSCustomObject]@{
PrintID = "PRINT_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
ProjectName = $ProjectName
Date = Get-Date
Material = $Material
GcodeFile = $gcodeFile
GcodeSizeMB = [math]::Round($gcodeSize, 2)
EstimatedTimeHours = [math]::Round($estimatedTime / 3600, 2)
EstimatedWeightG = [math]::Round($weightG, 1)
EstimatedCostUSD = [math]::Round($estimatedCost, 2)
Notes = $Notes
}
# Append to CSV
if (Test-Path $printLogCsv) {
$entry | Export-Csv -Path $printLogCsv -NoTypeInformation -Append
} else {
$entry | Export-Csv -Path $printLogCsv -NoTypeInformation
}
Write-Host "Print logged:"
Write-Host " Project: $ProjectName"
Write-Host " File: $($gcodeInfo.Name)"
Write-Host " Estimated time: $([math]::Round($estimatedTime / 3600, 2)) hours"
Write-Host " Estimated weight: $([math]::Round($weightG, 1))g"
Write-Host " Estimated cost: $$([math]::Round($estimatedCost, 2))"
# Function to estimate print time from G-code (simplified)
function EstimateTime {
param([string]$GcodeFile)
$lines = @(Get-Content $GcodeFile | Select-String "^G1" | Measure-Object).Count
# Rough estimate: 10 lines per second
return $lines / 10
}
# Function to estimate weight from G-code (simplified)
function EstimateWeight {
param([string]$GcodeFile)
# G-code filament estimate (if supported by your slicer)
$content = Get-Content $GcodeFile -Raw
if ($content -match "filament used = ([\d.]+)") {
return [double]$matches[1]
} else {
return 0 # Fallback
}
}
Usage:
.\log_print.ps1 `
-GcodeFile "C:\Projects\3dMake\gcode\bracelet_holder.gcode" `
-ProjectName "Bracelet Holder" `
-Material "PLA" `
-Notes "Final design, tested with actual bracelets"
Printer Communication & Monitoring
Script: Send G-code to Printer (USB)
# send_to_printer.ps1 - Copy G-code to printer USB
param(
[string]$GcodeFile,
[string]$PrinterUSBLetter = "E" # E:, F:, G:, etc.
)
$printerDrive = "${PrinterUSBLetter}:\"
# Check if USB drive connected
if (-not (Test-Path $printerDrive)) {
Write-Host "[NO] ERROR: Printer USB not found at $PrinterUSBLetter"
Write-Host "Available drives:"
Get-PSDrive -PSProvider FileSystem | Where-Object Name -Like "[D-Z]" | Select-Object Name
exit 1
}
# Copy file
$filename = Split-Path $GcodeFile -Leaf
$destination = Join-Path $printerDrive $filename
Copy-Item -Path $GcodeFile -Destination $destination -Force
Write-Host " G-code copied to printer USB:"
Write-Host " From: $GcodeFile"
Write-Host " To: $destination"
Write-Host "`nNext steps:"
Write-Host " 1. Eject USB safely from computer"
Write-Host " 2. Insert USB into printer"
Write-Host " 3. Select file on printer screen"
Write-Host " 4. Press print"
Usage:
.\send_to_printer.ps1 -GcodeFile "C:\Projects\3dMake\gcode\bracelet_holder.gcode" -PrinterUSBLetter "E"
Script: Monitor Printer Status (Network Printers)
# monitor_printer.ps1 - Check printer status via API
param(
[string]$PrinterIP,
[int]$CheckInterval = 30, # seconds
[int]$MaxChecks = 240 # 2 hours max
)
$printerUrl = "[https://example.com](https://example.com)
$checksPerformed = 0
Write-Host "Monitoring printer at $PrinterIP"
Write-Host "Check interval: $CheckInterval seconds"
Write-Host "Ctrl+C to stop`n"
while ($checksPerformed -lt $MaxChecks) {
try {
# Get printer status
$response = Invoke-WebRequest -Uri $printerUrl -UseBasicParsing -ErrorAction Stop
$status = $response.Content | ConvertFrom-Json
$state = $status.state.text
$progress = $status.progress.completion
$timeRemaining = $status.progress.printTimeLeft
Write-Host "[$((Get-Date).ToString("HH:mm:ss"))] State: $state | Progress: $progress% | Time remaining: $timeRemaining seconds"
# If print completed, exit loop
if ($state -eq "Operational") {
Write-Host "`n Print complete!"
break
}
} catch {
Write-Host "Connection error: $_"
}
Start-Sleep -Seconds $CheckInterval
$checksPerformed++
}
if ($checksPerformed -ge $MaxChecks) {
Write-Host "Max monitoring time reached. Stopping."
}
Usage:
.\monitor_printer.ps1 -PrinterIP "192.168.1.100" -CheckInterval 30
Complete Workflow Integration
Master Script: Design -> Print -> Log
# full_workflow.ps1 - Complete automation from design to printing
param(
[string]$ProjectName,
[string]$ScadFile,
[string]$Material = "PLA",
[string]$Notes = "",
[switch]$SendToPrinter,
[string]$PrinterUSBLetter = "E"
)
$ProjectRoot = "C:\Projects\3dMake"
Write-Host "=== 3dMake Full Workflow Automation ==="
Write-Host "Project: $ProjectName"
Write-Host ""
# Step 1: Build (SCAD -> STL -> G-code)
Write-Host "Step 1: Building..."
& "$ProjectRoot\scripts\build.ps1" -ScadFile $ScadFile
if ($LASTEXITCODE -ne 0) { exit 1 }
# Step 2: Log the print
Write-Host "`nStep 2: Logging print metadata..."
$gcodeFile = Join-Path $ProjectRoot "gcode" ([System.IO.Path]::GetFileNameWithoutExtension($ScadFile) + ".gcode")
& "$ProjectRoot\scripts\log_print.ps1" `
-GcodeFile $gcodeFile `
-ProjectName $ProjectName `
-Material $Material `
-Notes $Notes
# Step 3: Send to printer (optional)
if ($SendToPrinter) {
Write-Host "`nStep 3: Sending to printer..."
& "$ProjectRoot\scripts\send_to_printer.ps1" `
-GcodeFile $gcodeFile `
-PrinterUSBLetter $PrinterUSBLetter
}
Write-Host "`n Workflow complete!"
Usage:
# Full workflow with automatic printer transfer
.\full_workflow.ps1 `
-ProjectName "Bracelet Holder" `
-ScadFile "bracelet_holder.scad" `
-Material "PLA" `
-Notes "Final design, v2 with improved peg strength" `
-SendToPrinter `
-PrinterUSBLetter "E"
PowerShell Skills Applied to SCAD
Lesson Mapping: PowerShell -> SCAD Workflows
| PowerShell Lesson | SCAD Application | Example |
|---|---|---|
| PS 1: Navigation | Working with project directories | Using cd to navigate src/ -> stl/ -> gcode/ |
| PS 2: File Manipulation | Copying, organizing SCAD files | Copy design files, organize variants by date |
| PS 3: Piping & Objects | Pass data between scripts | $results | Export-Csv exports analysis |
| PS 4: Variables & Aliases | Parameterize SCAD workflows | Variables store file paths, material types, etc. |
| PS 5: Functions & Modules | Reusable automation blocks | Build, slice, log = functions in one script |
| PS Unit Test | Verify workflow correctness | Test that STL file exists before slicing |
Example: File Navigation with SCAD Projects
# Navigate between SCAD project folders
$ProjectRoot = "C:\Projects\3dMake"
# Go to source directory
cd "$ProjectRoot\src"
Get-ChildItem -Filter "*.scad" # List all SCAD files
# Process each file
foreach ($file in Get-ChildItem -Filter "*.scad") {
Write-Host "Processing: $($file.Name)"
# Do something with each file
}
# Go to output directory and check results
cd "$ProjectRoot\stl"
Get-ChildItem -Filter "*.stl" | Measure-Object -Sum
Example: Working with Piping & Objects
# Build all files, then get statistics
Get-ChildItem -Path "$ProjectRoot\src" -Filter "*.scad" |
ForEach-Object {
# Build each one
& .\build.ps1 -ScadFile $_.Name
} |
# Analyze results
Group-Object -Property Status |
Select-Object Name, Count
Accessibility Considerations
Why PowerShell for SCAD?
- Screen Reader Friendly: Console output = text (not UI clicks)
- Scriptable: Can run overnight, results logged
- Auditable: Every step written to log file
- Shareable: Scripts document the process for others
- Testable: Can verify each step independently
Example: Accessible Build Output
[2024-01-15 14:30:22] Starting build for bracelet_holder.scad
[2024-01-15 14:30:23] Step 1: Exporting SCAD to STL...
[2024-01-15 14:30:25] STL exported in 2.41 seconds
[2024-01-15 14:30:26] Step 2: Slicing STL to G-code...
[2024-01-15 14:30:28] G-code generated in 2.31 seconds
[2024-01-15 14:30:28] File sizes: STL=2.15MB, G-code=4.32MB
[2024-01-15 14:30:28] BUILD COMPLETE
Ready to print: C:\Projects\3dMake\gcode\bracelet_holder.gcode
All information is text-based and sequential-perfectly accessible to screen readers.
Best Practices & Tips
1. Always Log Everything
function Write-Log {
param(
[string]$Message,
[string]$LogFile
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$entry = "[$timestamp] $Message"
Write-Host $entry
Add-Content -Path $LogFile -Value $entry
}
2. Verify Steps Succeed Before Continuing
# Don't just run commands-check they worked
& "C:\Program Files\OpenSCAD\openscad.exe" -o "$StlFile" "$ScadFile"
if (-not (Test-Path $StlFile)) {
Write-Log "ERROR: STL creation failed"
exit 1
}
Write-Log " STL created successfully"
3. Make Scripts Reusable
# BAD: Hardcoded paths
$scadFile = "C:\Users\John\Desktop\bracelet_holder.scad"
# GOOD: Parameters with defaults
param(
[string]$ScadFile = "bracelet_holder.scad",
[string]$ProjectRoot = "C:\Projects\3dMake"
)
$fullPath = Join-Path $ProjectRoot "src" $ScadFile
4. Use Configuration Files
# config.ps1 - Centralized settings
$Config = @{
ProjectRoot = "C:\Projects\3dMake"
OpenSCADPath = "C:\Program Files\OpenSCAD\openscad.exe"
SlicerPath = "C:\Program Files\Prusa3D\PrusaSlicer\prusa-slicer.exe"
PrinterUSB = "E"
DefaultMaterial = "PLA"
DefaultInfill = 20
}
# Use in scripts:
# . .\config.ps1
# & $Config.OpenSCADPath ...
Troubleshooting Automated Workflows
| Problem | Cause | Solution |
|---|---|---|
| Scripts won’t run | Execution policy | Set-ExecutionPolicy -ExecutionPolicy RemoteSigned |
| Command not found | Tool not in PATH | Specify full path: & "C:\Program Files\OpenSCAD\openscad.exe" |
| Files not created | Wrong working directory | Use absolute paths with Join-Path |
| Script hangs | Waiting for user input | Disable UI input; use -o flag for batch operations |
| CSV data corrupted | Special characters in notes | Properly quote strings in CSV export |
| No output/logging | Path doesn’t exist | Create directory first: New-Item -Type Directory -Path ... -Force |
Summary: PowerShell + SCAD Integration Benefits
Time Savings
| Task | Manual | Automated | Savings |
|---|---|---|---|
| Single design build | 5 min | 2.5 min | 50% |
| Test 10 parameter variations | 50 min | 5 min | 90% |
| Batch processing 20 designs | 100 min | 10 min | 90% |
Consistency
- [YES] Same process every time (no missed steps)
- [YES] Reproducible results (exact same settings)
- [YES] Comprehensive logging (documentation automatic)
Scalability
- [YES] 1 design or 1,000 designs = same time commitment
- [YES] Run overnight for batch operations
- [YES] Easy to expand with new features
Accessibility
- [YES] All interaction is text-based (screen reader friendly)
- [YES] Results logged in CSV (machine-readable, sortable)
- [YES] Repeatable (can verify steps later)
Next Steps
- Try the basic build script (Script 1) with one of your SCAD files
- Add logging (Script 3) to track what you create
- Test parametric sweeps (Script 2) with design variations
- Integrate full workflow (Script 5) for complete automation
- Customize config.ps1 for your specific setup
Remember: Start simple, test each script individually, then combine them into larger workflows. PowerShell is most powerful when built incrementally!
Appendix C: Git Bash / POSIX Shell Integration for SCAD Workflows
This appendix mirrors Appendix D (PowerShell) but provides examples for Git Bash (mingw) or other POSIX-compatible shells on Windows, macOS, and Linux. Use these scripts when you prefer shell utilities (bash, sed, awk, curl) and Unix-like tooling.
Overview: Why Git Bash / POSIX Shell?
- Leverage standard Unix tools for text processing and automation.
- Cross-platform: same scripts often work on Linux/macOS and Windows via Git Bash.
- Good fit for containerized CI like GitHub Actions.
Prerequisites & Setup
Required Software
command -v openscad # OpenSCAD
command -v prusa-slicer # PrusaSlicer (or use CLI slicer)
command -v python3 # Python (optional)
command -v curl # curl for API calls
Directory Structure (POSIX-style)
~/projects/3dMake/
|- src/
|- stl/
|- gcode/
|- logs/
`- scripts/
|- build.sh
`- batch_build.sh
Basic Workflow: Single-file build (bash)
scripts/build.sh:
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
SCAD_FILE="$1"
SCAD_PATH="$ROOT_DIR/src/$SCAD_FILE"
STL_PATH="$ROOT_DIR/stl/${SCAD_FILE%.*}.stl"
GCODE_PATH="$ROOT_DIR/gcode/${SCAD_FILE%.*}.gcode"
echo "Exporting SCAD -> STL: $SCAD_FILE"
"/c/Program Files/OpenSCAD/openscad.exe" -o "$STL_PATH" "$SCAD_PATH" || { echo "STL export failed"; exit 1; }
echo "Slicing STL -> G-code"
"/c/Program Files/Prusa3D/PrusaSlicer/prusa-slicer.exe" --load-config-file "$ROOT_DIR/scripts/default.ini" -o "$GCODE_PATH" "$STL_PATH" || { echo "Slicing failed"; exit 1; }
echo "BUILD COMPLETE: $GCODE_PATH"
Notes:
- Paths to GUI apps on Windows may need the
/c/Program\ Files/...form under Git Bash, or call the native.exewith full quoted path. - On Linux/macOS adjust executable paths accordingly.
Batch Build (directory)
scripts/batch_build.sh:
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
find "$ROOT_DIR/src" -name "*.scad" -print0 | while IFS= read -r -d '' file; do
fname="$(basename "$file")"
echo "Building $fname"
"$ROOT_DIR/scripts/build.sh" "$fname"
done
Parametric Sweeps
Use sed or python to template parameter changes, then call build.sh for each variant. Example (bash + python hybrid):
python3 scripts/gen_variants.py "$ROOT_DIR/src/bracelet_holder.scad" --out variants/
for v in variants/*.scad; do
./scripts/build.sh "$(basename "$v")"
done
Logging & CSV
Append simple CSV lines using printf:
printf "%s,%s,Success\n" "$(date +'%Y-%m-%d %H:%M:%S')" "${SCAD_FILE%.*}" >> "$ROOT_DIR/logs/build_history.csv"
Send to Printer (USB)
# Copy to mounted USB drive (example mount point /media/usb)
cp "$GCODE_PATH" "/media/usb/"
Monitoring (network printers)
Use curl to call a printer API (e.g., OctoPrint):
curl -s "[https://example.com](https://example.com) -H "X-Api-Key: $API_KEY" | jq .
Best Practices
- Use
set -euo pipefailfor safer scripts. - Use
mktempor avariants/directory for generated files. - Keep heavy text processing to
pythonif logic becomes complex. - Make scripts executable (
chmod +x scripts/*.sh).