New Laser Build - raster engraving

More
8 years 2 months ago #116199 by andypugh
Use M67 to set the duty-cycle of the PWM.

Please Log in or Create an account to join the conversation.

More
8 years 2 months ago #116200 by kramdradoow
Oops. Andy not Pete. I'll have to research how to do that. Thx for tip.

Mark0

Please Log in or Create an account to join the conversation.

More
8 months 3 weeks ago #342700 by raggielyle1@gmail.com
Try this code
#!/bin/bash
# Laser Engraving Converter - Final Vertical Fix

echo "========================================"
echo " LASER ENGRAVING CONVERTER "
echo "========================================"
echo ""

# Get filename
read -p "Enter image name (without .png): " base_name

# Find the file
if [ -f "${base_name}.png" ]; then
png_file="${base_name}.png"
elif [ -f "${base_name}.PNG" ]; then
png_file="${base_name}.PNG"
elif [ -f "$base_name" ]; then
png_file="$base_name"
else
echo "ERROR: File '${base_name}' not found!"
read -p "Press Enter to exit..."
exit 1
fi

echo "Found: $png_file"
gcode_file="${base_name}laser.ngc"
echo "Output: $gcode_file"
echo ""

# Get settings
read -p "Enter target width in mm: " target_width_mm
read -p "Enter DPI [75]: " dpi_input
dpi=${dpi_input:-75}
read -p "Enter feed rate [500]: " feed_rate_input
feed_rate=${feed_rate_input:-500}
read -p "Enter safe Z height [10]: " safe_z_input
safe_z=${safe_z_input:-10}
read -p "Enter engrave Z height [0]: " engrave_z_input
engrave_z=${engrave_z_input:-0}
read -p "Enter minimum power [1]: " min_power_input
min_power=${min_power_input:-1}
read -p "Enter maximum power [500]: " max_power_input
max_power=${max_power_input:-500}
read -p "Cross-hatching? (y/n) [n]: " cross_hatch_input
cross_hatch=${cross_hatch_input:-n}

echo ""
echo "=== SETTINGS ==="
echo "Image: $png_file"
echo "Width: ${target_width_mm}mm"
echo "DPI: $dpi"
echo "Feed: ${feed_rate} mm/min"
echo "Safe Z: ${safe_z}mm"
echo "Engrave Z: ${engrave_z}mm"
echo "Power: ${min_power}-${max_power}"
echo "Cross-hatch: $cross_hatch"
echo ""

read -p "Continue? (y/n): " confirm
if ! "$confirm" =~ ^[Yy]$; then
echo "Cancelled."
read -p "Press Enter to exit..."
exit 0
fi

echo "Converting..."
echo ""

cat > /tmp/laser_final_fix.py << 'PYTHON_EOF'
import sys
import os
from PIL import Image

def main():
if len(sys.argv) != 11:
print("ERROR: Wrong number of arguments")
return 1

# Parse arguments
input_file = sys.argv[1]
target_width_mm = float(sys.argv[2])
dpi = int(sys.argv[3])
feed_rate = int(sys.argv[4])
safe_z = float(sys.argv[5])
engrave_z = float(sys.argv[6])
min_power = int(sys.argv[7])
max_power = int(sys.argv[8])
cross_hatch = sys.argv[9]
output_file = sys.argv[10]

print(f"Converting: {os.path.basename(input_file)}")
print(f"Power range: {min_power}-{max_power}")

try:
# Load image
img = Image.open(input_file)
if img.mode != 'L':
img = img.convert('L')

orig_w, orig_h = img.size

# Calculate new size
pixels_per_mm = dpi / 25.4
target_px_w = int(target_width_mm * pixels_per_mm)
aspect_ratio = orig_h / orig_w
target_px_h = int(target_px_w * aspect_ratio)

actual_mm_w = target_px_w / pixels_per_mm
actual_mm_h = target_px_h / pixels_per_mm
mm_per_pixel = 25.4 / dpi

print(f"Image size: {target_px_w}x{target_px_h} pixels")
print(f"Physical size: {actual_mm_w:.1f}x{actual_mm_h:.1f} mm")
print(f"mm per pixel: {mm_per_pixel:.3f}")

# Resize
img = img.resize((target_px_w, target_px_h))
pixels = list(img.getdata())

# Generate G-code
with open(output_file, 'w') as f:
# Header
f.write('; Laser engraving - FINAL FIX\n')
f.write(f'; Image: {os.path.basename(input_file)}\n')
f.write(f'; Size: {actual_mm_w:.1f}x{actual_mm_h:.1f} mm\n')
f.write(f'; DPI: {dpi}\n')
f.write(f'; Feed: {feed_rate}\n')
f.write(f'; Power: {min_power}-{max_power}\n\n')

# Setup
f.write('G21\nG90\nG64\nG17\nG54\n\n')

# Initialize
f.write(f'F{feed_rate}\n')
f.write(f'G0 Z{safe_z:.1f}\n')
f.write('G0 X0 Y0\n')
f.write(f'G0 Z{engrave_z:.1f}\n')
f.write('M3\n\n')

# HORIZONTAL PASSES
f.write('; --- HORIZONTAL PASSES ---\n')
print("\nGenerating horizontal passes...")

for y in range(target_px_h):
# Flip Y: image row y (0=top) -> machine Y (target_px_h-1-y)*mm_per_pixel
y_pos = (target_px_h - 1 - y) * mm_per_pixel

if y % 2 == 0:
# Left to right
f.write(f'G0 X0 Y{y_pos:.3f}\n')
for x in range(target_px_w):
pixel = pixels[y * target_px_w + x]
power = min_power + int((max_power - min_power) * (255 - pixel) / 255)
f.write(f'G1 X{x*mm_per_pixel:.3f} S{power}\n')
else:
# Right to left
f.write(f'G0 X{actual_mm_w:.3f} Y{y_pos:.3f}\n')
for x in range(target_px_w-1, -1, -1):
pixel = pixels[y * target_px_w + x]
power = min_power + int((max_power - min_power) * (255 - pixel) / 255)
f.write(f'G1 X{x*mm_per_pixel:.3f} S{power}\n')

if y % 50 == 0:
print(f" Row {y+1}/{target_px_h}")

# VERTICAL PASSES
if cross_hatch.lower() == 'y':
f.write('\n; --- VERTICAL PASSES (CROSS-HATCH) ---\n')
print("\nGenerating vertical passes...")
print("DEBUG: This should engrave on the way UP, not on the way DOWN!")

for x in range(target_px_w):
x_pos = x * mm_per_pixel

if x % 2 == 0:
# Even columns: bottom to top (engrave on the way UP)
f.write(f'G0 X{x_pos:.3f} Y0\n')
# We want to go from Y=0 to Y=max
# Read pixels from BOTTOM (image row target_px_h-1) to TOP (image row 0)
for step in range(target_px_h):
# step: 0 to target_px_h-1 (bottom to top in machine coords)
# image_y: target_px_h-1 to 0 (bottom to top in image)
image_y = target_px_h - 1 - step
pixel = pixels[image_y * target_px_w + x]
power = min_power + int((max_power - min_power) * (255 - pixel) / 255)
current_y = step * mm_per_pixel # 0 to max
f.write(f'G1 Y{current_y:.3f} S{power}\n')
else:
# Odd columns: top to bottom (engrave on the way DOWN)
f.write(f'G0 X{x_pos:.3f} Y{actual_mm_h:.3f}\n')
# We want to go from Y=max to Y=0
# Read pixels from TOP (image row 0) to BOTTOM (image row target_px_h-1)
for step in range(target_px_h):
# step: 0 to target_px_h-1 (represents position from top)
image_y = step # 0 to target_px_h-1 (top to bottom in image)
pixel = pixels[image_y * target_px_w + x]
power = min_power + int((max_power - min_power) * (255 - pixel) / 255)
current_y = actual_mm_h - (step * mm_per_pixel) # max to 0
f.write(f'G1 Y{current_y:.3f} S{power}\n')

if x % 50 == 0:
print(f" Column {x+1}/{target_px_w}")


# End program
f.write('\n; --- END ---\n')
f.write('M5\n')
f.write(f'G0 Z{safe_z:.1f}\n')
f.write('G0 X0 Y0\n')
f.write('M2\n')

print(f"\n✅ G-code written to: {output_file}")

# Show sample of vertical section
if cross_hatch.lower() == 'y' and os.path.exists(output_file):
with open(output_file, 'r') as f:
lines = f.readlines()

# Find vertical section
for i, line in enumerate(lines):
if 'VERTICAL PASSES' in line:
print("\n=== SAMPLE OF VERTICAL SECTION ===")
# Show first 20 lines of vertical section
for j in range(i, min(i+20, len(lines))):
print(f" {lines[j].rstrip()}")
print("...")
break

return 0

except Exception as e:
print(f"\n❌ ERROR: {e}")
import traceback
traceback.print_exc()
return 1

if __name__ == "__main__":
sys.exit(main())
PYTHON_EOF

# Run the Python script
python3 /tmp/laser_final_fix.py \
"$png_file" \
"$target_width_mm" \
"$dpi" \
"$feed_rate" \
"$safe_z" \
"$engrave_z" \
"$min_power" \
"$max_power" \
"$cross_hatch" \
"$gcode_file"

result=$?

echo ""
echo "========================================"
if [ $result -eq 0 ]; then
echo "CONVERSION COMPLETE!"
echo "File: $gcode_file"
echo ""

else
echo "CONVERSION FAILED!"
fi
echo ""

read -p "Press Enter to exit..."

Please Log in or Create an account to join the conversation.

More
8 months 6 days ago #343530 by andypugh
Pasting this into the message body has destroyed the indentation. As it contains embedded Python this means that it won't work at all.

Can you attach the script as a file instead?

(I tried correcting the file, but it became too difficult in the Python code without doing real code analysis)
The following user(s) said Thank You: tommylight

Please Log in or Create an account to join the conversation.

More
7 months 4 weeks ago #343703 by rdtsc
Guys, try the attached.  No idea if it works, but it's not giving any errors and outputs data. :)
The following user(s) said Thank You: tommylight

Please Log in or Create an account to join the conversation.

More
7 months 4 weeks ago #343707 by tommylight
I linked to a post containing absolutely everything you need, and as Andy noted, M67 should be used, not spindle stuff.

Please Log in or Create an account to join the conversation.

Moderators: snowgoer540
Time to create page: 0.514 seconds
Powered by Kunena Forum