3dSynth

Tutorials / Custom G-code

Open in the interactive tutorials reader

---
title: Tutorial 39 Real-Time G-code Streaming
description: Playing the printer like an instrument.
---

Tutorial 39 Real-Time G-code Streaming

1. Lesson Header

2. Concept Introduction

The SD Card vs USB.
Most prints run from an SD card (buffered, reliable).
But for interactivity, we need USB Streaming.
The host (PC) sends a line (G1 X10).
The printer replies (ok).
The host sends the next line.
This allows us to change the plan mid-print.

3. Machine State Explanation

The Buffer.
The printer has a small buffer (16-32 commands).
If you send too fast, you overflow it (Printer crashes/stalls).
If you send too slow, the printer stutters (buffer underrun).
Flow Control: Wait for ok before sending the next line.

4. Command Breakdown

5. Minimal Working Example

The Sender Script.

import serial
import time

ser = serial.Serial('COM3', 115200)
time.sleep(2) # Wait for reboot

def send(cmd):
    ser.write((cmd + '\n').encode())
    while True:
        line = ser.readline().decode().strip()
        if line == 'ok': break

send("G28")
send("G1 X10 F3000")

6. Visual Representation

Interactive preview is available in the interactive reader.

7. Build Exercise

Task: Connect a Game Controller (or Keyboard) to move the printer.
Use pygame or keyboard library.
- Arrow Keys: Move X/Y.
- Space: Extrude.
- Enter: Home.

Algorithm:
Loop:
1. Read input.
2. Calculate delta_x, delta_y.
3. Send G1 X{current_x + delta_x} Y{current_y + delta_y} via relative mode (G91).
4. Wait for ok.

8. Deep Insight Section

Latency.
USB is slow.
Sending G1 X0.1 repeatedly at 60Hz floods the buffer.
Solution: Send longer moves (G1 X10) but interrupt them? No, standard G-code can't be interrupted easily.
Better Solution: Send small moves but manage the queue depth to keep it full (smooth motion) but not too full (low latency).
This is how Klipper works (mostly).

9. Common Failure Modes

  1. Buffer Underrun: If your Python script pauses (GC, slow calculation), the printer stops. The print gets a blob.
  2. Baud Rate Mismatch: 115200 vs 250000. Garbage characters.

10. Real-World Application

Sand Tables (Sisyphus).
These kinetic art tables run G-code generated on the fly by algorithms (Perlin noise, geometric patterns) that react to music or user input.
The "Slicer" runs in real-time on a Raspberry Pi.

11. Final Clean Version

The Keyboard Jogger:

import serial
import time
import keyboard # pip install keyboard

# Connect
ser = serial.Serial('COM3', 115200)
time.sleep(2)
ser.write(b"G91\n") # Relative Mode
ser.write(b"G1 F3000\n")

print("Use Arrow Keys to Move. ESC to Quit.")

while True:
    cmd = None
    if keyboard.is_pressed('up'): cmd = "G1 Y10"
    elif keyboard.is_pressed('down'): cmd = "G1 Y-10"
    elif keyboard.is_pressed('left'): cmd = "G1 X-10"
    elif keyboard.is_pressed('right'): cmd = "G1 X10"
    elif keyboard.is_pressed('esc'): break
    
    if cmd:
        ser.write((cmd + '\n').encode())
        # Simple blocking wait (bad for smooth motion, good for safety)
        while True:
            if ser.in_waiting:
                line = ser.readline().decode().strip()
                if line == 'ok': break
        time.sleep(0.1) # Debounce

ser.close()

12. Stretch Challenge

Challenge: Write a "Drawing Bot".
Use your mouse to draw on a canvas (Tkinter/PyGame).
Stream the coordinates to the printer in real-time.
Map the screen (800x600) to the bed (200x200).
Hint: You need a "Pen Up / Pen Down" logic (Z-hop).