tutorial update

This commit is contained in:
Martin Fuchs
2026-09-06 00:47:31 +02:00
parent b5692fb607
commit ef83f35fdd
4 changed files with 305 additions and 227 deletions
+5 -3
View File
@@ -9,13 +9,15 @@ p5-wallplotter/
├── tutorial.md # setup + plotter + G-code ├── tutorial.md # setup + plotter + G-code
├── hardware.md # motors, board, geometry ├── hardware.md # motors, board, geometry
├── gcode.md # G-code used by this setup ├── gcode.md # G-code used by this setup
├── plotter.svg # devices + signal path
├── lib/ ├── lib/
│ ├── WallPlotter.js # library │ ├── WallPlotter.js # library
│ ├── README.md # short intro + example │ ├── README.md # short intro + example
│ └── doc.md # full API │ └── doc.md # full API
└── examples/ ├── examples/
├── zickzack/ # example sketch │ ├── zickzack/ # example sketch
└── rechteck/ # rectangle sketch │ └── rechteck/ # rectangle sketch
└── sketches/ # your sketches
``` ```
## Run the example ## Run the example
+111
View File
@@ -0,0 +1,111 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1080 640" width="1080" height="640" role="img" aria-labelledby="title desc">
<title id="title">Wandplotter: Geräte und Signalweg</title>
<desc id="desc">Medientechnische Zeichnung. Laptop mit Chrome sendet G-code per USB an die SKR Mini E3 V3. Dort läuft Marlin und steuert zwei Schrittmotoren und den Stift-Servo der Gondel.</desc>
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
<path d="M 0 1.5 L 10 5 L 0 8.5 Z" fill="#1a1a1a"/>
</marker>
<style>
.label { font-family: Helvetica, Arial, sans-serif; fill: #1a1a1a; }
.small { font-size: 12px; }
.tiny { font-size: 11px; fill: #333; }
.title { font-size: 18px; font-weight: 700; }
.box-title { font-size: 13px; font-weight: 700; }
.muted { fill: #555; }
.line { fill: none; stroke: #1a1a1a; stroke-width: 1.6; }
.thin { fill: none; stroke: #1a1a1a; stroke-width: 1.2; }
.dash { fill: none; stroke: #1a1a1a; stroke-width: 1.2; stroke-dasharray: 5 4; }
.fill-paper { fill: #f7f5f0; }
.fill-device { fill: #ffffff; }
.fill-marlin { fill: #f3efe4; }
</style>
</defs>
<rect width="1080" height="640" fill="#faf9f6"/>
<text x="40" y="36" class="label title">Wandplotter  Geräte und Signalweg</text>
<text x="40" y="56" class="label tiny muted">p5.js im Browser sendet G-code. Marlin auf dem Controller bewegt die Motoren.</text>
<!-- Wall / plotter -->
<rect x="40" y="78" width="1000" height="268" class="fill-paper" stroke="#1a1a1a" stroke-width="1.2"/>
<text x="52" y="98" class="label tiny muted">Wand / Polargraph</text>
<!-- Motor X -->
<rect x="88" y="112" width="118" height="56" class="fill-device" stroke="#1a1a1a" stroke-width="1.6"/>
<circle cx="147" cy="140" r="11" fill="none" stroke="#1a1a1a" stroke-width="1.6"/>
<circle cx="147" cy="140" r="3" fill="#1a1a1a"/>
<text x="147" y="186" text-anchor="middle" class="label box-title">Motor X</text>
<text x="147" y="202" text-anchor="middle" class="label tiny muted">links · Riemen links</text>
<!-- Motor Y -->
<rect x="874" y="112" width="118" height="56" class="fill-device" stroke="#1a1a1a" stroke-width="1.6"/>
<circle cx="933" cy="140" r="11" fill="none" stroke="#1a1a1a" stroke-width="1.6"/>
<circle cx="933" cy="140" r="3" fill="#1a1a1a"/>
<text x="933" y="186" text-anchor="middle" class="label box-title">Motor Y</text>
<text x="933" y="202" text-anchor="middle" class="label tiny muted">rechts · Riemen rechts</text>
<!-- Idler line -->
<line x1="206" y1="140" x2="874" y2="140" class="thin"/>
<text x="540" y="128" text-anchor="middle" class="label tiny muted">Umlenkrollen · Abstand 1660 mm</text>
<!-- Belts -->
<line x1="147" y1="168" x2="540" y2="268" class="line"/>
<line x1="933" y1="168" x2="540" y2="268" class="line"/>
<!-- Gondola -->
<rect x="502" y="258" width="76" height="40" class="fill-device" stroke="#1a1a1a" stroke-width="1.6"/>
<line x1="540" y1="298" x2="540" y2="318" stroke="#1a1a1a" stroke-width="1.6"/>
<line x1="532" y1="318" x2="548" y2="318" stroke="#1a1a1a" stroke-width="2"/>
<text x="540" y="338" text-anchor="middle" class="label box-title">Gondel + Stift</text>
<text x="540" y="354" text-anchor="middle" class="label tiny muted">Position aus zwei Riemenlängen</text>
<!-- ========== Signal row ========== -->
<!-- Laptop -->
<rect x="40" y="380" width="280" height="200" class="fill-device" stroke="#1a1a1a" stroke-width="1.8"/>
<rect x="58" y="398" width="244" height="118" fill="#eceae4" stroke="#1a1a1a" stroke-width="1.2"/>
<text x="180" y="428" text-anchor="middle" class="label box-title">Laptop · Chrome</text>
<text x="180" y="450" text-anchor="middle" class="label small">p5.js · WallPlotter.js</text>
<text x="180" y="470" text-anchor="middle" class="label tiny muted">zeichnet Linien in Millimetern</text>
<text x="180" y="488" text-anchor="middle" class="label tiny muted">Web Serial</text>
<rect x="120" y="524" width="120" height="12" fill="#ddd" stroke="#1a1a1a" stroke-width="1"/>
<text x="180" y="562" text-anchor="middle" class="label tiny muted">1 Host</text>
<!-- USB arrow -->
<line x1="320" y1="480" x2="398" y2="480" class="line" marker-end="url(#arrow)"/>
<text x="360" y="466" text-anchor="middle" class="label box-title">G-code</text>
<text x="360" y="500" text-anchor="middle" class="label tiny muted">USB</text>
<!-- Controller / Marlin -->
<rect x="400" y="380" width="300" height="200" class="fill-device" stroke="#1a1a1a" stroke-width="1.8"/>
<text x="550" y="408" text-anchor="middle" class="label box-title">Controller SKR Mini E3 V3</text>
<rect x="422" y="424" width="256" height="88" class="fill-marlin" stroke="#1a1a1a" stroke-width="1.4"/>
<text x="550" y="452" text-anchor="middle" class="label box-title">Marlin (Polargraph)</text>
<text x="550" y="472" text-anchor="middle" class="label tiny">liest G1 X& Y& · inverse Kinematik</text>
<text x="550" y="490" text-anchor="middle" class="label tiny muted">Riemenlängen ’ Schrittimpulse</text>
<text x="550" y="536" text-anchor="middle" class="label tiny">Netzteil 24 V · USB nur Daten</text>
<text x="550" y="562" text-anchor="middle" class="label tiny muted">2 Steuerung</text>
<!-- Cables to motors -->
<path d="M 480 380 V 220 H 206" class="dash" marker-end="url(#arrow)"/>
<text x="250" y="214" class="label tiny">Schritte X</text>
<path d="M 620 380 V 220 H 874" class="dash" marker-end="url(#arrow)"/>
<text x="780" y="214" text-anchor="end" class="label tiny">Schritte Y</text>
<!-- Servo -->
<line x1="550" y1="380" x2="550" y2="300" class="dash" marker-end="url(#arrow)"/>
<text x="562" y="248" class="label tiny">Servo Stift</text>
<text x="562" y="262" class="label tiny muted">M280</text>
<!-- Legend -->
<rect x="740" y="380" width="300" height="200" class="fill-device" stroke="#1a1a1a" stroke-width="1.2"/>
<text x="756" y="408" class="label box-title">Ablauf</text>
<text x="756" y="436" class="label small">1 Chrome zeichnet den Pfad</text>
<text x="756" y="458" class="label small">2 G-code geht per USB raus</text>
<text x="756" y="480" class="label small">3 Marlin auf der SKR rechnet</text>
<text x="756" y="502" class="label small">4 Motoren wickeln die Riemen</text>
<text x="756" y="524" class="label small">5 Gondel fährt, Stift hebt / senkt</text>
<text x="756" y="554" class="label tiny muted">Antwort: Marlin sendet ok zurück</text>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

+3
View File
@@ -0,0 +1,3 @@
# sketches
Eigene Sketches hier ablegen, zum Beispiel *mein-sketch/*. Vorlagen kopieren aus *examples/*.
+186 -224
View File
@@ -1,121 +1,151 @@
# Tutorial: p5-wallplotter # Tutorial: p5-wallplotter
Dieses Tutorial erklärt, wie du das Repository lädst, den Beispiel-Sketch lokal startest, den Wandplotter per USB verbindest und was Marlin mit G-code eigentlich macht. Dieses Tutorial erklärt, wie du das Repository lädst, den Beispiel-Sketch in **VS Code** lokal startest, den Wandplotter per USB verbindest und was Marlin mit G-code eigentlich macht.
**Kurzüberblick:** Der Browser zeichnet mit p5.js. Die Library `WallPlotter.js` wandelt Linien in G-code um und schickt ihn über **Web Serial** an die Steuerung. Dort läuft **Marlin** mit Polargraph-Kinematik: Kartesische Koordinaten (`X`/`Y` in Millimetern) werden in zwei Riemenlängen übersetzt, die die Motoren auf- und abwickeln. **Kurzüberblick:** Der Browser zeichnet mit p5.js. Die Library WallPlotter.js wandelt Linien in G-code um und schickt ihn über **Web Serial** an die Steuerung. Dort läuft **Marlin** mit Polargraph-Kinematik: Kartesische Koordinaten (X/Y in Millimetern) werden in zwei Riemenlängen übersetzt, die die Motoren auf- und abwickeln.
Voraussetzungen: Voraussetzungen:
- **Chrome** oder **Edge** (Web Serial gibt es nicht in Firefox oder Safari) - **Chrome** (Web Serial; nicht Firefox oder Safari)
- **VS Code** (Editor für den Workshop)
- Python 3 - Python 3
- USB-Kabel zum Plotter - USB-Kabel zum Plotter
- Plotter mit Stromversorgung (Steuerung + Motoren) - Plotter mit Stromversorgung (Steuerung + Motoren)
Web Serial funktioniert **nicht** über `file://`. Die Seite muss von `http://localhost` oder über HTTPS kommen. ## 1.1 Repository herunterladen
---
## 1. Repository herunterladen
Das Projekt liegt hier: Das Projekt liegt hier:
[https://git.collectivemedia.space/martin/p5-wallplotter](https://git.collectivemedia.space/martin/p5-wallplotter) [https://git.collectivemedia.space/martin/p5-wallplotter](https://git.collectivemedia.space/martin/p5-wallplotter)
Auf der Projektseite **Code → Download ZIP** (oder direkt [archive/main.zip](https://git.collectivemedia.space/martin/p5-wallplotter/archive/main.zip)). Archiv entpacken und in den Ordner `p5-wallplotter` wechseln. Auf der Projektseite **Code → Download ZIP** (oder direkt [archive/main.zip](https://git.collectivemedia.space/martin/p5-wallplotter/archive/main.zip)). Archiv entpacken. Den Ordner *p5-wallplotter* an einem sinnvollen Ort ablegen. Wir arbeiten innerhalb von diesem Ordner weiter.
### Was im Ordner liegt ### Was im Ordner liegt
- *lib/WallPlotter.js* — p5-Library: Serial, G-code, Preview, Tasten
- *lib/README.md* — Kurzüberblick + Beispiel
- *lib/doc.md* — API-Referenz
- *examples/zickzack/* — Einstiegsbeispiel
- *examples/rechteck/* — Rechteck zeichnen
- *hardware.md* — Motoren, Board, Geometrie
- *gcode.md* — G-code in diesem Setup
- *plotter.svg* — Geräte und Signalweg
- *tutorial.md* — diese Anleitung
## 1.2 Umgebung starten
Ziel: den Ordner in **VS Code** öffnen, dort den lokalen HTTP-Server starten, danach den Sketch in **Chrome** öffnen. Chrome bringt **Web Serial** mit — darüber redet der Browser mit dem Plotter über USB. Firefox und Safari können das nicht.
### Server in VS Code
1. VS Code: *Datei → Ordner öffnen* → Ordner *p5-wallplotter* wählen.
2. Terminal in der IDE öffnen: *Ansicht → Terminal*. Das Terminal startet **im Projektordner**.
3. Server im VS-Code-Terminal starten:
Linux und macOS:
``` ```
p5-wallplotter/
├── lib/
│ ├── WallPlotter.js # p5-Library: Serial, G-code, Preview, Tasten
│ ├── README.md # Kurzüberblick + Beispiel
│ └── doc.md # API-Referenz
├── examples/
│ ├── zickzack/ # Einstiegsbeispiel
│ └── rechteck/ # Rechteck zeichnen
├── hardware.md # Motoren, Board, Geometrie
├── gcode.md # G-code in diesem Setup
└── tutorial.md # diese Anleitung
```
Es gibt kein `npm install`. Der Sketch lädt p5.js vom CDN; der Rest sind statische Dateien.
---
## 2. Umgebung starten
Ziel: einen lokalen HTTP-Server im Projektordner, danach den Sketch in Chrome/Edge öffnen.
### Linux
Im Projektordner:
```bash
cd p5-wallplotter
python3 -m http.server 8080 python3 -m http.server 8080
``` ```
Browser: [http://localhost:8080/examples/zickzack/](http://localhost:8080/examples/zickzack/) Windows (Python von [python.org](https://www.python.org/downloads/), beim Setup **Add python.exe to PATH**):
Server mit `Ctrl+C` beenden.
Für den USB-Port braucht dein Benutzer die Serial-Gruppe. Nach der Änderung einmal ab- und anmelden:
```bash
# Debian / Ubuntu
sudo usermod -aG dialout "$USER"
# Arch / CachyOS
sudo usermod -aG uucp "$USER"
``` ```
Prüfen, ob das Board erscheint (SKR Mini E3 V3 als USB-CDC):
```bash
ls /dev/ttyACM* /dev/ttyUSB*
```
### Windows
1. Python von [python.org](https://www.python.org/downloads/) installieren. Beim Setup **Add python.exe to PATH** aktivieren.
2. PowerShell oder Eingabeaufforderung im Projektordner:
```powershell
cd p5-wallplotter
py -m http.server 8080 py -m http.server 8080
``` ```
Falls `py` unbekannt ist: `python -m http.server 8080`. Falls *py* unbekannt ist: *python -m http.server 8080*.
3. Chrome oder Edge: [http://localhost:8080/examples/zickzack/](http://localhost:8080/examples/zickzack/) 4. Die Seite in Chrome öffnen: [http://localhost:8080/examples/zickzack/](http://localhost:8080/examples/zickzack/)
Das Board erscheint in der Geräte-Manager-Ansicht unter **Anschlüsse (COM & LPT)** als virtueller COM-Port. Web Serial zeigt denselben Port im Verbindungsdialog. Extra-Treiber sind bei der SKR Mini E3 V3 meist nicht nötig; wenn der Port fehlt, USB-Kabel wechseln (manche Kabel sind nur Ladekabel) und den STM32 Virtual COM Port / WinUSB-Pfad prüfen. Server kann mit *Ctrl+C* im VS-Code-Terminal beendet werden.
### macOS ### USB je nach System
Im Terminal: **macOS.** Der Port heisst typischerweise */dev/cu.usbmodem…*. macOS hält einen Serial-Port oft fest, wenn der Tab nicht sauber schliesst. Seite neu laden oder den Tab schliessen, bevor du erneut verbindest — die Library gibt den Port beim Schliessen frei. Falls *python3* fehlt: *xcode-select --install* oder Python über Homebrew.
```bash **Linux.** Für den USB-Port braucht dein Benutzer die Serial-Gruppe. Nach der Änderung einmal ab- und anmelden.
cd p5-wallplotter
python3 -m http.server 8080
```
**Chrome** oder **Edge** öffnen (nicht Safari): [http://localhost:8080/examples/zickzack/](http://localhost:8080/examples/zickzack/) **Windows.** Das Board erscheint im Geräte-Manager unter **Anschlüsse (COM & LPT)** als virtueller COM-Port. Web Serial zeigt denselben Port im Verbindungsdialog. Extra-Treiber sind bei der SKR Mini E3 V3 meist nicht nötig; wenn der Port fehlt, USB-Kabel wechseln (manche Kabel sind nur Ladekabel) und den STM32 Virtual COM Port / WinUSB-Pfad prüfen.
Der Port heisst typischerweise `/dev/cu.usbmodem…`. macOS hält einen Serial-Port oft fest, wenn der Tab nicht sauber schliesst. Seite neu laden oder den Tab schliessen, bevor du erneut verbindest — die Library gibt den Port beim Schliessen frei.
Falls `python3` fehlt: `xcode-select --install` oder Python über Homebrew.
### Browser-Check ### Browser-Check
- Chrome oder Edge, aktuelle Version - **Chrome**, aktuelle Version (Web Serial)
- `chrome://flags` nicht nötig; Web Serial ist standardmässig an - *chrome://flags* nicht nötig; Web Serial ist standardmässig an
- Beim ersten Verbinden erscheint ein Port-Picker — das ist gewollt (Browser-Sicherheitsmodell) - Beim ersten Verbinden erscheint ein Port-Picker — das ist gewollt (Browser-Sicherheitsmodell)
--- ## 2.1 Eigenen Sketch schreiben
## 3. Plotter anschliessen Unter *examples/* liegen die Vorlagen (siehe Abschnitt 1): *zickzack/* und *rechteck/*. Lege einen Ordner *sketches/* an (neben *examples/*). Dupliziere ein Beispiel **hinein** und benenne es um, zum Beispiel *sketches/mein-sketch/*
```
p5-wallplotter/
├── examples/ Vorlagen, nicht ändern
│ ├── zickzack/
│ └── rechteck/
└── sketches/ deine Sketches
└── mein-sketch/
├── index.html
└── sketch.js hier drawPlot anpassen
```
Dann öffne darin *sketch.js*. Den Rest (HTML, Buttons, Verbinden) kannst du lassen. Ändere die Inhalte in der Funktion *drawPlot*: dort steht, was gezeichnet wird.
Chrome, gleicher Server: [http://localhost:8080/sketches/mein-sketch/](http://localhost:8080/sketches/mein-sketch/) (Ordnername in der URL anpassen). Nach dem Speichern die Seite neu laden (*F5*). Der Python-Server muss nicht neu starten.
### Was *sketch.js* macht
*setup* läuft einmal beim Start:
- *createCanvas* — Zeichenfläche im Browser, so gross wie das Fenster
- *parent('sketch-host')* — Canvas in die HTML-Seite einhängen
- *new WallPlotter({ … })* — Verbindung zum Plotter. *penUpGcode* / *penDownGcode* sind die Servo-Winkel für Stift hoch und runter
- *setSpeed(3000, 1500)* — Tempo in mm/min: zuerst leer fahren (Travel), dann zeichnen (Draw)
- *enableKeyInputs* — Tastatur: C, H, Pfeile, …
- *enablePlotButtons* — Buttons Connect, Plot, Test, Abort
*plot* und *test* rufen dieselbe Zeichnung auf. *false* = G-code an den Plotter, *true* = nur Vorschau im Browser.
### Die Zeichnung: *drawPlot*
**Koordinatensystem.** In p5 liegt *(0, 0)* sonst oben links, und *y* wächst nach **unten**. Hier ist es anders: Ursprung ist die **Blattmitte** (Home, *x = 0*, *y = 0*), Werte in **Millimetern**. *y* nach **oben** ist positiv, *x* nach rechts ebenfalls.
```
+y (oben)
|
|
-x ------0------ +x
|
|
-y (unten)
```
```
async function drawPlot(testOnly) {
const s = 200; // halbe Seitenlänge: 200 mm → Quadrat 400 × 400 mm
wp.startPath(); // Queue leeren, neu beginnen
wp.move(-s, -s); // Stift hoch, zur unteren linken Ecke (−200, −200)
wp.draw(s, -s); // Stift runter, nach rechts unten (200, −200)
wp.draw(s, s); // nach rechts oben (200, 200)
wp.draw(-s, s); // nach links oben (−200, 200)
wp.draw(-s, -s); // zurück zur ersten Ecke, Rechteck zu
wp.move(0, 0); // Stift hoch, zurück zur Blattmitte
await wp.runPath(testOnly); // Queue abarbeiten: Test oder Plot
}
```
*move* und *draw* fahren noch nicht. Sie hängen nur Punkte an die Queue (Array). *runPath* arbeitet sie der Reihe nach ab.
- *wp.move(x, y)* — Stift oben, hinfahren, keine Linie
- *wp.draw(x, y)* — Stift unten, Linie vom letzten Punkt hierhin
- *wp.wait(ms)* — Pause
- *wp.startPath()* — Queue leeren
- *wp.runPath(true)* — Vorschau (Test)
- *wp.runPath(false)* — wirklich plotten
Mehr Befehle: [lib/doc.md](https://git.collectivemedia.space/martin/p5-wallplotter/src/branch/main/lib/doc.md).
## 3.1 Plotter anschliessen
### Hardware-Reihenfolge ### Hardware-Reihenfolge
@@ -126,193 +156,125 @@ Falls `python3` fehlt: `xcode-select --install` oder Python über Homebrew.
5. **Connect** klicken oder Taste **C**. 5. **Connect** klicken oder Taste **C**.
6. Im Dialog den Serial-Port der Steuerung wählen. 6. Im Dialog den Serial-Port der Steuerung wählen.
### Home setzen (wichtig) ## 3.2 Home setzen (wichtig)
Marlin kennt nach dem Einschalten **keine** absolute Wand-Position. Du sagst ihm, wo „jetzt“ der Ursprung ist. Die Pfeiltasten funktionieren erst nach dem ersten Home: Marlin kennt nach dem Einschalten **keine** absolute Wand-Position. Du sagst ihm, wo „jetzt“ der Ursprung (Blattmitte = Koordinate 0/0) ist. Die Pfeiltasten funktionieren erst nach dem ersten Home:
1. Taste **H** — Home an der **aktuellen** Gondelposition (`G92 X0 Y0`). 1. Einschalten oder nach neuer Verbindung
2. Mit den Pfeiltasten (Stift oben) zur **Mitte des Blattes** fahren. 2. Taste **H** — Home an der **aktuellen** Gondelposition.
3. Nochmals **H** — das setzt den Ursprung auf die Blattmitte. 3. Mit den Pfeiltasten zur **Mitte des Blattes** fahren.
4. Nochmals **H** — das setzt den Ursprung auf die Blattmitte.
Ohne Home bleibt Plot gesperrt. Ohne Home bleibt Plot gesperrt.
### Not-Aus ### Not-Aus
Diese Firmware hat keinen `EMERGENCY_PARSER`. Ein Stopp im Browser bricht nur das *weitere Senden* ab; die bereits laufende `G1`-Fahrt läuft weiter. Für einen harten Stopp: **Strom trennen**. Diese Firmware hat keinen *EMERGENCY_PARSER*. Ein Stopp im Browser bricht nur das *weitere Senden* ab; die bereits laufende *G1*-Fahrt läuft weiter. Für einen harten Stopp: **Strom trennen**.
--- ## 3.3 Bedienung
## 4. Der Plotter und seine Teile ### Tasten, Buttons, HUD
Shortcuts:
- **C** — Verbinden / trennen (Port-Dialog)
- **H** — Hier ist Home (*G92 X0 Y0*): zuerst an der Gondel, nach dem Joggen nochmals in der Blattmitte
- **T** — Test (Vorschau)
- **A** — Abort: Test auf Start zurück
- **P** — Stift umschalten
- **0** — Nach (0, 0) fahren (Stift oben)
- **← → ↑ ↓** — Joggen, Stift oben
- **Shift + Pfeile** — Joggen, Stift unten
- **+** / **−** — Schrittweite 1–100 mm
- **S** — Plot stoppen / pausieren
Das HUD (unten und als Overlay) zeigt Verbindung, Home, Position, Stift, Serial-Log.
## 4. Polargraph und Marlin
### Der Plotter und seine Teile
Der Wandplotter ist ein **Polargraph**: eine Gondel hängt an zwei Riemen, die oben links und rechts über Motoren und Umlenkrollen laufen. Es gibt keine X/Y-Schienen. Position entsteht allein aus den beiden Riemenlängen. Der Wandplotter ist ein **Polargraph**: eine Gondel hängt an zwei Riemen, die oben links und rechts über Motoren und Umlenkrollen laufen. Es gibt keine X/Y-Schienen. Position entsteht allein aus den beiden Riemenlängen.
Baugruppen, Anschlüsse und Geometrie: [hardware.md](hardware.md). Baugruppen, Anschlüsse und Geometrie: [hardware.md](https://git.collectivemedia.space/martin/p5-wallplotter/src/branch/main/hardware.md).
--- ### Was Marlin mit G-code macht
## 5. Was Marlin mit G-code macht **Marlin** ist die Firmware auf dem Controller: ein Programm, das dauernd auf der Platine läuft. Der Name klingt wie *Merlin* der Zauberer — gemeint ist aber der Speerfisch, und die Software für 3D-Drucker (hier mit Polargraph-Kinematik): [marlinfw.org](https://marlinfw.org/). Die Platine heisst **SKR** (BigTreeTech SKR Mini E3 V3): die Steuerkarte am Plotter, mit USB, Motorbuchsen und 24-V-Netzteil. Marlin nimmt G-code entgegen, rechnet ihn in Motorbewegungen um und gibt *ok* zurück. Ohne Marlin sind USB und Motoren nur Hardware — niemand versteht die Zeichenbefehle.
### G-code in einem Satz ### G-code in einem Satz
G-code ist die Kommandosprache von CNC- und 3D-Druck-Steuerungen: eine Textzeile, ein Befehl. Der Host (hier der Browser) schreibt Zeilen auf die serielle Schnittstelle. Marlin parst sie, plant Bewegung oder I/O, und antwortet mit `ok`, wenn der Befehl angenommen ist. G-code ist die Kommandosprache von CNC- und 3D-Druck-Steuerungen: eine Textzeile, ein Befehl. Der Host (hier der Browser) schreibt Zeilen auf die serielle Schnittstelle. Marlin liest sie, plant Bewegung oder I/O, und antwortet mit *ok*, wenn der Befehl angenommen ist.
Beispiel einer Fahrt mit Stift unten: Beispiel einer Fahrt mit Stift unten:
```gcode ```
M280 P0 S30 ; Servo 0 auf 30° → Stift aufs Papier M280 P0 S30 ; Servo 0 auf 30° → Stift aufs Papier
G1 X120 Y-40 F2500 ; Gerade zu (120, −40) mm mit 2500 mm/min G1 X120 Y-40 F2500 ; Gerade zu (120, −40) mm mit 2500 mm/min
M400 ; warten, bis die Bewegung wirklich fertig ist M400 ; warten, bis die Bewegung wirklich fertig ist
``` ```
### Der Weg einer Linie ### Der Weg einer Linie
``` *wp.draw* legt die Linie nur in eine Queue (ein Array von Punkten). Der Plotter hat noch nichts gehört.
p5 sketch
wp.draw(x, y) → nur in eine Warteschlange legen
wp.runPath(false) → nacheinander senden
│
▼
USB Serial, 115200 Baud
│
▼
Marlin auf der SKR
1. Zeile lesen, parsen
2. G1: Ziel in mm merken
3. inverse Kinematik: (X, Y) → (linke Riemenlänge, rechte Riemenlänge)
4. Planner: Beschleunigung, Segmentierung
5. Stepper-ISR: X- und Y-Treiber takten
6. "ok" zurück an den Browser
│
▼
WallPlotter wartet auf ok, schickt M400, erst dann den nächsten Punkt
```
**Test** (`runPath(true)`) bleibt im Browser: gleiche Pfadliste, nur Animation. Es geht kein G-code raus. **Plot** (*runPath(false)*) nimmt die Queue Eintrag für Eintrag und schickt sie per USB an Marlin. Marlin liest das Ziel in Millimetern, rechnet daraus zwei Riemenlängen, bewegt die Motoren und antwortet mit *ok*: Befehl angekommen, nächster Eintrag bitte. Die Library wartet auf *ok* und auf *M400* (Gondel steht wirklich still), erst dann kommt die nächste Linie. Sonst würde der Browser schon weiterreden, während die Gondel noch unterwegs ist.
**Test** (*runPath(true)*): gleiche Queue, nur Animation im Browser. Marlin bekommt keinen G-code. Gut zum Üben, bevor der Stift die Wand kennenlernt.
### Inverse Kinematik (Polargraph) ### Inverse Kinematik (Polargraph)
Ein kartesischer Drucker fährt Schlitten auf Schienen. Der Polargraph fährt **Riemenlängen**. Marlin rechnet für jedes Ziel: Ein kartesischer Drucker fährt Schlitten auf Schienen. Der Polargraph fährt **Riemenlängen**. Jeder Riemen ist die Hypotenuse vom Umlenkpunkt zur Gondel — Pythagoras, in Marlin *HYPOT*:
``` ```
linke Länge = hypot( X − linke_Rolle , Y − Rollenlinie ) L_links = sqrt( (X − X_links)^2 + (Y − Y_rollen)^2 )
rechte Länge = hypot( rechte_Rolle − X , Y − Rollenlinie ) L_rechts = sqrt( (X_rechts − X)^2 + (Y − Y_rollen)^2 )
``` ```
In der Firmware entspricht das `HYPOT` in `inverse_kinematics`: X-Stepper = linke Länge, Y-Stepper = rechte Länge. Eine waagerechte Linie auf der Wand ist für die Motoren **keine** gleichmässige Rotation — beide Motoren ändern die Länge gekoppelt. Marlin zerlegt die kartesische Gerade intern in kurze Segmente (`DEFAULT_SEGMENTS_PER_SECOND`), damit die Kurve der Riemelängen der Geraden auf der Wand folgt. Auf dieser Maschine sitzen die Rollen bei *X_links = −830*, *X_rechts = 830*, *Y_rollen = 800* (Millimeter, Ursprung Blattmitte):
Deshalb muss Home zur realen Geometrie passen. Falsches `G92` (z. B. Ursprung zu weit unter den Rollen) erzeugt den typischen **Bogen nach oben** bzw. eine konische Zeichnung: Marlin glaubt, die Gondel sei woanders, als sie hängt. ```
L_links = sqrt( (X + 830)^2 + (Y − 800)^2 )
L_rechts = sqrt( (830 − X)^2 + (Y − 800)^2 )
```
Befehlsliste: [gcode.md](gcode.md). Beispiel Home *(0, 0)*: beide Riemen *sqrt(830² + 800²) ≈ 1153 mm*. Weiter unten oder zur Seite wird ein Riemen länger, der andere kürzer.
In der Firmware ist der X-Stepper die linke Länge, der Y-Stepper die rechte. Eine waagerechte Linie auf der Wand ist für die Motoren **keine** gleichmässige Rotation — beide Längen ändern sich gekoppelt. Marlin zerlegt die kartesische Gerade intern in kurze Segmente (*DEFAULT_SEGMENTS_PER_SECOND*), damit die Kurve der Riemenlängen der Geraden auf der Wand folgt.
Deshalb muss Home zur realen Geometrie passen. Falscher Ursprung (z. B. Ursprung zu weit unter den Rollen) erzeugt den typischen **Bogen nach oben** bzw. eine konische Zeichnung: Marlin glaubt, die Gondel sei woanders, als sie hängt.
Befehlsliste: [gcode.md](https://git.collectivemedia.space/martin/p5-wallplotter/src/branch/main/gcode.md).
### Was Marlin nicht ist ### Was Marlin nicht ist
Marlin ist keine Zeichen-App. Es weiss nichts von p5 oder deinem Sketch. Es bekommt nur Koordinaten und Servo-Winkel. Pfadplanung, Stiftlogik und die Vorschau liegen in `WallPlotter.js`. Die Firmware ist der Bewegungscontroller: parsen, in Riemenlängen umrechnen, Schritte erzeugen, `ok` sagen. Marlin ist keine Zeichen-App. Es weiss nichts von p5 oder deinem Sketch. Es bekommt nur Koordinaten und Servo-Winkel. Pfadplanung, Stiftlogik und die Vorschau liegen in *WallPlotter.js*. Die Firmware ist der Bewegungscontroller: parsen, in Riemenlängen umrechnen, Schritte erzeugen, *ok* sagen.
--- ## 5. Fehlerbehebung
## 6. Eigenen Sketch schreiben - **Kein Port-Dialog, oder „Serial not supported“** — **Chrome**, nicht Firefox oder Safari. Seite über *http://localhost:…* öffnen, nicht als Datei.
- **Port erscheint nicht** — Strom der Steuerung an? Daten-USB-Kabel? Linux: Gruppe *dialout* oder *uucp*, neu einloggen; im Terminal nach *ttyACM* oder *ttyUSB* schauen. Windows: Geräte-Manager → COM-Port. macOS: Tab schliessen und neu öffnen (Port oft blockiert).
Kopiere den Ordner `examples/zickzack/` und öffne darin `sketch.js`. Den Rest (HTML, Buttons, Verbinden) kannst du lassen. Du änderst nur `drawPlot`: dort steht, was gezeichnet wird. - **Connect, aber keine Bewegung** — Netzteil an der SKR einstecken (typisch 24 V). USB allein speist die Motoren nicht. Dann **H** drücken. Linker Motor an Buchse **X**, rechter an **Y**.
- **„Home not set“** — Zuerst **H**, dann zur Blattmitte joggen, nochmals **H**.
Koordinaten sind **Millimeter**, Ursprung = Home, **Y nach oben**. - **Stift bleibt oben oder kratzt** — *penUpGcode* / *penDownGcode* an den Servo anpassen (*S*-Winkel). Mit **P** testen.
- **Zeichnung bogenförmig / konisch** — Home falsch: Gondel 80 cm unter den Rollen, dann *G92 X0 Y0*. Rollenabstand 1660 mm.
Fertiges Rechteck: [examples/rechteck/](examples/rechteck/). Im Browser `http://localhost:8080/examples/rechteck/` öffnen. Nach dem Speichern die Seite neu laden (`F5`). - **Motoren heiss, Schritte verloren** — *M906 X1000 Y1000*. Riemen nicht zu straff. Tempo senken.
- **Plot hängt bei „waiting for Marlin ok“** — USB wackelt, Board reset, oder lange *G1* ohne *ok*. **Resume** nach Timeout; sonst USB neu stecken und Home neu setzen.
### Was `sketch.js` macht - **Stop im Browser, Gondel fährt weiter** — Normal: laufendes *G1* wird nicht abgebrochen. Strom weg für Not-Aus.
- **macOS verbindet nur einmal** — Seite komplett schliessen, Port freigeben, erneut **C**.
`setup()` läuft einmal beim Start: - **Beispiel sieht alt aus nach einem Update** — Browser-Cache: hart neu laden (*Ctrl+Shift+R*) oder *?v=* an *WallPlotter.js* in der HTML erhöhen.
- `createCanvas` — Zeichenfläche im Browser, so gross wie das Fenster
- `parent('sketch-host')` — Canvas in die HTML-Seite einhängen
- `new WallPlotter({ … })` — Verbindung zum Plotter. `penUpGcode` / `penDownGcode` sind die Servo-Winkel für Stift hoch und runter
- `setSpeed(3000, 1500)` — Tempo in mm/min: zuerst leer fahren (Travel), dann zeichnen (Draw)
- `enableKeyInputs()` — Tastatur: C, H, Pfeile, …
- `enablePlotButtons()` — Buttons Connect, Plot, Test, Abort
`draw()` läuft dauernd (p5-Schleife): Hintergrund und HUD (`showInfos`).
`plot()` und `test()` rufen dieselbe Zeichnung auf. `false` = G-code an den Plotter, `true` = nur Vorschau im Browser.
### Die Zeichnung: `drawPlot`
```javascript
async function drawPlot(testOnly) {
const s = 200; // halbe Seitenlänge: 200 mm → Quadrat 400 × 400 mm
wp.startPath(); // alte Linie löschen, neue Liste beginnen
wp.move(-s, -s); // Stift hoch, zur unteren linken Ecke (−200, −200)
wp.draw(s, -s); // Stift runter, nach rechts unten (200, −200)
wp.draw(s, s); // nach rechts oben (200, 200)
wp.draw(-s, s); // nach links oben (−200, 200)
wp.draw(-s, -s); // zurück zur ersten Ecke, Rechteck zu
wp.move(0, 0); // Stift hoch, zurück zur Blattmitte
await wp.runPath(testOnly); // Liste abarbeiten: Test oder Plot
}
```
`move` und `draw` fahren noch nicht. Sie merken sich nur Punkte. `runPath` arbeitet die Liste der Reihe nach ab.
| Aufruf | Stift | Wohin |
|--------|--------|--------|
| `wp.move(x, y)` | oben | hinfahren, keine Linie |
| `wp.draw(x, y)` | unten | Linie vom letzten Punkt hierhin |
| `wp.wait(ms)` | — | Pause |
| `wp.startPath()` | — | Liste leeren |
| `wp.runPath(true)` | — | Vorschau (Test) |
| `wp.runPath(false)` | — | wirklich plotten |
Mehr Befehle: [lib/doc.md](lib/doc.md).
---
## 7. Tasten, Buttons, HUD
| Taste | Aktion |
|-------|--------|
| **C** | Verbinden / trennen (Port-Dialog) |
| **H** | Hier ist Home (`G92 X0 Y0`) — zuerst an der Gondel, nach dem Joggen nochmals in der Blattmitte |
| **T** | Test (Vorschau) |
| **A** | Abort: Test auf Start zurück |
| **P** | Stift umschalten |
| **0** | Nach `(0, 0)` fahren (Pen-up) |
| **← → ↑ ↓** | Joggen, Stift oben |
| **Shift + Pfeile** | Joggen, Stift unten |
| **+** / **-** | Schrittweite 1–100 mm |
| **S** | Plot stoppen / pausieren |
Buttons: **Connect**, **Plot** (wird Pause / Resume), **Test** (wird Stop), **Abort**.
Das HUD (unten und als Overlay) zeigt Verbindung, Home, Position, Stift, Serial-Log. Das Log rechts unten kannst du markieren — nützlich, wenn Marlin nicht mit `ok` antwortet.
---
## 8. Fehlerbehebung
| Symptom | Was prüfen |
|---------|------------|
| Kein Port-Dialog, oder „Serial not supported“ | Chrome oder Edge, nicht Firefox/Safari. Seite über `http://localhost:…`, nicht als Datei öffnen. |
| Port erscheint nicht | Strom der Steuerung an? Daten-USB-Kabel? Linux: Gruppe `dialout` oder `uucp`, neu einloggen. `ls /dev/ttyACM*`. Windows: Geräte-Manager → COM-Port. macOS: Tab schliessen und neu öffnen (Port oft blockiert). |
| Connect, aber keine Bewegung | Netzteil an der SKR einstecken (typisch 24 V). USB allein speist die Motoren nicht. Dann **H** drücken. Linker Motor an Buchse **X**, rechter an **Y**. |
| „Home not set“ | Zuerst **H**, dann zur Blattmitte joggen, nochmals **H**.
| Stift bleibt oben oder kratzt | `penUpGcode` / `penDownGcode` an den Servo anpassen (`S`-Winkel). Mit **P** testen. |
| Zeichnung bogenförmig / konisch | Home falsch: Gondel 80 cm unter den Rollen, dann `G92 X0 Y0`. Rollenabstand 1660 mm. |
| Motoren heiss, Schritte verloren | `M906 X1000 Y1000`. Riemen nicht zu straff. Tempo senken. |
| Plot hängt bei „waiting for Marlin ok“ | USB wackelt, Board reset, oder lange `G1` ohne `ok`. **Resume** nach Timeout; sonst USB neu stecken und Home neu setzen. |
| Stop im Browser, Gondel fährt weiter | Normal: laufendes `G1` wird nicht abgebrochen. Strom weg für Not-Aus. |
| macOS verbindet nur einmal | Seite komplett schliessen, Port freigeben, erneut **C**. |
| Beispiel sieht alt aus nach einem Update | Browser-Cache: hart neu laden (`Ctrl+Shift+R`) oder `?v=` an `WallPlotter.js` in der HTML erhöhen. |
Serial-Log im HUD lesen: Marlin-Fehler stehen dort als Text, nicht als Browser-Alert. Serial-Log im HUD lesen: Marlin-Fehler stehen dort als Text, nicht als Browser-Alert.
--- ## 6. Mini-Ablauf zum Merken
## 9. Mini-Ablauf zum Merken 1. ZIP herunterladen, in VS Code den Ordner *p5-wallplotter* öffnen.
2. Terminal in VS Code öffnen, *python3 -m http.server 8080* (Windows: *py -m http.server 8080*).
3. Chrome: [http://localhost:8080/examples/zickzack/](http://localhost:8080/examples/zickzack/).
4. Eigenen Ordner unter *sketches/* anlegen, *drawPlot* anpassen.
5. Plotter mit Strom und USB verbinden.
6. **C** → Port wählen.
7. **H** (aktuelle Position), zur Blattmitte joggen, nochmals **H**.
8. **Test**, dann **Plot**.
1. ZIP herunterladen und entpacken. API-Referenz: [lib/doc.md](https://git.collectivemedia.space/martin/p5-wallplotter/src/branch/main/lib/doc.md). G-code: [gcode.md](https://git.collectivemedia.space/martin/p5-wallplotter/src/branch/main/gcode.md).
2. `python3 -m http.server 8080` (Windows: `py -m http.server 8080`).
3. Chrome/Edge: `http://localhost:8080/examples/zickzack/`.
4. Plotter mit Strom und USB verbinden.
5. **C** → Port wählen.
6. **H** (aktuelle Position), zur Blattmitte joggen, nochmals **H**.
7. **Test**, dann **Plot**.
API-Referenz: [lib/doc.md](lib/doc.md). G-code: [gcode.md](gcode.md).